文章作者:zhuwg
信息來源:邪惡八進制信息安全團隊(
www.eviloctal.com)
1。取得shadow ssdt真實地址
系統只提供了KeServiceDescriptorTable導出
KeServiceDescriptorTableShadow是個未導出結構
定義
復制內容到剪貼板
代碼:
typedef struct _SYSTEM_SERVICE_TABLE
{
? ?PNTPROC ServiceTable; // array of entry points
? ?PDWORD CounterTable; // array of usage counters
? ?DWORD ServiceLimit;??// number of table entries
? ?PBYTE??ArgumentTable; // array of byte counts
}
SYSTEM_SERVICE_TABLE,
*PSYSTEM_SERVICE_TABLE,
**PPSYSTEM_SERVICE_TABLE;
//-----------------------------------------------------------------------------------------------------------
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
? ?SYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe ( native api )
??SYSTEM_SERVICE_TABLE win32k;??// win32k.sys (gdi/user support)
? ?SYSTEM_SERVICE_TABLE Table3;??// not used
? ?SYSTEM_SERVICE_TABLE Table4;??// not used
}
SYSTEM_DESCRIPTOR_TABLE,
*PSYSTEM_DESCRIPTOR_TABLE,
**PPSYSTEM_DESCRIPTOR_TABLE;
其實 KeServiceDescriptorTableShadow包含4個子結構,其中第一個就是ntoskrnl.exe ( native api ),和KeServiceDescriptorTable指向一樣 我們真正需要獲得的是第二個win32k.sys (gdi/user support),第三個和第四個一般不使用
定位方法
1。硬編碼
復制內容到剪貼板
代碼:
//for xp
if(gKernelVersion==WINXP)
KeServiceDescriptorTableShadow=KeServiceDescriptorTable-0x40;
//for 2k
if(gKernelVersion==WIN2K)
KeServiceDescriptorTableShadow=KeServiceDescriptorTable+0xE0;
2。搜索KeAddSystemServiceTable
反匯編代碼可以看出
復制內容到剪貼板
代碼:
lkd> u KeAddSystemServiceTable l 40
nt!KeAddSystemServiceTable:
805ba589 8bff? ?? ?mov??edi,edi
805ba58b 55? ?? ? push??ebp
805ba58c 8bec? ?? ?mov??ebp,esp
805ba58e 837d1803? ? cmp??dword ptr [ebp+18h],3
805ba592 774e? ?? ?ja? ?nt!KeAddSystemServiceTable+0x6b (805ba5e2)
805ba594 8b4518? ???mov??eax,dword ptr [ebp+18h]
805ba597 c1e004? ???shl??eax,4
805ba59a 83b880a6558000 cmp??dword ptr nt!KeServiceDescriptorTable (8055a680)[eax],0
805ba5a1 753f? ?? ?jne??nt!KeAddSystemServiceTable+0x6b (805ba5e2)
805ba5a3 8d8840a65580??lea??ecx,nt!KeServiceDescriptorTableShadow (8055a640)[eax]
805ba5a9 833900? ???cmp??dword ptr [ecx],0
805ba5ac 7534? ?? ?jne??nt!KeAddSystemServiceTable+0x6b (805ba5e2)
805ba5ae 837d1801? ? cmp??dword ptr [ebp+18h],1
805ba5b2 8b5508? ???mov??edx,dword ptr [ebp+8]
805ba5b5 56? ?? ? push??esi
805ba5b6 8b7510? ???mov??esi,dword ptr [ebp+10h]
805ba5b9 57? ?? ? push??edi
805ba5ba 8b7d14? ???mov??edi,dword ptr [ebp+14h]
805ba5bd 8911? ?? ?mov??dword ptr [ecx],edx
805ba5bf 8b4d0c? ???mov??ecx,dword ptr [ebp+0Ch]
805ba5c2 898844a65580??mov??dword ptr nt!KeServiceDescriptorTableShadow+0x4 (8055a644)[eax],ecx
805ba5c8 89b048a65580??mov??dword ptr nt!KeServiceDescriptorTableShadow+0x8 (8055a648)[eax],esi
805ba5ce 89b84ca65580??mov??dword ptr nt!KeServiceDescriptorTableShadow+0xc (8055a64c)[eax],edi
805ba5d4 0f855a3e0300??jne??nt!KeAddSystemServiceTable+0x4d (805ee434)
805ba5da 5f? ?? ? pop??edi
805ba5db b001? ?? ?mov??al,1
805ba5dd 5e? ?? ? pop??esi
805ba5de 5d? ?? ? pop??ebp
805ba5df c21400? ???ret??14h
805ba5e2 32c0? ?? ?xor??al,al
805ba5e4 ebf8? ?? ?jmp??nt!KeAddSystemServiceTable+0x6d (805ba5de)
805ba5e6 90? ?? ? nop
805ba5e7 90? ?? ? nop
805ba5e8 90? ?? ? nop
805ba5e9 90? ?? ? nop
805ba5ea 90? ?? ? nop
搜索辦法很簡單
就是找到
805ba5a3 8d8840a65580??lea??ecx,nt!KeServiceDescriptorTableShadow (8055a640)[eax]
的8d88 40a65580??lea??ecx, nt!KeServiceDescriptorTableShadow
代碼如下
復制內容到剪貼板
代碼:
void GetKeServiceDescriptorTableShadow()
{
??PUCHAR cPtr, pOpcode;
??ULONG Length;
??
??for (cPtr = (PUCHAR)KeAddSystemServiceTable;
? ? cPtr < (PUCHAR)KeAddSystemServiceTable + PAGE_SIZE;
? ? cPtr += Length)
??{
? ? if (!MmIsAddressValid(cPtr)) break;
? ? Length = SizeOfCode(cPtr, &pOpcode);
? ? if (!Length || (Length == 1 && *pOpcode == 0xC3)) break;
? ?
? ? if (*(PUSHORT)pOpcode == 0x888D)
? ? {
? ?? ?KeServiceDescriptorTableShadow = *(PVOID *)(pOpcode + 2);
? ?? ?break;
? ? }
??}
}
3。從KTHREAD.ServiceTable里面搜索
如果是GUI線程 那么就是指向ServiceDescriptorTableShadow
其實個人更加傾向這個辦法 穩定安全可靠,上面那個代碼也許會受到以后系統代碼變化影響 但是這個肯定不會
復制內容到剪貼板
代碼:
GetServiceDescriptorTableShadowAddress proc uses esi edi ebx
local dwThreadId:DWORD
??xor ebx, ebx? ?? ???; = NULL. Assume ServiceDescriptorTableShadow will be not found
??mov eax, KeServiceDescriptorTable
??mov esi, [eax]
??; Find KTHREAD.ServiceTable field
??; For non-GUI threads this field == KeServiceDescriptorTable
??; and it points to ServiceDescriptorTable
??; For GUI threads
??; ServiceDescriptorTableShadow
??invoke KeGetCurrentThread
??mov edi, 200h-4
??.while edi
? ? .break .if dword ptr [eax][edi] == esi
? ? dec edi
??.endw
??.if edi != 0
? ? ; edi = offset to ServiceTable field in KTHREAD structure
? ? mov dwThreadId, 080h
? ? .while dwThreadId < 400h
? ?? ?push eax? ?? ?? ? ; reserve DWORD on stack
? ?? ?invoke PsLookupThreadByThreadId, dwThreadId, esp
? ?? ?pop ecx? ?? ?? ?? ?; -> ETHREAD/KTHREAD
? ?? ?.if eax == STATUS_SUCCESS
? ?? ???push dword ptr [ecx][edi]
? ?? ???fastcall ObfDereferenceObject, ecx
? ?? ???pop eax
? ?? ???.if eax != esi
? ?? ?? ? mov edx, MmSystemRangeStart
? ?? ?? ? mov edx, [edx]
? ?? ?? ? mov edx, [edx]
? ?? ?? ? .if eax > edx? ? ; some stupid error checking
? ?? ?? ?? ?mov ebx, eax
? ?? ?? ?? ?invoke DbgPrint, $CTA0("FindShadowTable: Found in thread with ID: %X\n"), dwThreadId
? ?? ?? ?? ?.break
? ?? ?? ? .endif
? ?? ???.endif
? ?? ?.endif
? ?? ?add dwThreadId, 4
? ? .endw
??.endif
??mov eax, ebx
??ret
GetServiceDescriptorTableShadowAddress endp
4.mj0011所說的搜索有效內存地址的辦法
復制內容到剪貼板
代碼:
/*
define structure for the system service table
*/
struct SYS_SERVICE_TABLE {
void **ServiceTable;
unsigned long CounterTable;
unsigned long ServiceLimit;
void **ArgumentsTable;
};
SYSTEM_DESCRIPTOR_TABLE KeServiceDescriptorTableShadow;
/*
Define KeServiceDescriptorTable based on the SST structure
*/
extern struct SYS_SERVICE_TABLE *KeServiceDescriptorTable;
/*
Declare function GetServiceDescriptorShadowTableAddress()
*/
//struct SYS_SERVICE_TABLE * GetServiceDescriptorShadowTableAddress ();
/*
Declare the KeAddSystemServiceTable. This is just a
handle to the call function, it will be used by the function
above to obtain the correct address of the KeServiceDescriptorShadowTable
*/
__declspec(dllimport) KeAddSystemServiceTable (ULONG, ULONG, ULONG, ULONG, ULONG);
struct SYS_SERVICE_TABLE * GetServiceDescriptorShadowTableAddress ()
{
// First, obtain a pointer to KeAddSystemServiceTable
unsigned char *check = (unsigned char*)KeAddSystemServiceTable;
int i;
//Initialize an instance of System Service Table, will be used to
//obtain an address from KeAddSystemServiceTable
struct SYS_SERVICE_TABLE *rc=0;
// Make 100 attempts to match a valid address with that of KeServiceDescriptorTable
for (i=0; i<=99; i++) {
__try {
// try to obtain an address from KeAddSystemServiceTable
rc = *(struct SYS_SERVICE_TABLE**)check;
// if this address is NOT valid OR it itself is the address of
//KeServiceDescriptorTable OR its first entry is NOT equal
//to the first entry of KeServiceDescriptorTable
if (!MmIsAddressValid (rc) || (rc == KeServiceDescriptorTable)
??|| (memcmp (rc, KeServiceDescriptorTable, sizeof (*rc)) != 0)) {
??// Proceed with the next address
??check++;
??// don't forget to reset the old address
??rc = 0;
}
} __except (EXCEPTION_EXECUTE_HANDLER) { rc = 0; }
// when the loop is completed, check if it produced a valid address
if (rc)
// because if it didn't, we failed to find the address of KeServiceDescriptorTableShadow
break;
}
// otherwise, there is a valid address! So return it!
return rc;
}
二。函數名定位
這個似乎沒有多少好辦法,解析pdb可以是可以 但是很麻煩
不過還好的是 同一個版本的系統 調用號一樣
所以只需要3套 2k xp 2k3的調用號就可以完成hook
pdb辦法
SymInitialize初始化
SymSetSearchPath “srv**symbols*
http://msdl.microsoft.com/”
SymLoadModule
SymGetSymFromName
老v曾經發出1個獲取shadow地址和函數名稱的工具 使用他可以很方便的獲取
具體代碼可以F5查看
以上讀取pdb的方法測試使用的可以的
但是實際使用很麻煩
經過分析 我找到了1個比較好的定位辦法
我們以SetWindowsHookExA為例子
復制內容到剪貼板
代碼:
.text:77D311D1 ; HHOOK __stdcall SetWindowsHookExA(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId)
.text:77D311D1? ?? ???public _SetWindowsHookExA@16
.text:77D311D1 _SetWindowsHookExA@16 proc near
.text:77D311D1
.text:77D311D1 idHook? ???= dword ptr 8
.text:77D311D1 lpfn? ?? ?= dword ptr 0Ch
.text:77D311D1 hModule? ? = dword ptr 10h
.text:77D311D1 dwThreadId? ?= dword ptr 14h
.text:77D311D1
.text:77D311D1? ?? ???mov??edi, edi
.text:77D311D3? ?? ???push??ebp
.text:77D311D4? ?? ???mov??ebp, esp
.text:77D311D6? ?? ???push??2? ?? ? ; int
.text:77D311D8? ?? ???push??[ebp+dwThreadId] ; int
.text:77D311DB? ?? ???push??[ebp+hModule] ; hModule
.text:77D311DE? ?? ???push??[ebp+lpfn]? ?; int
.text:77D311E1? ?? ???push??[ebp+idHook]??; int
.text:77D311E4? ?? ???call??_SetWindowsHookExAW@20 ; SetWindowsHookExAW(x,x,x,x,x)
.text:77D311E9? ?? ???pop??ebp
.text:77D311EA? ?? ???retn??10h
.text:77D311EA _SetWindowsHookExA@16 endp
.text:77D2DCFD ; int __stdcall SetWindowsHookExAW(int, int, HMODULE hModule, int, int)
.text:77D2DCFD _SetWindowsHookExAW@20 proc near? ? ; CODE XREF: SetWindowsHookExW(x,x,x,x)+13p
.text:77D2DCFD? ?? ?? ?? ?? ?? ???; SetWindowsHookExA(x,x,x,x)+13p
.text:77D2DCFD
.text:77D2DCFD Filename? ? = word ptr -20Ch
.text:77D2DCFD var_4? ???= dword ptr -4
.text:77D2DCFD arg_0? ???= dword ptr 8
.text:77D2DCFD arg_4? ???= dword ptr 0Ch
.text:77D2DCFD hModule? ? = dword ptr 10h
.text:77D2DCFD arg_C? ???= dword ptr 14h
.text:77D2DCFD arg_10? ???= dword ptr 18h
.text:77D2DCFD
.text:77D2DCFD? ?? ???mov??edi, edi
.text:77D2DCFF? ?? ???push??ebp
.text:77D2DD00? ?? ???mov??ebp, esp
.text:77D2DD02? ?? ???sub??esp, 20Ch
.text:77D2DD08? ?? ???mov??eax, ___security_cookie
.text:77D2DD0D? ?? ???push??esi
.text:77D2DD0E? ?? ???mov??esi, [ebp+hModule]
.text:77D2DD11? ?? ???test??esi, esi
.text:77D2DD13? ?? ???push??edi
.text:77D2DD14? ?? ???mov??edi, [ebp+arg_4]
.text:77D2DD17? ?? ???mov??[ebp+var_4], eax
.text:77D2DD1A? ?? ???jz? ?short loc_77D2DD33
.text:77D2DD1C? ?? ???push??104h? ?? ?; nSize
.text:77D2DD21? ?? ???lea??eax, [ebp+Filename]
.text:77D2DD27? ?? ???push??eax? ?? ?; lpFilename
.text:77D2DD28? ?? ???push??esi? ?? ?; hModule
.text:77D2DD29? ?? ???call??ds:__imp__GetModuleFileNameW@12 ; GetModuleFileNameW(x,x,x)
.text:77D2DD2F? ?? ???test??eax, eax
.text:77D2DD31? ?? ???jz? ?short loc_77D2DD52
.text:77D2DD33
.text:77D2DD33 loc_77D2DD33:? ?? ?? ?? ? ; CODE XREF: SetWindowsHookExAW(x,x,x,x,x)+1Dj
.text:77D2DD33? ?? ???push??[ebp+arg_10]
.text:77D2DD36? ?? ???mov??eax, esi
.text:77D2DD38? ?? ???push??edi
.text:77D2DD39? ?? ???push??[ebp+arg_0]
.text:77D2DD3C? ?? ???neg??eax
.text:77D2DD3E? ?? ???push??[ebp+arg_C]
.text:77D2DD41? ?? ???sbb??eax, eax
.text:77D2DD43? ?? ???lea??ecx, [ebp+Filename]
.text:77D2DD49? ?? ???and??eax, ecx
.text:77D2DD4B? ?? ???push??eax
.text:77D2DD4C? ?? ???push??esi
.text:77D2DD4D? ?? ???call??__SetWindowsHookEx@24 ; _SetWindowsHookEx(x,x,x,x,x,x)
.text:77D2DD52
.text:77D2DD52 loc_77D2DD52:? ?? ?? ?? ? ; CODE XREF: SetWindowsHookExAW(x,x,x,x,x)+34j
.text:77D2DD52? ?? ???mov??ecx, [ebp+var_4]
.text:77D2DD55? ?? ???pop??edi
.text:77D2DD56? ?? ???pop??esi
.text:77D2DD57? ?? ???call??@__security_check_cookie@4 ; __security_check_cookie(x)
.text:77D2DD5C? ?? ???leave
.text:77D2DD5D? ?? ???retn??14h
.text:77D2DD5D _SetWindowsHookExAW@20 endp
.text:77D2DD5D
.text:77D2DD65 ; __stdcall _SetWindowsHookEx(x, x, x, x, x, x)
.text:77D2DD65 __SetWindowsHookEx@24 proc near? ? ; CODE XREF: SetWindowsHookExAW(x,x,x,x,x)+50p
.text:77D2DD65
.text:77D2DD65 var_10? ???= byte ptr -10h
.text:77D2DD65 var_8? ???= dword ptr -8
.text:77D2DD65 var_4? ???= dword ptr -4
.text:77D2DD65 arg_0? ???= dword ptr 8
.text:77D2DD65 arg_4? ???= dword ptr 0Ch
.text:77D2DD65 arg_8? ???= dword ptr 10h
.text:77D2DD65 arg_C? ???= dword ptr 14h
.text:77D2DD65 arg_10? ???= dword ptr 18h
.text:77D2DD65 arg_14? ???= dword ptr 1Ch
.text:77D2DD65
.text:77D2DD65? ?? ???mov??edi, edi
.text:77D2DD67? ?? ???push??ebp
.text:77D2DD68? ?? ???mov??ebp, esp
.text:77D2DD6A? ?? ???sub??esp, 10h
.text:77D2DD6D? ?? ???push??[ebp+arg_4]
.text:77D2DD70? ?? ???and??[ebp+var_4], 0
.text:77D2DD74? ?? ???lea??eax, [ebp+var_10]
.text:77D2DD77? ?? ???push??eax
.text:77D2DD78? ?? ???mov??[ebp+var_8], eax
.text:77D2DD7B? ?? ???call??ds:__imp__RtlInitUnicodeString@8 ; RtlInitUnicodeString(x,x)
.text:77D2DD81? ?? ???push??[ebp+arg_14]
.text:77D2DD84? ?? ???push??[ebp+arg_10]
.text:77D2DD87? ?? ???push??[ebp+arg_C]
.text:77D2DD8A? ?? ???push??[ebp+arg_8]
.text:77D2DD8D? ?? ???push??[ebp+var_8]
.text:77D2DD90? ?? ???push??[ebp+arg_0]
.text:77D2DD93? ?? ???call??_NtUserSetWindowsHookEx@24 ; NtUserSetWindowsHookEx(x,x,x,x,x,x)
.text:77D2DD98? ?? ???leave
.text:77D2DD99? ?? ???retn??18h
.text:77D2DD99 __SetWindowsHookEx@24 endp
.text:77D2DD99
.text:77D2DDA1 ; __stdcall NtUserSetWindowsHookEx(x, x, x, x, x, x)
.text:77D2DDA1 _NtUserSetWindowsHookEx@24 proc near??; CODE XREF: _SetWindowsHookEx(x,x,x,x,x,x)+2Ep
.text:77D2DDA1? ?? ???mov??eax, 1225h
.text:77D2DDA6? ?? ???mov??edx, 7FFE0300h
.text:77D2DDAB? ?? ???call??dword ptr [edx]
.text:77D2DDAD? ?? ???retn??18h
.text:77D2DDAD _NtUserSetWindowsHookEx@24 endp
我們看這里 .text:77D2DDA1? ?? ???mov??eax, 1225h
里面有和ssdt一樣的調用號 但是有個問題 NtUserSetWindowsHookEx在user32里面沒有導出,我們需要多次搜索才行
復制內容到剪貼板
代碼:
HHOOK STDCALL??NtUserSetWindowsHookEx (HINSTANCE Mod, PUNICODE_STRING UnsafeModuleName, DWORD ThreadId, int HookId, HOOKPROC HookProc, BOOL Ansi)
這個就是原型 不過搜索起來確實是比較麻煩的,不知道還有無其他好辦法了