刺激战场
六合彩
贵宾厅
  • 1619阅读
  • 0回复

64位下SSDT获取 [复制链接]

上一主题 下一主题
离线天道酬勤
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-02-14
— 本帖被 天道酬勤 从 驱动保护 移动到本区(2016-05-19) —

Windows Vista x64版本相比于以前版本使用了不同的数据结构和不同的地址。

根据在ReactOS中找到的KTHREAD信息中,包含了SSDT表的信息。

SSDT in KTHREAD
typedef struct _KTHREAD {
   DISPATCHER_HEADER Header;
   LIST_ENTRY MutantListHead;
   PVOID InitialStack;
   PVOID StackLimit;
#if defined(_IA64_)
   PVOID InitialBStore;
   PVOID BStoreLimit;
#endif
   PVOID Teb;
   PVOID TlsArray;
   PVOID KernelStack;
#if defined(_IA64_)
   PVOID KernelBStore;
#endif
   BOOLEAN DebugActive;
   UCHAR State;
   BOOLEAN Alerted[MaximumMode];
   UCHAR Iopl;
   UCHAR NpxState;
   CHAR Saturation;
   SCHAR Priority;
   KAPC_STATE ApcState;
   ULONG ContextSwitches;
   LONG_PTR WaitStatus;
   KIRQL WaitIrql;
   KPROCESSOR_MODE WaitMode;
   BOOLEAN WaitNext;
   UCHAR WaitReason;
   PRKWAIT_BLOCK WaitBlockList;
   LIST_ENTRY WaitListEntry;
   ULONG WaitTime;
   SCHAR BasePriority;
   UCHAR DecrementCount;
   SCHAR PriorityDecrement;
   SCHAR Quantum;
   KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1];
   PVOID LegoData;
   ULONG KernelApcDisable;
   KAFFINITY UserAffinity;
   BOOLEAN SystemAffinityActive;
   UCHAR PowerState;
   UCHAR NpxIrql;
   UCHAR Pad[1];
   PVOID ServiceTable;
   ......
} KTHREAD, *PKTHREAD;

使用KeGetCurrentThread()就可以得到这个结构。
但在Vista x64中ServiceTable字段已经被去掉了,并且KeSystemSeriveTable也没有导出。
要使用SSDT表就要找到新的获得地址方法。

以下实验数据在build number 6001的Vista SP1中得到。
在使用becedit /debug on来开启本地调试模式,并载入正确的符号表。

windows仍然使用ntdll.dll来切换CPL3到CPL0,Vista使用SYSCALL指令完成切换。
根据Intel instructions hoodbook中的解释:
SYSCALL:Fast System Call
SYSRET: Return From Fast System Call

SYSCALL保存RIP的值到RCX中,并载入新的RIP值:IA32_LSTAR。

lkd> u ntdll!NtCreateFile
ntdll!NtCreateFile:
00000000`772b5fc0 4c8bd1          mov     r10,rcx ;NtXXXXX
00000000`772b5fc3 b852000000      mov     eax,52h ; FunctionIndex
00000000`772b5fc8 0f05            syscall
00000000`772b5fca c3              ret

在KiInitializeBootStructures中:

lea rax, KiSystemCall32
mov ecx, 0C0000083 ;CSTAR
mov rdx, rax
shr rdx, 20
wrmsr

lea rax, KiSystemCall64
mov ecx, 0C0000082 ;LSTAR
mov rdx, rax
shr rdx, 20
wrmsr

所以在Windbg中使用rdmsr命令直接看C0000082寄存器的值:
lkd> rdmsr C0000082
msr[c0000082] = fffff800`018b8c00
lkd> ln fffff800`018b8c00
(fffff800`018b8c00)   nt!KiSystemCall64   |  (fffff800`018b8cbe)   nt!KiSystemServiceStart
Exact matches:
   nt!KiSystemCall64 =

返回的值正是KiSystemCall64。
反汇编此函数发现它确实和Vista之前的版本不一样,以前是使用当前线程的KTHREAD中查找SSDT值,而现在是直接使用。

lkd> uf fffff800`018b8c00
Flow analysis was incomplete, some code may be missing
nt!KiSystemCall64:
fffff800`018b8c00 0f01f8                swapgs
fffff800`018b8c03 654889242510000000    mov   qword ptr gs:[10h],rsp
fffff800`018b8c0c 65488b2425a8010000    mov   rsp,qword ptr gs:[1A8h]
fffff800`018b8c15 6a2b                  push    2Bh
fffff800`018b8c17 65ff342510000000      push    qword ptr gs:[10h]
fffff800`018b8c1f 4153                  push    r11
fffff800`018b8c21 6a33                  push    33h
fffff800`018b8c23 51                    push    rcx
fffff800`018b8c24 498bca                mov     rcx,r10
fffff800`018b8c27 4883ec08              sub     rsp,8
fffff800`018b8c2b 55                    push    rbp
fffff800`018b8c2c 4881ec58010000        sub     rsp,158h
fffff800`018b8c33 488dac2480000000      lea     rbp,[rsp+80h]
fffff800`018b8c3b 48899dc0000000        mov     qword ptr [rbp+0C0h],rbx
fffff800`018b8c42 4889bdc8000000        mov     qword ptr [rbp+0C8h],rdi
fffff800`018b8c49 4889b5d0000000        mov     qword ptr [rbp+0D0h],rsi
fffff800`018b8c50 c645ab02              mov     byte ptr [rbp-55h],2
fffff800`018b8c54 65488b1c2588010000    mov   rbx,qword ptr gs:[188h]
fffff800`018b8c5d 0f0d8bc8010000        prefetchw [rbx+1C8h]
fffff800`018b8c64 0fae5dac              stmxcsr dword ptr [rbp-54h]
fffff800`018b8c68 650fae142580010000    ldmxcsr dword ptr gs:[180h]
fffff800`018b8c71 807b0300              cmp     byte ptr [rbx+3],0
fffff800`018b8c75 66c785800000000000    mov   word ptr [rbp+80h],0
fffff800`018b8c7e 7430                  je      nt!KiSystemCall64+0xb0 (fffff800`018b8cb0)
fffff800`018b8c80 488945b0              mov     qword ptr [rbp-50h],rax
fffff800`018b8c84 48894db8              mov     qword ptr [rbp-48h],rcx
fffff800`018b8c88 488955c0              mov     qword ptr [rbp-40h],rdx
fffff800`018b8c8c 4c8945c8              mov     qword ptr [rbp-38h],r8
fffff800`018b8c90 4c894dd0              mov     qword ptr [rbp-30h],r9
fffff800`018b8c94 e8770b0000            call    nt!KiSaveDebugRegisterState (fffff800`018b9810)
fffff800`018b8c99 488b45b0              mov     rax,qword ptr [rbp-50h]
fffff800`018b8c9d 488b4db8              mov     rcx,qword ptr [rbp-48h]
fffff800`018b8ca1 488b55c0              mov     rdx,qword ptr [rbp-40h]
fffff800`018b8ca5 4c8b45c8              mov     r8,qword ptr [rbp-38h]
fffff800`018b8ca9 4c8b4dd0              mov     r9,qword ptr [rbp-30h]
fffff800`018b8cad 666690                xchg    ax,ax
fffff800`018b8cb0 fb                    sti
fffff800`018b8cb1 48898bd0010000        mov     qword ptr [rbx+1D0h],rcx
fffff800`018b8cb8 8983e8010000          mov     dword ptr [rbx+1E8h],eax
fffff800`018b8cbe 4889a3c8010000        mov     qword ptr [rbx+1C8h],rsp
fffff800`018b8cc5 8bf8                  mov     edi,eax
fffff800`018b8cc7 c1ef07                shr     edi,7
fffff800`018b8cca 83e720                and     edi,20h
fffff800`018b8ccd 25ff0f0000            and     eax,0FFFh
fffff800`018b8cd2 4c8d15a74c1d00        lea     r10,[nt!KeServiceDescriptorTable (fffff800`01a8d980)]
fffff800`018b8cd9 4c8d1de04c1d00        lea     r11,[nt!KeServiceDescriptorTableShadow (fffff800`01a8d9c0)]
fffff800`018b8ce0 f783f400000000010000  test dword ptr [rbx+0F4h],100h
fffff800`018b8cea 4d0f45d3              cmovne  r10,r11
fffff800`018b8cee 423b441710            cmp     eax,dword ptr [rdi+r10+10h]
fffff800`018b8cf3 0f83ad020000          jae     nt!KiSystemServiceExit+0x16b (fffff800`018b8fa6)

这个函数直接使用汇编写成,所以改动的可能性不大。
从018b8c00到018b8cFF搜索就可以找到KeServiceDescriptorTable的值。
f783f400000000010000和4c8d15a74c1d00、4c8d1de04c1d00都可以作为搜索特征定位。

下面就要从汇编指令中提取地址信息。
lkd> dd fffff800018b8cd2
fffff800`018b8cd2  a7158d4c 4c001d4c 4ce01d8d 83f7001d
fffff800`018b8ce2  000000f4 00000100 d3450f4d 17443b42
fffff800`018b8cf2  ad830f10 4e000002 4d17148b 49821c63
fffff800`018b8d02  c149c38b 034d04fb 20ff83d3 8b4c5075
fffff800`018b8d12  0000b09b bb834100 00001740 483f7400
fffff800`018b8d22  48b04589 48b84d89 49c05589 8b49d88b
fffff800`018b8d32  f28b49f9 4b3415ff 8b48001d 8b48b045
fffff800`018b8d42  8b48b84d 8b4cc055 cf8b4cc3 66d68b4c

01a8d980 - 018b8cd2 = 1D4CAE a74c1d00
01a8d9c0 - 018b8cd9 = 1D4CE7 e04c1d00

通过观察汇编指令4c8d15a74c1d00中数据与目标地址的关系可以得到下面的算法:

PDWORD  pdwFindCodeAddress = (PBYTE)018b8cd2;
PVOID   pTable = NULL;
pTable = (ULONG_PTR)pdwFindCodeAddress + (ULONG_PTR)((((*(pdwFindCodeAddress + 1)) & 0xFFFF) << 8) + ((*pdwFindCodeAddress) >> 24 + 7));

pTable就是我们要找的SSDT表地址。SSDT表项也有原来的32位变成64位。


#include
#include

EXTERN_C
__int64 __readmsr(int);

#pragma intrinsic(__readmsr)

#define SEARCH_RANGE 0xFF

ULONG_PTR
GetSSDTAddressAtVista64(
   VOID
   )
{
   PUCHAR     pucStartSearchAddress   = (PUCHAR)__readmsr(0xC0000082);
   PUCHAR     pucEndSearchAddress     = pucStartSearchAddress + SEARCH_RANGE;
   PDWORD      pdwFindCodeAddress      = NULL;
   ULONG_PTR   SSDT = 0;

   for (; pucStartSearchAddress < pucEndSearchAddress; pucStartSearchAddress++)
   {
       if ((*(PDWORD)pucStartSearchAddress & 0xFFFFFF00) == 0x83f70000)
       {
           pdwFindCodeAddress = (PDWORD)(pdwStartSearchAddress - 12);
           SSDT = (ULONG_PTR)pdwFindCodeAddress +
                   (((*(PDWORD)pdwFindCodeAddress) >> 24) + 7) + //ae
                   (ULONG_PTR)(((*(PDWORD)(pdwFindCodeAddress + 1)) & 0xFFFF) << 8); //id4c
           break;
       }
   }
   return SSDT;
}
快速回复
限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
上一个 下一个