获取PE段地址

逆向工程 部件 聚乙烯 部分 地址
2021-06-19 10:24:00

我研究了一篇关于木马的分析论文,有以下流水线:

.text:004010D0 Get_PE_section_address proc near        
.text:004010D0
.text:004010D0 arg_0           = dword ptr  4
.text:004010D0
.text:004010D0                 mov     ecx, [esp+arg_0]
.text:004010D4                 mov     eax, [ecx+3Ch]
.text:004010D7                 movzx   edx, word ptr [eax+ecx+14h]
.text:004010DC                 add     eax, ecx
.text:004010DE                 movzx   ecx, word ptr [eax+6]
.text:004010E2                 push    ebx
.text:004010E3                 add     edx, eax
.text:004010E5                 push    esi
.text:004010E6                 lea     esi, [ecx+ecx*4]
.text:004010E9                 lea     eax, [edx+esi*8-38h]
.text:004010ED                 xor     edx, edx
.text:004010EF                 test    ecx, ecx
.text:004010F1                 jbe     short loc_401102
.text:004010F3

.text:004010F3 loc_4010F3:                            
.text:004010F3                 mov     bl, [eax+5]
.text:004010F6                 test    bl, bl
.text:004010F8                 jz      short loc_401104
.text:004010FA                 inc     edx
.text:004010FB                 sub     eax, 28h
.text:004010FE                 cmp     edx, ecx
.text:00401100                 jb      short loc_4010F3
.text:00401102

.text:00401102 loc_401102:                             
.text:00401102                 xor     eax, eax
.text:00401104

.text:00401104 loc_401104:                             
.text:00401104                 pop     esi
.text:00401105                 pop     ebx
.text:00401106                 retn

.text:00401106 Get_PE_section_address endp

如您所见,论文的所有者将该函数重命名为 Get_PE_section_address。我花了很多时间来理解他/她为什么会这样调用,因为我无法理解为什么这段代码会检索到 PE 部分地址。

所以,我的问题是,是否有人可以告诉我哪些行告诉我们这是关于 PE 部分地址的。

PS:我尝试使用偏移量,但成功率很低。我唯一能说的是 ebx = 指向恶意可执行文件的指针和 arg_0 = 0 在搜索了一段时间 PE 文件格式文件后,我发现第二行

   mov     eax, [ecx+3Ch]

给了我 exe 头文件的地址。

最好的祝福,

2个回答

调用此函数Get_PE_section_address相当具有误导性,因为它通常不会获取“PE 节的地址”,而是用于获取第一个节的节表条目的虚拟地址,其中 a0x00作为其节名的第 5 个字节,从倒数第二个部分开始向后迭代部分。

例如,如果部分名称是:

  • 秒10
  • 秒20
  • 秒30
  • 秒40

其中0是空字节,那么此函数将返回 sec3 的部分表条目的虚拟地址0,因为它是第一个满足空字节要求的部分,当向后遍历各部分时,从倒数第二个开始部分。

我已经为你评论了反汇编:

.text:004010D0 Get_PE_section_address proc near        
.text:004010D0
.text:004010D0 arg_0           = dword ptr  4
.text:004010D0
.text:004010D0                 mov     ecx, [esp+arg_0]             ; ecx = arg_0 = beginning of PE file mapped into memory
.text:004010D4                 mov     eax, [ecx+3Ch]               ; eax = ((IMAGE_DOS_HEADER*)ecx)->e_lfanew
.text:004010D7                 movzx   edx, word ptr [eax+ecx+14h]  ; edx = ((IMAGE_NT_HEADERS*)(eax+ecx))->FileHeader.SizeOfOptionalHeader
.text:004010DC                 add     eax, ecx                     ; eax = (IMAGE_NT_HEADERS*)(eax+ecx)
.text:004010DE                 movzx   ecx, word ptr [eax+6]        ; ecx = ((IMAGE_NT_HEADERS*)eax)->FileHeader.NumberOfSections
.text:004010E2                 push    ebx                          ; save ebx on stack
.text:004010E3                 add     edx, eax                     ; edx = (pointer to PE header) + SizeOfOptionalHeader
.text:004010E5                 push    esi                          ; save esi on stack
.text:004010E6                 lea     esi, [ecx+ecx*4]             ; esi = NumberOfSections * 5
.text:004010E9                 lea     eax, [edx+esi*8-38h]         ; eax = (pointer to PE header) + SizeOfOptionalHeader + NumberOfSections * 0x28 - 0x38 =
                                                                    ;       &(((IMAGE_SECTION_HEADERS*)(edx + 0x18))[NumberOfSections - 2])
.text:004010ED                 xor     edx, edx                     ; edx = section counter = 0
.text:004010EF                 test    ecx, ecx                     ; if NumberOfSections <= 0
.text:004010F1                 jbe     short loc_401102             ; then goto loc_401102
.text:004010F3

.text:004010F3 loc_4010F3:                            
.text:004010F3                 mov     bl, [eax+5]                  ; bl = ((IMAGE_SECTION_HEADERS*)eax)->Name[5]
.text:004010F6                 test    bl, bl                       ; if ((IMAGE_SECTION_HEADERS*)eax)->Name[5] == 0
.text:004010F8                 jz      short loc_401104             ; then goto loc_401104
.text:004010FA                 inc     edx                          ; edx = (section counter)++
.text:004010FB                 sub     eax, 28h                     ; ((IMAGE_SECTION_HEADERS*)eax)-- 
.text:004010FE                 cmp     edx, ecx                     ; if section counter < NumberOfSections
.text:00401100                 jb      short loc_4010F3             ; then goto loc_4010F3
.text:00401102

.text:00401102 loc_401102:                             
.text:00401102                 xor     eax, eax                     ; eax = 0
.text:00401104

.text:00401104 loc_401104:                             
.text:00401104                 pop     esi                          ; restore esi
.text:00401105                 pop     ebx                          ; restore ebx
.text:00401106                 retn                                 ; return eax = IMAGE_SECTION_HEADERS* with a 0x00 at Name[5]

.text:00401106 Get_PE_section_address endp

请注意,汇编代码中存在一个错误:它向后遍历NumberOfSections节表条目,但由于它从倒数第二个节开始,因此最终可能会读到第一个节的节表条目的开头。

所以,我的问题是,是否有人可以告诉我哪些行告诉我们这是关于 PE 部分地址的。

通常,您会知道函数的输入参数(在本例中,是内存中 PE 模块的开头),这将帮助您单步执行汇编代码。但是,即使你得到的这段代码几乎没有上下文(就像我们在这里一样),它正在处理 PE 部分的一个很好的提示是以下行:

.text:004010DE                 movzx   ecx, word ptr [eax+6]        ; ecx = ((IMAGE_NT_HEADERS*)eax)->FileHeader.NumberOfSections

每当您看到 时movzx ..., word ptr [...+6],它强烈暗示正在读取的节数,因为在结构中没有其他word从 6 个字节开始的通用大小的值。

如果部分名称 [5] 为空终止符 \x0,则看起来像是一些用于检索部分地址的自制未经测试的代码会检索第二部分的地址

MOV     ECX, DWORD PTR SS:[ESP+4]          ; ecx = 1000000 base of image
MOV     EAX, DWORD PTR DS:[ECX+3C]         ; eax = 0xe8 ptrtopeheader
MOVZX   EDX, WORD PTR DS:[EAX+ECX+14]      ; edx = e0 = sizeofoptional header
ADD     EAX, ECX                           ; eax = peheader
MOVZX   ECX, WORD PTR DS:[EAX+6]           ; ecx - no of sections
PUSH    EBX
ADD     EDX, EAX                           ; edx = 10001c8 ?
PUSH    ESI
LEA     ESI, DWORD PTR DS:[ECX+ECX*4]      ; esi = 14 ?
LEA     EAX, DWORD PTR DS:[EDX+ESI*8-38]   ; ? esi * 8 - 38 + edx ; 1000230
XOR     EDX, EDX
TEST    ECX, ECX
JBE     SHORT 01063F5E                     ; jmp ot if no sections
MOV     BL, BYTE PTR DS:[EAX+5]            ; checks for 0 in section name ?
TEST    BL, BL                             ; some kind of homemade crap
JE      SHORT 01063F6C                     ; jmps out of proc with address of second section if section name was sya .rsrc
INC     EDX                                ; loop checking section 1
SUB     EAX, 28                            ;  size of section header
CMP     EDX, ECX                           ; counting no of sections
JB      SHORT 01063F05                     ; <loop>