首页
论坛
课程
招聘
[旧帖] [原创]一种另类的对抗debugport清零的方法(有点标题党了= 。=) 0.00雪花
2011-2-17 00:39 3859

[旧帖] [原创]一种另类的对抗debugport清零的方法(有点标题党了= 。=) 0.00雪花

2011-2-17 00:39
3859

前几天晚上看intel手册突然想到了一种另类的对抗debug port的方法,理论上蛮通用的,于是写了几百行代码实验了下
但是结果很悲催,IDT HOOK死活蓝屏,群里几个人帮我查那段IDT HOOK的代码的错误,结果一致认为代码没错,说我RP不行
哎……好吧,我认了,我很久没扶老奶奶过马路了

我简单说下思路,如果你能搞定,请联系我habo.amy@gmail.com,让我把这些代码close,好睡个安稳觉,谢了
前提:你必须知道目标驱动保护(简称213.sys)的哪个进程(简称213.exe),从而得到此进程的debugport地址
      你必须确认213.sys是否创建了一个线程来不断清零这个debugport

好了,不卖关子了,嘿嘿,进入正题
1:先hook 掉NtGetContextThread,NtSetContextThread,如果目标驱动没对这两个函数做手脚,此步略过
2:IDT HOOK 一号向量
3:枚举系统线程,对每个线程下硬件写入断点,地址就是213.sys要保护的进程的debugport
理论流程是这样的,保护线程对213.exe的debugport清零,触发断点-->1号向量接管-->进入我们的函数->简单的处理是把debugport改回来,复杂的处理是自动寻找213.sys清零213.exe的debugport的函数体内可以modify的位置,改掉它,使清零操作无效,我用XDE(一个反汇编引擎)试过,后来把这块代码去掉了,太浪费生命了……因为我觉得硬件断点速度很快,所以也就直接使用简单的办法,我承认我很懒,因为使用后者,又要多百多行代码,几千个字母,我手抽筋了

你们在后面的代码会看到我是针对TP做的,也许你们会说干掉TP有很多种方法,但是我要的不是结果,我要的过程……所以才有了这段尝试

现在思路有了,写代码就简单了。但是我很悲催的没有完成……我承认代码写的很烂很烂很烂,但是我第一次发帖,大家还是低调点给我找找错误吧,因为我的确不喜欢太傲的人,我会说他很213,怎么说呢,有兴趣就读完我的垃圾代码吧,没兴趣,飘过吧~

贴代码会不会太多了……而且我写代码不习惯写很多注释。呃……将就着看吧

#include <Ntddk.h>
#include <winDef.h>
#include <strsafe.h>


#define MAKELONG(a,b) ((ULONG)(((USHORT)(a))|((ULONG)((USHORT)(b))) << 16)) 

void Recover_NtGetContextThread();  //用SDT HOOK来防止驱动对硬件断点的检测
void IDT_HOOK();   
void Modify_Code();  //修改DEBUGPORT为非0


ULONG NtGetContextThread_Addr; //SDT中,原来的NtGetContextThread地址,下同
ULONG NtSetContextThread_Addr;
ULONG Old_Addr;    //IDT 1号向量原来的ISR的地址,
ULONG DebugPort_Addr;  //驱动保护的进程的debugport的 地址

typedef struct _ServiceDescriptorTable 
{ 
    PVOID      ServiceTable;  
    PULONG   Counter;  
    ULONG    LimitNum;
    PBYTE       ArgsTable; 
} 
ServiceDescriptorTable, 
*PServiceDescriptorTable, 
**PPServiceDescriptorTable;
  
extern PServiceDescriptorTable KeServiceDescriptorTable;

#pragma pack(1)
typedef struct _IDTENTRY{
USHORT offsetLow;
USHORT selector;
UCHAR  NoUseful;
UCHAR  segType:4;
UCHAR  segFlag:1;
UCHAR  DPL:2;
UCHAR  present:1;
USHORT offsetHigh;
}IDTENTRY,*PIDTENTRY;   //定义IDT中的每个向量的格式,每个向量占8字节,我们只关心高位偏移和低位偏移就可以了
                                         //加起来就是这个ISR的地址        

typedef struct _IDT{
USHORT limit;
USHORT baseLow;
USHORT baseHigh;
}IDT,*PIDT;
#pragma pack()  //定义IDT的格式,指出基址,大小


void PPON(){
_asm{
  cli
  mov eax,cr0
  or eax,10000h
  mov cr0,eax
  sti
}
}

void PPOFF(){
_asm{
 cli
 mov eax,cr0
 and eax,not 10000h
 mov cr0,eax
 sti
}
}  //这两个函数不解释,你懂得

ULONG GetFuncAddrFromSDT(ULONG index){
ULONG Addr;
_asm{
  push eax
  mov eax,KeServiceDescriptorTable
  mov eax,[eax]
  shr eax,2                       //-->eax/4
  add eax,index  //-->eax/4+index
  shl eax,2   //-->(eax/4+index)*4=eax+index*4
  mov eax,[eax]  
  mov Addr,eax
  pop eax
  }
return Addr;  
}   //从SDT中取得函数地址

ULONG GetAddr(){   //Get the list link start address
#define DEBUG_PORT 0xBC
#define IMAGE_NAME 0x174
#define LIST_ADDR  0x88
ULONG cur=(ULONG)PsGetCurrentProcess();  //Current process must be SYSTEM
ULONG curAddr,startAddr;
curAddr=startAddr=(ULONG)(cur+LIST_ADDR);  //save the start address
do{
if(strcmp((char*)(curAddr-LIST_ADDR+IMAGE_NAME),"DNF.EXE")) curAddr=*(ULONG*)curAddr;  //如果是DNF进程,就搞到它的debugport地址,大家蛋定,DNF是垃圾
   else{
    DebugPort_Addr=(ULONG)(curAddr-LIST_ADDR+DEBUG_PORT);  
break;
}   
}while(curAddr!=startAddr);
return *(ULONG*)(cur+0x114)+0x22C;  //return the thread list head   //返回system的线程当前线程链表首地址,供遍历系统线程使用
}

ULONG SetAllSystemThreads(ULONG ListAddr){
#define THREADLIST_ADDR 0x22C
#define CID_OFFSET 0x1EC
ULONG curAddr,startAddr;
OBJECT_ATTRIBUTES ObjAttr;
PCLIENT_ID cid;
HANDLE h;
CONTEXT con;
NTSTATUS status;
InitializeObjectAttributes(&ObjAttr,0,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,0,0);
curAddr=startAddr=ListAddr;   //你懂得,开始遍历系统线程,没得到一个,都设置对其下硬件断点
do{
cid=(PCLIENT_ID)(ULONG)(curAddr-THREADLIST_ADDR+CID_OFFSET);
//h=NtOpenThread(h,THREAD_ADLL_ACCESS,&ObjAttr,cid);
_asm{
    push eax
    push edx
push 80h
call GetFuncAddrFromSDT  //得到NtOpenThread的函数地址,0x80是它的index
mov edx,eax
push cid
lea eax,ObjAttr
push eax
push THREAD_ALL_ACCESS
push h
call edx  //这几句代码就是 NtOpenThread(h,THEAD_ALL_SUCCESS,&ObjAttr,cid);
             //这里主要起作用的是CLIENT_ID,它能标识出正确的线程
pop edx
pop eax
    }
if(h){   //如果成功打开
   _asm{
    push eax
lea eax,con
push eax
push h
call NtGetContextThread_Addr //等价于NtGetContextThread(h,&con);
mov status,eax
pop eax
    }
   if(NT_SUCCESS(status)){ //如果成功取得上下文
    con.Dr3=DebugPort_Addr; 
con.Dr7=0xD0002080;  //至于为什么这样设置,可以去翻Intel手册,因为实在不好解释
//NtSetContextThread(h,&con);
_asm{
push eax
lea eax,con
push eax
push h
call NtSetContextThread_Addr
pop eax
}
}
ZwClose(h); //关闭句柄
   }
}while(curAddr!=startAddr);
}

NTSTATUS Jmp_NtGetContextThread(
IN HANDLE ThreadHandle,
OUT PCONTEXT Context
){
if(strcmp((char*)((ULONG)PsGetCurrentProcess()+0x174),"DNF.EXE")){  //不要让DNF得到自身的上下文,否则,否则我也不知道会怎样
_asm jmp Jmp_NtGetContextThread
}
else return STATUS_UNSUCCESSFUL;
}

NTSTATUS Jmp_NtSetContextThread(
IN HANDLE ThreadHandle,
IN PCONTEXT Context
){
if(strcmp((char*)((ULONG)PsGetCurrentProcess()+0x174),"DNF.EXE")){  //同上
_asm jmp Jmp_NtSetContextThread
}
else return STATUS_UNSUCCESSFUL;
}
    
void Rcover_NtGetAndSetContextThread(){   //简单的SDT HOOK,至于那段汇编,和前面GetFuncAddrFromSDT雷同
//SSDT HOOK
KIRQL irq;
irq=KeRaiseIrqlToDpcLevel();
PPOFF();
_asm{
sti
push eax
push ebx
mov  eax,KeServiceDescriptorTable
mov eax,[eax]
shr eax,2
add eax,0x55
shl eax,2
mov ebx,[eax]
mov NtGetContextThread_Addr,ebx
mov ebx,Jmp_NtGetContextThread
mov [eax],ebx
shr eax,2
add eax,0x90
shl eax,2
mov ebx,[eax]
mov NtSetContextThread_Addr,ebx
mov ebx,Jmp_NtSetContextThread
mov [eax],ebx
pop ebx
pop eax
sti
}
PPON();
KeLowerIrql(irq);
}

void Unhook_NtGetAndSetContextThread(){
KIRQL irq=KeRaiseIrqlToDpcLevel();
PPOFF();
_asm{
push eax
push ebx
mov eax,KeServiceDescriptorTable
mov eax,[eax]
shr eax,2
add eax,55h
shl eax,2
mov ebx,NtGetContextThread_Addr
mov [eax],ebx
shr eax,2
add eax,90h
shl eax,2
mov ebx,NtSetContextThread_Addr
mov [eax],ebx
pop ebx
pop eax
}
PPON();
KeLowerIrql(irq);
}

void _stdcall Modify_Code(){  //这个驱动核心的功能,把debugport改回来,是不是有点杀鸡用牛刀的感觉了 = =
_asm mov dword ptr[DebugPort_Addr],0x44  //set the value of debugport to nozero
}

_declspec(naked) IDT_ProxyFunc(){  //代理函数,用它替换IDT 一号向量
_asm{
pushad
pushfd
call Modify_Code
popfd
popad
jmp Old_Addr
}
}

void IDT_HOOK(){  //IDT HOOK,简单解释下
IDT idt;
PIDTENTRY entry;
_asm sidt idt   //储存IDT信息到变量idt
entry=(PIDTENTRY)MAKELONG(idt.baseLow,idt.baseHigh); //得到基址
KdPrint(("the base address of IDT is %X",(ULONG)entry));
Old_Addr=(ULONG)MAKELONG(entry[1].offsetLow,entry[1].offsetHigh);  //保存一号向量的原ISR地址
KdPrint(("the NO.1 ISR address is %X",Old_Addr));
PPOFF();
_asm{
//entry[1].offsetLow=(USHORT)(ULONG)&IDT_ProxyFunc;
//entry[1].offsetHigh=(USHORT)((ULONG)&IDT_ProxyFunc>>16);  //分别修改高位和低位偏移,地址就是那个啥,呃,代理函数IDT_ProxyFunc
cli
push eax
push edx
mov eax,entry
add eax,8 //move to next ISR
mov edx,IDT_ProxyFunc
mov word ptr[eax],dx           //****注意了哈****就是这个鸟指令,蓝屏的关键,肯定是eax问题
                                            //但是,运行到此时eax的地址是正确的,为什么?我用windbg看的呗
add eax,6 //move to offsetHigh
shr edx,16
mov word ptr[eax],dx
pop edx
pop eax
sti
}
PPON();  //上面的就是导致蓝屏的代码,我实在郁闷~咋就蓝了呢,我用汇编改写了上面注释的两句C代码
//虽然ASM写的很累赘,但是我只是为了找出哪里导致了蓝屏,大家不要鄙视
//我用notepad++写了几小时代码通宵也不容易诶,一个字一个字母敲,呜呜
}

void IDT_UNHOOK(){  //不解释了,对应IDT_HOOK
IDT idt;
PIDTENTRY entry;
_asm sidt idt
entry=(PIDTENTRY)(ULONG)MAKELONG(idt.baseLow,idt.baseHigh);
_asm cli
entry[1].offsetLow=(USHORT)Old_Addr;
entry[1].offsetHigh=(USHORT)((ULONG)Old_Addr>>16);
_asm sti
}

NTSTATUS DriverUnload(PDRIVER_OBJECT driver){
 Unhook_NtGetAndSetContextThread();
 IDT_UNHOOK();
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg){
ULONG ListAddr;
#if DBG
_asm int 3
#endif
driver->DriverUnload=DriverUnload;
Rcover_NtGetAndSetContextThread();
IDT_HOOK();
ListAddr=GetAddr();
if(DebugPort_Addr) SetAllSystemThreads(ListAddr);
}

看雪2022 KCTF 秋季赛 防守篇规则,征题截止日期11月12日!(iPhone 14等你拿!)

收藏
点赞0
打赏
分享
最新回复 (24)
雪    币: 8669
活跃值: 活跃值 (1188)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2011-2-17 04:30
2
0
好多汇编啊~
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
mszjk 活跃值 2011-2-17 05:21
3
0
神马神马神马神马
雪    币: 489
活跃值: 活跃值 (317)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
坚持到底 活跃值 7 2011-2-17 09:47
4
0
呵呵, 之前也想到过这方法, 我是用AMD-V来做的, 只是进入int1之后数据已经被改过了, 不懂怎么恢复, 后面就没做了。
雪    币: 284
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jerrynpc 活跃值 2011-2-17 11:36
5
0
BC改44能行么?
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-17 12:49
6
0
不是滴,是任意非0值,我就随手写了个0x44咯
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-17 12:51
7
0
实在不好意思,因为是原本不是准备拿来发帖的,于是哪里C简单我就用C,哪里ASM简单我就用ASM,嘿嘿
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-17 15:00
8
0
你是指断点激活后的事情吧?
雪    币: 489
活跃值: 活跃值 (317)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
坚持到底 活跃值 7 2011-2-17 20:53
9
0
是的, 断点激活后, 数据已经被改过。
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-17 21:03
10
0
因为是个自陷嘛,数据当然被改过,我就是认为硬件断点速度很快,它改回去后我再改回来,这样对应用层调试器而言感觉没什么发生过……我们改回来之后,再交给原来的一号向量,让它自己去处理吧~不知实际情况是不是如此,哎
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-19 16:37
11
0
斑竹大婶!!!

求加精……我可以把文章修改的严肃点的~
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
smove 活跃值 2011-2-21 13:22
12
0
写的乱七八糟的 压根就不对
shr eax,2
这个是啥  把整个服务表地址数除以2
再加上偏移 再乘以2
地址早就变了
雪    币: 2080
活跃值: 活跃值 (238)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
exile 活跃值 1 2011-2-21 13:26
13
0
拜模123456
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-21 14:02
14
0
乱七八糟那你指出来啊,我本来就是来找错误的,给你个小学知识
(x/4+y)*4=x+y*4
shr eax,2 = eax/4
shl eax,2=eax*4

你说变没变?
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-21 15:00
15
0
刚那位哥们……虽然你的发现的错误其实是正确的,为了避免在这里吵架,我贴段测试代码,自己看吧,只有几十行。两种方法获取SDT服务地址,我只不过用了点数学知识,就认为我是错的

#include <ntddk.h>
#include <winDef.h>
#include <strsafe.h>
 
typedef struct _ServiceDescriptorTable 
{ 
    PVOID    ServiceTable;  // array of entry points 
    PULONG     CounterTable;  // array of usage counters 
    DWORD      ServiceLimit;   // number of table entries 
    PBYTE     ArgumentTable;   // array of byte counts 
} 
ServiceDescriptorTable, 
*PServiceDescriptorTable, 
**PPServiceDescriptorTable;
  
extern PServiceDescriptorTable KeServiceDescriptorTable; 


NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg){
ULONG Addr1,Addr2;
_asm{
push eax
push ebx
mov eax,KeServiceDescriptorTable  //这是我自己用移位乱移一通的方法,用一个寄存器
mov eax,[eax]
shr eax,2
add eax,55h
shl eax,2
mov eax,[eax]
mov Addr1,eax
mov eax,KeServiceDescriptorTable //下面是网上流传的通法,使用两个寄存器
mov eax,[eax]
mov ebx,55h
shl ebx,2
add eax,ebx
mov eax,[eax]
mov Addr2,eax
pop ebx
pop eax
}
KdPrint(("the result by the first way is %X \n",Addr1));
KdPrint(("the result by the second way is %X \n",Addr2));
return STATUS_SUCCESS;
}


windbg输出结果:
the result by the first way is 805C87A6
the result by the second way is 805C87A6
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
smove 活跃值 2011-2-21 19:27
16
0
大哥俺没上过大学 不好意思 但是何必这样让人不好理解 难道是为了加些花指令
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
smove 活跃值 2011-2-21 19:32
17
0
我这里提出几个疑问 清0会触发断点 杂没听说过有这方面资料 难道一个 xchg [eax],ebx指令会触发1号向量

还有 TP好象是 0X20号向量 不是1号
雪    币: 175
活跃值: 活跃值 (19)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
无泪城 活跃值 2011-2-21 19:47
18
0
既然是自己写的代码,挂上虚拟机调试一下就知道到底是什么地方出错了,快到出错的位置时候,单步分析分析,应该很快能找到问题所在的。
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-21 20:03
19
0
[QUOTE=smove;927943]我这里提出几个疑问 清0会触发断点 杂没听说过有这方面资料 难道一个 xchg [eax],ebx指令会触发1号向量

还有 TP好象是 0X20号向量 不是1号[/QUOTE]

哥们,TP是创建一个系统线程对debugport不断清零,我的思路就是,得到debugport的地址A,枚举系统线程,对A下硬件写入断点,intel手册有提及,将触发1号向量,我们接管,只干一件事,把debugport改回来,然后交给原来的ISR处理,速度应该不是问题,因为是硬件断点

还有~哪段移位的代码,只是俺随手敲的代码,想到什么就敲什么,倒不是玩花指令,嘿嘿
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-21 20:10
20
0
呃……我单步过不下五遍了,后来很不爽就不理它了,发上来让大家帮忙看看,实在是时间有限
话说,问题只出在那行汇编指令,就是对IDT的1号向量赋新值时,而且蓝屏也很tnnd离谱,有时
候dump文件显示是5号异常,就是访问违例,有时候是个我也没见过的bugcheck,寄存器全部被打乱了,EIP直接飞到了0x00000008去了,实在纠结啊
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-22 11:09
21
0
求好心人在自己的虚拟机里帮我把代码调试下

直接copy下来就可以编译了
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
smove 活跃值 2011-2-22 14:21
22
0
我刚才在本机测试蓝屏了

你还不如 直接修改他的代码
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
smove 活跃值 2011-2-22 14:22
23
0
我QQ 我们研究下 我已经过TP了的 stolen007@qq.com
雪    币: 27
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
smove 活跃值 2011-2-22 14:24
24
0
现在TP也有硬件断点的
雪    币: 46
活跃值: 活跃值 (26)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
长风傲天 活跃值 1 2011-2-22 15:40
25
0
我们QQ里说吧,我已经找到了蓝屏原因所在了……很郁闷啊~
游客
登录 | 注册 方可回帖
返回