继续PE系列笔记的更新
PE其它笔记索引可前往:
PE文件笔记一 PE介绍
在前面的PE文件笔记八 实战之HOOK程序添加弹窗中能够使用OD达到在运行态时添加弹窗的功能,接下来则要对先前的反汇编的硬编码稍作修改然后插入到PE文件中,最后再修改入口点即可;具体流程在上个笔记已经说明了,这里不再赘述
PE实战之给程序添加弹窗续
最终效果图
先看一下最终的效果图
反汇编和硬编码的对应关系
因为后面要将反汇编代码转换为硬编码,于是这里就要研究一下两者的对应关系
先贴上先前得到的反汇编代码
00401130 > 6A 00 push 0x0
00401132 68 52114000 push MessageB.00401152
00401137 68 48114000 push MessageB.00401148
0040113C 6A 00 push 0x0
0040113E E8 A7F69477 call user32.MessageBoxA
00401143 - E9 8801C4FF jmp 000412D0
00401148 6c db 6c
........
push对应硬编码
压入的第一个和第四个参数是一个立即数0,于是它对应的硬编码固定为6A 00
00401130 > 6A 00 push 0x0
0040113C 6A 00 push 0x0
前面的6A是固定的,表示压入一个立即数,
后面的字节码表示压入的立即数,该立即数 的范围是0~0x7F即0~127
当立即数大于0x7F后硬编码和其含义就都改变了
6A 7F push 0x7F
68 80000000 push 0x80
于是得到了push 立即数 对应的硬编码为6A 立即数
压入的第二个和第三个参数是内存地址0x00401152中存储的数据和0x00401148中存储的数据
00401132 68 52114000 push MessageB.00401152
00401137 68 48114000 push MessageB.00401148
观察硬编码和压入的地址的关系
前面的68是固定的,表示push 一个内存地址
后面的则是要push的内存地址的小端存储,倒过来看就是压入的内存地址了
举个例子,如果push 的内存地址为0x12345678
则其对应的硬编码为 68 78 56 34 12
68 78563412 push 0x12345678
于是得到了push 内存地址 对应的硬编码为 68 内存地址的小端存储
call对应硬编码
接下来就是调用MessageBoxA了,其形式为:call MessageBoxA对应的内存地址
0040113E E8 A7F69477 call user32.MessageBoxA
上面为OD的反汇编引擎为方便我们观看,而显示的内容,下面才是实际的反汇编代码
0040113E E8 A7F69477 call 77D507EA
分析call 内存地址 和 硬编码的关系
前面的E8是固定的,表示直接调用一个内存地址
后面的则是偏移,该偏移为小端存储,此时值为:0x7794F6A7
不难发现这个偏移 = call的内存地址 - 当前的内存地址 - 当前指令的总长度
即 0x7794F6A7 = 0x77D507EA - 0x0040113E - 5
再举个例子,如果当前要跳转的内存地址为0x12345678
则其对应的 偏移 = 0x12345678 - 0040113E - 5 = 11F44535
0040113E E8 3545F411 call 12345678
于是得到了call 内存地址 对应的硬编码为 E8 偏移的小端存储,其中偏移 = 要调用的内存地址 - 当前的内存地址 - 当前指令的总长度
jmp对应硬编码
00401143 - E9 8801C4FF jmp 000412D0
前面的E9是固定的,表示跳转到一个内存地址
后面的也是偏移,该偏移为小端存储,此时值为:0xFFC40188
偏移 = jmp的内存地址 - 当前的内存地址 - 当前指令总长度
即 0xFFC40188 = 0x000412D0 - 0x00401143 - 5
于是得到了jmp 内存地址 对应的硬编码为 E9 偏移的小端存储,其中偏移 = 要跳转的内存地址 - 当前的内存地址 - 当前指令总长度
寻找空白区
知道了硬编码如何构造以后,就要在PE文件中找一块空白区,向里面写入要执行的硬编码
有了先前PE文件笔记二 PE文件的两种状态的知识,可以知道由于文件对齐,在块表和块之间是存在空隙(空白区)的,于是可以向这块区域写入硬编码
这次用来演示的软件是前面经常出场的EverEdit.exe,直接找到其对应的空白区:
构造硬编码
找到空白区的地址为0x2B0,注意此时的地址为文件中的状态
在内存中其对应的地址为ImageBase+0x2B0 = 0x00400000+0x2B0 = 0x004002B0
PS:如果选取的空白区为块中的空白区,则需要进行FOA到VA的转换,相关内容在PE文件笔记七 RVA与FOA转换已说明,这里不再赘述
于是从这里开始构造硬编码
根据前面反汇编一行一行来构造硬编码
00401130 > 6A 00 push 0x0
00401132 68 52114000 push MessageB.00401152
00401137 68 48114000 push MessageB.00401148
0040113C 6A 00 push 0x0
0040113E E8 A7F69477 call user32.MessageBoxA
00401143 - E9 8801C4FF jmp 000412D0
00401148 6c db 6c
push 参数
1.push 0 直接用6A 00填充,无需修改
2.push 内存地址,该内存地址存储第二个参数"tips",因为内存地址尚未填充,先用00代替,为 68 00 00 00 00
3.push 内存地址,该内存地址存储第三个参数"lyl610abc",同样先用00代替,为 68 00 00 00 00
4.push 0 直接用6A 00填充,无需修改
于是得到的硬编码为:6A 00 68 00 00 00 00 68 00 00 00 00 6A 00,将其写入文件中:
6A00680000000068000000006A00
call MessageBoxA
call 内存地址,要call的内存地址为MessageBoxA对应的地址,在本机中为0x77D507EA
此时的内存地址为ImageBase+0x2BE= 0x004002BE
于是可以算出 偏移 = 要跳转的地址 - 内存地址 - 当前指令总长度 = 0x77D507EA - 0x004002BE -5 = 0x77950527
于是得到的硬编码为 E8 27 05 95 77,将其写入文件中:
E827059577
jmp 程序入口点
所谓的程序入口点就是 扩展PE头中的AddressOfEntryPoint + ImageBase得到的内存地址
在先前的PE文件笔记五 PE文件头之扩展PE头中其实已经看过了,这里再用PE工具:Detect It Easy查看一下:
可以得到AddressOfEntryPoint 为0x16AF12,加上ImageBase得到0x56AF12
于是要跳转的地址就是0x56AF12,对应汇编为 jmp 0x56AF12
此时的内存地址为ImageBase+0x2C3 = 0x004002C3
偏移 = 要跳转的地址 - 此时的内存地址 - 当前指令的总长度 = 0x56AF12 - 0x004002C3 - 5 = 0x0016AC4A
于是得到对应的硬编码为 E9 4A AC 16 00,将其写入文件中:
E94AAC1600
填充字符串ASCII码
参数 |
含义 |
字符串 |
ASCII |
文件中起始地址 |
内存中起始地址 |
第三个参数 |
窗口内容 |
lyl610abc |
6C796C36313061626300 |
2C8 |
004002C8 |
第二个参数 |
窗口标题 |
tips |
7469707300 |
2D2 |
004002D2 |
填充完如下:
修正参数
填充完字符串就可以修正参数了
第二个参数 为 push 0x004002D2,对应"tips",对应硬编码为68 D2 02 40 00
68D2024000
第三个参数为 push 0x004002C8,对应"lyl610abc",对应硬编码为68 C8 02 40 00
68C8024000
修正完的结果为:
所有的硬编码
6A0068D202400068C80240006A00E827059577E94AAC16006C796C363130616263007469707300
修改程序入口点
构造完硬编码后,只要将程序入口点改为硬编码的首地址:0x2B0即可
找到原本的程序入口点
修改程序入口点
保存程序并测试运行
最后便是将程序保存,然后打开
保存
我这里另存为了EverEdit2.exe
测试运行
可以看到能够成功地为程序添加弹窗,完毕(~ ̄▽ ̄)~
说明
测试的系统务必为32位的XP系统,在高版本的系统有DEP和ASLR等保护机制,使用以上方法修改后无法运行
上面的MessageBoxA的地址是用OD获取的,具体方法见上一篇笔记,如果想要复现为程序添加弹窗,则要修改对应的MessageBoxA的地址
附件
附上本笔记中要修改的EverEdit文件:点我下载