过TP保护的基本方案.docx

上传人:b****0 文档编号:12610377 上传时间:2023-04-20 格式:DOCX 页数:21 大小:307.46KB
下载 相关 举报
过TP保护的基本方案.docx_第1页
第1页 / 共21页
过TP保护的基本方案.docx_第2页
第2页 / 共21页
过TP保护的基本方案.docx_第3页
第3页 / 共21页
过TP保护的基本方案.docx_第4页
第4页 / 共21页
过TP保护的基本方案.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

过TP保护的基本方案.docx

《过TP保护的基本方案.docx》由会员分享,可在线阅读,更多相关《过TP保护的基本方案.docx(21页珍藏版)》请在冰豆网上搜索。

过TP保护的基本方案.docx

过TP保护的基本方案

过TP保护分析过程~【转帖】

本文只为研究技术,请所有童鞋切勿使用本文之方法做下那天理难容罪恶不舍之坏事。

既是研究游戏保护,那么总要有一个研究对象。

本文就以TMD_TP这款游戏保护为例进行分析讲解。

请勿对号入座,如有雷同之处。

纯属反汇编引擎之错误,不关我的事!

转载请注明出处

关键字:

DNF驱动保护

鉴于最近很多同学找上门来求解这那问题,反正这东西又不是绝密档案,放在我手里大半个月了,还不如放出来让大家一起进步算了。

另外都是取之看雪还之看雪罢了。

索性我也就公布一个全套的方案。

绝无其他意思,所以还请同道中人嘴下留情。

切勿背地使坏!

在正式开篇之前我要感谢看雪ID:

十年寒窗在我最困惑的时候,他给予了最大的帮助!

另外还有一位和我同岁的神秘人物也给予了不小的帮助,感谢你们。

废话了半天,正式开始吧。

tmd_TP也就是国内比较流行的游戏D_N*F的游戏保护。

它在ring0层一共HOOK了几个地方和一些其他的工作。

来达到保护的目的

下面是简报:

[url=javascript:

]复制代码[/url]

1.NtOpenThread//防止调试器在它体内创建线程

NtOpenProcess//防止OD等在进程列表看到它

KiAttachProcess//防止其他软件附加它

NtReadVirtualMemory//防止别人读取它的内存

NtWriteVirtualMemory//防止别人在它的内存里面乱写乱画

KDCOM.dll:

KdReceivePacket//这两个是COM串口的接受和发送数据

KDCOM.dll:

KdSendPacket//主要用来方式别人双机调试

使用了KdDisableDebugger来禁用双机调试

代码:

[url=javascript:

]复制代码[/url]

1..text:

010025F0jzshortloc_1002622

.text:

010025F2callsub_10022A4

.text:

010025F7callds:

KdDisableDebugger

.text:

010025FDpushoffsetbyte_10022EC

.text:

01002602pushesi

.text:

01002603pushoffsetbyte_10022DC

.text:

01002608pushedi

.text:

01002609pushdword_100CF24

并对debugport进行了疯狂的清零操作

甚至还包括EPROCESS+70\+74\+78等几处位置

图片:

1.jpg

处理的手段通常都是向64端口写入FE导致计算机被重启

代码:

[url=javascript:

]复制代码[/url]

1..text:

01001665moval,0FEh

.text:

01001667out64h,al;ATKeyboardcontroller8042.

.text:

01001667;Resendthelasttransmission

.text:

01001669popa

.text:

0100166Aretn

下面简单看下他关键的几个HOOK:

KiAttachProcess

图片:

2.jpg

NtReadVirtualMemory

图片:

3.jpg

NtWriteVirtualMemory

图片:

4.jpg

NtOpenThread

图片:

5.jpg

NtOpenProcess

图片:

6.jpg

引用:

其中,前3个直接恢复即可。

第4个有监视,直接恢复即刻重启

第5个和ring3有通信,直接恢复1分钟内SX非法模块

根据上面的分析,下面给出相应的解决方案

1.直接恢复第1、2、3处HOOK

2.绕过4、5处HOOK

3.将debugport清零的内核线程干掉

4.恢复硬件断点

但是要有一个先后的逻辑顺序

因为内核有一个线程负责监视几个地方,必须要先干掉它。

但是这个内容我写在了处理debugport清零的一起,也就是第3步。

所以大家在照搬源码的时候

注意代码执行次序

先从简单的工作讲起,恢复1、2、3处的HOOK

KiAttachProcess的处理

代码:

[url=javascript:

]复制代码[/url]

1.//////////////////////////////////////////////////////////////////////

//名称:

Nakd_KiAttachProcess

//功能:

My_RecoveryHook_KiAttachProcess的中继函数

//参数:

//返回:

//////////////////////////////////////////////////////////////////////

staticNAKEDVOIDNakd_KiAttachProcess()

{

__asm

{

movedi,edi

pushebp

movebp,esp

pushebx

pushesi

moveax,KiAttachProcessAddress//注意这个是全局变量BYTE*

addeax,7

jmpeax

}

}

//////////////////////////////////////////////////////////////////////

//名称:

RecoveryHook_KiAttachProcess

//功能:

解除游戏保护对_KiAttachProcess函数的HOOK(DNF)

//参数:

//返回:

状态

//////////////////////////////////////////////////////////////////////

NTSTATUSMy_RecoveryHook_KiAttachProcess()

{

BYTE*KeAttachProcessAddress=NULL;//KeAttachProcess函数地址

BYTE*p;

BYTEMovEaxAddress[5]={0xB8,0,0,0,0};//

BYTEJmpEax[2]={0xff,0xe0};

KIRQLIrql;

//特征码

BYTESignature1=0x56,//p-1

Signature2=0x57,//p-2

Signature3=0x5F,//p-3

Signature4=0x5E,//p+5

Signature5=0xE8;//p第一个字节

//获得KeAttachProcess地址,然后通过特征码找到

//KiAttachProcess的地址

KeAttachProcessAddress=(BYTE*)MyGetFunAddress(L"KeAttachProcess");

if(KeAttachProcessAddress==NULL)

{

KdPrint(("KeAttachProcess地址获取失败\n"));

returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;

}

//将p指向KeAttachProcess函数开始处

p=KeAttachProcessAddress;

while

(1)

{

if((*(p-1)==Signature1)&&

(*(p-2)==Signature2)&&

(*(p+5)==Signature3)&&

(*(p+6)==Signature4)&&

(*p==Signature5))

{

//定位成功后取地址

KiAttachProcessAddress=*(PULONG)(p+1)+(ULONG)(p+5);

break;

}

//推动指针

p++;

}

//计算中继函数地址

*(ULONG*)(MovEaxAddress+1)=(ULONG)Nakd_KiAttachProcess;

WPOFF();//清除CR0

//提升IRQL中断级

Irql=KeRaiseIrqlToDpcLevel();

//写入

RtlCopyMemory(KiAttachProcessAddress,MovEaxAddress,5);

RtlCopyMemory(KiAttachProcessAddress+5,JmpEax,2);

//恢复Irql

KeLowerIrql(Irql);

WPON();//恢复CR0

returnSTATUS_SUCCESS;

}

NtReadVirtualMemory和

NtWriteVirtualMemory的处理

注意这里,我对他们俩开头的第2句PUSH的处理

我直接写入了push0x78563412

大家可以根据自己的地址来硬编码一次。

或者干脆这样使用

代码:

[url=javascript:

]复制代码[/url]

1.//////////////////////////////////////////////////////////////////////

//名称:

My_RecoveryHook_NtReadAndWriteMemory

//功能:

解除游戏保护对NtReadVirtualMemory和

//NtWriteVirtualMemory的HOOK

//参数:

//返回:

//////////////////////////////////////////////////////////////////////

NTSTATUSMy_RecoveryHook_NtReadAndWriteMemory()

{

BYTEPush1Ch[2]={0x6a,0x1c};//0~2字节

BYTEPushAdd[5]={0x68,0x12,0x34,0x56,0x78};//NtReadVirtualMemory[物理机]

//BYTEPushAdd2[5]={0x68,0xf0,0x6f,0x4f,0x80};//NtWriteVirtualMemory[物理机]

KIRQLIrql;

BYTE*NtReadVirtualMemoryAddress=NULL;//NtReadVirtualMemory的地址

BYTE*NtWriteVirtualMemoryAddress=NULL;//NtWriteVirtualMemory的地址

//从SSDT表中获取NtReadVirtualMemory函数地址

NtReadVirtualMemoryAddress=(BYTE*)myGetCurrentAddress(0xBA);

if(NtReadVirtualMemoryAddress==NULL)

{

KdPrint(("NtReadVirtualMemory函数地址获取失败!

\n"));

returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;

}

//从SSDT表中获取NtWriteVirtualMemory函数地址

NtWriteVirtualMemoryAddress=(BYTE*)myGetCurrentAddress(0x115);

if(NtWriteVirtualMemoryAddress==NULL)

{

KdPrint(("NtWriteVirtualMemory函数地址获取失败!

\n"));

returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;

}

WPOFF();//清除CR0

//提升IRQL中断级

Irql=KeRaiseIrqlToDpcLevel();

//写入

RtlCopyMemory(NtReadVirtualMemoryAddress,Push1Ch,2);

RtlCopyMemory(NtReadVirtualMemoryAddress+2,PushAdd,5);

RtlCopyMemory(NtWriteVirtualMemoryAddress,Push1Ch,2);

RtlCopyMemory(NtWriteVirtualMemoryAddress+2,PushAdd,5);

//恢复Irql

KeLowerIrql(Irql);

WPON();//恢复CR0

returnSTATUS_SUCCESS;

}

好了,下面来处理

NtOpenProcess和

NtOpenThread

这两个函数的处理上不能太鲁莽了。

手法要风骚一点细腻一点了

介于篇幅的原因,我只贴出来前者的处理方法,后者雷同

细微之处大家自行修改。

我总不能真的给你方法又给你工具。

眼看着自己变成教唆犯

代码:

[url=javascript:

]复制代码[/url]

1.//NtOpenProcess用到的全局变量[为了方便堆栈平衡的处理使用全局变量]

PEPROCESSprocessEPROCESS=NULL;//保存访问者的EPROCESS

ANSI_STRINGp_str1,p_str2;//保存进程名称

BYTE*ObOpenObjectByPointerAddress=NULL;//ObOpenObjectByPointer的地址

BYTE*p_TpHookAddress=NULL;//TP的HOOK函数地址

BYTE*p_ReturnAddress=NULL;//返回到的地址

BYTE*p_MyHookAddress=NULL;//我们的HOOK函数在哪写入

#defineDNF_EXE"DNF.exe"//要检索的进程名

//////////////////////////////////////////////////////////////////////

//名称:

Nakd_NtOpenProcess

//功能:

My_RecoveryHook_NtOpenProcess的中继函数

//参数:

//返回:

//////////////////////////////////////////////////////////////////////

staticNAKEDVOIDNakd_NtOpenProcess()

{

//获得调用者的EPROCESS

processEPROCESS=IoGetCurrentProcess();

//将调用者的进程名保存到str1中

RtlInitAnsiString(&p_str1,(ULONG)processEPROCESS+0x174);

//将我们要比对的进程名放入str2

RtlInitAnsiString(&p_str2,DNF_EXE);

if(RtlCompareString(&p_str1,&p_str2,TRUE)==0)

{

//说明是DNF进程访问了这里

__asm

{

pushdwordptr[ebp-38h]

pushdwordptr[ebp-24h]

pushp_ReturnAddress

moveax,p_TpHookAddress

jmpeax

}

}

else

{

__asm

{

pushdwordptr[ebp-38h]

pushdwordptr[ebp-24h]

pushp_ReturnAddress

moveax,ObOpenObjectByPointerAddress

jmpeax

}

}

}

//////////////////////////////////////////////////////////////////////

//名称:

My_RecoveryHook_NtOpenProcess

//功能:

解除游戏保护对NtOpenProcess的HOOK

//参数:

//返回:

状态

//////////////////////////////////////////////////////////////////////

NTSTATUSMy_RecoveryHook_NtOpenProcess()

{

BYTE*NtOpenProcessAddress=NULL;//NtOpenProcess的地址

BYTE*p=NULL;//临时

TOP5CODE*top5code=NULL;//保存5字节内容

BYTEJmpAddress[6]={0xE9,0,0,0,0,0x90};

KIRQLIrql;

//获取NtOpenProcess的地址

NtOpenProcessAddress=(BYTE*)MyGetFunAddress(L"NtOpenProcess");

if(NtOpenProcessAddress==NULL)

{

KdPrint(("NtOpenProcess地址获取失败\n"));

returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;

}

//获取ObOpenObjectByPointer的地址

ObOpenObjectByPointerAddress=(BYTE*)MyGetFunAddress(L"ObOpenObjectByPointer");

if(ObOpenObjectByPointerAddress==NULL)

{

KdPrint(("ObOpenObjectByPointer地址获取失败\n"));

returnFAILED_TO_OBTAIN_FUNCTION_ADDRESSES;

}

//将p指向NtOpenProcess函数开始处

p=NtOpenProcessAddress;

//用一个无限循环来判断给定的特征码来确定被HOOK位置

while

(1)

{

if((*(p-7)==0x50)&&

(*(p-0xE)==0x56)&&

(*(p+0xd)==0x50)&&

(*(p+0x16)==0x3b)&&

(*(p+0x17)==0xce)&&

(*p==0xE8)&&

(*(p+5)==0x8b)&&

(*(p+6)==0xf8))

{

KdPrint(("%0X\n",(ULONG)p));

break;

}

//推动指针向前走

p++;

}

//将top5code指向p的当前处

//用以取出call[地址]这5字节里面的地址

top5code=(TOP5CODE*)p;

p_TpHookAddress=(BYTE*)((ULONG)p+5+top5code->address);

//找到我们写入自定义函数的地址

p_MyHookAddress=p-6;

//保存调用ObOpenObjectByPointer函数以后的返回地址

p_ReturnAddress=p+5;

//将一条JMPNakd_NtOpenProcess写入到数组中

*(ULONG*)(JmpAddress+1)=(ULONG)Nakd_NtOpenProcess-((ULONG)p_MyHookAddress+5);

WPOFF();//清除CR0

//提升IRQL中断级

Irql=KeRaiseIrqlToDpcLevel();

//写入

RtlCopyMemory(p_MyHookAddress,JmpAddress,6);

//恢复Irql

KeLowerIrql(Irql);

WPON();//恢复CR0

returnSTATUS_SUCCESS;

}

处理之后:

图片:

7.jpg

简而言之其原理就是,任何人调用了NtOpenProcess的时候会先进入

Nakd_NtOpenProcess函数,我们判断。

如果是游戏进程访问的话,就有可能是验证之类的

我们转到它自己的函数里面。

让它保持与ring3层的通信。

否则的话,嘿嘿……

接下来是第3步处理debugport清零的这块了。

我想绝大多数人关心的都是这里了

网络上能搜多到的办法几乎都失效了

有办法的人又不肯放出来,急眼了就自己想了个土办法

虽然不那么时尚。

但是绝对的奏效。

由于代码凌乱不堪,简单说下其原理。

我们定位内核模块TxxxSxxx.sys的首地址

然后根据特征码遍历整个模块找到我们需要的地方,然后干掉他们。

那么我们又如何能够通过人工的判断出来到底是哪里在作怪呢

利用syser或StartSoftICE对EPROCESS+BC处设置断点。

就可以一层一层的追溯上去了

到底如何用他们,我想大家自己多花点时间在看雪和GOOGLE或者BAIDU上面是不会吃亏的。

由于ZwQuerySystemInformation函数的使用非常繁琐。

而且篇幅有限。

所以我只给出关键代码,至于这个函数如何使用。

大家可以自己在搜索引擎找“枚举内核模块”

代码:

[url=javascript:

]复制代码[/url]

1.//////////////////////////////////////////////////////////////////////

//名称:

MyEnumKernelModule

//功能:

枚举内核模块

//参数:

str:

内核模块名称

//moduleadd:

该模块地址[传出]

//modulesie:

该模块大小[传出]

//返回:

//////////////////////////////////////////////////////////////////////

NTSTATUSMyEnumKernelModule(INCHAR*str,OUTULONG*moduleadd,OUTULONG*modulesie)

{

NTSTATUSstatus=STATUS_SUCCESS;

ULONGn

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 计算机硬件及网络

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1