寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
? 華麗的分割線 ?
小節
本篇的知識挺簡單的,就介紹了兩個表:程式控制程式碼表和全域性控制程式碼表。程式控制程式碼表歸屬程式私有,每一個程式一個控制程式碼表。上一篇的課後思考題答案先不急的給你,下面我們來擴充套件一下控制程式碼表的相關知識,你在來看看你寫的程式碼。
控制程式碼表結構體擴充套件
如下是WRK
裡面的控制程式碼表結構,重點是看註釋,介紹擴充套件一下我講解基礎沒講到知識點:
typedef struct _HANDLE_TABLE {
//
// A pointer to the top level handle table tree node.
//
ULONG_PTR TableCode;
//
// The process who is being charged quota for this handle table and a
// unique process id to use in our callbacks
//
struct _EPROCESS *QuotaProcess;
HANDLE UniqueProcessId;
//
// These locks are used for table expansion and preventing the A-B-A problem
// on handle allocate.
//
#define HANDLE_TABLE_LOCKS 4
EX_PUSH_LOCK HandleTableLock[HANDLE_TABLE_LOCKS];
//
// The list of global handle tables. This field is protected by a global
// lock.
//
LIST_ENTRY HandleTableList;
//
// Define a field to block on if a handle is found locked.
//
EX_PUSH_LOCK HandleContentionEvent;
//
// Debug info. Only allocated if we are debugging handles
//
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
//
// The number of pages for additional info.
// This counter is used to improve the performance
// in ExGetHandleInfo
//
LONG ExtraInfoPages;
//
// This is a singly linked list of free table entries. We don't actually
// use pointers, but have each store the index of the next free entry
// in the list. The list is managed as a lifo list. We also keep track
// of the next index that we have to allocate pool to hold.
//
ULONG FirstFree;
//
// We free handles to this list when handle debugging is on or if we see
// that a thread has this handles bucket lock held. The allows us to delay reuse
// of handles to get a better chance of catching offenders
//
ULONG LastFree;
//
// This is the next handle index needing a pool allocation. Its also used as a bound
// for good handles.
//
ULONG NextHandleNeedingPool;
//
// The number of handle table entries in use.
//
LONG HandleCount;
//
// Define a flags field
//
union {
ULONG Flags;
//
// For optimization we reuse handle values quickly. This can be a problem for
// some usages of handles and makes debugging a little harder. If this
// bit is set then we always use FIFO handle allocation.
//
BOOLEAN StrictFIFO : 1;
};
} HANDLE_TABLE, *PHANDLE_TABLE;
然後我再把WinDbg
獲取的結構體拿出來:
kd> dt _HANDLE_TABLE
ntdll!_HANDLE_TABLE
+0x000 TableCode : Uint4B
+0x004 QuotaProcess : Ptr32 _EPROCESS
+0x008 UniqueProcessId : Ptr32 Void
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO
+0x02c ExtraInfoPages : Int4B
+0x030 FirstFree : Uint4B
+0x034 LastFree : Uint4B
+0x038 NextHandleNeedingPool : Uint4B
+0x03c HandleCount : Int4B
+0x040 Flags : Uint4B
+0x040 StrictFIFO : Pos 0, 1 Bit
可以說是一模一樣的,編制作業系統時為了保持相容性,結構體的每一個成員的含義是一樣的。
有沒有注意到HandleCount
這個成員,這個是我們做思考題的一個坑,就是我知道遍歷有效控制程式碼的次數,就得需要這個值,其實也可以不用,因為控制程式碼是按物理頁進行的。當然,如何獲取一個程式的控制程式碼數可以通過ObGetProcessHandleCount
這個函式。這個函式是未匯出的,如下是我整理好的虛擬碼:
int __stdcall ObGetProcessHandleCount(PEPROCESS Process)
{
_HANDLE_TABLE *objtable; // eax
int handleCount; // esi
objtable = ObReferenceProcessHandleTable(Process);
if ( !objtable )
return 0;
handleCount = objtable->HandleCount;
ObDereferenceProcessHandleTable(Process);
return handleCount;
}
_HANDLE_TABLE *__stdcall ObReferenceProcessHandleTable(_EPROCESS *Process)
{
_HANDLE_TABLE *objtable; // edi
objtable = 0;
if ( ExAcquireRundownProtection(&Process->RundownProtect) )
{
objtable = Process->ObjectTable;
if ( !objtable )
ExReleaseRundownProtection(&Process->RundownProtect);
}
return objtable;
}
void __stdcall ObDereferenceProcessHandleTable(_EPROCESS *eprocess)
{
ExReleaseRundownProtection(&eprocess->RundownProtect);
}
ObReferenceObjectByHandle 淺析
我們來看看作業系統是如何使用控制程式碼的,定位到我們熟悉的函式ObReferenceObjectByHandle
:
NTSTATUS __stdcall ObReferenceObjectByHandle(HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID *Object, POBJECT_HANDLE_INFORMATION HandleInformation)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
currentThread = KeGetCurrentThread();
attachedProcess = currentThread;
*Object = 0;
if ( Handle >= 0 )
{
objHandleTable = currentThread->Tcb.ApcState.Process->ObjectTable;
}
else
{
if ( Handle == -1 )
{
if ( ObjectType == PsProcessType || !ObjectType )
{
attachedProcess = currentThread->Tcb.ApcState.Process;
if ( (~attachedProcess->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
{
if ( HandleInformation )
{
HandleInformation->GrantedAccess = attachedProcess->GrantedAccess;
HandleInformation->HandleAttributes = 0;
}
if ( ObpTraceEnabled )
ObpPushStackInfo(&attachedProcess[-1u].584, 1);
++attachedProcess[-1u].Flags;
LABEL_12:
*Object = attachedProcess;
return 0;
}
return STATUS_ACCESS_DENIED;
}
return STATUS_OBJECT_TYPE_MISMATCH;
}
if ( Handle == -2 )
{
if ( ObjectType == PsThreadType || !ObjectType )
{
if ( (~currentThread->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
{
if ( HandleInformation )
{
HandleInformation->GrantedAccess = currentThread->GrantedAccess;
HandleInformation->HandleAttributes = 0;
}
if ( ObpTraceEnabled )
ObpPushStackInfo(¤tThread[-1].ReadClusterSize, 1);
++attachedProcess[-1].Flags;
goto LABEL_12;
}
return STATUS_ACCESS_DENIED;
}
return STATUS_OBJECT_TYPE_MISMATCH;
}
if ( AccessMode )
return STATUS_INVALID_HANDLE;
Handle = (Handle ^ 0x80000000);
objHandleTable = ::ObpKernelHandleTable;
}
--attachedProcess->WorkingSetLock.Contention;
ObpKernelHandleTable = objHandleTable;
handleEntry = ExMapHandleToPointerEx(objHandleTable, Handle, AccessMode);
if ( handleEntry )
{
v12 = (handleEntry->InfoTable & 0xFFFFFFF8);
BackTraceHash = v12;
if ( v12[1].Object == ObjectType || !ObjectType )
{
if ( (NtGlobalFlag & 0x2000) != 0 )
{
if ( AccessMode || HandleInformation )
{
v14 = ObpTranslateGrantedAccessIndex(handleEntry->GrantedAccessIndex);
v12 = BackTraceHash;
v15 = v14;
}
else
{
v15 = Handle;
}
}
else
{
v15 = ~ObpAccessProtectCloseBit & handleEntry->GrantedAccess;
}
if ( (~v15 & DesiredAccess) == 0 || !AccessMode )
{
if ( *(ObpKernelHandleTable + 44) )
{
v16 = ExpGetHandleInfo(ObpKernelHandleTable, Handle, 1);
v12 = BackTraceHash;
ObjectTypea = v16;
}
else
{
ObjectTypea = 0;
}
if ( HandleInformation )
{
HandleInformation->GrantedAccess = v15;
if ( (ObpAccessProtectCloseBit & handleEntry->GrantedAccess) != 0 )
v17 = handleEntry->ObAttributes & 6 | 1;
else
v17 = handleEntry->ObAttributes & 6;
HandleInformation->HandleAttributes = v17;
}
if ( (handleEntry->Object & 4) != 0
&& ObjectTypea
&& ObjectTypea->Mutex.SystemResourcesList.Flink
&& DesiredAccess
&& AccessMode )
{
ObpAuditObjectAccess(Handle, ObjectTypea, v12[1].ObAttributes + 64, DesiredAccess);
v12 = BackTraceHash;
}
if ( ObpTraceEnabled )
ObpPushStackInfo(v12, 1);
++BackTraceHash->ObAttributes;
ExUnlockHandleTableEntry(ObpKernelHandleTable, handleEntry);
v19 = attachedProcess->WorkingSetLock.Contention++ == 4294967295;
if ( v19 && attachedProcess->Pcb.ActiveProcessors != &attachedProcess->Pcb.ActiveProcessors )
{
LOBYTE(v18) = 1;
BYTE1(attachedProcess->Pcb.SwapListEntry.Next) = 1;
HalRequestSoftwareInterrupt(v18);
}
*Object = &BackTraceHash[3];
return 0;
}
v13 = STATUS_ACCESS_DENIED;
}
else
{
v13 = STATUS_OBJECT_TYPE_MISMATCH;
}
ExUnlockHandleTableEntry(ObpKernelHandleTable, handleEntry);
}
else
{
v13 = STATUS_INVALID_HANDLE;
}
v19 = attachedProcess->WorkingSetLock.Contention++ == 4294967295;
if ( v19 && attachedProcess->Pcb.ActiveProcessors != &attachedProcess->Pcb.ActiveProcessors )
{
LOBYTE(v10) = 1;
BYTE1(attachedProcess->Pcb.SwapListEntry.Next) = 1;
HalRequestSoftwareInterrupt(v10);
}
return v13;
}
當然上面的翻譯的虛擬碼也是不準確的,有些函式我不熟悉,逆向不太明白,如下是我逆向的結果,由於彙編太長了,請點選摺疊觀看:
? 點選檢視程式碼 ?
; NTSTATUS __stdcall ObReferenceObjectByHandle(HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID *Object, POBJECT_HANDLE_INFORMATION HandleInformation)
public _ObReferenceObjectByHandle@24
_ObReferenceObjectByHandle@24 proc near ; CODE XREF: IopUnloadDriver(x,x)+1B8↑p
; MmCreateSection(x,x,x,x,x,x,x,x)+221↑p ...
ObpKernelHandleTable= dword ptr -8
OBJHeader = dword ptr -4
Handle = dword ptr 8
DesiredAccess = dword ptr 0Ch
ObjectType = dword ptr 10h
AccessMode = byte ptr 14h
Object = dword ptr 18h
HandleInformation= dword ptr 1Ch
mov edi, edi
push ebp
mov ebp, esp
push ecx
push ecx
push ebx
push esi
push edi
mov eax, large fs:_KPCR.PrcbData.CurrentThread
mov edi, [ebp+Object]
xor ebx, ebx ; EBX = 0
cmp [ebp+Handle], ebx ; CMP Handle , 0
mov esi, eax ; ESI = CurrentThread
mov [edi], ebx
jge NotFakeHandle ; >=0 JMP
cmp [ebp+Handle], -1
jnz short CheckIsThreadFake ; != -1 JMP
mov eax, [ebp+ObjectType]
cmp eax, _PsProcessType ; 判斷是否為 Process 型別
jz short CurrentProcessHandle ; 是的話跳走
cmp eax, ebx
jnz short loc_4D9B4F
CurrentProcessHandle: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+31↑j
mov esi, [esi+_KTHREAD.ApcState.Process]
mov ecx, [esi+_EPROCESS.GrantedAccess]
mov eax, ecx ; 至此 esi = AttachedProcess , eax = ecx = GrantedAccess
not eax ; 按位取反 Access
test [ebp+DesiredAccess], eax ; 如果是0的話,說明一致
jz short DesiredIsGranted ; 一致的話跳走
cmp [ebp+AccessMode], KernelMode
jnz short IsUserMode
DesiredIsGranted: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+47↑j
mov eax, [ebp+HandleInformation]
cmp eax, ebx ; CMP HandleInformation , 0
lea edx, [esi-18h] ; _OBJECT_HEADER
mov [ebp+OBJHeader], edx
jz short HandleInformationIsNULL
mov [eax+_OBJECT_HANDLE_INFORMATION.GrantedAccess], ecx
mov [eax+_OBJECT_HANDLE_INFORMATION.HandleAttributes], ebx
HandleInformationIsNULL: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+5A↑j
cmp _ObpTraceEnabled, 0
jz short ObpTraceEnabledIsFalse
push 1 ; char
push edx ; BackTraceHash
call _ObpPushStackInfo@8 ; ObpPushStackInfo(x,x)
ObpTraceEnabledIsFalse: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+68↑j
mov eax, 1
mov ecx, [ebp+OBJHeader]
xadd [ecx+_OBJECT_HEADER.PointerCount], eax ; 引用計數加1
loc_4D9B33: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+F0↓j
mov [edi], esi
jmp loc_4D9D25
; ---------------------------------------------------------------------------
CheckIsThreadFake: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+26↑j
cmp [ebp+Handle], -2
jnz short NotCurrentThread ; Handle != -2 跳走
mov eax, [ebp+ObjectType]
cmp eax, _PsThreadType
jz short IsThreadType
cmp eax, ebx ; cmp eax , 0
jz short IsThreadType
loc_4D9B4F: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+35↑j
mov eax, STATUS_OBJECT_TYPE_MISMATCH
jmp ProcEnding
; ---------------------------------------------------------------------------
IsThreadType: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+93↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+97↑j
mov ecx, [esi+_ETHREAD.GrantedAccess]
mov eax, ecx
not eax
test [ebp+DesiredAccess], eax
jz short DesiredIsGranted_0
cmp [ebp+AccessMode], KernelMode
jz short DesiredIsGranted_0
IsUserMode: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+4D↑j
mov eax, STATUS_ACCESS_DENIED
jmp ProcEnding
; ---------------------------------------------------------------------------
DesiredIsGranted_0: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+B0↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+B6↑j
mov eax, [ebp+HandleInformation]
cmp eax, ebx ; CMP HandleInformation , 0
lea edx, [esi-18h] ; _OBJECT_HEADER
mov [ebp+OBJHeader], edx
jz short loc_4D9B8A
mov [eax+_OBJECT_HANDLE_INFORMATION.GrantedAccess], ecx
mov [eax+_OBJECT_HANDLE_INFORMATION.HandleAttributes], ebx
loc_4D9B8A: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+CD↑j
cmp _ObpTraceEnabled, 0
jz short ObpTraceEnabledIsFalse_0
push 1 ; char
push edx ; BackTraceHash
call _ObpPushStackInfo@8 ; ObpPushStackInfo(x,x)
ObpTraceEnabledIsFalse_0: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+DB↑j
mov eax, 1
mov ecx, [ebp+OBJHeader]
xadd [ecx+_OBJECT_HEADER.PointerCount], eax ; 引用計數加1
jmp short loc_4D9B33
; ---------------------------------------------------------------------------
NotCurrentThread: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+88↑j
cmp [ebp+AccessMode], KernelMode
jnz short IsUserMode_1
xor [ebp+Handle], 80000000h
mov eax, _ObpKernelHandleTable
jmp short loc_4D9BCF
; ---------------------------------------------------------------------------
IsUserMode_1: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+F6↑j
mov eax, STATUS_INVALID_HANDLE
jmp ProcEnding
; ---------------------------------------------------------------------------
NotFakeHandle: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1C↑j
mov eax, [esi+_KTHREAD.ApcState.Process]
mov eax, [eax+_EPROCESS.ObjectTable]
loc_4D9BCF: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+104↑j
push dword ptr [ebp+AccessMode] ; char
dec [esi+_ETHREAD.Tcb.KernelApcDisable]
push [ebp+Handle] ; BugCheckParameter1
mov [ebp+ObpKernelHandleTable], eax
push eax ; BugCheckParameter2
call _ExMapHandleToPointerEx@12 ; ExMapHandleToPointerEx(x,x,x)
mov edi, eax
cmp edi, ebx
jz InvalidHandleProc
mov edx, [edi+_HANDLE_TABLE_ENTRY.___u0.InfoTable]
mov eax, [ebp+ObjectType]
and edx, 0FFFFFFF8h
cmp [edx+8], eax
mov [ebp+OBJHeader], edx
jz short loc_4D9C09
cmp eax, ebx
jz short loc_4D9C09
mov ebx, STATUS_OBJECT_TYPE_MISMATCH
jmp short ErrorProc
; ---------------------------------------------------------------------------
loc_4D9C09: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+146↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+14A↑j
test byte ptr _NtGlobalFlag+1, 20h
jz short loc_4D9C30
cmp [ebp+AccessMode], KernelMode
jnz short IsUserMode_0
cmp [ebp+HandleInformation], ebx
jz short loc_4D9C3E
IsUserMode_0: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+160↑j
xor eax, eax
mov ax, [edi+4]
push eax
call _ObpTranslateGrantedAccessIndex@4 ; ObpTranslateGrantedAccessIndex(x)
mov edx, [ebp+OBJHeader]
mov ebx, eax
jmp short loc_4D9C41
; ---------------------------------------------------------------------------
loc_4D9C30: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+15A↑j
mov eax, _ObpAccessProtectCloseBit
mov ebx, [edi+4]
not eax
and ebx, eax
jmp short loc_4D9C41
; ---------------------------------------------------------------------------
loc_4D9C3E: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+165↑j
mov ebx, [ebp+Handle]
loc_4D9C41: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+178↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+186↑j
mov eax, ebx
not eax
test [ebp+DesiredAccess], eax
jz short loc_4D9C63
cmp [ebp+AccessMode], 0
jz short loc_4D9C63
mov ebx, STATUS_ACCESS_DENIED
ErrorProc: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+151↑j
push edi ; BugCheckParameter2
push [ebp+ObpKernelHandleTable] ; int
call _ExUnlockHandleTableEntry@8 ; ExUnlockHandleTableEntry(x,x)
jmp loc_4D9D2E
; ---------------------------------------------------------------------------
loc_4D9C63: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+192↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+198↑j
mov eax, [ebp+ObpKernelHandleTable]
cmp dword ptr [eax+2Ch], 0
jz short loc_4D9C7F
push 1
push [ebp+Handle]
push eax
call _ExpGetHandleInfo@12 ; ExpGetHandleInfo(x,x,x)
mov edx, [ebp+OBJHeader]
mov [ebp+ObjectType], eax
jmp short loc_4D9C83
; ---------------------------------------------------------------------------
loc_4D9C7F: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1B4↑j
and [ebp+ObjectType], 0
loc_4D9C83: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1C7↑j
mov eax, [ebp+HandleInformation]
test eax, eax
jz short loc_4D9CA7
mov [eax+4], ebx
mov ecx, _ObpAccessProtectCloseBit
test [edi+4], ecx
mov ecx, [edi]
jz short loc_4D9CA2
and ecx, 6
or ecx, 1
jmp short loc_4D9CA5
; ---------------------------------------------------------------------------
loc_4D9CA2: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1E2↑j
and ecx, 6
loc_4D9CA5: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1EA↑j
mov [eax], ecx
loc_4D9CA7: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1D2↑j
test byte ptr [edi], 4
jz short loc_4D9CDA
mov eax, [ebp+ObjectType]
test eax, eax
jz short loc_4D9CDA
cmp dword ptr [eax], 0
jz short loc_4D9CDA
cmp [ebp+DesiredAccess], 0
jz short loc_4D9CDA
cmp [ebp+AccessMode], 0
jz short loc_4D9CDA
push [ebp+DesiredAccess]
mov ecx, [edx+8]
add ecx, 40h ; '@'
push ecx
push eax
push [ebp+Handle]
call _ObpAuditObjectAccess@16 ; ObpAuditObjectAccess(x,x,x,x)
mov edx, [ebp+OBJHeader]
loc_4D9CDA: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1F4↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+1FB↑j ...
cmp _ObpTraceEnabled, 0
jz short loc_4D9CEB
push 1 ; char
push edx ; BackTraceHash
call _ObpPushStackInfo@8 ; ObpPushStackInfo(x,x)
loc_4D9CEB: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+22B↑j
mov eax, 1
mov ecx, [ebp+OBJHeader]
xadd [ecx+_OBJECT_HANDLE_INFORMATION.HandleAttributes], eax
push edi ; BugCheckParameter2
push [ebp+ObpKernelHandleTable] ; int
call _ExUnlockHandleTableEntry@8 ; ExUnlockHandleTableEntry(x,x)
inc [esi+_ETHREAD.Tcb.KernelApcDisable]
jnz short MovObject
lea eax, [esi+_ETHREAD.Tcb.ApcState]
cmp [eax+_KAPC_STATE.ApcListHead.Flink], eax
jz short MovObject
mov cl, 1
mov byte ptr [esi+49h], 1
call ds:__imp_@HalRequestSoftwareInterrupt@4 ; HalRequestSoftwareInterrupt(x)
MovObject: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+24F↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+256↑j
mov eax, [ebp+OBJHeader]
mov ecx, [ebp+Object]
add eax, 18h
mov [ecx], eax
loc_4D9D25: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+7F↑j
xor eax, eax
jmp short ProcEnding
; ---------------------------------------------------------------------------
InvalidHandleProc: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+132↑j
mov ebx, STATUS_INVALID_HANDLE
loc_4D9D2E: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+1A8↑j
inc [esi+_ETHREAD.Tcb.KernelApcDisable]
jnz short loc_4D9D49
lea eax, [esi+_ETHREAD.Tcb.ApcState]
cmp [eax+_KAPC_STATE.ApcListHead.Flink], eax
jz short loc_4D9D49
mov cl, 1
mov [esi+_ETHREAD.Tcb.ApcState.KernelApcPending], 1
call ds:__imp_@HalRequestSoftwareInterrupt@4 ; HalRequestSoftwareInterrupt(x)
loc_4D9D49: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+27E↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+285↑j
mov eax, ebx
ProcEnding: ; CODE XREF: ObReferenceObjectByHandle(x,x,x,x,x,x)+9E↑j
; ObReferenceObjectByHandle(x,x,x,x,x,x)+BD↑j ...
pop edi
pop esi
pop ebx
leave
retn 18h
_ObReferenceObjectByHandle@24 endp
為了方便講解,我就用虛擬碼,具體細節請參考我的彙編註釋。我們把注意力放到如下程式碼上:
if ( Handle == -1 )
{
if ( ObjectType == PsProcessType || !ObjectType )
{
attachedProcess = currentThread->Tcb.ApcState.Process;
if ( (~attachedProcess->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
{
if ( HandleInformation )
{
HandleInformation->GrantedAccess = attachedProcess->GrantedAccess;
HandleInformation->HandleAttributes = 0;
}
if ( ObpTraceEnabled )
ObpPushStackInfo(&attachedProcess[-1u].584, 1);
++attachedProcess[-1u].Flags;
LABEL_12:
*Object = attachedProcess;
return 0;
}
return STATUS_ACCESS_DENIED;
}
return STATUS_OBJECT_TYPE_MISMATCH;
}
if ( Handle == -2 )
{
if ( ObjectType == PsThreadType || !ObjectType )
{
if ( (~currentThread->GrantedAccess & DesiredAccess) == 0 || !AccessMode )
{
if ( HandleInformation )
{
HandleInformation->GrantedAccess = currentThread->GrantedAccess;
HandleInformation->HandleAttributes = 0;
}
if ( ObpTraceEnabled )
ObpPushStackInfo(¤tThread[-1].ReadClusterSize, 1);
++attachedProcess[-1].Flags;
goto LABEL_12;
}
return STATUS_ACCESS_DENIED;
}
return STATUS_OBJECT_TYPE_MISMATCH;
}
看到函式會提前判斷控制程式碼的值是否為負數,再判斷是否是-1
還是-2
。通過虛擬碼可知,如果是-1
就是獲取了當前程式的結構體,如果是-2
就會找當前執行緒的結構體,這個值被稱之為偽控制程式碼。
我敢說只要你在三環與程式執行緒相關打交道的時候,肯定用到過偽控制程式碼。用沒用過GetCurrentProcess
和GetCurrentThread
這倆函式?
; HANDLE __stdcall GetCurrentProcess()
public _GetCurrentProcess@0
_GetCurrentProcess@0 proc near ; CODE XREF: UnhandledExceptionFilter(x)+81↓p
; UnhandledExceptionFilter(x)+69E↓p ...
or eax, 0FFFFFFFFh
retn
_GetCurrentProcess@0 endp
; HANDLE __stdcall GetCurrentThread()
public _GetCurrentThread@0
_GetCurrentThread@0 proc near ; DATA XREF: .text:off_7C802654↑o
push 0FFFFFFFEh
pop eax
retn
_GetCurrentThread@0 endp
上面的函式是不是挺弱智的,為啥我必須呼叫這樣的函式獲取偽控制程式碼呢,直接一個巨集定義不就行了?為什麼不能用CloseHandle
關閉偽控制程式碼是不是明白了?
好,下面我繼續接著擴充套件。當我們的控制程式碼為負值時,作業系統就會使用ObpKernelHandleTable
這個表,這個是通過逆向得到的結果。我們使用WinDbg
看看裡面有什麼東西:
kd> dd ObpKernelHandleTable
8055a7d8 e1001cc8 00000000 00000000 00000000
8055a7e8 00000000 00000000 00000000 00000000
8055a7f8 00000000 00000000 00000000 00000000
8055a808 00000000 00000000 00000000 00000000
8055a818 00000000 00000000 00000000 00000000
8055a828 00000000 00000000 00000000 00000000
8055a838 00000000 00000000 00000000 00000000
8055a848 00000000 00000000 00000000 00000000
kd> dt _HANDLE_TABLE e1001cc8
ntdll!_HANDLE_TABLE
+0x000 TableCode : 0xe1002000
+0x004 QuotaProcess : (null)
+0x008 UniqueProcessId : 0x00000004 Void
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY [ 0xe100f384 - 0x8055c448 ]
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : (null)
+0x02c ExtraInfoPages : 0n0
+0x030 FirstFree : 0x64c
+0x034 LastFree : 0
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount : 0n221
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0
kd> dq 0xe1002000
ReadVirtual: e1002000 not properly sign extended
e1002000 fffffffe`00000000 001f0fff`89fb09e9
e1002010 00000000`89fb0329 000f003f`e13c9119
e1002020 00000000`e1011449 00020019`e13d6731
e1002030 00020019`e13d2791 00020019`e101f421
e1002040 0002001f`e13de479 00020019`e13c3079
e1002050 00020019`e13d1419 0002001f`e13bee51
e1002060 00020019`e13d84d1 001f0003`89fa7a11
e1002070 00000040`00000000 000000a8`00000000
關於該函式的擴充套件,就這些。
控制程式碼表擴充套件
為了方便大家進行學習控制程式碼表,我們給出一個小結論,我在WinDbg
找到了一個程式的控制程式碼表,如下所示:
kd> dq 0xe10ec000
e10ec000 fffffffe`00000000 00000000`00000000
e10ec010 00000004`00000000 00000008`00000000
e10ec020 0000000c`00000000 00000010`00000000
e10ec030 00000014`00000000 00000018`00000000
e10ec040 0000001c`00000000 00000020`00000000
e10ec050 00000024`00000000 00000028`00000000
e10ec060 0000002c`00000000 00000030`00000000
e10ec070 00000034`00000000 00000038`00000000
每一個QWORD
,發現了什麼規律了嗎?每一個專案展示的前半部分是索引,後面就是我們對應的結構體地址,每一個索引都是4的倍數。
其實每一個控制程式碼都是一個結構體,名為_HANDLE_TABLE_ENTRY
,我們可以看一下它的結構:
kd> dt _HANDLE_TABLE_ENTRY
nt!_HANDLE_TABLE_ENTRY
+0x000 Object : Ptr32 Void
+0x000 ObAttributes : Uint4B
+0x000 InfoTable : Ptr32 _HANDLE_TABLE_ENTRY_INFO
+0x000 Value : Uint4B
+0x004 GrantedAccess : Uint4B
+0x004 GrantedAccessIndex : Uint2B
+0x006 CreatorBackTraceIndex : Uint2B
+0x004 NextFreeTableEntry : Int4B
可以看出,這是個用共用體複雜巢狀出的結構體,我們可以看看WRK
的定義:
typedef struct _HANDLE_TABLE_ENTRY {
//
// The pointer to the object overloaded with three ob attributes bits in
// the lower order and the high bit to denote locked or unlocked entries
//
union {
PVOID Object;
ULONG ObAttributes;
PHANDLE_TABLE_ENTRY_INFO InfoTable;
ULONG_PTR Value;
};
//
// This field either contains the granted access mask for the handle or an
// ob variation that also stores the same information. Or in the case of
// a free entry the field stores the index for the next free entry in the
// free list. This is like a FAT chain, and is used instead of pointers
// to make table duplication easier, because the entries can just be
// copied without needing to modify pointers.
//
union {
union {
ACCESS_MASK GrantedAccess;
struct {
USHORT GrantedAccessIndex;
USHORT CreatorBackTraceIndex;
};
};
LONG NextFreeTableEntry;
};
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
目前掌握的細節已經在程式控制程式碼表裡解釋了,也就擴充套件這些。如想更好的掌握這些細節,推薦潘愛民的《Windows核心原理與實現》,注意這本書基於WRK
,僅供參考。
PsLookupProcessByProcessId 淺析
對於作業系統,它是如何實現通過控制程式碼查詢對應的結構體地址呢?我們可以通過以逆向PsLookupProcessByProcessId
為例子進行:
NTSTATUS __stdcall PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process)
{
_KTHREAD *v2; // esi
struct _EPROCESS **v3; // eax
int v4; // ecx
ULONG_PTR v5; // ebx
struct _EPROCESS *v6; // edi
bool v7; // zf
HANDLE ProcessIda; // [esp+10h] [ebp+8h]
v2 = KeGetCurrentThread();
--v2->KernelApcDisable;
v3 = ExMapHandleToPointer(PspCidTable, ProcessId);
v5 = v3;
ProcessIda = STATUS_INVALID_PARAMETER;
if ( v3 )
{
v6 = *v3;
if ( (*v3)->Pcb.Header.Type == 3 && v6->GrantedAccess && ObReferenceObjectSafe(*v3) )
{
ProcessIda = 0;
*Process = &v6->Pcb;
}
ExUnlockHandleTableEntry(PspCidTable, v5);
}
v7 = v2->KernelApcDisable++ == -1;
if ( v7 && v2->ApcState.ApcListHead[0].Flink != &v2->ApcState )
{
LOBYTE(v4) = 1;
v2->ApcState.KernelApcPending = 1;
HalRequestSoftwareInterrupt(v4);
}
return ProcessIda;
}
我們可以看出這個函式功能主要是由ExMapHandleToPointer
實現的,點選去看看:
_EPROCESS *__stdcall ExMapHandleToPointer(int pspcidtable, int processid)
{
_EPROCESS *eprocess; // eax
_EPROCESS *eprocess_1; // esi
int v5; // ebx
_EPROCESS *v8; // eax
int *v9; // esi
ULONG v10; // eax
int v11; // [esp+4h] [ebp-8h]
_EPROCESS *v12; // [esp+8h] [ebp-4h]
_DWORD *pspcidtablea; // [esp+14h] [ebp+8h]
if ( (processid & 0x7FC) == 0 )
return 0;
eprocess = ExpLookupHandleTableEntry(pspcidtable, processid);
eprocess_1 = eprocess;
if ( eprocess )
{
v12 = eprocess;
while ( 1 )
{
v5 = *&eprocess_1->Pcb.Header.Type;
v11 = *&eprocess_1->Pcb.Header.Type;
if ( (*&eprocess_1->Pcb.Header.Type & 1) != 0 )
{
_ECX = v12;
_EDX = v5 - 1;
__asm { cmpxchg [ecx], edx }
if ( v11 == v5 )
return eprocess_1;
}
else if ( !v5 )
{
break;
}
ExpBlockOnLockedHandleEntry(pspcidtable, eprocess_1);
}
}
pspcidtablea = *(pspcidtable + 40);
if ( pspcidtablea )
{
v8 = (*pspcidtablea)++;
v9 = &pspcidtablea[20 * ((v8 + 1) & 0xFFF) + 1];
*v9 = *&KeGetCurrentThread()[1].DebugActive;
v9[2] = processid;
v9[3] = 3;
v10 = RtlWalkFrameChain(&pspcidtablea[20 * ((v8 + 1) & 0xFFF) + 5], 0x10u, 0);
RtlWalkFrameChain(&v9[v10 + 4], 16 - v10, 1u);
}
return 0;
}
這個函式是由ExpLookupHandleTableEntry
實現的,繼續:
unsigned int __stdcall ExpLookupHandleTableEntry(_HANDLE_TABLE *pspcidtable, int processid)
{
unsigned int index; // eax
int pageCount; // ecx
_HANDLE_TABLE *tablebase; // esi
int base; // ecx
unsigned int processindex; // [esp+Ch] [ebp+Ch]
processindex = processid & 0xFFFFFFFC;
index = processindex >> 2;
if ( processindex >= pspcidtable->NextHandleNeedingPool )
return 0;
pageCount = pspcidtable->TableCode & 3;
tablebase = (pspcidtable->TableCode & 0xFFFFFFFC);
if ( !pageCount )
return tablebase + 8 * index;
if ( pageCount == 1 )
{
base = *(&tablebase->TableCode + (processindex >> 11));
}
else
{
index = (processindex >> 2) - (processindex >> 21 << 19);
base = *(*(&tablebase->TableCode + (processindex >> 21)) + 4 * (index >> 9));
}
return base + 8 * (index & 0x1FF);
}
這個就是作業系統實現的偽C程式碼了,上面的pageCount
就是控制程式碼表的級數,我們重點分析一下它是怎樣查詢的:
pageCount 值為 0
tablebase
就是我們的真正的控制程式碼表專案的地址,這個很簡單,正如其實現:tablebase + 8 * index
。
你可能會有疑問,為什麼有這條程式碼:processindex = processid & 0xFFFFFFFC;
這個程式碼就是去掉餘數,保證這個就是4的倍數。processindex >> 2
就是我們之前所謂的CID / 4
。剩下的應該沒有理解上的難度了。
pageCount 值為 1
我們之前介紹過控制程式碼表是按物理頁的,如果是一級,那麼第一層一個專案可以儲存4 * 1024 / 8
個控制程式碼,也就是 29,由於還要除以4,所以就是 211,我們的Base
算出來了,那麼真正的index
還沒有計算,但是作業系統是這樣算的:base + 8 * (index & 0x1FF)
。
為什麼index & 0x1FF
就是我們的真正的索引呢?0x200
十進位制為512
,也就是級數為1的一個專案儲存的控制程式碼數,我只需取出餘數即可,由於這個是二進位制,除數又是2,我們就可以用與運算進行替代。
pageCount 值為 2
這個更復雜一些,但思想是統一的。如果值為2,第一層一個專案儲存的專案為4096 / 4 * 4096 / 8
,也就是 219,第二層一個專案就是29,第一層的base
就是*(&tablebase->TableCode + (processindex >> 21)
,只需要再找第二層就是真正的Base
了,也就是所謂的4 * (index >> 9)
偏移。
剩下就是Index
了,是不是對於這個程式碼非常懵:(processindex >> 2) - (processindex >> 21 << 19)
?
上面的就是為了重用上一種情況的公式,把它轉換到第二層,作用就是看看第一層(級數為2的層數)剝削後剩下的控制程式碼個數。如果讀懂這一塊,就沒問題了,如果實在看不懂,我換一種寫法:index - index >> 19 << 19
,這樣是不是明白了?
遍歷全域性控制程式碼表練習參考
有了上面一大波鋪墊,那麼我們就開始了寫上一篇的思考題參考了:
? 點選檢視程式碼 ?
#include <ntddk.h>
typedef struct _HANDLE_TABLE {
ULONG_PTR TableCode;
struct _EPROCESS* QuotaProcess;
HANDLE UniqueProcessId;
#define HANDLE_TABLE_LOCKS 4
EX_PUSH_LOCK HandleTableLock[HANDLE_TABLE_LOCKS];
LIST_ENTRY HandleTableList;
EX_PUSH_LOCK HandleContentionEvent;
int* DebugInfo;
LONG ExtraInfoPages;
ULONG FirstFree;
ULONG LastFree;
ULONG NextHandleNeedingPool;
LONG HandleCount;
union {
ULONG Flags;
BOOLEAN StrictFIFO : 1;
};
} HANDLE_TABLE, * PHANDLE_TABLE;
unsigned int* __stdcall ExpLookupHandleTableEntry(struct _HANDLE_TABLE* pspcidtable, int cid)
{
unsigned int index;
int pageCount;
int tablebase;
int base;
unsigned int processindex;
processindex = cid & 0xFFFFFFFC;
index = processindex >> 2;
if (processindex >= pspcidtable->NextHandleNeedingPool)
return 0;
pageCount = pspcidtable->TableCode & 3;
tablebase = (pspcidtable->TableCode & 0xFFFFFFFC);
if (!pageCount)
return *(int*)(tablebase + 8 * index);
if (pageCount == 1)
{
base = *(int*)(tablebase + (processindex >> 11));
}
else
{
index = (processindex >> 2) - (processindex >> 21 << 19);
base = *((char*)*(int*)(tablebase + (processindex >> 21)) + 4 * (index >> 9));
}
return *(int*)(base + 8 * (index & 0x1FF));
}
VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unloaded Successfully!");
}
UNICODE_STRING process, thread;
void ShowInfo(int* TableCode, int cid)
{
int* addr = ((int)ExpLookupHandleTableEntry(TableCode, cid) & ~3);
if (addr && MmIsAddressValid(addr))
{
int* type = addr[-4];
if (!type)
{
return;
}
PUNICODE_STRING str = &type[16];
if (!RtlCompareUnicodeString(str, &process, FALSE))
{
UCHAR* imgname = &addr[93];
DbgPrint("PID : %d , Type : %wZ , Name : %s\n", cid, str, imgname);
return;
}
if (!RtlCompareUnicodeString(str, &thread, FALSE))
{
DbgPrint("TID : %d , Type : %wZ\n", cid, str);
return;
}
}
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = UnloadDriver;
DbgPrint("Loaded Successfully!");
RtlInitUnicodeString(&process, L"Process");
RtlInitUnicodeString(&thread, L"Thread");
UNICODE_STRING PsLookupProcessThreadByCid;
RtlInitUnicodeString(&PsLookupProcessThreadByCid, L"PsLookupProcessThreadByCid");
UCHAR* f = MmGetSystemRoutineAddress(&PsLookupProcessThreadByCid);
DWORD32* PspCidTable = *(DWORD32*)(f + 32);
UINT32 TableCode = *PspCidTable;
switch (TableCode & 3)
{
case 0:
for (int i = 0; i < 512; i++)
{
ShowInfo(TableCode, 4 * i);
}
break;
case 1:
for (int i = 0; i < 512 * 1024; i++)
{
ShowInfo(TableCode, 4 * i);
}
break;
case 2:
for (int i = 0; i < 512 * 1024 * 1024; i++)
{
ShowInfo(TableCode, 4 * i);
}
break;
}
return STATUS_SUCCESS;
}
效果如下圖所示:
到最後,我們看看WRK
是如何寫的ExpLookupHandleTableEntry
程式碼:
? 點選檢視程式碼 ?
PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
IN EXHANDLE tHandle
)
/*++
Routine Description:
This routine looks up and returns the table entry for the
specified handle value.
Arguments:
HandleTable - Supplies the handle table being queried
tHandle - Supplies the handle value being queried
Return Value:
Returns a pointer to the corresponding table entry for the input
handle. Or NULL if the handle value is invalid (i.e., too large
for the tables current allocation.
--*/
{
ULONG_PTR i,j,k;
ULONG_PTR CapturedTable;
ULONG TableLevel;
PHANDLE_TABLE_ENTRY Entry = NULL;
EXHANDLE Handle;
PUCHAR TableLevel1;
PUCHAR TableLevel2;
PUCHAR TableLevel3;
ULONG_PTR MaxHandle;
PAGED_CODE();
//
// Extract the handle index
//
Handle = tHandle;
Handle.TagBits = 0;
MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;
//
// See if this can be a valid handle given the table levels.
//
if (Handle.Value >= MaxHandle) {
return NULL;
}
//
// Now fetch the table address and level bits. We must preserve the
// ordering here.
//
CapturedTable = *(volatile ULONG_PTR *) &HandleTable->TableCode;
//
// we need to capture the current table. This routine is lock free
// so another thread may change the table at HandleTable->TableCode
//
TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
CapturedTable = CapturedTable - TableLevel;
//
// The lookup code depends on number of levels we have
//
switch (TableLevel) {
case 0:
//
// We have a simple index into the array, for a single level
// handle table
//
TableLevel1 = (PUCHAR) CapturedTable;
//
// The index for this level is already scaled by a factor of 4. Take advantage of this
//
Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[Handle.Value *
(sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];
break;
case 1:
//
// we have a 2 level handle table. We need to get the upper index
// and lower index into the array
//
TableLevel2 = (PUCHAR) CapturedTable;
i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
Handle.Value -= i;
j = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof (PHANDLE_TABLE_ENTRY));
TableLevel1 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel2[j];
Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[i * (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];
break;
case 2:
//
// We have here a three level handle table.
//
TableLevel3 = (PUCHAR) CapturedTable;
i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
Handle.Value -= i;
k = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof (PHANDLE_TABLE_ENTRY));
j = k % (MIDLEVEL_COUNT * sizeof (PHANDLE_TABLE_ENTRY));
k -= j;
k /= MIDLEVEL_COUNT;
TableLevel2 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel3[k];
TableLevel1 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel2[j];
Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[i * (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];
break;
default :
_assume (0);
}
return Entry;
}
OBJECT_HEADER 擴充套件
這裡我們來介紹一些該結構體的擴充套件知識,你可以通過這些內容運用於對抗技術。先看看其結構體:
kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
+0x000 PointerCount : Int4B
+0x004 HandleCount : Int4B
+0x004 NextToFree : Ptr32 Void
+0x008 Type : Ptr32 _OBJECT_TYPE
+0x00c NameInfoOffset : UChar
+0x00d HandleInfoOffset : UChar
+0x00e QuotaInfoOffset : UChar
+0x00f Flags : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body : _QUAD
我們把關注點放到Type
中,之前我們也涉及過,用來判斷該物件體的型別,再看一看其結構:
kd> dt _OBJECT_TYPE
ntdll!_OBJECT_TYPE
+0x000 Mutex : _ERESOURCE
+0x038 TypeList : _LIST_ENTRY
+0x040 Name : _UNICODE_STRING
+0x048 DefaultObject : Ptr32 Void
+0x04c Index : Uint4B
+0x050 TotalNumberOfObjects : Uint4B
+0x054 TotalNumberOfHandles : Uint4B
+0x058 HighWaterNumberOfObjects : Uint4B
+0x05c HighWaterNumberOfHandles : Uint4B
+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0ac Key : Uint4B
+0x0b0 ObjectLocks : [4] _ERESOURCE
然後看到TypeInfo
這個成員,這個是一個結構體,dt
一下:
kd> dt _OBJECT_TYPE_INITIALIZER
ntdll!_OBJECT_TYPE_INITIALIZER
+0x000 Length : Uint2B
+0x002 UseDefaultObject : UChar
+0x003 CaseInsensitive : UChar
+0x004 InvalidAttributes : Uint4B
+0x008 GenericMapping : _GENERIC_MAPPING
+0x018 ValidAccessMask : Uint4B
+0x01c SecurityRequired : UChar
+0x01d MaintainHandleCount : UChar
+0x01e MaintainTypeList : UChar
+0x020 PoolType : _POOL_TYPE
+0x024 DefaultPagedPoolCharge : Uint4B
+0x028 DefaultNonPagedPoolCharge : Uint4B
+0x02c DumpProcedure : Ptr32 void
+0x030 OpenProcedure : Ptr32 long
+0x034 CloseProcedure : Ptr32 void
+0x038 DeleteProcedure : Ptr32 void
+0x03c ParseProcedure : Ptr32 long
+0x040 SecurityProcedure : Ptr32 long
+0x044 QueryNameProcedure : Ptr32 long
+0x048 OkayToCloseProcedure : Ptr32 unsigned char
我們把注意力放到後面幾個成員,也就是用來回撥函式的地方:
+0x02c DumpProcedure : Ptr32 void
+0x030 OpenProcedure : Ptr32 long
+0x034 CloseProcedure : Ptr32 void
+0x038 DeleteProcedure : Ptr32 void
+0x03c ParseProcedure : Ptr32 long
+0x040 SecurityProcedure : Ptr32 long
+0x044 QueryNameProcedure : Ptr32 long
+0x048 OkayToCloseProcedure : Ptr32 unsigned char
如果我們把函式地址掛到上面,當呼叫相應的核心函式的時候,就就會回撥該函式,用WRK
程式碼片段來進行示例:
VOID
ObpDecrementHandleCount (
PEPROCESS Process,
POBJECT_HEADER ObjectHeader,
POBJECT_TYPE ObjectType,
ACCESS_MASK GrantedAccess
)
{
POBJECT_HEADER_HANDLE_INFO HandleInfo;
POBJECT_HANDLE_COUNT_DATABASE HandleCountDataBase;
POBJECT_HANDLE_COUNT_ENTRY HandleCountEntry;
PVOID Object;
ULONG CountEntries;
ULONG ProcessHandleCount;
ULONG_PTR SystemHandleCount;
LOGICAL HandleCountIsZero;
PAGED_CODE();
Object = (PVOID)&ObjectHeader->Body;
ProcessHandleCount = 0;
ObpLockObject( ObjectHeader );
SystemHandleCount = ObjectHeader->HandleCount;
HandleCountIsZero = ObpDecrHandleCount( ObjectHeader );
if ( HandleCountIsZero &&
(ObjectHeader->Flags & OB_FLAG_EXCLUSIVE_OBJECT)) {
OBJECT_HEADER_TO_QUOTA_INFO_EXISTS( ObjectHeader )->ExclusiveProcess = NULL;
}
if (ObjectType->TypeInfo.MaintainHandleCount) {
HandleInfo = OBJECT_HEADER_TO_HANDLE_INFO_EXISTS( ObjectHeader );
if (ObjectHeader->Flags & OB_FLAG_SINGLE_HANDLE_ENTRY) {
ASSERT(HandleInfo->SingleEntry.Process == Process);
ASSERT(HandleInfo->SingleEntry.HandleCount > 0);
ProcessHandleCount = HandleInfo->SingleEntry.HandleCount--;
HandleCountEntry = &HandleInfo->SingleEntry;
} else {
HandleCountDataBase = HandleInfo->HandleCountDataBase;
if (HandleCountDataBase != NULL) {
CountEntries = HandleCountDataBase->CountEntries;
HandleCountEntry = &HandleCountDataBase->HandleCountEntries[ 0 ];
while (CountEntries) {
if ((HandleCountEntry->HandleCount != 0) &&
(HandleCountEntry->Process == Process)) {
ProcessHandleCount = HandleCountEntry->HandleCount--;
break;
}
HandleCountEntry++;
CountEntries--;
}
}
else {
HandleCountEntry = NULL;
}
}
if (ProcessHandleCount == 1) {
HandleCountEntry->Process = NULL;
HandleCountEntry->HandleCount = 0;
}
}
ObpUnlockObject( ObjectHeader );
if (ObjectType->TypeInfo.CloseProcedure) {
#if DBG
KIRQL SaveIrql;
#endif
ObpBeginTypeSpecificCallOut( SaveIrql );
(*ObjectType->TypeInfo.CloseProcedure)( Process,
Object,
GrantedAccess,
ProcessHandleCount,
SystemHandleCount );
ObpEndTypeSpecificCallOut( SaveIrql, "Close", ObjectType, Object );
}
ObpDeleteNameCheck( Object );
InterlockedDecrement((PLONG)&ObjectType->TotalNumberOfHandles);
return;
}
為了突出主題,我把註釋給刪掉了,重點看這塊程式碼:
if (ObjectType->TypeInfo.CloseProcedure) {
#if DBG
KIRQL SaveIrql;
#endif
ObpBeginTypeSpecificCallOut( SaveIrql );
(*ObjectType->TypeInfo.CloseProcedure)( Process,
Object,
GrantedAccess,
ProcessHandleCount,
SystemHandleCount );
ObpEndTypeSpecificCallOut( SaveIrql, "Close", ObjectType, Object );
}
明白這塊地方的作用了吧?其他的我就不逐個列舉了。