从0开始写ShellCode加载器0x2-分离免杀
这是从0开始写ShellCode加载器的第2篇文章,文章列表,样本demo已上传到GitHub
之前的免杀都是把shellcode直接放在程序里面执行,分离免杀是将恶意代码放置在程序本身之外的一种加载方式。
从文件中加载shellcode
首先是最基本的,从raw格式的文件中读取shellcode
1 | msfvenom -p windows/exec cmd=calc.exe -f raw -o calc |
shellcode虽然平常使用16进制形式的居多,但它本质上还是一段二进制的字节流,要用二进制的方法来读取。使用C++中ifstream类相关方法。
1 |
|
运行结果如下,这里面有两个疑问点,一是data的size为什么是4字节, char *data = new char[length];
创建对象,大小为什么不是length的长度。二是data的内容,data[1]输出为\fffffffc
一共是8×4=32比特,4字节。这样算下来整个data内存段的大小远远大于shellcode原本的长度193字节。我第一时间想法是环境问题自动补位了,但其它的data[i]还有8位,4位的长度,补位了却没有完全补?
以上问题先留住,这段data内存加载执行是没问题的。
免杀效果还不错。静态过了360和defender,动态被defender拦截了,毕竟shellcode没混淆加密。
很奇怪,明明加载方式都没变,还是用VirtualAlloc加载的,这次很多杀软没查杀。
从web加载shellcode
从本地文件中加载只是一种免杀思路,实际利用时远程加载更方便。我能想到的web远程加载形式有两种,一是从socket连接加载,客户端连接c2端口,返回shellcode;二是从http连接加载,访问c2的url返回shellcode。socket加载方式倾旋大佬教程的第五课已经实现过了,我现在来写一个http加载。
网上一搜,C++ http请求怎么实现,感觉这画风不对啊,怎么一人一个写法。C++没有python和go那样,把所有的库放一起的网站吗?
找到了微软winsock的文档,https://docs.microsoft.com/zh-cn/windows/win32/winsock/getting-started-with-winsock
还有winhttp的文档,https://docs.microsoft.com/en-us/windows/win32/api/winhttp/
还是看文档比较靠谱,实例https://www.citext.cn/415.html。
http get方法C++实现
1 |
|
python一行代码解决的事,C++写了100多行
现在实现了从远程服务器通过http请求获取数据,但又面临一个新问题,shellcode是二进制的,要以二进制数据流加载进内存才能执行。但是GET请求的结果是字符串,有没有办法将二进制数据编码成字符串,再从字符串还原为二进制数据呢?如果上述过程能实现,就能为shellcode在传输过程中的加密解密打开局面,对字符串的处理要比二进制数据要灵活多了。
如何实现?这个问题说到底就是如何把010100101.....
的二进制数据作为可打印字符表示出来,再把这串字符转换回010100101.....
,平常见到的shellcode是这样子的。
\xfc,\xe8这是二进制11111100
,11101000
,编码为16进制的结果,char类型数组每位大小为1字节、8比特,刚好放下16进制的两位数据(16进制每位4比特)。所以理论上,我们创建一个shellcode_size长度的char数组,每位手动填充和16进制数据,最终这个数组在内存中的状态和直接加载shellcode是完全一样的。C++直接向数据中写16进制数据的方法我没找到,但是我可以填对应的10进制数据。
首先将shellcode按字节编码成10进制
将生成的数据放到char数组中,使用load函数加载。
1 | int main() { |
shellcode成功执行!和上次文件加载的内存相比较,两次的内存数据一模一样。
这样一来我们web远程加载的思路就清晰了。
首先将十进制的数据放到web服务器
字符串生成buf数组函数代码
1 | char *StrToShellcode(char str[]) |
远程加载shellcode成功。
用反弹shell进行实战测试。远程加载成功。
加一个简单的触发条件,避免沙箱直接运行白给。
1 | int main(int argc, char * argv[]) |
免杀效果,360静动全过,defender全查杀。virustotal检测效果如下。