前段时间的Exploit Me挑战赛,使我深刻的感到ShellCode编写完全不同与病毒,对于栈空间得按字节来省着用,ShellCode中hash式函数调用势在必行。另外,在unicode环境下的shell处理,也得过关才行。参照《The Shellcoder's Handbook》 中关于“百叶窗”法,及相关网文资料,自己也写了拆分ShellCode字符串模块,在此一并放出
CreateWinAPINameHash功能是获取指定模块中函数名称串的HASH值,并返回Hash的Key值,要计算实际中函数名称串的HASH值只需要调用次过程就可以;ShellCodeToStdByteChar功能是对于已经写好的ShellCode,将其中的非纯数字和字母字节进行拆分,使之转换为字母字节。另外,GetBaseAddressOfKernel32ByTEB与GetKernel32BaseAddressBySearch是两种不同方法获取Kernel32.dll 的基址的子过程,以及GetStringSize用于获取字符串大小的子程序,是顺便送给大家公用子程序,省的自己写
代码:
.586 .model flat, stdcall option casemap:none include windows.inc include kernel32.inc include user32.inc includelib kernel32.lib includelib user32.lib .const szLoadLibraryA db 'LoadLibraryA',0 szGetProcAddressA db 'GetProcAddressA',0 szGetProcAddresAs db 'GetProcAddresAs',0 szFileName db ".\ShellCode.log",0 szkernel32Dll db "kernel32.Dll",0 .data ppKernel32Win32ApiName dd szLoadLibraryA,szGetProcAddressA,szGetProcAddresAs .data? pKernel32Win32ApiHash dd lengthof ppKernel32Win32ApiName dup (?) .code start: call GetBaseAddressOfKernel32ByTEB push -1 push -1 push lengthof ppKernel32Win32ApiName push offset pKernel32Win32ApiHash push offset ppKernel32Win32ApiName push eax call CreateWinAPINameHash xor eax, eax ret ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ;如何编写Unicode形式的ShellCode及过WideCharToMultiByte ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ;--------------------------------------CreateWinAPINameHash--------------------------------------------- ;函数功能: 获取指定模块中函数名称串的HASH值,并返回Hash的Key值 ;入口参数: hModule : 被Hash函数所在模块的句柄或基址 ; lppWinApiName : 函数名称串地址表首地址 ; lpHashCode : 接收函数名称串被Hash后的缓冲区地址 ; dwNumberString : 函数名称串的个数 ; dwHashKey : 可以预设Hash的Key值的范围的上限(-1则为系统寻找合适KEY值)[0--31] ; dwFilterFlags : 设置Hash值过滤标志: ; 0--不含零字节;1--不含连续的零字节;-1--不过滤 ;出口参数: 成功则EAX返回正确Hash的Key值,否则为返回值小于零. ;------------------------------------------------------------------------------------------------------- CreateWinAPINameHash proc hModule:dword,lppWinApiName:dword,lpHashCode:dword,\ dwNumberString:dword,dwHashKey:dword,dwFilterFlags:dword local ret_value:dword pushad mov ebx, hModule mov eax, ebx ;******************************************************************** ; 从 PE 文件头的数据目录获取导出表地址 ;******************************************************************** add eax, dword ptr [ebx + IMAGE_DOS_HEADER.e_lfanew] assume eax : ptr IMAGE_NT_HEADERS mov eax, [eax].OptionalHeader.DataDirectory.VirtualAddress add eax, ebx assume eax : ptr IMAGE_EXPORT_DIRECTORY ;******************************************************************** ; 计算函数名的hash值 ;******************************************************************** mov ecx, dword ptr [eax].NumberOfNames ;检查是否搜索完毕 mov eax, dword ptr [eax].AddressOfNames ;取出函数名字符串地址表指针(RVA) add eax, ebx ;eax真实指向函数名字符串地址表首地址 push ecx push eax shl ecx, 2 push ecx push LMEM_ZEROINIT call LocalAlloc ;申请临时内存 pop esi pop ecx test eax, eax je exit_CreateWinAPINameHash mov edx, eax ;检测执行结果 mov eax, dwHashKey mov edi, -1 cmp eax, edi jne @f mov al, 32 @@: push eax jmp @f next_CreateWinAPINameHash: push edi @@: push ecx push edx push esi push ebx @@: call HashWinAPIString cmp eax, 0 jl @f cmp eax, edi je @f xchg eax, edi cmp edi, eax jl next_CreateWinAPINameHash push edi push dwNumberString push lpHashCode push lppWinApiName push dwFilterFlags jmp @b @@: push eax push edx call LocalFree ;释放临时内存 pop eax exit_CreateWinAPINameHash: mov ret_value, eax popad mov eax, ret_value ret CreateWinAPINameHash endp ;--------------------------------------HashWinAPIString------------------------------------------------- ;函数功能: 将函数名称串进行HASH计算 ;入口参数: lpBaseIED : 被Hash函数所在模块的IMAGE_EXPORT_DIRECTORY指针 ; lpStringTable : 函数名称串地址表首地址 ; lpHashCode : 接收函数名称串被Hash后的缓冲区地址 ; dwNumberString : 函数名称串的个数 ; dwHashKey : 可以预设Hash的Key值(-1则为系统寻找合适KEY值)[0--31] ;出口参数: 成功则EAX返回正确Hash的Key值,否则为-1. ;------------------------------------------------------------------------------------------------------- HashWinAPIString proc lpBaseIED:dword,lpStringTable:DWORD,lpHashCode:dword,\ dwNumberString:dword,dwHashKey:dword local hash_key:byte,string_table_base:dword pushad mov eax, dwHashKey inc eax test eax, eax jne @f mov al, 32 @@: dec eax mov hash_key, al mov ebx, lpBaseIED hash_key_HashWinAPIString: mov edi, lpHashCode mov ecx, dwNumberString push ebx push ecx push edi ; call CheckSameDwordByte dec ebx je @f inc ebx jge @f inc ebx @@: push lpStringTable pop string_table_base hash_next_HashWinAPIString: push ecx xor eax, eax cdq mov esi, string_table_base mov esi, dword ptr [esi] ;取出函数名字符串地址表第一个单元内容(函数名字符串RVA) add esi, ebx ;esi真实指向函数名字符串 ;----------------------hash-------------------------- @@: lodsb mov cl, hash_key ror edx, cl add edx, eax test al, al jne @b ;---------------------------------------------------- mov eax, edx stosd add string_table_base, 4 pop ecx loop hash_next_HashWinAPIString call CheckSameDwordByte test eax, eax je @f dec hash_key jge hash_key_HashWinAPIString @@: popad movzx eax, hash_key ret HashWinAPIString endp ;--------------------------------------CheckSameDwordByte----------------------------------------------- ;函数功能: 检验HASH值的合法性 ;入口参数: lpBuffer : 需要检验的数据缓冲区首地址 ; dwLengthOfType : 被检数据的长度(以字位单位计算DWORD) ; dwFlags : 设定检验标志: 0,则检验是否非法ASCII码0; ; 1,则检验是否非法Unicode码0; ; -1,则不检验是否非法; ; 否则检验是否含重复字. ;出口参数: 成功则EAX返回0, 否则为EAX≠0 ;------------------------------------------------------------------------------------------------------- CheckSameDwordByte proc lpBuffer:dword, dwLengthOfType:dword, dwFlags:dword push ecx push edi push esi mov esi, lpBuffer mov ecx, dwLengthOfType cld xor eax, eax push eax dec eax cmp dwFlags, eax pop eax je ret_CheckSameDwordByte mov edi, esi cmp dwFlags, eax je ASCII_CheckSameDwordByte cmp dwFlags, 1 je Unicode_CheckSameDwordByte dec ecx @@: push ecx lodsd mov edi, esi repne scasd test ecx, ecx jne @f pop ecx loop @b push ecx @@: pop ecx jmp exit_CheckSameDwordByte ASCII_CheckSameDwordByte: shl ecx, 2 repne scasb jmp exit_CheckSameDwordByte Unicode_CheckSameDwordByte: lodsd ror eax, 8 test ax, ax je exit_CheckSameDwordByte loop Unicode_CheckSameDwordByte exit_CheckSameDwordByte: mov eax, ecx ret_CheckSameDwordByte: pop esi pop edi pop ecx ret CheckSameDwordByte endp ;----------------------------------ShellCodeToStdByteChar------------------------------ ;函数功能: 拆分ShellCode字符串,使之转换为纯数字和字母字符 ;入口参数: lpStdByteCharStr : 需要处理的ShellCode字符串地址 ; lpShellCodeCharStr : 转换后的接收缓冲区 ;出口参数: EAX返回转换后的字符串长度(BYTE) ;-------------------------------------------------------------------------------------- ShellCodeToStdByteChar proc lpStdByteCharStr:dword,lpShellCodeCharStr:dword push ebx push ecx push esi push edi mov esi, lpStdByteCharStr mov edi, lpShellCodeCharStr push edi xor eax, eax mov ecx, eax dec ecx cld repnz scasb not ecx dec ecx ; 获取待处理串长度 mov ebx, esp cld next_ShellCodeToStdByteChar: lodsb cmp al, 30h jl must_ShellCodeToStdByteChar cmp al, 7ah jg must_ShellCodeToStdByteChar cmp al, 61h jge @f cmp al, 5ah jg must_ShellCodeToStdByteChar cmp al, 41h jge @f cmp al, 3ah jge must_ShellCodeToStdByteChar @@: stosb ; 对数字,字母不需要拆分 jmp continue_ShellCodeToStdByteChar must_ShellCodeToStdByteChar: push eax @@: and al, 1111b ; 拆分: 将一个字节的高低四位分别扩展到 add al, 41h ; 两个字节中,加41h后存储 stosb pop eax shr al, 4 cmp ebx, esp je @b push eax continue_ShellCodeToStdByteChar: loop next_ShellCodeToStdByteChar xor eax, eax lodsb ; 添加串结束符 pop eax xchg eax, edi sub eax, edi ; 统计转换后串长度 pop edi pop esi pop ecx pop ebx ret ShellCodeToStdByteChar endp ;--------------------------------GetBaseAddressOfKernel32ByTEB----------------------------------- ;函数功能: 根据TEB结构定位Kernel32.dll 的基址 ;入口参数: 无 ;出口参数: 成功则返回Kernel32.dll 的基址在EAX中 ;------------------------------------------------------------------------------------------------ GetBaseAddressOfKernel32ByTEB proc assume fs:nothing xor eax, eax mov eax, dword ptr fs:[eax+30h] mov eax, dword ptr [eax+0ch] mov eax, dword ptr [eax+1ch] mov eax, dword ptr [eax] mov eax, dword ptr [eax+8] ret GetBaseAddressOfKernel32ByTEB endp ;-----------------------------------GetKernel32BaseAddressBySearch-------------------------------------- ;函数功能: 在内存中扫描 Kernel32.dll 的基址 ;入口参数: lpKernel32SpaceOfAny : Kernel32.dll领空内的任一地址 ;出口参数: 成功则返回Kernel32.dll 的基址在EAX中,否则为EAX=0 ;------------------------------------------------------------------------------------------------------- GetKernel32BaseAddressBySearch proc lpKernel32SpaceOfAny : DWORD mov eax,lpKernel32SpaceOfAny xor edx,edx @@: dec eax ;暴力查找 Kernel32.dll 的基地址 mov dx,word ptr [eax+IMAGE_DOS_HEADER.e_lfanew] test dx,0f000h jnz @b cmp eax,[eax+edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase] jnz @b ret 4 GetKernel32BaseAddressBySearch endp ;--------------------------------------------GetStringSize--------------------------------------------- ;函数功能: 用于获取字符串大小的子程序 ;入口参数: lpOutTexts : 需要检测字符串的地址 ;出口参数: eax字符串大小 ;------------------------------------------------------------------------------------------------------ GetStringSize proc lpStringBuffer:dword push ecx push edi mov edi, lpStringBuffer xor eax, eax mov ecx, eax dec ecx cld repnz scasb not ecx mov eax, ecx dec eax pop edi pop ecx ret GetStringSize endp end start