如何恢复存储在 .ctors 部分中的信息?

逆向工程 拆卸 x86 C++ 小精灵
2021-06-10 07:58:24

测试在 x86、32 位 Linux 上进行。我正在使用g++4.6.3 和objdump2.22

这是我正在处理的一个简单的 C++ 代码:

#include <iostream>

using namespace std;

main()
{
    cout << "Hello World!" << endl;
    return 0;
}

当我使用以下命令将其编译为汇编代码时:

gcc -S hello.cc

我可以ctors下面hello.s找到一个部分

.section    .ctors,"aw",@progbits
.align 4
.long   _GLOBAL__sub_I_main
.weakref    _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
.weakref    _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
.weakref    _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
.weakref    _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
.weakref    _ZL20__gthrw_pthread_joinmPPv,pthread_join
.weakref    _ZL21__gthrw_pthread_equalmm,pthread_equal
.weakref    _ZL20__gthrw_pthread_selfv,pthread_self
.weakref    _ZL22__gthrw_pthread_detachm,pthread_detach
.weakref    _ZL22__gthrw_pthread_cancelm,pthread_cancel
.weakref    _ZL19__gthrw_sched_yieldv,sched_yield
.weakref    _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
.weakref    _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
.weakref    _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
.weakref    _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
.weakref    _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
.weakref    _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
.weakref    _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
.weakref    _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
.weakref    _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
.weakref    _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
.weakref    _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
.weakref    _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
.weakref    _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
.weakref    _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
.weakref    _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
.weakref    _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy

但是,当我组装 asm 代码时,生成一个 exe 文件并使用objdump生成该ctors部分的包含如下所示:

objdump -Dr -j .ctors hellocpp

我所能得到的就是这样:

hellocpp:     file format elf32-i386


Disassembly of section .ctors:

08049efc <__CTOR_LIST__>:
 8049efc:   ff                      (bad)  
 8049efd:   ff                      (bad)  
 8049efe:   ff                      (bad)  
 8049eff:   ff 00                   incl   (%eax)

08049f00 <__CTOR_END__>:
 8049f00:   00 00                   add    %al,(%eax)
 ...

目前我正在尝试恢复一些从c++程序编译的 ELF 二进制文件的内容..

所以我想知道是否有办法让内容ctors等于g++产生的内容?

更新:

非常感谢@Igor 的帮助。但我仍然被困在 从 ELF 二进制文件中寻找class's constructordestructor信息。

在演化class定义时,g++ 会在该.ctors部分生成这些信息

    .globl  _ZN8ComputerC1Ev
    .set    _ZN8ComputerC1Ev,_ZN8ComputerC2Ev
    .globl  _ZN8ComputerD1Ev
    .set    _ZN8ComputerD1Ev,_ZN8ComputerD2Ev

通常_ZN8ComputerC2Ev是类的构造函数的名称,而_ZN8ComputerD2Ev是其析构函数的名称。

但是,我只是在objdump转储.ctors.init_array部分中找不到相应的信息..我也试过.eh_frameand gcc_except_table,但转储的信息海量..我无法弄清楚这些信息的含义..

有人可以给我指导吗?

2个回答

.ctors部分是一个以 -1 (0xFFFFFFFF) 结尾的指针列表,因此反汇编它没有意义。如果将字节重新排列为数据,则会得到:

__CTOR_LIST__: .long 0xffffffff
__CTOR_END__:  .long 0x00000000

因此,无论出于何种原因,生成的 exe 实际上并不使用该.ctors部分。我怀疑链接器将指针放入新样式.init_array部分。请注意,它也是一个指针列表,而不是代码。

编辑

.ctors.init_array部分仅包含所谓的构造函数-需要在启动时执行的功能,在之前main()本身。这些通常是编译器生成的函数,执行全局对象(如建筑cincout等等),或其他启动相关的任务。实际上,您可以使用 将自己的函数添加到该列表中__attribute__((constructor))

什么是走也有一般的C ++类的构造-没有必要执行那些启动。当您构造特定类的对象时,它们将被调用 - 例如,通过声明一个变量或调用operator new.

正如 Igor 所说,该.ctors部分是一个函数指针列表,以标记值0xffffffff. 要查看其内容,只需执行

$ objdump -s -j.ctors bar.so

但是您的程序集文件仅包含弱符号。这些是其他库中的外部函数,并在运行时加载它们的库时调用。

例如,把它放在一个文件中bar.cpp

class Foo {
public:
  int i;

  Foo(int n) : i(n) {
  }
};

Foo global_foo(123);

编译

$ g++ -shared -fPIC bar.cpp -obar.so

.init_array部分的内容

$ objdump -s -j.init_array bar.so

bar.so:     file format elf64-x86-64

Contents of section .init_array:
 200820 ad060000 00000000                    ........        

那里有一个函数指针,0xad060000 00000000但是您必须更改其字节顺序,例如使用 Python:

>>> import struct
>>> import binascii
>>> binascii.hexlify(struct.pack("<Q", 0xad06000000000000))
'00000000000006ad'

现在列出该地址的所有符号和 grep:

$ objdump -C --syms bar.so | grep 00000000000006ad
00000000000006ad l     F .text  0000000000000015
  [... on above line ...] global constructors keyed to bar.cpp

为它拆解,

$ objdump -C -d bar.so

显示

00000000000006ad <global constructors keyed to bar.cpp>:
 6ad:   55                      push   %rbp
 6ae:   48 89 e5                mov    %rsp,%rbp
 6b1:   be ff ff 00 00          mov    $0xffff,%esi
 6b6:   bf 01 00 00 00          mov    $0x1,%edi
 6bb:   e8 ba ff ff ff          callq  67a <__static_initialization_and_destruction_0(int, int)>
 6c0:   c9                      leaveq 
 6c1:   c3                      retq   

跳转到__static_initialization_and_destruction_0(int, int)

000000000000067a <__static_initialization_and_destruction_0(int, int)>:
 67a:   55                      push   %rbp
 67b:   48 89 e5                mov    %rsp,%rbp
 67e:   48 83 ec 10             sub    $0x10,%rsp
 682:   89 7d fc                mov    %edi,-0x4(%rbp)
 685:   89 75 f8                mov    %esi,-0x8(%rbp)
 688:   83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
 68c:   75 1d                   jne    6ab <__static_initialization_and_destruction_0(int, int)+0x31>
 68e:   81 7d f8 ff ff 00 00    cmpl   $0xffff,-0x8(%rbp)
 695:   75 14                   jne    6ab <__static_initialization_and_destruction_0(int, int)+0x31>
 697:   be 7b 00 00 00          mov    $0x7b,%esi
 69c:   48 8b 05 9d 03 20 00    mov    0x20039d(%rip),%rax        # 200a40 <_DYNAMIC+0x1e8>
 6a3:   48 89 c7                mov    %rax,%rdi
 6a6:   e8 f5 fe ff ff          callq  5a0 <Foo::Foo(int)@plt>
 6ab:   c9                      leaveq 
 6ac:   c3                      retq   

它将 123( 0x7b) 放在堆栈上并调用Foo::Foo(int).