-
-
[原创]手写仅包含一个节的可执行文件
-
发表于:
2008-6-12 10:48
10566
-
学习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文件即可。
如有错误之处,请不吝指出!
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课