shadow ssdt学习笔记

来源:互联网 发布:西语助手mac 编辑:程序博客网 时间:2024/06/06 01:04
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;
}

5。老V提出的搜索.data区段开头的办法


如图所示 开始地方就是地址表 用这个办法的好处是不会受到KeServiceDescriptorTableShadow .win32k.ServiceTable被重定位的影响,可以从win32k的基址直接+区段VA获得
代码如下
DWORD FindPeSection( DWORD pModuleBase, PCHAR SectionName )
{
    PIMAGE_DOS_HEADER        pDosHdr;
    PIMAGE_NT_HEADERS        pNtHdr;
    PIMAGE_SECTION_HEADER    pSecHdr;
    PIMAGE_EXPORT_DIRECTORY  pExtDir;

    UINT                    ui,uj;
    PCHAR                    FunName;
    DWORD                    *dwAddrName;
    DWORD                    *dwAddrFun;
    FARPROC                    pOldFun;
    ULONG                    uAttrib;


    pDosHdr = ( PIMAGE_DOS_HEADER )pModuleBase;

    if ( IMAGE_DOS_SIGNATURE == pDosHdr->e_magic )
    {
        pNtHdr = ( PIMAGE_NT_HEADERS )( pModuleBase + pDosHdr->e_lfanew );

        if( IMAGE_NT_SIGNATURE  == pNtHdr->Signature ||    IMAGE_NT_SIGNATURE1 == pNtHdr->Signature )
        {
            pSecHdr = ( PIMAGE_SECTION_HEADER )( pModuleBase + pDosHdr->e_lfanew + sizeof( IMAGE_NT_HEADERS ) );

            for ( ui = 0; ui < (UINT)pNtHdr->FileHeader.NumberOfSections; ui++ )
            {
                if ( !strcmp( pSecHdr->Name, SectionName ) )
//                if ( !strcmp( pSecHdr->Name, ".data" ) )
                { 
                    dprintf("find it %s",pSecHdr->Name);
                    return pModuleBase + pSecHdr->VirtualAddress;


                }

                pSecHdr++;
            }
        }
    }

    return 0;
}


二。函数名定位
这个似乎没有多少好办法,解析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)
这个就是原型 不过搜索起来确实是比较麻烦的,不知道还有无其他好办法了

 
原创粉丝点击