好友
阅读权限 10
听众
最后登录 1970-1-1
渐陌
发表于 2010-11-9 13:51
第一步,这也是最起码的,你必须要能够打开游戏进程和线程,能够开打进程和线程后不被检测到
第二步,能够读写进村内存
第三步,能够用OD附加游戏进程
第四步,能够下硬件断点而不被检测
跳过NtReadVirtualMemory,NtWriteVirtualMemory函数头的钩子
代码:
[code#include<ntddk.h>
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PVOID ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfService;
ULONG ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; //由于KeServiceDescriptorTable只有一项,这里就简单点了
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;//KeServiceDescriptorTable为导出函数
/////////////////////////////////////
VOID Hook();
VOID Unhook();
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
//////////////////////////////////////
ULONG JmpAddress;//跳转到NtOpenProcess里的地址
ULONG JmpAddress1;//跳转到NtOpenProcess里的地址
ULONG OldServiceAddress;//原来NtOpenProcess的服务地址
ULONG OldServiceAddress1;//原来NtOpenProcess的服务地址
//////////////////////////////////////
__declspec(naked) NTSTATUS __stdcall MyNtReadVirtualMemory(HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToRead,
PULONG NumberOfBytesReaded)
{
//跳过去
__asm
{
push 0x1c
push 804eb560h //共十个字节
jmp [JmpAddress]
}
}
__declspec(naked) NTSTATUS __stdcall MyNtWriteVirtualMemory(HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
ULONG NumberOfBytesToWrite,
PULONG NumberOfBytesReaded)
{
//跳过去
__asm
{
push 0x1c
push 804eb560h //共十个字节
jmp [JmpAddress1]
}
}
///////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = OnUnload;
DbgPrint("Unhooker load");
Hook();
return STATUS_SUCCESS;
}
/////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unhooker unload!");
Unhook();
}
/////////////////////////////////////////////////////
VOID Hook()
{
ULONG Address, Address1;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0xBA * 4;//0x7A为NtOpenProcess服务ID
Address1 = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x115 * 4;//0x7A为NtOpenProcess服务ID
DbgPrint("Address:0x%08X",Address);
OldServiceAddress = *(ULONG*)Address;//保存原来NtOpenProcess的地址
OldServiceAddress1 = *(ULONG*)Address1;//保存原来NtOpenProcess的地址
DbgPrint("OldServiceAddress:0x%08X",OldServiceAddress);
DbgPrint("OldServiceAddress1:0x%08X",OldServiceAddress1);
DbgPrint("MyNtOpenProcess:0x%08X",MyNtReadVirtualMemory);
DbgPrint("MyNtOpenProcess:0x%08X",MyNtWriteVirtualMemory);
JmpAddress = (ULONG)0x805b528a + 7; //跳转到NtOpenProcess函数头+10的地方,这样在其前面写的JMP都失效了
JmpAddress1 = (ULONG)0x805b5394 + 7;
DbgPrint("JmpAddress:0x%08X",JmpAddress);
DbgPrint("JmpAddress1:0x%08X",JmpAddress1);
__asm
{ //去掉内存保护
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)Address) = (ULONG)MyNtReadVirtualMemory;//HOOK SSDT
*((ULONG*)Address1) = (ULONG)MyNtWriteVirtualMemory;
__asm
{ //恢复内存保护
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
//////////////////////////////////////////////////////
VOID Unhook()
{
ULONG Address, Address1;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0xBA * 4;//查找SSDT
Address1 = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x115 * 4;
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
*((ULONG*)Address) = (ULONG)OldServiceAddress;//还原SSDT
*((ULONG*)Address1) = (ULONG)OldServiceAddress1;//还原SSDT
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
DbgPrint("Unhook");
}
由于它不断对DebugPort清零,所以要修改调试相关函数,使得所有的访问DebugPort的地方全部访问EPROCESS中的ExitTime字节,这样它怎么清零都无效了,也检测不到
代码:
.386
.model flat, stdcall
option casemap:none
include dnf_hook.inc
.const
Dspdo_1 equ 80643db6h
Dmpp_1 equ 80642d5eh
Dmpp_2 equ 80642d64h
Dct_1 equ 806445d3h
Dqm_1 equ 80643089h
Kde_1 equ 804ff5fdh
Dfe_1 equ 80644340h
Pcp_1 equ 805d1a0dh
Mcp_1 equ 805b0c06h
Mcp_2 equ 805b0d7fh
Dmvos_1 equ 8064497fh
Dumvos_1 equ 80644a45h
Pet_1 equ 805d32f8h
Det_1 equ 8064486ch
Dep_1 equ 806448e6h
.code
;还原自己的Hook
DriverUnload proc pDriverObject:PDRIVER_OBJECT
ret
DriverUnload endp
ModifyFuncAboutDbg proc addrOdFunc, cmd_1, cmd_2
pushad
mov ebx, addrOdFunc
mov eax, cmd_1
mov DWORD ptr [ebx], eax
mov eax, cmd_2
mov DWORD ptr [ebx + 4], eax
popad
ret
ModifyFuncAboutDbg endp
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
invoke ModifyFuncAboutDbg, Dspdo_1, 90784789h, 0fde89090h
invoke ModifyFuncAboutDbg, Dmpp_1, 90787e39h, 950f9090h
invoke ModifyFuncAboutDbg, Dct_1, 90785e39h, 840f9090h
invoke ModifyFuncAboutDbg, Dqm_1, 9078408bh, 45899090h
invoke ModifyFuncAboutDbg, Kde_1, 90787839h, 13749090h
invoke ModifyFuncAboutDbg, Dfe_1, 9078418bh, 0d2329090h
invoke ModifyFuncAboutDbg, Pcp_1, 90784389h, 45f69090h
invoke ModifyFuncAboutDbg, Mcp_1, 90785e39h, 950f9090h
invoke ModifyFuncAboutDbg, Mcp_2, 90784a89h, 5e399090h
invoke ModifyFuncAboutDbg, Dmvos_1, 9078498bh, 0cb3b9090h
invoke ModifyFuncAboutDbg, Dumvos_1, 00787983h, 74909090h
invoke ModifyFuncAboutDbg, Pet_1, 00787f83h, 74909090h
invoke ModifyFuncAboutDbg, Det_1, 9078498bh, 0c9859090h
invoke ModifyFuncAboutDbg, Dep_1, 9078498bh, 0c9859090h
;invoke ModifyFuncAboutDbg, Dmpp_2, 8bc0950fh, 8b90c032h
mov eax, pDriverObject
assume eax : ptr DRIVER_OBJECT
mov [eax].DriverUnload, offset DriverUnload
assume eax : nothing
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
mov eax, STATUS_SUCCESS
ret
DriverEntry endp
end DriverEntry
绕过NtOpenProcess,NtOpenThread,KiAttachProcess
以及最重要的,不能让它检测到有硬件断点,所以要对CONTEXT做一些伪装,把真实的DR0~DR7的数据存放到别的地方,OD访问的时候返回正确的数据,如果是DNF要获取上下文,就稍微做下手脚
代码:
.386
.model flat, stdcall
option casemap:none
include dnf_hook.inc
.const
NtOpenProcessHookAddr equ 805cc626h
NtOpenProcessRetAddr equ 805cc631h
NtOpenProcessNoChange equ 805cc62ch
NtOpenThreadHookAddr equ 805cc8a8h
NtOpenThreadRetAddr equ 805cc8b3h
NtOpenThreadNoChange equ 805cc8aeh
KiAttachProcessAddr equ 804f9a08h
KiAttachProcessRetAddr equ 804f9a0fh
ObOpenObjectByPointerAddr equ 805bcc78h
NtGetContextThreadAddr equ 805d2551h;805c76a3h
NtGetContextThreadRetAddr equ 805c76a7h;805d2555h
.data
nameOffset dd ?
threadCxtLink dd 0
tmpLink dd ?
.code
GetProcessName proc
invoke PsGetCurrentProcess
mov ebx, eax
add ebx, nameOffset
invoke DbgPrint, $CTA0("\n")
push ebx
invoke DbgPrint, ebx
pop ebx
invoke strncmp, $CTA0("DNF.exe"), ebx, 6
push eax
invoke DbgPrint, $CTA0("\n")
pop eax
ret
GetProcessName endp
HookCode proc
;执行被覆盖的代码
push dword ptr [ebp-38h]
push dword ptr [ebp-24h]
;判断是否dnf的进程
invoke GetProcessName
.if !eax ;如果是DNF自己的进程,那么跳转回去执行它的Hook代码
pushad
invoke DbgPrint, $CTA0("\nNotUnHook\n")
popad
mov eax, NtOpenProcessNoChange;805c13e6h
jmp eax
.else ;如果不是DNF自己的进程,那么直接调用ObOpenObjectByPointer,再返回到后面
pushad
invoke DbgPrint, $CTA0("\nUnHook\n")
popad
mov eax, ObOpenObjectByPointerAddr;805b13f0h
call eax
mov ebx, NtOpenProcessRetAddr;805c13ebh
jmp ebx
.endif
HookCode endp
;获取系统名称偏移
GetNameOffset proc epe
local tmpOffset
pushad
mov ebx, epe
invoke strlen, $CTA0("System")
xor ecx, ecx
@@:
push eax
push ecx
invoke strncmp, $CTA0("System"), ebx, eax
pop ecx
.if !eax
pop eax
mov tmpOffset, ecx
popad
mov eax, tmpOffset
ret
.elseif
pop eax
inc ebx
inc ecx
cmp ecx, 4096
je @F
jmp @B
.endif
@@:
popad
mov eax, -1
ret
GetNameOffset endp
Hook proc
pushad
;头5字节跳转
mov eax, offset HookCode
sub eax, NtOpenProcessHookAddr;805c13e0h;805c13edh
sub eax, 5
mov ebx, NtOpenProcessHookAddr;805c13e0h;805c13edh
mov cl, 0E9h
mov BYTE PTR [ebx], cl
mov DWORD PTR [ebx + 1], eax
popad
ret
Hook endp
HookThreadCode proc
;执行被覆盖的代码
push dword ptr [ebp-34h]
push dword ptr [ebp-20h]
;判断是否dnf的进程
invoke GetProcessName
.if !eax ;如果是DNF自己的进程,那么跳转回去执行它的Hook代码
pushad
invoke DbgPrint, $CTA0("\nNotUnHook\n")
popad
mov eax, NtOpenThreadNoChange;805c13e6h
jmp eax
.else ;如果不是DNF自己的进程,那么直接调用ObOpenObjectByPointer,再返回到后面
pushad
invoke DbgPrint, $CTA0("\nUnHook\n")
popad
mov eax, ObOpenObjectByPointerAddr;805b13f0h
call eax
mov ebx, NtOpenThreadRetAddr;805c13ebh
jmp ebx
.endif
HookThreadCode endp
HookThread proc
pushad
;头5字节跳转
mov eax, offset HookThreadCode
sub eax, NtOpenThreadHookAddr;805c13e0h;805c13edh
sub eax, 5
mov ebx, NtOpenThreadHookAddr;805c13e0h;805c13edh
mov cl, 0E9h
mov BYTE PTR [ebx], cl
mov DWORD PTR [ebx + 1], eax
popad
ret
HookThread endp
HookDbg proc
mov edi, edi
push ebp
mov ebp, esp
push ebx
push esi
mov esi, KiAttachProcessRetAddr
jmp esi
HookDbg endp
Dbg proc
pushad
;头5字节跳转
mov eax, offset HookDbg
sub eax, KiAttachProcessAddr;805c13e0h;805c13edh
sub eax, 5
mov ebx, KiAttachProcessAddr;805c13e0h;805c13edh
mov cl, 0E9h
mov BYTE PTR [ebx], cl
mov DWORD PTR [ebx + 1], eax
popad
ret
Dbg endp
;还原自己的Hook
DriverUnload proc pDriverObject:PDRIVER_OBJECT
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
;还原进程处理
mov eax, 0ffc875ffh
mov ebx, 805cc656h
mov DWORD ptr [ebx], eax
mov eax, 43e8dc75h
mov DWORD ptr [ebx + 4], eax
;还原线程处理
mov eax, 0ffcc75ffh
mov ebx, 805cc8d8h
mov DWORD ptr [ebx], eax
mov eax, 0c1e8e075h
mov DWORD ptr [ebx + 4], eax
;还原调试处理
mov eax, 08b55ff8bh
mov ebx, 804f9a08h
mov DWORD ptr [ebx], eax
mov eax, 08b5653ech
mov DWORD ptr [ebx + 4], eax
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
ret
DriverUnload endp
;显示LinkTable的信息
ShowLinkTableInfo proc ptrLT
pushad
invoke DbgPrint, $CTA0("\nThe LinkTable Info:\n")
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).ThreadHandle
invoke DbgPrint, $CTA0("ThreadHandle:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).Dr0Seg
invoke DbgPrint, $CTA0("Dr0Seg:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).Dr1Seg
invoke DbgPrint, $CTA0("Dr1Seg:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).Dr2Seg
invoke DbgPrint, $CTA0("Dr2Seg:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).Dr3Seg
invoke DbgPrint, $CTA0("Dr3Seg:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).Dr6Seg
invoke DbgPrint, $CTA0("Dr6Seg:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).Dr7Seg
invoke DbgPrint, $CTA0("Dr7Seg:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).LinkPtr
invoke DbgPrint, $CTA0("LinkPtr:%0X\n"), eax
mov ebx, ptrLT
mov eax, (LinkTable ptr [ebx]).NextLinkPtr
invoke DbgPrint, $CTA0("NextLinkPtr:%0X\n"), eax
popad
ret
ShowLinkTableInfo endp
;判断该线程是否存在
;如果不存在则返回0,存在则返回指向该链表的指针,1代表链表为空
ExsitsLinkTable proc pHandle
pushad
mov eax, threadCxtLink
.if !eax ;链表为空
pushad
invoke DbgPrint, $CTA0("\nLinkTable Is Null.\n")
popad
popad
mov eax, 1
ret
.endif
@@:
mov ebx, (LinkTable ptr [eax]).ThreadHandle
cmp ebx, pHandle ;如果匹配已经存在
je @F
mov eax, (LinkTable ptr [eax]).NextLinkPtr
.if !eax ;已经到达末尾,没有找到匹配
pushad
invoke DbgPrint, $CTA0("\pHandle Is Not Found.\n")
popad
popad
xor eax, eax
ret
.endif
jmp @B
@@:
pushad
invoke DbgPrint, $CTA0("\npHandle Is Exsits.\n")
popad
invoke ShowLinkTableInfo, eax
;返回链表指针
mov tmpLink, eax
popad
mov eax, tmpLink
ret
ExsitsLinkTable endp
;拷贝Context到LinkTable中
CopyContextToLinkTable proc ptrContext, ptrLT
pushad
mov ebx, ptrContext
mov edx, ptrLT
mov ecx, 4
@@:
mov eax, DWORD ptr [ebx + ecx]
mov DWORD ptr [edx + ecx], eax
add ecx, 4
cmp ecx, 18h
jbe @B
popad
ret
CopyContextToLinkTable endp
;添加LinkTable表
AddLinkTable proc pHandle, ptrContext
pushad
invoke ExsitsLinkTable, pHandle
.if eax > 1
;已经存在只需要更新dr寄存器即可
invoke CopyContextToLinkTable, eax, ptrContext
.else
push eax
invoke ExAllocatePool, 1, size LinkTable
.if eax
;申请内存成功
mov ebx, eax
pop eax
;置地一个元素
mov ecx, pHandle
mov (LinkTable ptr [ebx]).ThreadHandle, ecx
;拷贝dr寄存器的值
invoke CopyContextToLinkTable, ptrContext, ebx
;置另外两个元素
mov (LinkTable ptr [ebx]).LinkPtr, ebx
mov (LinkTable ptr [ebx]).NextLinkPtr, 0
invoke ShowLinkTableInfo, ebx
;把新的链表项添加到链表中
.if eax == 1
;如果链表为空,直接加在表头
mov threadCxtLink, ebx
.else
;如果链表不为空则加到末尾
mov eax, threadCxtLink
@@:
;指向下一个元素
mov ecx, (LinkTable ptr [eax]).NextLinkPtr
test ecx, ecx
je @F
mov eax, ecx
jmp @B
@@:
mov (LinkTable ptr [eax]).NextLinkPtr, ebx
.endif
.else
;申请内存失败
pop eax
pushad
invoke DbgPrint, $CTA0("\nAlloc Memory Faild.\n")
popad
jmp @F
.endif
.endif
@@:
popad
ret
AddLinkTable endp
;判断进程是否过虑进程
;如果是需要过虑的进程返回值为1,否则返回0
IsFilterProcess proc
pushad
;获取当前进程名
invoke PsGetCurrentProcess
mov ebx, eax
add ebx, nameOffset
invoke DbgPrint, $CTA0("\n%s: Call NtGetContextThread \n"), ebx
invoke strncmp, $CTA0("DNF.exe"), ebx, 7
test eax, eax
jne @F
popad
mov eax, 1
ret
@@:
popad
xor eax, eax
ret
IsFilterProcess endp
;显示Context的调试寄存器
ShowDrRegInfo proc ptrContext
pushad
invoke DbgPrint, $CTA0("\nThe Context Info:\n")
mov ebx, ptrContext
mov eax, DWORD ptr [ebx + 4]
invoke DbgPrint, $CTA0("Dr0:%0X\n"), eax
mov ebx, ptrContext
mov eax, DWORD ptr [ebx + 8]
invoke DbgPrint, $CTA0("Dr1:%0X\n"), eax
mov ebx, ptrContext
mov eax, DWORD ptr [ebx + 0ch]
invoke DbgPrint, $CTA0("Dr2:%0X\n"), eax
mov ebx, ptrContext
mov eax, DWORD ptr [ebx + 10h]
invoke DbgPrint, $CTA0("Dr3:%0X\n"), eax
mov ebx, ptrContext
mov eax, DWORD ptr [ebx + 14h]
invoke DbgPrint, $CTA0("Dr6:%0X\n"), eax
mov ebx, ptrContext
mov eax, DWORD ptr [ebx + 18h]
invoke DbgPrint, $CTA0("Dr7:%0X\n"), eax
popad
ret
ShowDrRegInfo endp
;恢复被隐藏的dr寄存器
RecoveryDrReg proc ptrContext, pHandle
pushad
;定位到LinkTable
mov ebx, threadCxtLink
NEXT:
test ebx, ebx
jne @F ;如果没有遍历完
popad
ret
@@:
mov eax, (LinkTable ptr [ebx]).ThreadHandle
cmp eax, pHandle
je @F ;如果找到匹配项
mov ebx, (LinkTable ptr [ebx]).NextLinkPtr
jmp NEXT
@@:
;拷贝完毕后立即结束
invoke CopyContextToLinkTable, ebx, ptrContext
xor ebx, ebx
jmp NEXT
RecoveryDrReg endp
;清空Context的dr寄存器
ClearDrReg proc ptrContext
pushad
mov ebx, ptrContext
mov ecx, 4
@@:
mov DWORD ptr [ebx + ecx], 0
add ecx, 4
cmp ecx, 18h
jbe @B
pushad
invoke DbgPrint, $CTA0("\n-------------ClearDrReg-------------\n")
popad
invoke ShowDrRegInfo, ptrContext
popad
ret
ClearDrReg endp
;NtGetContextThread钩子代码
NtGetContextThreadHookCode proc
;ebx存放CONTEXT指针
mov ebx, DWORD ptr [ebp + 10h]
;线程句柄
mov edx, DWORD ptr [ebp + 0ch]
pushad
invoke ShowDrRegInfo, ebx
invoke IsFilterProcess
.if eax ;如果是DNF.exe
invoke AddLinkTable, edx, ebx
invoke ClearDrReg, ebx
.else ;如果不是DNF.exe
invoke RecoveryDrReg, ebx, edx
.endif
invoke ShowDrRegInfo, ebx
;执行被覆盖的代码
popad
mov eax, esi
pop esi
leave
ret
NtGetContextThreadHookCode endp
;NtGetContextThread加跳转
HookNtGetContextThread proc
pushad
;头5字节跳转
mov eax, offset NtGetContextThreadHookCode
sub eax, NtGetContextThreadAddr;805c13e0h;805c13edh
sub eax, 5
mov ebx, NtGetContextThreadAddr;805c13e0h;805c13edh
mov cl, 0E9h
mov BYTE PTR [ebx], cl
mov DWORD PTR [ebx + 1], eax
popad
ret
HookNtGetContextThread endp
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
invoke DbgPrint, $CTA0("Begin")
invoke PsGetCurrentProcess
invoke GetNameOffset, eax
mov nameOffset, eax
cmp eax, -1
je @F
mov nameOffset, eax
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
call Hook
call HookThread
call Dbg
call HookNtGetContextThread
mov eax, pDriverObject
assume eax : ptr DRIVER_OBJECT
mov [eax].DriverUnload, offset DriverUnload
assume eax : nothing
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
invoke DbgPrint, $CTA0("End")
@@:
mov eax, STATUS_SUCCESS
ret
DriverEntry endp
end DriverEntry
][/code]