5系统详细设计
5.1 HOOK ntdll.dll的KiFastSystemCall()函数
下面这是用Windbg看的KiFastSystemCall函数,如下,它只有四个字节,一般的HOOK至少要5个字节以上。所以我们要采用另一种方式HOOK,在ntdll.dll里找到一片没有用的内存,正好KiFastSystemCall上面10个字节就没有用到。所以我们可以在这10个字节里填入我们最终要跳转到的地址,然后从KiFastSystemCall函数开头处改写代码使其跳到KiFastSystemCall-10字节处就行了,具体的HOOK代码和UnHook代码如下: 0:001> u KiFastSystemCall ntdll!KiFastSystemCall:
7c92e4f0 8bd4 mov edx,esp 7c92e4f2 0f34 sysenter
BOOL HOOK() {
PCHAR fun;
UCHAR HookCode[5]={0}; UCHAR Hook[2]={0}; fun = (PCHAR)GetProcAddress(GetModuleHandle(\;//得到KiFastSystemCall函数映射的地址 if (fun!= NULL) {
DWORD lOldProtect; DWORD oldprotect; fun-=10;
if(VirtualProtect((PVOID)((DWORD)fun),14,PAGE_EXECUTE_READWRITE,&lOldProtect))//改写代码段内存为可读写和执行的权限,方便我们能改写代码段 {
fun[0]=0xE9;
*((DWORD*)(fun+1))=((DWORD)HxKiFastSystemCall - (DWORD)fun-5);
fun+=10; fun[0]=0xEB; fun[1]=0xF4;
15
VirtualProtect((PVOID)((DWORD)fun-10),14,lOldProtect,&oldprotect); hookFunctionAddr=fun; return TRUE; } }
return FALSE; }
BOOL UNHOOK() {
if (hookFunctionAddr!= NULL) {
DWORD lOldProtect;
PCHAR fun = hookFunctionAddr;
if(VirtualProtect((PVOID)((DWORD)fun),4,PAGE_EXECUTE_READWRITE,&lOldProtect)) {
fun[0]=0x8b; fun[1]=0xd4;
VirtualProtect((PVOID)((DWORD)fun),4,lOldProtect,NULL); } }
return TRUE; }
5.2 实现HOOK的HxKiFastSystemCall(),改变系统调用的流程
当OD调试器调用一般的API时比如OpenProcess(),ReadVirtualMemory(),WriteVirtualMemory()函数时,最终会进入KiFastSystemCall()这个函数,从而进入我们自己实现的HxKiFastSystemCall()函数,在进入HxKiFastSystemCall函数的环境下,eax指向调用此API对应的系统调用ID,esp指向堆栈,里面放着用户层传来的参数。在这里我们把此函数定义成__declspec( naked ),这么定义是为了防止编译器额外的加上优化和自动保护堆栈代码。在此之前我们先要把esp压栈,保存上下文。然后传进系统调用ID,然后调用FilterServiceFun()函数对我们感兴趣的服务ID进行过滤改变,然后eax返回的是我们改写过的系统调用ID,之后再次调用sysenter真正的进行系统调用,实现Ring3进入Ring0层,这里没用sysenter指令,因为vc 6.0编译器不识别它,我用的是二进制代码,具体实现代码如下:
__declspec( naked ) HxKiFastSystemCall() { __asm {
push esp; //保存堆栈
16
push eax; //服务Id call FilterServiceFun; pop esp; mov edx,esp
__asm _emit 0x0F //sysenter指令进入ring0 __asm _emit 0x34 } }
extern DWORD MyServiceStartID;
int WINAPI FilterServiceFun(DWORD ServiceId) {
DWORD MyServicId; switch(ServiceId) {
case 122: //
MyServicId=MyServiceStartID; //OutputDebugStringA(\层调用了NtOpenProcess()\ break; case 128:
MyServicId=MyServiceStartID+1;
// OutputDebugStringA(\层调用了NtOpenThread()\ break; case 125:
MyServicId=MyServiceStartID+2;
// OutputDebugStringA(\层调用了NtOpenSection()\ break; case 53:
MyServicId=MyServiceStartID+3;
// OutputDebugStringA(\层调用了NtCreateThread()\ break; case 180:
MyServicId=MyServiceStartID+4;
// OutputDebugStringA(\层调用了NtQueueApcThread()\ break; case 186:
MyServicId=MyServiceStartID+5;
// OutputDebugStringA(\层调用了NtReadVirtualMemory()\ break; case 277:
MyServicId=MyServiceStartID+6;
// OutputDebugStringA(\层调用了NtWriteVirtualMemory()\ break; case 137:
17
MyServicId=MyServiceStartID+7;
//OutputDebugStringA(\层调用了NtProtectVirtualMemory()\ break; case 33:
MyServicId=MyServiceStartID+8;
// OutputDebugStringA(\层调用了NtCreateDebugObject()\ break; case 57:
MyServicId=MyServiceStartID+9;
// OutputDebugStringA(\层调用了NtDebugActiveProcess()\ break; case 58:
MyServicId=MyServiceStartID+10;
// OutputDebugStringA(\层调用了NtDebugContinue()\ break; case 269:
MyServicId=MyServiceStartID+11;
// OutputDebugStringA(\层调用了NtWaitForDebugEvent()\ break; case 223:
MyServicId=MyServiceStartID+12; // OutputDebugStringA(\层调用了NtSetInformationDebugObject()\ break; default:
MyServicId = ServiceId; }
return MyServicId; }
5.3 解析微软提供的PDB文件得到未导出的内核函数地址
1,Ring0层内核驱动先通过遍历当前Windows系统的内核模块链表得到hal.dll和ntoskrnl.exe内核模块的装载地址,然后传给Ring3层程序。具体代码如下:
BOOL HxGetKenelNameAndLoadAddr(HX_OSKRNL_INFO *KrnlInfo) {
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY DataTableEntry; ANSI_STRING AnsiString; PLIST_ENTRY NextEntry;
UNICODE_STRING KernelString; UNICODE_STRING HalString; int count=0;
18
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR); KernelString.MaximumLength = sizeof KERNEL_NAME;
HalString.Buffer = (const PUSHORT) HAL_NAME;
HalString.Length = sizeof (HAL_NAME) - sizeof (WCHAR); HalString.MaximumLength = sizeof HAL_NAME;
NextEntry = PsLoadedModuleList->Flink; while (NextEntry != PsLoadedModuleList) {
DataTableEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
//第一次得到内核的名字和装载基址
if (RtlEqualUnicodeString (&KernelString, &DataTableEntry->BaseDllName, TRUE)) {
DbgPrint(%ullDllName);
Status=RtlUnicodeStringToAnsiString(&AnsiString,&DataTableEntry->FullDllName,TRUE);
strcpy(KrnlInfo->KrnlPE,AnsiString.Buffer);
KrnlInfo->ModuleBase[0]=(DWORD)DataTableEntry->DllBase; if ( NT_SUCCESS(Status) ) {
RtlFreeAnsiString(&AnsiString); //释放空间 }
count++; }
//比较得到Hal.dll内核的装载基址
if (RtlEqualUnicodeString (&HalString, &DataTableEntry->BaseDllName, TRUE)) {
Status=RtlUnicodeStringToAnsiString(&AnsiString,&DataTableEntry->FullDllName,TRUE);
19