标 题: 重载内核全程分析笔记
作 者: Speeday 时 间: 2013-08-20,20:19:46 链 接: http://bbs.pediy.com/showthread.php?t=177555 还记得七夕的那几天,老V率先把AGP的源码发布出来,然后是EasyDebugger的源码出土,后面陆续有很多大牛把珍藏已久的代码拿出来晒太阳,那段疯狂的日子,让看雪论坛都面临崩溃的边缘。折腾了大半天,终于把代码都下载下来了,可是下载下来做什么呢,自己连看都看不懂。 于是,带着激动而又郁闷的心情继续学习内核编程。 由于是初学者,很多时候也有不清楚的地方,若下文中有什么地方理解有误,还请大牛多多指正, 文中所写代码参考自 看雪论坛大牛 和 梦织未来论坛大牛 好了,进入正题吧,继续看我的废话。 重载内核内容: 代码:
void FixBaseRelocTable(PVOID pNewImage) { //将新内核地址作为一个PE文件头,依次向下,目的是寻找重定位表结构 pImageDosHeader=(PIMAGE_DOS_HEADER)pNewImage; //定位到IMAGE_NT_HEADER pImageNtHeader=(PIMAGE_NT_HEADERS)((ULONG)pNewImage+pImageDosHeader->e_lfanew); //获取内核文件的imagebase,以便后面做偏移修改。 OriginalImageBase=pImageNtHeader->OptionalHeader.ImageBase; //定位到数据目录 ImageDataDirectory = pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; //定位到重定位表结构 pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)(ImageDataDirectory.VirtualAddress + (ULONG)pNewImage); if (pImageBaseRelocation==NULL) { return; } while (pImageBaseRelocation->SizeOfBlock) { //计算需要修改的地址的个数 uRelocTableSize=(pImageBaseRelocation->SizeOfBlock-8)/2; //循环遍历 for (uIndex=0;uIndex<uRelocTableSize;uIndex++) {//判断高4位是否等于3 Type=pImageBaseRelocation->TypeOffset[uIndex]>>12; if (Type==3) { //修改地址,相对地址加上一个新内核地址,使其成为一个实际地址 uRelocAddress=(ULONG *)((ULONG)(pImageBaseRelocation->TypeOffset[uIndex]&0x0fff)+pImageBaseRelocation->VirtualAddress+(ULONG)pNewImage); //再加上内核首地址到imagebase的偏移 *uRelocAddress=*uRelocAddress+(OrigImage-OriginalImageBase); } } //进行下一个重定位表的修改 pImageBaseRelocation=(IMAGE_BASE_RELOCATION *)((ULONG)pImageBaseRelocation+pImageBaseRelocation->SizeOfBlock); } } 这一步完成过后,我们又可以去内核里面看看了。按照上面所说的方法,如果正确修正基址后,结果如下图所示: 参考代码如下 : 代码:
VOID SetNewSSDT(PVOID pNewImage) { ULONG uIndex; ULONG uNewKernelInc,uOffset; //新内核地址-老内核地址,得到相对偏移 uNewKernelInc = (ULONG)pNewImage -OrigImage; //老内核的ssdt指针加上相对偏移,得到新内核的ssdt指针 pNewSSDT = (ServiceDescriptorTableEntry_t *)((ULONG)&KeServiceDescriptorTable + uNewKernelInc); if (!MmIsAddressValid(pNewSSDT)) { KdPrint(("pNewSSDT is unaviable!")); return; } //由于数量是一个数值,因此不必作相对偏移 pNewSSDT->NumberOfServices = KeServiceDescriptorTable.NumberOfServices; //计算相对函数地址 uOffset = (ULONG)KeServiceDescriptorTable.ServiceTableBase -OrigImage; //得到新的ssdt函数表地址 pNewSSDT->ServiceTableBase = (unsigned int*)((ULONG)pNewImage + uOffset); if (!MmIsAddressValid(pNewSSDT->ServiceTableBase)) { KdPrint(("pNewSSDT->ServiceTableBase: %X",pNewSSDT->ServiceTableBase)); return; } //依次遍历 for (uIndex = 0;uIndex<pNewSSDT->NumberOfServices;uIndex++) {//新的函数地址再加上相对加载地址,得到现在的ssdt函数地址 pNewSSDT->ServiceTableBase[uIndex] += uNewKernelInc; } } 好了,这一步完成后,重载内核差不多就完成了,但是只是这样做,我们就什么事都没干,重载内核就这么无聊么?以前不是听说很多过游戏保护的方式都是重载内核么?是啊,做到这一步如果不继续往下学习,那么重载内核是一件多么无聊的事啊。所以我们继续进行下一步吧。 代码:
8053e71c 8b3f mov edi,dword ptr [edi] 8053e71e 8b1c87 mov ebx,dword ptr [edi+eax*4] 8053e721 2be1 sub esp,ecx 8053e723 c1e902 shr ecx,2 8053e726 8bfc mov edi,esp 8053e728 3b35549b5580 cmp esi,dword ptr [nt!MmUserProbeAddress (80559b54)] 8053e72e 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (8053e8dc) 8053e734 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 8053e736 ffd3 call ebx 8053e738 8be5 mov esp,ebp 其中:8053e736 ffd3 call ebx这一句就是调用ssdt函数的call,而ebx就是ssdt中对应函数的地址。所以这里call进去,就会到达我们自己定义的函数内。而调用这个call ebx之前,会把下一条指令mov esp,ebp的地址放入堆栈中,便于调用完子程序后还能返回到程序的下一条指令,继续执行后面的代码。所以我们刚才说的mov eax,[ebp+4]就可以得到 这个栈中的地址。 代码:
ULONG display(ULONG ServiceTableBase,ULONG FuncIndex,ULONG OrigFuncAddress) { if (ServiceTableBase==(ULONG)KeServiceDescriptorTable.ServiceTableBase) {//比较当前调用的进程是不是ce if (!strcmp((char*)PsGetCurrentProcess()+0x174,"cheatengine-i38")) { return pNewSSDT->ServiceTableBase[FuncIndex]; } } return OrigFuncAddress; } __declspec(naked) void MyKiFastCallEntry() { __asm { pushad pushfd push ebx push eax push edi call display //再返回前修改堆栈里的数据 mov [esp+0x14],eax popfd popad //恢复以前的代码,以便内核正常运行 sub esp,ecx shr ecx,2 jmp jmp_ret } } void hook_KiFastCallEntry() { UCHAR jmp_code[5]; jmp_code[0]=0xe9; //计算jmp_code *(ULONG *)&jmp_code[1]=(ULONG)MyKiFastCallEntry-5-addr_hookaddr; //计算返回jmp jmp_ret = addr_hookaddr + 5; PageProtectOff(); //inline hook RtlCopyMemory((PVOID)addr_hookaddr,jmp_code,5); PageProtectOn(); } 到这里,重载内核的内容和目的我们都达成了,来个图看看效果。 |