看了那么多文章、视频,现在自己跟着做下恢复的驱动程序吧。今天拿冒险岛的HS驱动做为例子吧。冒险岛游戏对Xuetr进行检测了,打开XueTr不久就会游戏发现非法模块,最终游戏关闭。就拿上了一款叫Kernel Detective的ARK工具进行检测冒险岛的驱动吧。
因为Kernel Detective这款工具不能检测到有任何SSDT函数的挂钩,而使用一般的打开进程工具却不能打开它的进程,可知道,这款游戏驱动对NtOpenProcess函数进行了InlineHook,InlineHook如果比较简单的,用XueTr可以检测到的,我们用Kernel Detective工具转到NtOpenProcess的原始地址看看前15个字节是否有存在JMP XXXXXXXX之类的不如真实的指令,我们就知道它是否对NtOpenProcess进行了简单的InlineHook,如此分析
最终分析出:HS这个游戏保护对SSDT表的函数HOOK了三个函数。NtOpenProcess,NtReadVirtualMemory,NtWriteVirtualMemory。
NtOpenProcess的原指令:
NtOpenProcess的HOOK后指令:
NtReadVirtualMemory的原指令:
NtReadVirtualMemory的Hook后指令:
NtWriteVirtualMemory的原指令:
NtWriteVirtualMemory 的Hook后指令
跟住上面几幅图片已经可以看到,冒险岛的驱动保护并不是很强。我们编写代码恢复这几个被HOOK的函数
#include <ntddk.h>
#include <windef.h>
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PVOID ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberTableBase;
ULONG ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; //定义一个SSDT表的结构
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;//这个的变量名是不能变的
typedef NTSTATUS(*NTOPENTHREAD)(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId);
typedef NTSTATUS (*ZWOPENPROCESS)(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK AccessMask,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId);
typedef NTSTATUS(*NTREADVIRTUALMEMORY)(IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
OUT PVOID Buffer,
IN ULONG BufferLength,
OUT PULONG ReturnLength OPTIONAL);
typedef NTSTATUS(*NTWRITEVIRTUALMEMORY)(IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN PVOID Buffer,
IN ULONG BufferLength,
OUT PULONG ReturnLength OPTIONAL); //声明一系列被HOOK函数的原型
NTOPENTHREAD RealNtOpenThread;//接着定义这些结构的变量
ZWOPENPROCESS RealZwOpenProcess;
NTREADVIRTUALMEMORY RealNtReadVirtualMemory;
NTWRITEVIRTUALMEMORY RealNtWriteVirtualMemory;
VOID Hook();//挂钩SSDT表的函数,也就是恢复的过程函数
VOID UnHook();//恢复SSDT表的函数过程
VOID TD_DRIVER_UNLOAD(PDRIVER_OBJECT DriverObject);//驱动卸载例程
NTSTATUS returnvalue;
ULONG NtOpenProcess_15;
ULONG NtOpenThread_15;
ULONG NtReadVirtualMemory_12;
ULONG NtWriteVirtualMemory_12;
ULONG OldServiceAddress1,OldServiceAddress2,OldServiceAddress3,OldServiceAddress4;
__declspec(naked) NTSTATUS __stdcall My_NtReadVirtualMemory(HANDLE ProcessHandle,PVOID BaseAddress,PVOID Buffer,ULONG BufferLength,ULONG ReturnLength)//自定义的要替换的新的NtOpenProcess函数过程
{
returnvalue =(NTSTATUS)(NTREADVIRTUALMEMORY)RealNtReadVirtualMemory(ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength);//调用原函数,
__asm
{
push 1Ch
push 804DA4E0h
mov eax,80538FB0h
call eax
jmp [NtReadVirtualMemory_12]
}//恢复原指令。。。。。。
}
__declspec(naked) NTSTATUS __stdcall My_NtWriteVirtualMemory(HANDLE ProcessHandle,PVOID BaseAddress,PVOID Buffer,ULONG BufferLength,PULONG ReturnLength)//与此类推这就是恢复NtWriteVirtualMemory的自定义函数过程
{
returnvalue =(NTSTATUS)(NTWRITEVIRTUALMEMORY)RealNtWriteVirtualMemory(ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength);
__asm
{
push 1Ch
push 804DA4F8h
mov eax,80538FB0h//恢复原指令
call eax
jmp [NtWriteVirtualMemory_12]//跳转到恢复后的下一个地址继续执行
}
}
__declspec(naked) NTSTATUS __stdcall My_NtOpenProcess(PHANDLE ProcessHandle,ACCESS_MASK AccessMask,POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId)
{
returnvalue =(NTSTATUS)(ZWOPENPROCESS)RealZwOpenProcess(ProcessHandle,AccessMask,ObjectAttributes,ClientId);
__asm
{
push 0C4h
push 804DAAB0h
mov eax,80538FB0h
call eax
jmp [NtOpenProcess_15]
}//如此类推。。。。。。
}
__declspec(naked) NTSTATUS __stdcall My_NtOpenThread(PHANDLE ThreadHandle,ACCESS_MASK AccessMask,POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId)
{
returnvalue =(NTSTATUS)(NTOPENTHREAD)RealNtOpenThread(ThreadHandle,AccessMask,ObjectAttributes,ClientId);
__asm
{
push 0C0h
push 804DAAD8h
mov eax,80538FB0h
call eax
jmp [NtOpenThread_15]
}
}//这个是多余的。我只是为了方便如果其他游戏保护HOOK了NtOpenThread的几个字节可以恢复而已。
VOID Hook()
{
ULONG NtOpenProcess_Cur_Addr;
ULONG NtOpenThread_Cur_Addr;
ULONG NtReadVirtualMemory_Cur_Addr;
ULONG NtWriteVirtualMemory_Cur_Addr;
NtOpenProcess_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
NtOpenThread_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x80 * 4;
NtReadVirtualMemory_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x0BA * 4;
NtWriteVirtualMemory_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x115 * 4;
OldServiceAddress1=*(ULONG*)NtOpenProcess_Cur_Addr;
OldServiceAddress2=*(ULONG*)NtOpenThread_Cur_Addr;
OldServiceAddress3=*(ULONG*)NtReadVirtualMemory_Cur_Addr;
OldServiceAddress4=*(ULONG*)NtWriteVirtualMemory_Cur_Addr;
RealZwOpenProcess=(ZWOPENPROCESS)OldServiceAddress1;
RealNtOpenThread=(NTOPENTHREAD)OldServiceAddress2;
RealNtReadVirtualMemory=(NTREADVIRTUALMEMORY)OldServiceAddress3;
RealNtWriteVirtualMemory=(NTWRITEVIRTUALMEMORY)OldServiceAddress4;
NtOpenProcess_15 = OldServiceAddress1+15;
NtOpenThread_15 = OldServiceAddress2+15;
NtReadVirtualMemory_12=OldServiceAddress3+12;
NtWriteVirtualMemory_12=OldServiceAddress4+12;
__asm
{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)NtOpenProcess_Cur_Addr) = (ULONG)My_NtOpenProcess;
*((ULONG*)NtOpenThread_Cur_Addr) = (ULONG)My_NtOpenThread;
*((ULONG*)NtReadVirtualMemory_Cur_Addr) = (ULONG)My_NtReadVirtualMemory;
*((ULONG*)NtWriteVirtualMemory_Cur_Addr) = (ULONG)My_NtWriteVirtualMemory;
__asm
{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
KdPrint(("编程部落超级过游戏保护驱动恢复成功...\n"));
KdPrint((URL:DangDang.5d6d.com\));
}
VOID UnHook()
{
ULONG NtOpenProcess_Cur_Addr;
ULONG NtOpenThread_Cur_Addr;
ULONG NtReadVirtualMemory_Cur_Addr;
ULONG NtWriteVirtualMemory_Cur_Addr;
NtOpenProcess_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
NtOpenThread_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x80 * 4;
NtReadVirtualMemory_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x0BA * 4;
NtWriteVirtualMemory_Cur_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x115 * 4;
__asm
{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)NtOpenProcess_Cur_Addr) = (ULONG)OldServiceAddress1;
*((ULONG*)NtOpenThread_Cur_Addr) = (ULONG)OldServiceAddress2;
*((ULONG*)NtReadVirtualMemory_Cur_Addr) = (ULONG)OldServiceAddress3;
*((ULONG*)NtWriteVirtualMemory_Cur_Addr) = (ULONG)OldServiceAddress4;
__asm
{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
KdPrint(("编程部落超级过游戏保护驱动卸载成功...\n"));
KdPrint((URL:DangDang.5d6d.com\));
}
VOID TD_DRIVER_UNLOAD(PDRIVER_OBJECT DriverObject)
{
KdPrint(("The Driver Ready to End...\n"));
UnHook();
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING Register)
{
KdPrint(("The TDang Writting this Driver...\n"));
Hook();
DriverObject->DriverUnload =TD_DRIVER_UNLOAD;
return STATUS_SUCCESS;
}
为了方便上面就不打那么多字了。打字累啊。就这个驱动可以恢复到了冒险岛HOOK的前几个字节。但是恢复后。进程可以打开了。但是使用OD不能附加冒险岛的进程,刚刚忘记了分析还有一个函数就是KeAttachProcess和KiAttachProcess这两个函数,如果这两个函数被HOOK了,就不能正常的附加进程了,KeAttachProcess是个导出函数,使用MmGetSystemRoutineAddress这个内核函数很容易地就可以读出他的原始地址,好了。这次就分析到这里了。
做下广告吧。。。欢迎大家来DangDang.5d6d.com编程部落前来学习于观摩。好了。下次我们就来分析下腾讯的游戏吧。本文章是本人第一次写的文章,累死了,真体谅那些写那么长文章那些人了。顶你们。