查看原文
其他

无文件执行:一切皆是shellcode (上)

七夜安全 七夜安全博客 2021-01-04

微信公众号:七夜安全博客 关注信息安全技术、关注 系统底层原理。问题或建议,请公众号留言。


前言

良好的习惯是人生产生复利的有力助手。


2020年第三篇文章,继续2020年的flag:每周至少更新一篇文章。喜欢本文,您就点“在看” 并分享哈

有意思的项目

最近几篇文章都是关于无文件执行或者是逃逸execve检测的内容,今天接着延续着这个思路往下讲。在讲逃逸execve检测的时候,我提到直接执行shellcode是非常好的方式,但是shellcode的提取相对困难,就算是使用MSF工具生成的shellcode,功能也相对单一。如何能让shellcode的提取变得简单,成为我接下来思考的内容。

有一个大胆的想法:

ELF文件能否直接转化为shellcode

毕竟开发ELF的门槛还是挺低的

于是我就搜了一下elf2shellcode,没找到什么有用的项目,在寻找的过程中,找到了windows上的pe_to_shellcode项目:

https://github.com/hasherezade/pe_to_shellcode

这个项目很有意思,通过加壳的方式将exe文件转为了shellcode,接下会简单讲一下使用以及原理,打算分成几篇来讲,讲细致一些。 

使用说明

这个项目有趣的地方是 将exe转化为shellcode,转化后的文件依然是完整的PE文件,不仅可以单独运行,也可以通过shellcode的方式运行。在项目的release页面,根据自身的系统下载runshc 和pe2shc文件: 

使用pe2shc.exe转换您选择的PE:

  1. pe2shc.exe <path to your PE> [output path]

使用runshc.exe运行输出文件,并检查转换是否正常:

  1. runshc.exe <converted file>

演示

以windows中的calc.exe为例子,先转化calc,保存为calc_modify.exe ,命令如下:

  1. D:\pe_to_shellcode>pe2shc.exe calc.exe calc_modify.exe

  2. Reading module from: calc.exe

  3. [+] Saved as: calc_modify.exe


运行一下,奇迹就出现了,双击一下calc_modify.exe也是同样的效果:

  1. D:\pe_to_shellcode>runshc64.exe calc_modify.exe

  2. [*] Reading module from: calc_modify.exe

  3. [*] Running the shellcode:

计算器弹起来:

是障眼法吗?

到这一步,虽然看到calc_modify.exe执行了,但是只要细想 这会不会是个障眼法 !!!

calc_modify.exe是真的像shellcode一样运行了吗?还是直接通过创建进程的方式来执行calc_modify.exe?

看一下runshc的源码就可以知道了。

  1. #include <windows.h>

  2. #include <iostream>


  3. #include "peconv.h"


  4. int main(int argc, char *argv[])

  5. {

  6. if (argc < 2) {

  7. std::cout << "~ runshc ~\n"

  8. << "Run shellcode: loads and deploys shellcode file.\n";

  9. #ifdef _WIN64

  10. std::cout << "For 64-bit shellcodes.\n";

  11. #else

  12. std::cout << "For 32-bit shellcodes.\n";

  13. #endif

  14. std::cout << "Args: <shellcode_file>" << std::endl;

  15. system("pause");

  16. return 0;

  17. }


  18. size_t exe_size = 0;

  19. char* in_path = argv[1];


  20. std::cout << "[*] Reading module from: " << in_path << std::endl;

  21. BYTE *my_exe = peconv::load_file(in_path, exe_size);

  22. if (!my_exe) {

  23. std::cout << "[-] Loading file failed" << std::endl;

  24. return -1;

  25. }

  26. BYTE *test_buf = peconv::alloc_aligned(exe_size, PAGE_EXECUTE_READWRITE);

  27. if (!test_buf) {

  28. peconv::free_file(my_exe);

  29. std::cout << "[-] Allocating buffer failed" << std::endl;

  30. return -2;

  31. }

  32. //copy file content into executable buffer:

  33. memcpy(test_buf, my_exe, exe_size);


  34. //free the original buffer:

  35. peconv::free_file(my_exe);

  36. my_exe = nullptr;


  37. std::cout << "[*] Running the shellcode:" << std::endl;

  38. //run it:

  39. int (*my_main)() = (int(*)()) ((ULONGLONG)test_buf);

  40. int ret_val = my_main();


  41. peconv::free_aligned(test_buf, exe_size);

  42. std::cout << "[+] The shellcode finished with a return value: " << std::hex << ret_val << std::endl;

  43. return ret_val;

  44. }

在上述代码中,主要做了三件事:

  1. 读取exe文件到缓冲区

  2. 给exe文件分配可读可写可执行的内存,并将缓冲区中的内容复制到这段内存中

  3. 从内存中执行代码

基本上和shellcode的执行方式是一样的,看来真的是将PE文件转化为了shellcode。

原理

想知道原理,最直观的方式是比对calc.exe 与calc_modify.exe的二进制内容,看一下有哪些不同。

启动Beyond Compare工具采用16进制比对,会发现calc.exe后面附加了一段代码。

做过软件保护的同学,肯定可以看出这是一个类似壳的实现方式,至于这段壳是如何实现PE to shellcode功能的?

请看下次分解,涉及到汇编 ,大家心里有个准备哈。

总结

小想法

既然PE可以转化为shellcode,那么ELF应该也是可以通过这种方式转化为shellcode的,事实情况也是可以的,已经开始搞了。

那之后的网络+shellcode ,木马再也不落地,开心到起飞。。。。

小打算

最近又把汇编捡起来了,想写个专栏进行分享,毕竟只要是研究系统底层的,都逃不过汇编。

但是学习汇编,后台很多朋友反馈基本上都是学习基本理论和语法,没有什么实用的汇编项目来练手,导致学了但是总感觉没学会。

接下来我尽量通过专栏的方式,来帮助大家深入一下汇编的实战。


发了发了,看看书,早点睡觉,迎接上班。。。


推荐阅读:

linux无文件执行— fexecve 揭秘

沙盒syscall监控组件:strace and wtrace

无"命令"反弹shell-逃逸基于execve的命令监控(上)

APT组织武器:MuddyC3泄露代码分析

Python RASP 工程化:一次入侵的思考

教你学木马攻防 | 隧道木马 | 第一课


如果大家喜欢这篇文章的话,请不要吝啬分享到朋友圈,并置顶公众号。

关注公众号:七夜安全博客

回复【8】:领取 python神经网络 教程 

  • 回复【1】:领取 Python数据分析 教程大礼包

  • 回复【2】:领取 Python Flask 全套教程

  • 回复【3】:领取 某学院 机器学习 教程

  • 回复【4】:领取 爬虫 教程

  • 回复【5】:领取编译原理 教程

  • 回复【6】:领取渗透测试教程

  • 回复【7】:领取人工智能数学基础


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存