Win32病毒入门,PE结构分析

by admin on 2019年3月11日

在 PE文件头的 IMAGE_OPTIONAL_HEADE大切诺基 结构中的 DataDirectory(数据目录表)
的第①个分子就是指向输入表的。各种被链接进来的 DLL文件都分别对应叁个IMAGE_IMPORT_DESC卡宴IPTO中华V (简称IID) 数组结构。

在 PE文件头的 IMAGE_OPTIONAL_HEADE君越 结构中的 DataDirectory(数据目录表)
的首个分子就是指向输入表的。每一种被链接进来的 DLL文件都分别对应多个IMAGE_IMPORT_DESC景逸SUVIPTO奥德赛 (简称IID) 数组结构。

【pker / CVC.GB】 
5、关于FASM 
———– 
上面大家用FASM来编排我们的首先个程序。大家得以编写制定如下代码: 
format  PE GUI 4.0 
entry   __start 
section ‘.text’ code    readable executable 
    __start: 
Win32病毒入门,PE结构分析。            ret 
咱俩把这一个文件存为test.asm并编写翻译它: 
fasm test.asm test.exe 
未曾别的烦人的参数,很有益,不是么? 😛 
笔者们先来看一下以此顺序的布局。第1句是format提醒字,它内定了程序的体系,PE表示自己 
们编写的是1个PE文件,前面包车型地铁GUI提醒编写翻译器大家将选用Windows图形界面。假使要编写制定一 
个控制台应用程序则足以钦点为CONSOLE。纵然要写2个基石驱动,能够钦点为NATIVE,表示 
不须要子系统帮忙。最后的4.0点名了子系统的版本号(还记得前边的MajorSubsystemVersion 
和MinorSubsystemVersion么?)。 
下边一行钦点了先后的输入为__start。 
section提醒字表示大家要起始三个新节。大家的顺序唯有三个节,即代码节,大家将其命名 
为.text,并钦赐节属性为只读(readable)和可进行(executable)。 
而后就是大家的代码了,大家唯有用一条ret指令回到系统,那时堆栈里的回来地址为Exit- 
Thread,所以程序直接退出。 
下边运营它,程序只是简短地淡出了,我们中标地用FASM编写了二个先后!大家早已迈出了 
第壹步,上边要让大家的次第能够做点什么。大家想要调用1个API,大家要如何做吗?让 
作者们再来充充电吧 😀 

初稿链接地址:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

5.1、导入表 
———– 
作者们编辑如下代码并用TASM编写翻译: 

; tasm32 /ml /m5 test.asm 
; tlink32 -Tpe -aa test.obj ,,, import32.lib 

        ideal 
        p586 
        model   use32 flat 
extrn   MessageBoxA:near 
        dataseg 
str_hello       db      ‘Hello’,0 
        codeseg 
__start: 
        push    0 
        push    offset str_hello 
        push    offset str_hello 
        push    0 
        call    MessageBoxA 
        ret 
        end     __start 
上边大家用w32dasm反汇编,获得: 
:00401000   6A00                    push    00000000 
:00401002   6800204000              push    00402000 
:00401007   6800204000              push    00402000 
:0040100C   6A00                    push    00000000 
:0040100E   E801000000              call    00401014 
:00401013   C3                      ret 
:00401014   FF2530304000            jmp     dword ptr [00403030] 
能够见到代码中的call MessageBoxA被翻译成了call 00401014,在这么些地点处是多个跳转 
指令jmp dword ptr [00403030],大家能够规定在地址00403030处存放的是MessageBoxA的 
实在地址。 
实则这一个地点是放在PE文件的导入表中的。上面我们继承大家的PE文件的学习。大家先来看 
须臾间导入表的结构。导入表是由一多如牛毛的IMAGE_IMPORT_DESC大切诺基IPTO奥迪Q5结构构成的。结构的个 
数由文件引用的DLL个数控制,文件引用了不怎么个DLL就有微微个IMAGE_IMPORT_DESCRIPTOR 
组织,最后还有叁个全为零的IMAGE_IMPORT_DESCOdysseyIPTOSportage作为完成。 
typedef struct _IMAGE_IMPORT_DESCRIPTOR { 
    union { 
        DWORD   Characteristics; 
        DWORD   OriginalFirstThunk; 
    }; 
    DWORD   TimeDateStamp; 
    DWORD   ForwarderChain; 
    DWORD   Name; 
    DWORD   FirstThunk; 
} IMAGE_IMPORT_DESCRIPTOR; 
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR; 
Name字段是3个XC90VA,钦点了引入的DLL的名字。 
OriginalFirstThunk和FirstThunk在四个PE没有加载到内部存款和储蓄器中的时候是一致的,都以指向一 
个IMAGE_THUNK_DATA结构数组。最后以1个情节为0的构造结束。其实这么些组织正是二个双 
字。那个组织很有意思,因为在分裂的时候那么些布局意味着着不一样的意义。当以此双字的参天 
位为1时,表示函数是以序号的办法导入的;当最高位为0时,表示函数是以名称方式导入的, 
那是以此双字是三个劲客VA,指向一个IMAGE_IMPORT_BY_NAME结构,这么些布局用来钦定导入函数 
名称。 
typedef struct _IMAGE_IMPORT_BY_NAME { 
    WORD    Hint; 
    BYTE    Name[1]; 
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; 
Hint字段表示三个序号,然而因为是按名称导入,所以那些序号一般为零。 
Name字段是函数的称号。 
上面我们用一张图来表达这一个复杂的历程。若是一个PE引用了kernel32.dll中的LoadLibraryA 
和GetProcAddress,还有二个按序号导入的函数800一千2h。 
IMAGE_IMPORT_DESCRIPTOR                                  IMAGE_IMPORT_BY_NAME 
+——————–+   +–> +——————+     +———————–+ 
| OriginalFirstThunk | –+    | IMAGE_THUNK_DATA | –> | 023B |  ExitProcess   | <–+ 
+——————–+        +——————+     +———————–+    | 
|   TimeDataStamp    |        | IMAGE_THUNK_DATA | –> | 0191 | GetProcAddress | <–+–+ 
+——————–+        +——————+     +———————–+    |  | 
|   ForwarderChain   |        |     80010002h    |                                  |  | 
+——————–+        +——————+    +—> +——————+    |  | 
|        Name        | –+    |         0        |    |     | IMAGE_THUNK_DATA | —+  | 
+——————–+   |    +——————+    |     +——————+       | 
|     FirstThunk     |-+ |                            |     | IMAGE_THUNK_DATA | ——+ 
+——————–+ | |    +——————+    |     +——————+ 
                       | +–> |   kernel32.dll   |    |     |     80010002h    | 
                       |      +——————+    |     +——————+ 
                       |                              |     |         0        | 
                       +——————————+     +——————+ 
还记得前边大家说过在三个PE没有被加载到内部存款和储蓄器中的时候IMAGE_IMPORT_DESCRIPTOR中的 
OriginalFirstThunk和FirstThunk是同样的,那么为何Windows要占用三个字段呢?其实 
是那样的,在PE文件被PE加载器加载到内部存款和储蓄器中的时候那么些加载器会自动把FirstThunk的值替 
换为API函数的真正入口,也便是非凡前边jmp的真的地址,而OriginalFirstThunk只不过是 
用来反向查找函数名而已。 
好了,又讲了那般多是要做什么样吗?你立时就会面到。下边大家就来组织大家的导入表。 
咱俩用来下代码来早先大家的引入节: 
section ‘.idata’ import data    readable 
section提示字表示大家要起来1个新节。.idata是以此新节的称谓。import data表示那是 
3个引入节。readable代表这一个节的节属性是只读的。 
万一大家的先后只须求引入user32.dll中的MessageBoxA函数,那么大家的引入节唯有三个 
叙述那些dll的IMAGE_IMPORT_DESC福特ExplorerIPTOMurano和2个全0的结构。考虑如下代码: 
    dd      0                   ; 大家并不须求OriginalFirstThunk 
    dd      0                   ; 大家也不须要管那个日子戳 
    dd      0                   ; 我们也不关心这些链 
    dd      RVA usr_dll         ; 指向我们的DLL名称的LacrosseVA 
    dd      RVA usr_thunk       ; 指向大家的IMAGE_IMPORT_BY_NAME数组的RVA 
                                ; 注意那个数组也是以0末尾的 
    dd      0,0,0,0,0           ; 结束标志 
地点用到了一个TucsonVA伪指令,它内定的地方在编写翻译时被机关写为相应的TucsonVA值。上面定义我们 
要引入的动态链接库的名字,那是二个以0尾声的字符串: 
    usr_dll     db      ‘user32.dll’,0 
还有我们的IMAGE_THUNK_DATA: 
    usr_thunk: 
        MessageBox      dd      RVA __imp_MessageBox 
                        dd      0                   ; 甘休标志 
上面的__imp_MessageBox在编写翻译时出于前边有途观VA提示,所以表示是IMAGE_Win32病毒入门,PE结构分析。IMPORT_BY_NAME的 
GL450VA。上面大家定义那几个协会: 
    __imp_MessageBox    dw      0                   ; 大家不按序号导入,所以能够 
                                                    ; 简单地置0 
                        db      ‘MessageBoxA’,0     ; 导入的函数名 
好了,我们完结了导入表的确立。上边大家来看一个完完全全的程序,看看多个一体化的FASM程序 
是何其的上佳 😛 
format  PE GUI 4.0 
entry   __start 

PE文件定义

PE 文件(”Portable executable”,
可移植的可执行文件)文件格式,是微软Windows NT,
中Win3贰 、Win32s中的可实施的二进制的文件格式。 包罗:.exe, .dll, .sys,
.com, .ocs. PE文件最器重的多少个要素:

1.磁盘上的可执行文件和它被映射到windows内部存款和储蓄器之后的格式万分相似。

2.对此Win32 来讲,
模块中多选拔的拥有代码、数据、财富、导入表、和其余需求的模块数据结构都在1个连连的内部存储器块中。由此,只供给理解PE
Loader把可执行文件映射到了内部存款和储蓄器的怎么地点(基址),通过作为印象的一有些指针,就能够找到那些模块的有着分裂的块。

PE文件总览:

home88一必发 1

在那么些IID数组中,并不曾建议有多少个项(正是从未明了指明有稍许个链接文件),但它说到底是以二个全为NULL(0)
的 IID 作为实现的评释。

在那个IID数组中,并没有建议有稍许个项(就是从未领会指明有个别许个链接文件),但它说到底是以贰个全为NULL(0)
的 IID 作为达成的申明。


; data section… 

section ‘.data’ data    readable 
    pszText         db      ‘Hello, FASM world!’,0 
    pszCaption      db      ‘Flat Assembler’,0 

放另一张图:

home88一必发 2

再放一张:

home88一必发 3

下边只摘录比较主要的字段:

上面只摘录相比较关键的字段:


; code section… 

section ‘.text’ code    readable executable 
    __start: 
            push    0 
            push    pszCaption 
            push    pszText 
            push    0 
            call    [MessageBox] 
            push    0 
            call    [ExitProcess] 

1. DOS Header: (size:64byte)

_IMAGE_DOS_HEADER结构体:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

结构体中有三个根本的数码成员。第②个为e_magic,那些必须为MZ,即0x5LevinD。另三个生死攸关的数额成员是最终一个成员e_lfanew,这一个成员的值为IMAGE_NT_HEADERS的偏移。其中,*e_lfanew这一个字段的值:  
PE Header 在磁盘文件中相对于文本开端的舞狮地址.

实例截图:

home88一必发 4

2.     PE Header: (size: 248bytes)

IMAGE_NT_HEADERS 紧接在DOS Stub之后,位置有e_lfanew所指

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature; //4 bytes PE文件头标志:(e_lfanew)->‘PE’
    IMAGE_FILE_HEADER FileHeader;//20 bytes PE文件物理分布的信息
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;//224bytes PE文件逻辑分布的信息
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

home88一必发 5

PE Header 总览

home88一必发 6

IMAGE_NT_HEADE卡宴S结构体成员解析:

OriginalFirstThunk

它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function
name 的地址。

OriginalFirstThunk

它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function
name 的地址。


; import section… 

section ‘.idata’ import data    readable 
    ; image import descriptor 
    dd      0,0,0,RVA usr_dll,RVA usr_thunk 
    dd      0,0,0,RVA krnl_dll,RVA krnl_thunk 
    dd      0,0,0,0,0 
    ; dll name 
    usr_dll     db      ‘user32.dll’,0 
    krnl_dll    db      ‘kernel32.dll’,0 
    ; image thunk data 
    usr_thunk: 
        MessageBox      dd      RVA __imp_MessageBox 
                        dd      0 
    krnl_thunk: 
        ExitProcess     dd      RVA __imp_ExitProcess 
                        dd      0 
    ; image import by name 
    __imp_MessageBox    dw      0 
                        db      ‘MessageBoxA’,0 
    __imp_ExitProcess   dw      0 
                        db      ‘ExitProcess’,0 
观察此间作者相信我们都对FASM那个编写翻译器有了贰个开头的认识,也必将有广大读者会说:“ 
如此辛劳啊,干呢要用那个编写翻译器呢?”。是的,只怕上面的代码看起来很复杂,编写起来 
也很辛劳,但FASM的叁个便宜在于大家得以更主动地决定大家转变的PE文件结构,同时能对 
PE文件有更理性的认识。可是每种人的气味不一,嘿嘿,大概上面的理由还不够说服各位读 
者,没涉及,选择一款适合您的编写翻译器吧,它们都如出一辙出彩 😛 

2.1.Signature: (4 bytes)

home88一必发 7

Name

它象征DLL
名称的相对虚地址(译注:相对一个用null作为实现符的ASCII字符串的二个奥迪Q7VA,该字符串是该导入DLL文件的称号。如:KE奥迪Q7NEL32.DLL)。

Name

它表示DLL
名称的对峙虚地址(译注:相对一个用null作为完成符的ASCII字符串的二个RubiconVA,该字符串是该导入DLL文件的名号。如:KEWranglerNEL32.DLL)。

5.2、导出表 
———– 
透过导入表的上学,小编想各位读者已经对PE文件的就学进程有了上下一心认识和方法,所以上面 
关于导出表的一节自小编将加速局地进程。“朋友们注意啊!!! @#$%$%&#^”  😀 
在导出表的原初地点是3个IMAGE_EXPORT_DIRECTO汉兰达Y结构,但与引入表差别的是在导出表中 
只有二个这些布局。下边我们来看一下那一个结构的概念: 
typedef struct _IMAGE_EXPORT_DIRECTORY { 
    DWORD   Characteristics; 
    DWORD   TimeDateStamp; 
    WORD    MajorVersion; 
    WORD    MinorVersion; 
    DWORD   Name; 
    DWORD   Base; 
    DWORD   NumberOfFunctions; 
    DWORD   NumberOfNames; 
    DWORD   AddressOfFunctions;     // RVA from base of image 
    DWORD   AddressOfNames;         // RVA from base of image 
    DWORD   AddressOfNameOrdinals;  // RVA from base of image 
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; 
Characteristics、MajorVersion和MinorVersion不使用,一般为0。 
TimeDataStamp是光阴戳。 
Name字段是一个LacrosseVA值,它指向了那一个模块的原来名称。这几个名称与编写翻译后的文本名无关。 
Base字段钦赐了导出函数序号的胚胎序号。即便Base的值为n,那么导出函数入口地址表中 
的首先个函数的序号便是n,第②个正是n+1… 
NumberOfFunctions钦定了导出函数的总数。 
NumberOfNames内定了按名称导出的函数的总数。按序号导出的函数总数正是其一值与所在 
总数NumberOfFunctions的差。 
AddressOfFunctions字段是一个奥德赛VA值,指向3个HighlanderVA数组,数组中的各个安德拉VA均指向2个导 
出函数的输入地址。数组的项数等于NumberOfFuntions。 
AddressOfNames字段是1个GL450VA值,同样指向三个大切诺基VA数组,数组中的种种双字是贰个针对函 
数名字符串的大切诺基VA。数组的项数等于NumberOfNames。 
AddressOfNameOrdinals字段是三个HavalVA值,它指向二个篇幅组,注意那里不再是双字了!! 
这些数组起着很重点的作用,它的项数等于NumberOfNames,并与AddressOfNames指向的数组 
各样对应。其每一种门类的值代表了那些函数在进口地址表中索引。未来我们来看二个例证, 
若是七个导出函数Foo在导出入口地址表中居于第m个职位,大家摸索Ordinal数组的第m项, 
一经那一个值为x,大家把那一个值与导出序号的初阶值Base的值n相加获得的值正是函数在入口 
地点表中索引。 
下图表示了导出表的组织和上述进程: 
+———————–+         +—————–+ 
|    Characteristics    |  +—-> | ‘dlltest.dll’,0 | 
+———————–+  |      +—————–+ 
|     TimeDataStamp     |  | 
+———————–+  |  +-> +—————–+ 
|      MajorVersion     |  |  | 0 | 函数输入地址TiguanVA | ==> 函数Foo,序号n+0    <–+ 
+———————–+  |  |   +—————–+                            | 
|      MinorVersion     |  |  |   |       …       |                            | 
+———————–+  |  |   +—————–+                            | 
home88一必发,|         Name          | -+  | x | 函数入口地址LacrosseVA | ==> 按序号导出,序号为n+x  | 
+———————–+     |   +—————–+                            | 
|    Base(借使值为n)  |     |   |       …       |                            | 
+———————–+     |   +—————–+                            | 
|   NumberOfFunctions   |     |                                                  | 
+———————–+     |  +-> +—–+     +———-+      +—–+ <-+   | 
|     NumberOfNames     |     |  |   | RVA | –> | ‘_foo’,0 | <==> |  0  | –+—+ 
+———————–+     |  |   +—–+     +———-+      +—–+   | 
|   AddressOfFunctions  | —-+  |   | … |                       | … |   | 
+———————–+        |   +—–+                       +—–+   | 
|     AddressOfNames    | ——-+                                           | 
+———————–+                                                    | 
| AddressOfNameOrdinals | —————————————————+ 
+———————–+ 
好了,上边大家来看构键大家的导出表。假如大家按名称导出1个函数_foo。大家以如下代 
码开始: 
section ‘.edata’ export data    readable 
接着是IMAGE_EXPORT_DIRECTORY结构: 
    dd      0                   ; Characteristics 
    dd      0                   ; TimeDataStamp 
    dw      0                   ; MajorVersion 
    dw      0                   ; MinorVersion 
    dd      RVA dll_name        ; RVA,指向DLL名称 
    dd      0                   ; 起首序号为0 
    dd      1                   ; 只导出1个函数 
    dd      1                   ; 这些函数是按名称方式导出的 
    dd      RVA addr_tab        ; 帕杰罗VA,指向导出函数进口地址表 
    dd      RVA name_tab        ; PRADOVA,指向函数名称地址表 
    dd      RVA ordinal_tab     ; EvoqueVA,指向函数索引表 
下边大家定义DLL名称: 
    dll_name    db      ‘foo.dll’,0     ; DLL名称,编写翻译的文书名能够与它分化 
接下去是导出函数入口地址表和函数名称地址表,大家要导出二个叫_foo的函数: 
    addr_tab    dd      RVA _foo        ; 函数进口地址 
    name_tab    dd      RVA func_name 
    func_name   db      ‘_foo’,0        ; 函数名称 
末尾是函数索引表: 
    ordinal_tab     dw      0           ; 只有3个按名称导出函数,序号为0 
下边大家看一个全部的次序: 
format  PE GUI 4.0 DLL at 76000000h 
entry   _dll_entry 

2.2.IMAGE_FILE_HEADER(20 bytes)

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                //运行平台
    WORD    NumberOfSections;        //文件区块数目
    DWORD   TimeDateStamp;            //文件创建日期和时间
    DWORD   PointerToSymbolTable;    //指向符号表(主要用于调试)
    DWORD   NumberOfSymbols;        //符号表中符号个数
    WORD    SizeOfOptionalHeader;        //IMAGE_OPTIONAL_HEADER32 结构大小
    WORD    Characteristics;            //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

IMAGE_FILE_HEADE哈弗结构体成员解析:

FirstThunk

它富含由IMAGE_THUNK_DATA定义的 first
thunk数组的虚地址,通过loader用函数虚地址开端化thunk。

在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function
names的thunks。

 

上面来分解下OriginalFirstThunk和FirstThunk。就个人知道而言:

1.
在文书中时,他们都各自指向一个昂CoraVA地址。那个地方转换成文件中,分别对应多个以
IMAGE_THUNK_DATA 为因素的的数组,那多少个数组是以二个填写为 0
的IMAGE_THUNK_DATA作为达成标识符。固然她们那几个表地方区别,但实际上内容是一模一样的。此时,种种IMAGE_THUNK_DATA 成分指向的是四个笔录了函数名和相呼应的DLL文件名的
IMAGE_IMPORT_BY_NAME结构体。

  1. 干什么会有八个一样的数组呢?是有原因的:

OriginalFirstThunk 指向的数组经常称为  hint-name table,即 HNT ,他在 PE
加载到内部存款和储蓄器中时被封存了下来且永远不会被涂改。不过在 Windows 加载过 PE
到内部存款和储蓄器之后,Windows 会重写 FirstThunk
所指向的数组成分中的内容,使得数组中各类 IMAGE_THUNK_DATA
不再代表针对带有函数描述的 IMAGE_THUNK_DATA
元素,而是向来指向了函数地址。此时,FirstThunk
所指向的数组就叫做输入地址表(Import Address Table ,即平日说的
IAT)。

重写前:

home88一必发 8

重写后:

 home88一必发 9

(以上两张图纸来源于:)

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE  指向一个转向者字符串的RVA
        DWORD Function;             // PDWORD 被输入的函数的内存地址
         DWORD Ordinal;              // 被输入的 API 的序数值
         DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME   指向 IMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

根据 _IMAGE_THUNK_DATA32 所指虚拟地址转到文件地方能够获得实质上的
_IMAGE_IMPORT_BY_NAME 数据

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD   Hint;     // 序号 

    CHAR   Name[1];  // 实际上是一个可变长的以0为结尾的字符串

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

比如有先后:

home88一必发 10

文字版:

#include <windows.h>
int WINAPI WinMain(_In_ HINSTANCE hInstance, 
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd)
{
    MessageBoxA(0, "hello", "my message", MB_OK);
    SetWindowTextA(0, "Si Wang");

    return 0;
}

此程序采纳了三个 Windows API : MessageBoxA 和 SetWindowTextA

编写翻译得到程序(为简化表明,区段地点由软件总括出):

home88一必发 11

home88一必发 12

大家试着找出 MessageBoxA。首先分析 PE 头文件,找到导出表在文件中的地点:

home88一必发 13

输入表地方在 .rdata 区段内, 0x2264 – 0x两千 = 0x0264
获得偏移量。加上文件地方 0x0E00 获得实在文件偏移量(0x0E00 + 0x264 =
0x1064):0x1064。

接下去查看 0x1064 处:

home88一必发 14

能够获得多个 DLL 的描述,最后四个_IMAGE_IMPORT_DESC汉兰达IPTORubicon以0填充表示截至:

那么一旦一个个查看各种DLL对应的数量就能找到,可是在此之前本人把全体的数目都看了下,在首先个DLL中

依据第②个DLL描述的 OriginalFirstThunk 的 0x2350
转换能够领会,_IMAGE_THUNK_DATA32 在文书的 0x1150处,FirstThunk
指向的数额一致:

home88一必发 15

于是就拿走了文件中的 MessageBoxA 的音信。

最终,在内部存款和储蓄器中 FirstThunk 所指地方上的_IMAGE_THUNK_DATA32 数组被
Windows 加载后被重写后就成了遗闻中的 IAT ,Import Address
Table,输入地址表。使用 OllyDbg 查看运行时情形:

home88一必发 16

FirstThunk

它包涵由IMAGE_THUNK_DATA定义的 first
thunk数组的虚地址,通过loader用函数虚地址开端化thunk。

在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function
names的thunks。

 

上面来表达下OriginalFirstThunk和FirstThunk。就个人通晓而言:

1.
在文件中时,他们都分别针对2个大切诺基VA地址。这一个地址转换来文件中,分别对应七个以
IMAGE_THUNK_DATA 为因素的的数组,那八个数组是以三个填写为 0
的IMAGE_THUNK_DATA作为完结标识符。尽管她们那多个表地点分歧,但实质上内容是一模一样的。此时,每种IMAGE_THUNK_DATA 成分指向的是3个笔录了函数名和相呼应的DLL文件名的
IMAGE_IMPORT_BY_NAME结构体。

  1. 干什么会有七个一样的数组呢?是有来头的:

OriginalFirstThunk 指向的数组通常称为  hint-name table,即 HNT ,他在 PE
加载到内部存款和储蓄器中时被封存了下来且永远不会被涂改。不过在 Windows 加载过 PE
到内部存款和储蓄器之后,Windows 会重写 FirstThunk
所指向的数组成分中的内容,使得数组中种种 IMAGE_THUNK_DATA
不再代表针对带有函数描述的 IMAGE_THUNK_DATA
元素,而是平素指向了函数地址。此时,FirstThunk
所指向的数组就叫做输入地址表(Import Address Table ,即日常说的
IAT)。

重写前:

home88一必发 17

重写后:

 home88一必发 18

(以上两张图片来自:)

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE  指向一个转向者字符串的RVA
        DWORD Function;             // PDWORD 被输入的函数的内存地址
         DWORD Ordinal;              // 被输入的 API 的序数值
         DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME   指向 IMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

根据 _IMAGE_THUNK_DATA32 所指虚拟地址转到文件地方能够获取实际的
_IMAGE_IMPORT_BY_NAME 数据

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD   Hint;     // 序号 

    CHAR   Name[1];  // 实际上是一个可变长的以0为结尾的字符串

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

譬如井然有序:

home88一必发 19

文字版:

#include <windows.h>
int WINAPI WinMain(_In_ HINSTANCE hInstance, 
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd)
{
    MessageBoxA(0, "hello", "my message", MB_OK);
    SetWindowTextA(0, "Si Wang");

    return 0;
}

此程序行使了两个 Windows API : MessageBoxA 和 SetWindowTextA

编写翻译获得程序(为简化表明,区段地点由软件总计出):

home88一必发 20

home88一必发 21

咱俩试着找出 Message博克斯A。首先分析 PE 头文件,找到导出表在文件中的地方:

home88一必发 22

输入表地方在 .rdata 区段内, 0x2264 – 0x三千 = 0x0264
获得偏移量。加上文件地方 0x0E00 获得实在文件偏移量(0x0E00 + 0x264 =
0x1064):0x1064。

接下去翻看 0x1064 处:

home88一必发 23

能够赢得八个 DLL 的叙述,最终两个_IMAGE_IMPORT_DESCPRADOIPTO中华V以0填充表示甘休:

那就是说只要3个个查看各种DLL对应的数码就能找到,可是此前作者把富有的数额都看了下,在第⑥个DLL中

基于第③个DLL描述的 OriginalFirstThunk 的 0x2350
转换能够了然,_IMAGE_THUNK_DATA32 在文件的 0x1150处,FirstThunk
指向的多少一致:

home88一必发 24

于是乎就拿走了文本中的 Message博克斯A 的新闻。

最终,在内部存款和储蓄器中 FirstThunk 所指地点上的_IMAGE_THUNK_DATA32 数组被
Windows 加载后被重写后就成了传说中的 IAT ,Import Address
Table,输入地址表。使用 OllyDbg 查看运维时情状:

home88一必发 25


; data section… 

section ‘.data’ data    readable 
    pszText         db      ‘Hello, FASM world!’,0 
    pszCaption      db      ‘Flat Assembler’,0 

1). Machine 代表了CPU的花色  //运营平台
#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
………………….
#define IMAGE_FILE_MACHINE_CEE               0xC0EE


; code section… 

section ‘.text’ code    readable executable 
    _foo: 
            push    0 
            push    pszCaption 
            push    pszText 
            push    0 
            call    [MessageBox] 
            ret 
    _dll_entry: 
            xor     eax,eax 
            inc     eax 
            ret     0ch 

2)       NumberOfSections: 代表区块的数目,区块表紧跟在IMAGE_NT_HEADEHavalS前面, 区块表大约是二个链表结构,链表长度由NumberOfSection的数值决定.


; import section… 

section ‘.idata’ import data    readable 
    ; image import descriptor 
    dd      0,0,0,RVA usr_dll,RVA usr_thunk 
    dd      0,0,0,RVA krnl_dll,RVA krnl_thunk 
    dd      0,0,0,0,0 
    ; dll name 
    usr_dll     db      ‘user32.dll’,0 
    krnl_dll    db      ‘kernel32.dll’,0 
    ; image thunk data 
    usr_thunk: 
        MessageBox      dd      RVA __imp_MessageBox 
                        dd      0 
    krnl_thunk: 
        ExitProcess     dd      RVA __imp_ExitProcess 
                        dd      0 
    ; image import by name 
    __imp_MessageBox    dw      0 
                        db      ‘MessageBoxA’,0 
    __imp_ExitProcess   dw      0 
                        db      ‘ExitProcess’,0 

3)       TimeDataStamp: 评释文件的创制时间


; export section… 

section ‘.edata’ export data    readable 
    ; image export directory 
    dd      0,0,0,RVA dll_name,0,1,1 
    dd      RVA addr_tab 
    dd      RVA name_tab 
    dd      RVA ordinal_tab 
    ; dll name 
    dll_name        db      ‘foo.dll’,0 
    ; function address table 
    addr_tab        dd      RVA _foo 
    ; function name table 
    name_tab        dd      RVA ex_foo 
    ; export name table 
    ex_foo          db      ‘_foo’,0 
    ; ordinal table 
    ordinal_tab     dw      0 

4)       SizeOfOptionalHeader: 是IMAGE_NT_HEADE奥迪Q5S的另一个子布局IMAGE_OPTIONAL_HEADER的大小


; relocation section… 

section ‘.reloc’ fixups data     discardable 
先后的一开始用format钦点了PE和GUI,在子系统版本号的末端大家应用了DLL提示字,表示 
那是多少个DLL文件。最终还有3个at关键字,提醒了文本的image base。 
先后的尾声3个节是重定位节,对于重定位表小编不做过多解释,有趣味的读者能够参照其余 
书籍或文章。我们能够把刚刚的先后编译成一个DLL: 
fasm foo.asm foo.dll 
上面大家编辑一个测试程序检验程序的正确: 
#include <windows.h> 
int __stdcall WinMain (HINSTANCE,HINSTANCE,LPTSTR,int) 

    HMODULE hFoo=LoadLibrary (“foo.dll”); 
    FARPROC _foo=GetProcAddress (hFoo,”_foo”); 
    _foo (); 
    FreeLibrary (hFoo); 
    return 0; 

小编们把编译后的exe和刚刚的dll放在同三个索引下并运营,看看程序运行是还是不是正确 😛 

5)       Characteristics: 代表文件的属性EXE文件一般是0100h DLL文本一般是210Eh,两种本性可以用或运算同时负有
#define IMAGE_FILE_RELOCS_STRIPPED   0x0001 // 重定位信息被移除 
#define IMAGE_FILE_EXECUTABLE_IMAGE   0x0002 // 文件可执行 
#define IMAGE_FILE_LINE_NUMS_STRIPPED  0x0004 // 行号被移除 
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符号被移除 
……..
#define IMAGE_FILE_32BIT_MACHINE  0x0100 // 32位机器 
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的调试信息被移除 
………………….
#define IMAGE_FILE_SYSTEM       0x1000 // 系统文件 
#define IMAGE_FILE_DLL         0x2000 // 文件是一个dll 
#define IMAGE_FILE_UP_SYSTEM_ONLY    0x4000 // 文件只能运行在单处理器上

实例截图:

home88一必发 26

5.三 、强大的宏 
————- 
关于FASM,还有一个强劲的意义就是宏。大家对宏一定都不生疏,上边大家来探视在FASM中 
怎么定义宏。假诺大家要编写一个复制字符串的宏,个中源、指标串由ESI和EDI内定,大家 
可以: 
macro @copysz 

        local   next_char 
    next_char: 
        lodsb 
        stosb 
        or      al,al 
        jnz     next_char 

上面大家再来看多少个带参数的宏定义: 
macro @stosd _dword 

    mov     eax,_dword 
    stosd 

倘使大家要再三存入几个不等的双字大家得以总结地在概念宏时把参数用中括号括起来,比 
如: 
macro @stosd [_dword] 

    mov     eax,_dword 
    stosd 

那般当大家调用@stosd 1,2,3的时候,大家的代码被编写翻译成: 
mov     eax,1 
stosd 
mov     eax,2 
stosd 
mov     eax,3 
stosd 
对此那种多参数的宏,FASM提供了四个伪指令common、forward和reverse。他们把宏代码分 
成块并分别处理。上面作者分别来介绍: 
forward限定的块象征指令块对参数举行逐一处理,比如上边包车型大巴宏,假若把上边的代码定义在 
forward块中,大家得以获取相同的结果。对于forward块大家能够这么定义 
macro @stosd [_dword] 

    forward 
        mov     eax,_dword 
        stosd 

reverse和forward正好相反,表示指令块对参数举行反向处理。对于地点的一声令下块假若用 
reverse限定,那么大家的参数将被根据相反的一一存入内部存款和储蓄器。 
macro @stosd [_dword] 

    reverse 
        mov     eax,_dword 
        stosd 

那会儿当大家调用@stosd 1,2,3的时候,大家的代码被编写翻译成: 
mov     eax,3 
stosd 
mov     eax,2 
stosd 
mov     eax,1 
stosd 
common限定的块将仅被处理处理三遍。大家前天编写制定3个调用API的宏@invoke: 
macro @invoke _api,[_argv] 

    reverse 
        push    _argv 
    common 
        call    [_api] 

于今我们得以选用这么些宏来调用API了,比如: 
@invoke     MessageBox,0,pszText,pszCaption,0 
对于宏的使用我们就介绍这一个,更加多的代码能够参见作者的useful.inc(当中有诸多29A的宏, 
tnx 29a :P)

2.3. IMAGE_OPTIONAL_HEADER(224 bytes)

紧接IMAGE_FILE_HEADER之后,IMAGE_OPTIONAL_HEADE酷威的大小由IMAGE_FILE_HEADE奥迪Q3中尾数第二个成员(SizeOfOptionalHeader)钦命.
IMAGE_OPTIONAL_HEADE汉兰达结构体如下:

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;    //映像文件的状态
    BYTE    MajorLinkerVersion;    //连接器的主版本号
    BYTE    MinorLinkerVersion;    //连接器的次版本号
    DWORD   SizeOfCode;    //代码段的大小,如果有多个代码段则为总和
    DWORD   SizeOfInitializedData; //初始化数据段大小.如果多个则为总和
    DWORD   SizeOfUninitializedData;//未初始化数据段大小,.如果多个则为总和.bbs
    DWORD   AddressOfEntryPoint;    //PE文件入口地址的RAV:OEP = ImageBase + RAV
    DWORD   BaseOfCode;    //代码块起始地址的RVA
    DWORD   BaseOfData;//数据块的起始地址的RVA
    //
    // NT additional fields.
    //
    DWORD   ImageBase;    //可执行文件的基址ImageBase
    DWORD   SectionAlignment; //每一个块必须保证始于这个值的整数倍
    DWORD   FileAlignment; //对齐映射文件部分原始数据 2 or 512 or 64: 默认为512
    WORD    MajorOperatingSystemVersion;//要求的操作系统的主版本号
    WORD    MinorOperatingSystemVersion;//要求的操作系统的次版本号
    WORD    MajorImageVersion;//映像的主版本号
    WORD    MinorImageVersion;//映像的次版本号
    WORD    MajorSubsystemVersion;//子系统的主版本号
    WORD    MinorSubsystemVersion;//子系统的次版本号
    DWORD   Win32VersionValue;//保留值.必须为0
    DWORD   SizeOfImage;//映像文件的大小
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;//映像文件的校验和
    WORD    Subsystem;//运行此映像的字系统
    WORD    DllCharacteristics;//映像文件的DLL特征
    DWORD   SizeOfStackReserve;//堆栈保留字节. 0x100000
    DWORD   SizeOfStackCommit;//线程开始提交的初始堆栈大小
    DWORD   SizeOfHeapReserve;//为初始进程保留的虚拟内存总数
    DWORD   SizeOfHeapCommit;//进程开始提交初始虚拟内存的大小
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes; //0x10
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
//指向第一个IMAGE_DATA_DIRECTORY
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

_IMAGE_OPTIONAL_HEADELacrosse结构体实例截图以及成员解析:

home88一必发 27

1) Magic:三1拾贰人可执行文件来:0x010B

   陆十四人可执行文件来:0x020B

   0x107

2) SizeOfCode: 代码段的高低,借使有四个代码段则为总数

RAW:经过文件对齐处理后大小(PE文件中的大小)

home88一必发 28

home88一必发 29

3) SizeOfInitializedData: 早先化数据段大小.假使四个则为总数

home88一必发 30

home88一必发 31

4) ImageBase: 提出的装载地点

PE建议装载地点:

home88一必发 32

实则装载地方:

home88一必发 33

5) AddressOfEntryPoint: 程序执行的入口MuranoVA地址

OEP = ImageBase + (AddressOfEntryPoint)RVA

home88一必发 34

home88一必发 35

6) SectionAlignment:为内部存款和储蓄器中节的对齐大小,一般为0×0000一千

home88一必发 36

7) FileAlignment:为PE文件中节的对齐大小

home88一必发 37

8)  SizeofImage:影像文件的高低

home88一必发 38

home88一必发 39

9) DataDirectory为多少目录表数组,比较关键:共有1多个表项

Size = sizeof(_IMAGE_DATA_DIRECTORY) * 16

sizeof(_IMAGE_DATA_DIRECTORY) = 8 bytes

_IMAGE_DATA_DIRECTOEscortY结构体以及成员定义:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
// Directory Entries
#define IMAGE_DIRECTORY_ENTRY_EXPORT            0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY          4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC        5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG             6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT        7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE  7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR         8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS                 9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT     11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT              12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT    13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Import Table RVA & Size截图:

home88一必发 40

3. Section Header:

Section Header 数量:_IMAGE_FILE_HEADE奥迪Q5结构体中NumberOfSections成员。

Section Header 定位:紧跟在IMAGE_NT_HEADERS后面

结构体大小:40 bytes

_IMAGE_SECTION_HEADEXC90 结构体以及重点变量的概念:

#define IMAGE_SIZEOF_SHORT_NAME   8
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress;    //内存中偏移地址
    DWORD   SizeOfRawData;    //PE文件中对其之后的大小
    DWORD   PointerToRawData;//为PE块区在PE文件中偏移
    DWORD   PointerToRelocations;
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics;    //块区的属性:可读、可写..
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
#define IMAGE_SIZEOF_SECTION_HEADER          40

重要数据成员

1)      Name[IMAGE_SIZEOF_SHORT_NAME]:

8字节高低的NAME,
假设节区名称小于7个字节,则多余的用0填充,不然全体填充节名,末尾不保障有二个0,同样会被名字填写

2)      PointerToRawData:

为节区在PE文件中的偏移

3)      Characteristics:
为节区的习性,如可读、可写、可举办等

#define IMAGE_SCN_CNT_CODE       0x00000020  // Section contains code.
#define IMAGE_SCN_LNK_NRELOC_OVFL   0x01000000  // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE   0x02000000  // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED    0x04000000  // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED         0x08000000  // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED             0x10000000  // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE            0x20000000  // Section is executable.
#define IMAGE_SCN_MEM_READ              0x40000000  // Section is readable.

Section Header 实例截图:

home88一必发 41

4.  导入表

_IMAGE_IMPORT_DESC瑞虎IPTO奔驰G级 数据结构:(20 bytes)

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;     // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA)
    };
DWORD   TimeDateStamp;    
    DWORD   ForwarderChain;     // -1 if no forwarders
    DWORD   Name;            //dll 名称
    DWORD   FirstThunk;         //指向引入函数真实地址单元处的RVA  IAT
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

当中OriginalFirstThunk和FirstThunk相当相近,指向七个真相上同样的数组IMAGE_THUNK_DATA。

1) 稳定查找IMAGE_IMPORT_DESCRIPTO结构

home88一必发 42

插入另一图

home88一必发 43

A 获取引入表的PAJEROVA.也正是

data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
所指的值

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory

查看 IMAGE_DIRECTORY_ENTRY_IMPORT的值

home88一必发 44

&data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress =
0x0002D51C

&data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualSize= 0xA0

B. 查找导入表所在的区段:

if(RVA>=SECTION.VirtualAddress && RVA<SECTION.Misc.VirtualSize)

{

     //处于该节

}

else

{

     //不在该节中

}

home88一必发 45

C.
找到引入表所在的节后就足以用该节的VirtualAddress和PointerToRawData三个域鲜明引入表在文件中的偏移量:WranglerVA
–△k

VirtualAddress = 0x00026000

PointerToRawData = 0x00025000

△k = VirtualAddress – PointerToRawData = 0x1000H

Address = 0x0002D51C – △k = 0x0002C51C

接下来定位到文件偏移处:

home88一必发 46

黑影部分是IID的始末, IID的尺寸为20h,
阴影部分存在链种种IID,最终3个为0000000, 说明此PE文件唯有几个IID,
对应九个dll。

0xA0  = 160 bytes = 7 * 20bytes +20bytes(空白)

截图

home88一必发 47

第多个变量的地点奥迪Q3VA:0002DB14, 需求转换来对应的文件偏移

(0x0002DB14 –△k) = 0x0002cb14,定位到文件偏移为0x0002cb14的地点

翻开内容,里面著录的是IMAGE_IMPORT_DESCKoleosIPTO锐界的第多少个分子变量多对应的dll的名字,mfc90u.dll,到此,大家早已找到了那些输入的dll

home88一必发 48

2) 获得dll调用的全部函数

IMAGE_IMPORT_DESC奥迪Q5IPTOGL450中的第③个参数和末段2个参数,original_first_thunk
和first_thunk分别指向了INT(输入名称表)IAT(输入地址表)那三个表里面分别记录了指向调用函数名的地址,和此函数在dll中的序号(序号用来火速索引dll中的函数)

home88一必发 49

0x0002D85C和0x000262A0是INT
和IAT数组的首地址,上边大家跳到该地址(由于△K=0x1000,故哈弗VA=文件偏移0x0002C85C和0x000252A0)

_IMAGE_THUNK_DATA32数据结构:

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;               //数据
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

a.当二个函数以序号导入.MAGE_THUNK_DATA结构中的AddressOfData最高位被设成1.用16进制表示为(0x九千0000),例入一个IMAGE_THUNK_DATA的AddressOfData的值为0x
800010E4在mfc90u.dll数组中.就表示IMAGE_THUNK_DATA将引入mfc90u.dll

中的第10E4号函数

INT数组:

home88一必发 50

IAT数组:

home88一必发 51

b.假如3个函数以名称导入.IMAGE_THUNK_DATA结构中的Ordinal域就隐含二个RubiconVA.这一个PRADOVA指向贰个IMAGE_IMPORT_BY_NAME
结构.该组织保留了多少个引入函数的有关音讯:例如MSVC昂科雷90.dll

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint; //序列号
    BYTE    Name[1];//函数名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

home88一必发 52

有第二个变量Name定位dll名字

home88一必发 53

定位INT和IAT:

INT:

home88一必发 54

IAT

home88一必发 55

数组里面包车型地铁值为指向IMAGE_IMPORT_BY_NAME的地址,IMAGE_IMPORT_BY_NAME里面存放的是所调用函数的名字的地方,下来我们挑选一个数组里面包车型客车值,跳转到相应的IMAGE_IMPORT_BY_NAME

home88一必发 56

能够观望,下边包车型大巴卓殊图片中体现了所调用的函数的名字,名字前边的八个字节代表的是函数在dll中的序号,方便今后快捷索引到

  1. 亟需注意的地点

INT
和IAT数组在一初叶的时候,里面存放的地点都以千篇一律的,他们都以指向所调用函数的名字的字符串。而在加载到内部存款和储蓄器的时候,IAT的值会发生转移,它的值存放的是dll中等学校函授数实际被调用的地址,在加载到内部存款和储蓄器后,就只要求保存IAT就能够了,利用它来调用函数

home88一必发 57

home88一必发 58

方另一张图

home88一必发 59

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图