学习PE文件格式有一段时间了。下面是一个只包含一个节的exe文件,文件大小一共为1024字节。所写的东西没有太多新意,只能被初学的看官

当作厕中佳品哈哈!~~

->DOS Header
   e_magic:         0x5A4D    //'MZ'
   e_cblp:          0x0000
   e_cp:            0x0000
   e_crlc:          0x0000
   e_cparhdr:       0x0000
   e_minalloc:      0x0000
   e_maxalloc:      0x0000
   e_ss:            0x0000
   e_sp:            0x0000
   e_csum:          0x0000
   e_ip:            0x0000
   e_cs:            0x0000
   e_lfarlc:        0x0000
   e_ovno:          0x0000
   e_res:           0x0000000000000000
   e_oemid:         0x0000
   e_oeminfo:       0x0000
   e_res2:          0x0000000000000000000000000000000000000000
   e_lfanew:        0x00000040

->Signature         0x00004550   //'PE'

->File Header
   Machine:                  0x014C  (I386)
   NumberOfSections:         0x0001  //只包含一个section
   TimeDateStamp:            0x00000000
   PointerToSymbolTable:     0x00000000
   NumberOfSymbols:          0x00000000
   SizeOfOptionalHeader:     0x00E0
   Characteristics:          0x010F  (为exe文件)

->Optional Header
   Magic:                         0x010B
   MajorLinkerVersion:            0x00
   MinorLinkerVersion:            0x00
   SizeOfCode:                    0x00000200
   SizeOfInitializedData:         0x00000200
   SizeOfUninitializedData:       0x00000000
   AddressOfEntryPoint:           0x00001000
   BaseOfCode:                    0x00001000
   BaseOfData:                    0x00000000(试验知这个值在xp下任意)
   ImageBase:                     0x00400000
   SectionAlignment:              0x00001000
   FileAlignment:                 0x00000200
   MajorOperatingSystemVersion:   0x0004
   MinorOperatingSystemVersion:   0x0000  -> 4.00
   MajorImageVersion:             0x0000
   MinorImageVersion:             0x0000
   MajorSubsystemVersion:         0x0004
   MinorSubsystemVersion:         0x0000  -> 4.00
   Win32VersionValue:             0x00000000
   SizeOfImage:                   0x00002000 //只含一个节,包括文件头部一共是2000h
   SizeOfHeaders:                 0x00000200 //从文件头到第一节的原始数据的偏移量
   CheckSum:                      0x00000000
   Subsystem:                     0x0002  (WINDOWS_GUI)
   DllCharacteristics:            0x0000
   SizeOfStackReserve:            0x00100000
   SizeOfStackCommit:             0x00001000
   SizeOfHeapReserve:             0x00100000
   SizeOfHeapCommit:              0x00001000
   LoaderFlags:                   0x00000000
   NumberOfRvaAndSizes:           0x0000000F

   DataDirectory (F)             RVA             Size
   -------------                 ----------      ----------
   ExportTable                   0x00000000 0x00000000
   ImportTable                   0x00001100 0x0000003C  //把导入表放在0X300处
   Resource                      0x00000000 0x00000000
   Exception                     0x00000000 0x00000000
   Security                      0x00000000 0x00000000
   Relocation                    0x00000000 0x00000000
   Debug                         0x00000000 0x00000000
   Copyright                     0x00000000 0x00000000
   GlobalPtr                     0x00000000 0x00000000
   TLSTable                      0x00000000 0x00000000
   LoadConfig                    0x00000000 0x00000000
   BoundImport                   0x00000000 0x00000000
   IAT                           0x00000000 0x00000000  
   DelayImport                   0x00000000 0x00000000
   COM                           0x00000000 0x00000000
   Reserved                      0x00000000 0x00000000

(二)下面是节表了
Name     VSize   VAddress    RSize    Roffset    characterstics
.text    200     1000        200       200         60000020 (代码节属性)
剩余字段置0x00


(三)代码
因为我们的代码只有25byte,所以可以将要显示的字符串放置在0x200 + 25 = 0x225再加一个字节0x00,干脆放到0x230
0x230: 'xiep',其余字节用0x00填充

接着是代码:
PUSH       0               6A 00
push       'xiep'          68 30104000    //指向'xiep'
PUSH       'xiep'          68 30104000 
push       0               6A 00
call       MessageBoxA     E8 07000000    //向后7个字节
push       0               6A 00
call       ExitProcess     E8 06000000    //向后6个字节
jmp        MessageBoxA     FF25 F0104000  //跳到IMAGE_THUNK_DATA
jmp        ExitProcess     FF25 F8104000  //将IMAGE_THUNK_DATA放在0x2F8处
从0x200处开始将上面的Opcode按序输入,再以0x00填充到0x300

(四)导入表
导入表的原理:
http://photo.store.qq.com/http_imgload.cgi?/rurl2=8e75544ae53199819508a139094a4fdcdbb3d8e95b418608dd5c285f4e1a64770e981f01782b85f3d8cfc33b36186d8de7bea4f2b46e58091b25612590557da46c5b02abddc6dbadc213378830db3b4c355debe3

如上图:0040100E是Call MessageBoxA,当CPU执行这条指令跳转到0040101A,0040101A再跳转到00402008处包含的地址,而这个地址在文件被

加载之前并不是一个合法的地址,而是一个指向IMPORT_BY_NAME结构的RVA(假设以函数名导入),当文件被加载的时候,操作系统会依据

IMPORT_BY_NAME将00402008处替换为MessageBoxA的地址。

具体的过程如下:
1)OS根据PE文件头找到IMAGE_DIRECTORY_ENTRY_IMPORT数据目录
2)再通过IMAGE_DIRECTORY_ENTRY_IMPORT的成员变量找到IMAGE_IMPORT_DESCRIPTOR,该结构的name是一个指向DLL文件名的RVA
3)IMAGE_IMPORT_DESCRIPTOR的FirstThunk指向00402008,而[00402008]是一个IMAGE_THUNK_DATA结构,假设它的最高位是0,即以名称导入,

此时它的低四位就是一个指向IMPORT_BY_NAME结构的RVA
4)IMPORT_BY_NAME包含需要导入的函数的名称以及导入序号
5)OS根据IMPORT_BY_NAME包含的名称,以及IMAGE_IMPORT_DESCRIPTOR中name指向的DLL文件名,在user32dll中查找函数MessageBoxA的地址,

然后将[00402008]替换成MessageBoxA的实际地址

程序中引入了两个函数,而前面的ImportTable我们设为1100对应文件偏移0x300.因为IMAGE_IMPOR_DESCRIPTOR结构的大小为5*4=20字节,我们

从user32.dll和kernel32.dll分别导入MessageBoxA和ExitProcess,所以加上一个以全0结束的IMAGE_IMPOR_DESCRIPTOR,一共是3*20=60字节(3

个IMAGE_IMPOR_DESCRIPTOR结构),所以我们可以在0x300+60(十进制)=0x33C处放置DLL文件名和IMAGE_IMPORT_BY_NAME结构。

我们先在0x33C处,
Hint  0x00           33C--->113C--->0040113C  //函数的导入序号
'MessageBoxA'        //IMPORT_BY_NAME
填充一个字节 0x00
'User32.dll'         34A--->114A--->0040114A  //DLL文件名
填充一个字节 0x00
接下来:
Hint  0x00           355--->1155--->00401155  //函数的导入序号
'ExitProcess'        //IMPORT_BY_NAME
'Kelnel32.dll'       363--->1163--->00401163  //DLL文件名

而上面我们在代码中是这样写的:
jmp        MessageBoxA     FF25 F0104000  //跳到IMAGE_THUNK_DATA
jmp        ExitProcess     FF25 F8104000  //将IMAGE_THUNK_DATA放在0x2F8处
所以我们需要将IMAGE_THUNK_DATA放在004010F0即0x2F0和004010F8即0x2F8处。
所以在0x2F0处:
3C110000  (0x33C处是MessageBoxA的IMPORT_BY_NAME)
00000000  (以全0结束)
55110000  (0x355处是ExitProcess的IMPORT_BY_NAME)
00000000  (以全0结束)

只剩下IMAGE_IMPORT_DIRECTORY
Name1                                FirstThunk
4A110000   //0x34A处‘user32.dll’    F0100000  //0x2F0处的IMPORT_BY_NAME
63110000   //0x363处‘kernel32.dll’  F8100000  //0x2F8处的IMPORT_BY_NAME
其余字段可以置0,以0x00对齐到3ff,保存为.exe文件即可。

如有错误之处,请不吝指出!