前言
良好的习惯是人生产生复利的有力助手。
最近几篇文章都是关于无文件执行或者是逃逸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:
pe2shc.exe <path to your PE> [output path]
使用runshc.exe运行输出文件,并检查转换是否正常:
runshc.exe <converted file>
以windows中的calc.exe为例子,先转化calc,保存为calc_modify.exe ,命令如下:
D:\pe_to_shellcode>pe2shc.exe calc.exe calc_modify.exeReading module from: calc.exe[+] Saved as: calc_modify.exe
运行一下,奇迹就出现了,双击一下calc_modify.exe也是同样的效果:
D:\pe_to_shellcode>runshc64.exe calc_modify.exe[*] Reading module from: calc_modify.exe[*] Running the shellcode:
计算器弹起来:
到这一步,虽然看到calc_modify.exe执行了,但是只要细想 这会不会是个障眼法 !!!
calc_modify.exe是真的像shellcode一样运行了吗?还是直接通过创建进程的方式来执行calc_modify.exe?
看一下runshc的源码就可以知道了。
#include <windows.h>#include <iostream>
#include "peconv.h"
int main(int argc, char *argv[]){ if (argc < 2) { std::cout << "~ runshc ~\n" << "Run shellcode: loads and deploys shellcode file.\n";#ifdef _WIN64 std::cout << "For 64-bit shellcodes.\n";#else std::cout << "For 32-bit shellcodes.\n";#endif std::cout << "Args: <shellcode_file>" << std::endl; system("pause"); return 0; }
size_t exe_size = 0; char* in_path = argv[1];
std::cout << "[*] Reading module from: " << in_path << std::endl; BYTE *my_exe = peconv::load_file(in_path, exe_size); if (!my_exe) { std::cout << "[-] Loading file failed" << std::endl; return -1; } BYTE *test_buf = peconv::alloc_aligned(exe_size, PAGE_EXECUTE_READWRITE); if (!test_buf) { peconv::free_file(my_exe); std::cout << "[-] Allocating buffer failed" << std::endl; return -2; } //copy file content into executable buffer: memcpy(test_buf, my_exe, exe_size);
//free the original buffer: peconv::free_file(my_exe); my_exe = nullptr;
std::cout << "[*] Running the shellcode:" << std::endl; //run it: int (*my_main)() = (int(*)()) ((ULONGLONG)test_buf); int ret_val = my_main();
peconv::free_aligned(test_buf, exe_size); std::cout << "[+] The shellcode finished with a return value: " << std::hex << ret_val << std::endl; return ret_val;}
在上述代码中,主要做了三件事:
基本上和shellcode的执行方式是一样的,看来真的是将PE文件转化为了shellcode。
想知道原理,最直观的方式是比对calc.exe 与calc_modify.exe的二进制内容,看一下有哪些不同。
启动Beyond Compare工具采用16进制比对,会发现calc.exe后面附加了一段代码。
做过软件保护的同学,肯定可以看出这是一个类似壳的实现方式,至于这段壳是如何实现PE to shellcode功能的?
请看下次分解,涉及到汇编 ,大家心里有个准备哈。
既然PE可以转化为shellcode,那么ELF应该也是可以通过这种方式转化为shellcode的,事实情况也是可以的,已经开始搞了。
那之后的网络+shellcode ,木马再也不落地。
推荐阅读:
沙盒syscall监控组件:strace and wtrace