首页
社区
课程
招聘
[原创]通过ReverseMe学习PE文件结构-输入表
发表于: 2007-5-4 13:15 10831

[原创]通过ReverseMe学习PE文件结构-输入表

2007-5-4 13:15
10831

SantMat-ReverseMe #3
任务:
给程序添加一个功能:显示一个MessageBox显示"I am a LaMe rEvErSeR!:p"
规则:
1.通过编辑PE头的Optional头结构给文件增加一个区块。
2.必须通过调整输入表的方式为程序增加MessageBoxA函数
3.把入口点调整到MessageBox所在的那个新区段
4.执行了MessageBox之后要跳回文件原来的入口点,好让程序能正常的退出。
5.你只能修改PE Header/Optional Header结构和你新增加的区块,不能动其他的部分。
6.最后,你只能使用十六进制编辑器。
7.所有工作必须全手工完成。

工具:U-Edit、OllyDBG
需要知识:PE头结构、输入表结构、少许汇编知识

============
1.增加区块
程序原先已经有3个块:
Name:                        .text
VirtualSize:                0x000E
VirtualAddress:                0x1000
SizeOfRawData:                0x2000
PointerToRawData:                0x0400
PointerToRelocations:        0
PointerToLinenumbers:        0
NumberOfRelocations:        0
NumberOfLinenumbers:        0
Characteristics:                0x60000020
--
Name:                        .rdata
VirtualSize:                0x0054
VirtualAddress:                0x2000
SizeOfRawData:                0x2000
PointerToRawData:                0x0600
PointerToRelocations:        0
PointerToLinenumbers:        0
NumberOfRelocations:        0
NumberOfLinenumbers:        0
Characteristics:                0x40000040
--
Name:                        .data
VirtualSize:                0x00e2
VirtualAddress:                0x3000
SizeOfRawData:                0x2000
PointerToRawData:                0x0800
PointerToRelocations:        0
PointerToLinenumbers:        0
NumberOfRelocations:        0
NumberOfLinenumbers:        0
Characteristics:                0xC0000040

在0x220处按以下信息新建一个块:
Name:                        .Nukou
VirtualSize:                0x0100
VirtualAddress:                0x4000
SizeOfRawData:                0x0200
PointerToRawData:                0x0A00
PointerToRelocations:        0
PointerToLinenumbers:        0
NumberOfRelocations:        0
NumberOfLinenumbers:        0
Characteristics:                0xC0000040

然后将块数目调整成4(0xB6处),映像尺寸改为0x5000(0x100处)。
最后在文件结尾出为我们的块增加长度为0x200(512)的空间
============
2.通过调整输入表的方式为程序增加MessageBoxA函数

0x130处是输入表的RVA,找到输入表在0x608位置
当前的IAT:
OriginalFirstThunk:        0x2030
TimeDateStamp:        0x0000
ForwarderChain:        0x0000
Name:                0x2046
FirstThunk:        0x2000
因为要求不能动原来的IAT,所以只好在新加的区块里重新写一个IAT了(原有的函数名与DLL名在原来的文件里就已经存在,不需要再加了):

OriginalFirstThunk:        0x2030(ExitProcess)
TimeDateStamp:        0x0000
ForwarderChain:        0x0000
Name:                0x2046(KERNEL32.DLL)
FirstThunk:        0x2000(ExitProcess)

OriginalFirstThunk:        0x404C(MessageBoxA)
TimeDateStamp:        0x0000
ForwarderChain:        0x0000
Name:                0x4062(USER32.DLL)
FirstThunk:        0x4040(MessageBoxA)

顺便在40A0位置添加字符串"I am a LaMe rEvErSeR!:p"
将输入表的指针指向新加的块(0x130处,里面存的是RVA)
============
3.把入口点调整到个新区段
0xD8处为入口点,改为4080,我们从4080位置开始写代码(直接在OllyDBG里写不违规吧,毕竟考的不是用机器码写程序=.=):
00404080 >  6A 00           push    0
00404082    6A 00           push    0
00404084    68 A0404000     push    004040A0                         ; ASCII "I am a LaMe rEvErSeR!:p"
00404089    6A 00           push    0
0040408B    FF15 44404000   call    dword ptr [<&USER32.MessageBoxA>>; USER32.MessageBoxA
00404091  - E9 6ACFFFFF     jmp     00401000

============
4.一些PE编辑器是通过什么方式向输入表添加函数的呢?
先用LordPE为ReverseMe.exe添加函数USER32.MessageBoxA然后用U-Edit打开该文件,会发现程序末尾处多了个.Silvana块:
Name:                        .Silvana
VirtualSize:                0x1000
VirtualAddress:                0x4000
SizeOfRawData:                0x005D
PointerToRawData:                0x0A00
PointerToRelocations:        0
PointerToLinenumbers:        0
NumberOfRelocations:        0
NumberOfLinenumbers:        0
Characteristics:                0xC0000040
而且输入表的RVA也与这个区块吻合。解读一下新输入表:
OriginalFirstThunk:        0x2030(ExitProcess)
TimeDateStamp:        0x0000
ForwarderChain:        0x0000
Name:                0x2046(KERNEL32.DLL)
FirstThunk:        0x2000(ExitProcess)

OriginalFirstThunk:        0x4019(MessageBoxA)
TimeDateStamp:        0x0000
ForwarderChain:        0x0000
Name:                0x4000(USER32.DLL)
FirstThunk:        0x4019(MessageBoxA)
与我们手工加进去的输入表结构一模一样,都是重新建立一个输入表,将原来的输入函数都包含进去。
我很失望:(,从一开始我就幻想着能有更漂亮的解决办法,直接在原来的输入表上修改之类的。仔细想想,如果直接在原输入表增加IID的结构的话难免要在文件中插入新内容,而后面的数据的偏移量就会随之变化,很有可能导致程序不能运行。
============
因为我也是初学,错误总是难免的,还望大家包涵!


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (5)
雪    币: 200
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
zcg
2
经典呀!好好的学习!感谢!
2007-5-4 23:05
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不太懂
但也要顶了
谢谢分享
2007-5-5 12:40
0
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
顶一下!!努力学习!!
2007-6-27 21:03
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
5
呵呵,添加导入函数我一般喜欢用自己写的代码完成

_MyGetProcAddressByName   proc         uses ebx esi edi _hModule,  _lpszProcName
                          local        loc_iLenOfName, loc_lpExportDir, loc_lpItemAddrName
                         
                          ;在_hModule指定的模块中查找名为_lpszProcName的导出函数
                          ;入口参数:_hModule——模块的句柄,数值上等于其基地址
                          ;          _lpszProcName——指向要查找函数的名称字符串的指针
                          ;返回值:成功则返回所需函数的地址,失败时返回NULL(在eax中)
                         
                          xor          eax, eax
                          mov          edi, _lpszProcName
                          or           ecx, -1
                          repnz scas   byte ptr es:[edi]
                          not          ecx
                          mov          loc_iLenOfName, ecx    ;以上取_lpszProcName的长度
                         
                          mov          edx, _hModule          ;以下edx中皆为此模块基地址不再声明
                          cmp          word ptr [edx], 5A4Dh  ;5A4Dh == IMAGE_DOS_SIGNATURE
                          jne          short locret_1
                          mov          eax, edx
                          add          eax, [eax+3Ch]         ;3Ch == IMAGE_DOS_HEADER.e_lfanew
                          cmp          word ptr [eax], 4550h  ;4550h == IMAGE_NT_SIGNATURE
                          jne          short locret_1
                          mov          ebx, [eax+78h]         ;78h == sizeof IMAGE_FILE_HEADER
                                                              ;       +

IMAGE_OPTIONAL_HEADER32.DataDirectory
                          add          ebx, edx
                          mov          loc_lpExportDir, ebx
                          mov          eax, [ebx+20h]         ;20h ==

IMAGE_EXPORT_DIRECTORY.AddressOfNames
                          add          eax, edx
                          mov          loc_lpItemAddrName, eax
                         
                          ;遍历函数名数组以匹配要查找的函数名
                         
                          mov          ebx, [ebx+18h]         ;18h ==

IMAGE_EXPORT_DIRECTORY.NumberOfNames
                     @@:      
                          mov          esi, [eax]
                          add          esi, edx
                          mov          edi, _lpszProcName
                          mov          ecx, loc_iLenOfName
                          repe cmps    byte ptr es:[edi], byte ptr [esi]
                          jnz          _continue1
                         
                          ;找到了的情况
                         
                          sub          eax, loc_lpItemAddrName ;获得该名称在函数名数组中的偏移
                          mov          ebx, loc_lpExportDir
                          mov          ebx, [ebx+1Ch]          ;1Ch ==

IMAGE_EXPORT_DIRECTORY.AddressOfFunctions
                          add          ebx, edx
                          mov          ebx, [ebx+eax]               ;以同一偏移在函数地址数组中搜索,
                                                               ;即得所求函数的地址
                          lea          eax, [edx+ebx]
                          jmp          @F
                          
             _continue1:  ;没有找到,继续
                          add          eax, 4
                          dec          ebx
                          jnz          @B
                         
               locret_1:
                          xor          eax, eax
                     @@:     
                          ret

_MyGetProcAddressByName   endp

通过GetModuleHandle("User32.dll")得到hUser32,同时在程序里随便什么地方写进"MessageBoxA"这个字符串,就可以调用上面这个函数来得到它的地址
2007-6-28 23:43
0
雪    币: 112
活跃值: (16)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
6
这个人太猛了!
2007-7-1 11:51
0
游客
登录 | 注册 方可回帖
返回
//