概述
目录
说明
准备工作 编译生成A.exe
一、使用OD 找到messagebox的函数地址
二、使用PE 找到程序入口点
三、找出一个节,看看空闲空间够不够存放我们自己的代码
四、将汇编语言转化为二进制 嵌入到文件中
五、修改程序入口OEP
六、一些其他事项
说明
本次实验的内容就是先找出A.exe的messagebox()函数地址,把调用messagebox()的代码嵌入到 A.exe的文件当中 ,使得A.exe在启动的时候可以先调用messagebox() 再调用fun() 这样做的目的是可以手工调用原程序的函数
思路就是将调用messagebox()的代码写入A.exe的空闲区,然后将程序入口修改为我们嵌入的代码,执行完嵌入的代码后 让程序再跳回原来的入口点
准备工作 编译生成A.exe
#include <stdio.h>
#include <Windows.h>
void fun()
{
printf("hello n");
}
int main()
{
fun();
return 0;
}
一、使用OD 找到messagebox的函数地址
在MessageBoxA处下个断点,找到内存中messagebox函数地址为 774D0A50
二、使用PE 找到程序入口点
找到A程序入口地址为0x00001380
三、找出一个节,看看空闲空间够不够存放我们自己的代码
根据PE结构,可以看出 第一节从文件的400处开始,大小为31D6,到400+3200处结束,空闲空间为 3200-31D6= 2A ,够放我们的代码,所以最终我们会把自己的代码写到文件的 400+31D6=35D6 后面
35D6 后面3600前面 都可以写, 那我们就写到35D0的下一行 35E0处
四、将汇编语言转化为二进制 嵌入到文件中
因为我们要修改文件,添加我们自己的代码,所以添加的内容是关键。这里不能直接将call 774D0A50 和 jmp 00001380 写进去,因为文件中全是16进制的数字,所以我们也只能写16进制的数字进去,这样就需要将汇编语言转化为二进制数字
就是将 call 774D0A50和 jmp 00001380 转化为二进制
call对应的硬编码为E8
jmp对应的硬编码为E9
另外,需要计算E8和E9后面跟的跳转地址X。
比如下面这段代码:
int main()
{
内存地址 文件编码 汇编指令
00601050 55 push ebp
00601051 8B EC mov ebp,esp
fun();
00601053 E8 AD FF FF FF call fun (601005h)
return 0;
00601058 33 C0 xor eax,eax
}
从上面汇编代码可以看出 如果 我们想调用fun()函数,需要跳到 601005h这个地址,对应的文件编码并不是 E8 601005, 而是 E8 ADFFFFFF ,那么文件编码地址ADFFFFFF和内存地址601005有什么关系呢
可以根据下面这个公式计算得出 【内存函数地址 = 文件指令地址 + 偏移地址(文件编码地址) 】
公式:文件编码地址 = 内存中的函数地址 - 当前指令的下条指令地址
= 内存中的函数地址 - (当前指令地址 + 5)
即 偏移地址 = 内存地址 - (调用处地址 + 5)
其实就是计算调用处地址距离内存函数地址有多远
说明
E8和E9后面跟的其实不是内存的函数地址,而是当前指令的偏移地址,我们需要根据内存地址和当前指令地址(E8的地址) 计算出偏移地址 ,嵌入到文件中的代码为 E8 偏移地址
计算过程:
例如上面那段汇编代码
内存中的函数地址 = 601005
当前指令地址 = 601053
偏移地址 = 内存中的函数地址 - (当前指令地址 + 5)
= 601005 - ( 601053 + 5 )
= FFFFFFAD
所以 文件编码是 E8 AD FF FF FF
因为要调用messagebox() 还需要传4个参数 PUSH 0 (6A 00),所以我们要嵌入的代码就是
6A 00 6A 00 6A 00 6A 00 E8 XX XX XX XX E9 XX XX XX XX
由上图可以看出
E8 当前指令地址为35E8 但是在内存中 当前指令地址为 imagebase + 节偏移 + ( 35E8 - 文件节起始地址)
E9 当前指令地址为35ED 但是在内存中 当前指令地址为 imagebase + 节偏移 + ( 35ED - 文件节起始地址)
然后计算后面的偏移地址 XX XX XX XX
E8 偏移地址 = 内存中的函数地址 - (当前指令地址 + 5)
= 774D0A50 - (( imagebase + 节偏移 + ( 35E8 - 文件节起始地址) ) + 5 )
= 774D0A50 - (4041E8 + 5)
= 770CC863
E9 偏移地址 = 内存中的函数地址 - (当前指令地址 + 5)
= 00400000 + 13A0 - (当前指令地址 + 5)
= 00401380 - (4041ED + 5)
= FFFFD18E
注意:以上【当前指令地址】这个变量 应该是对应的内存中的地址,35E8 是文件中的指令偏移,真正在内存中的指令地址应该是
imagebase + vir节偏移 + ( 文件节指令偏移 - 文件节起始地址)
00400000 00001000 35E8 400
综上所述 我们嵌入的代码为
6A 00 6A 00 6A 00 6A 00 E8 63 C8 0C 77 E9 8E D1 FF FF
五、修改程序入口OEP
35E0距离节开始位置 (35E0-400)
真正在内存中的程序入口为 节在内存偏移+入口偏移节
= 节在内存偏移 + ( 入口 - 节开始位置)
= 1000 + ( 35E0 - 400)
= 41E0
所以将 原来的程序入口 1380 改为 41E0
因为入口点是相对于imagebase的偏移量,所以这里入口点的值应该改为入口点距离imagebase有多远的偏移量,而不是入口点的内存地址 4041E0
用PE软件解析以下,发现已经改过来了
六、一些其他事项
在我们修改完文件之后,重新运行发现无法运行,使用OD查看,发现每次修改完文件,OD中的内存地址都会改变,为了使内存地址固定,我们需要关闭ASLR 或者 在项目中关闭动态基址
https://www.52pojie.cn/thread-1099755-1-1.html
改了以后如果还不好使 试试这个方法
关闭程序的动态基地址功能_别人家的好孩子的博客-CSDN博客
最后
以上就是年轻羽毛为你收集整理的手工解析PE(文件注入)的全部内容,希望文章能够帮你解决手工解析PE(文件注入)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复