查看: 136|回复: 0

[原创图文] 手写PE第九课 在text段写入可执行代码

[复制链接]
发表于 2020-5-7 21:33:54 | 显示全部楼层 |阅读模式
  写入可执行代码这部分我们借助OD来完成(这部分比较考验汇编语言)
  首先认识一下MessageBoxA这个API:

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  更详细的解释自己可以在编译器里面去搜索

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  在反汇编代码中可以看到他的参数传递方式同声明时的正好相反
  认识程序中参数的传递,将2.exe载入到OD中。
  按照编译器的习惯我们将”Hello word”字符串的地址放入eax寄存器中。我们知道Hello Word这个字符串被放入了.data段的开始部分,所以他的地址也就是.data的起始地址,.data段是我们手写PE的最后一个区段,他的RVA为3000h    Va=Imagebase+Rva=400000h+3000h=403000h双击第一行写入:mov eax,403000
  

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  
  按照反向弹入堆栈的规则MessageBoxA的第四个参数 :
  UINT uType       //对话框的样式
  最先弹入堆栈,他的值有很多种可以参考msdn去填写,我们这里显示一个简单的样式这个值填写为0。双击第二行写入push 0
  

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  接着弹入MessageBoxA的第三个参数 :
  LPCTSTR lpCaption,   //消息框的标题,如果为NULL,则标题为”错误”
  压入堆栈,他的值填写为0。双击第三行写入push 0

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  接着弹入MessageBoxA的第二个参数 :
  LPCTSTR lpText,    //对话框上显示的空终止的字符串。
  压入堆栈,他的值填写为Hello Word的地址,这个地址保存在eax寄存器中,双击第四行写入push  eax

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  接着弹入MessageBoxA的第一个参数 :
  HWND hWnd,  //消息框所属的父窗口,如果为NULL这个消息框将没有所属的窗口
  弹入堆栈,他的值填写为0,说明他没有所属的父窗口。双击第四行写入push  0

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  接着写调用MessageBoxA这个api的代码 :
  这里涉及到了导入表,我们在讲这一部分知识时说过,函数的地址最终放在IAT里
  而MessageBoxA的IAT恰好在.rdata的段起始,所以他的RVA是2000h。Va=Imagebase+Rva=400000h+2000h=402000h
  按照call dword ptr ds:[xxxxxx]这个格式写入:call dword ptr ds:[402000]

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  这个时候已经能够弹出对话框了,我们可以复制到可执行文件试一下。程序运行弹出对话框后就崩溃了

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  哇咔咔~~~~猜到什么原因了嘛???没错,罪魁祸首是调用弹窗函数之后的那句add byte ptr ds:[eax],al,这条语句应该在od中很常见,因为它的字节码是0000,如果我们什么代码都没写,OD就会把它识别为这条语句
  下面我们来修正崩溃问题:
  程序在结束后应该正确的返回,返回值放在eax寄存器中,一般的这个值为0,写入:xor eax,eax(清空eax)然后程序返回写入: ret复制到可执行文件后测试。
  
  以前经常经常有人问我,retn后面的参数是什么意思,为啥是retn10而不直接写retn呢?我只能说这是汇编的基础知识,这个不懂就说明汇编的很多知识都不知道,真的要去系统性的学习一下汇编语言了。不废话,我来解释吧~~~~~
  先拆分一下retn的结构
  RETN等价于一条指令:POP   eip
  而带有操作数的RETN指令则是在POP之后,执行ESP=ESP+操作数1
  我们刚才压入了16字节的数据,占用了堆栈16个字节的空间,为了保持堆栈平衡,必须要释放堆栈的空间(你用了多少,用完就要还回去多少)
  通常情况下,在被调用过程里,分配局部变量使用add esp -xx或着sub esp xx 这就是在分配局部变量,那么释放局部变量也有2种方式,第一种使用add esp -xx或着sub esp xx来移动堆栈指针,第二种就是retn xx
  我们可以使用add esp 10来平衡堆栈,也可以使用retn10来平衡堆栈,为啥这里要使用retn10呢?因为编译器使用的retn10。

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  如果你还想问为啥是retn10而不是retn16,那你真的要从第一课开始看了
  
  

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  执行完retn10之后开始执行结束线程函数,这下一切都正常了

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
  这就是我们纯手工把一个记事本文件变成了可执行程序的全过程,希望大家亲自动手完成。
  至此手写PE文件部分就结束了,希望大家能够通过这部分的学习能够对PE结构有更深的了解。教程有理解不到位或者错误的地方,请联系QQ1481645902一起探讨学习,欢迎大家来纠正我的错误。

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
                                                                        xuenixiang                                                                        

手写PE第九课 在text段写入可执行代码

手写PE第九课 在text段写入可执行代码
                                                                原创文章 127获赞 34访问量 3万+                                                                                            关注                                                                私信                                                                                                                           

温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的最好奖励,还可以获得学币奖励,请尊重作者的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046
微信公众号
快速回复 返回顶部 返回列表