计算机编程基本功:反调试技巧总结_第1页
计算机编程基本功:反调试技巧总结_第2页
计算机编程基本功:反调试技巧总结_第3页
计算机编程基本功:反调试技巧总结_第4页
计算机编程基本功:反调试技巧总结_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

反调试技巧总结-原理和实现

总结:

1.FindWindowo比如FindWindowA("OLLYDBG",NULL);

2.EnumWindow函数调用后,系统枚举所有顶级窗口,为每个窗口调用一次回调函数。在回调函数中用

GetWindowText得到窗口标题,进行检测。

3.GetForeGroundWindow返回前台窗II(用户当前工作的窗II)。当程序被调试时,调用这个函数将获得

Ollydbg的窗口句柄,再用GetWindowTextA检测。

4枚举进程列表,看是否有调试器进程(OLLYDBG.EXE,windbg.exe等)

5.父进程是否是Explorer。通常进程的父进程是explorer.exe(双击执行的情况下),否则可能程序被调试。

6.RDTSC/GetTickCount时间敏感程序段。当进程被调试时,调试器事件处理代码、步过指令等将占用CPU

循环当进程被调试时,调试器事件处理代码、步过指令等将占用CPU循环。

7.Startuplnfo结构检测。Windows操作系统中的explorer.exe创建进程的时候会把STARTUPINFO结构中

的值设为0,而非explorer.exe创建进程的时候会忽略这个结构中的值,也就是结构中的值不为0,所以可以

利用这个来判断OD是否在调试程序。

8.BeinaDebuQaed,kernel32!lsDebuggerPresent()API检测进程环境块(PEB)中的BeingDebugged标志检

查这个标志以确定进程是否正在被用户模式的调试器调试。

9.PEB.NtGlobalFlag.Heap.HeapFlaqs.Heap.ForceFlaqs3通常程序没有被调试时,PEB另一个成员

NtGlobalFlag(偏移0x68)值为0,如果进程被调试通常值为0x70(代表下述标志被设置)由于NtGlobalFlag

标志的设置,堆也会打开几个标志,这个变化可以在ntdll!RtlCreateHeap()里观测到。正常情况下系统为进程

创建第一个堆时会将Flags和ForceFlags分别设为2(HEAP一GROWABLE)和0。当进程被调试时,这两

个标志通常被设为50000062(取决于NtGlobalFlag)和0x40000060(等于FlagsAND0x6001007D),

1OEPROCESS的DebuqPort城员:CheckRemoteDebuggerPresent()/NtQuerylnformationProcess()o

Kernel32!CheckRemoteDebuggerPresent()是用于确定是否有调试器被附加到进程,内部调用了

mdll!NtQuerylnformationProcess()检索内核结构EPROCESS的DebugPort成员。

11.SetUnhardledExceptionFilter/DebuQQerInterrupts调试器中步过INT3和INT1指令的时候,由于调试器

通常会处理这些调试中断,所以设置的异常处理例程默认情况下不会被调用,这样我们可以在异常处理例程

中设置标志,通过INT指令后如果这些标志没有被设置则意味着进程正在被调试.

12.TrauFla。堆步标志异常。TF=1的时候,会触发单步异常,在异常中设定检测,正常程序可进入,未修改

OD调试程序不能进入此异常,从而检测调试。

13SeDebuQPrivileqe进程权限.默认情况下进程没有SeDebuaPrivileae权限,调试时,会从调试器继承这个

权限。

14.DebugObiect:NtQueryObiecm内核对象检测。

15QllyDba:GuardPaaesQIlvDbci允许设置一个内存访问/写入断点,这种类型的断点是通过页面保护来实

现的,虫面保护是通过PAGE_GUARD页面保护修改符来设置的,如果访问的内存地址是受保护页面的一部

分、将会产生一个STATUS_GUARD_PAGE_VIOLATION(0x80000001)S^o如果进程被OllyDbg调试并

且受保护的页面被访问,将不会抛出异常,访问将会被当作内存断点来处理,从而检测到。

16.SoftwareBreakpoint.通过修改目标地址代码为OxCC(INT3/Breakpointlnterrupt)来设置的断点v通过在

受保护的代码段和(或)API函数中扫描字节OxCC来识别软件断点。

17.Hardwa「eBreakpoints.通过传递给异常处理例程的ContextRecord参数来访问,含有调试寄存器值的

CONTEXT结构,判断Dr0-Dr3是否设置了值,来判断调试。

18PatchingDetectionCodeChec<sumCalculation补「检测,代码检验和•能识别克的代码是否被修改,或软件

断点。

19.blockinput封锁键盘、鼠标输入。

20.ErableWirdow禁用窗口。

21.ThreadHideFromDebugger.ntdll!NtSetlnformationThread()用来设置一个线程的相关信息。把

ThreadlnformationClass参数设为ThreadHideFromDebugger(11H)可以禁止线可以禁止线程产生调试事

件,

22.DisablinaBreakpoints禁用硬件断点。利用CONTEXT结构,该结构利用异常处理获得,异常处理完后会

自动写回。

23QllyDbaQutDLitDebuQStriraCFormatStrinaBUQ。OutputDebugString函数用于向调试器发送一个格式化

的串,Ollydbg会在底端显示相应的信息。OllyDbg存在格式化字符串溢出漏洞,非常严重,轻则崩溃,重

则执行任意代码。这个漏洞是由于Ollydbg对传递给kemel32!OutputDebugString()的字符串参数过滤不严导

致的,它只对参数进行那个长度检查,只接受255个字节,但没对参数进行检查,所以导致缓冲区溢出溢出。

24.TLSCallbacks。使用ThreadLocalStorage(TLS)回调函数可以实现在实际的入口点之前执行反调试的代

码,这也是OD载入程序就退出的原因所在。

25.CreateFile检测。Win32程序对vxd程序通信时,是通过调月DeviceloControl函数进入vxd,此函数的■

个参数就是由createfile获得的设备句柄。这样,同样生成或打开文件的调用也能够打开一个到vxd的通道。

要用createfile打开一个vxd,而不是一个通常的文件,在文件名的地方必须使用特殊形式。

1.FindWindow

比如FindWindowA("OLLYDBG",NULL);

szClassNamedb'ollydbg,,0

invokeFindWindow,addrszClassName,NULL;通过类名进行检测

.ifeax;找到

jmpdebuggerjound

.endif

2.EnumWindow

系统枚举所有顶级窗口,为每个窗口调用一次回调函数。在回调函数中用GetWindowText得到窗口标

题,进行检测。

.386

.modelflat,stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includelibuser32.lib

includekernel32.inc

includelibkernel32.lib

includeShlwapi.inc

includelibShlwapi.lib;strstr

.const

szTitledb,ollydbg,,0

szCaptiondb‘结果',0

szFindODdb’发现目标窗口',0

szTextdb'枚举已结束,没提示发现目标,则没有找到目标窗口',0

.code

;定义回调函数

_CloseWndprocusesebxediesi,_hWnd,」Param

LOCAL@szBuffer[1024]BYTE;接收窗口标题

invokeIsWindowVisible,hWnd

.ifeax;是否是可见的窗口

invokeGetWindowTAxt,,hWnd,addr(§)s:7RuffAr,si7Aof©s/Buffer

invokeStrStrl,addr@szBuffer,offsetszTitle;查找标题中有无字符串,不带I的大小”敏感

.ifeax

invokeMessageBox,NULL,addrszFindOD,addrszCap!ion,MB_OK

invokePostMessage,_hWnd,WM_CLOSEt0,0;关闭H标

.endif

.endif

moveax,TRUE;返回true时,EnumWindows继续枚举下•个窗口,false退出枚举.

ret

_CloscWndcndp

start:

invokeEnumWindows,addr_CloseWnd,NULL

;EnumWindows调用,系统枚举所有顶级窗口,为每个窗口调用一次回调函数

invokeMessageBox,NULL,addrszText,addrszCaption,MB_OK

invokeExitProcess,NULL

endstart

3.GetForeGroundWindow返回前台窗口

GetForeGroundWindow返回前台窗口(用户当前工作的窗口)。当程序被调试时,调用这个函数将获

得Ollydbg的窗口句柄,这样就可以向其发送WM_CLOSE消息将其关闭了。

invokeIsDebuggerPresent

.ifeax

invokeGetForegroundWindow;获得的是OD的窗口句柄

invokeSendMessage,eax,WMCLOSE,NULL,NULL

.endif

获取OD窗口句柄后的处理

(1)向窗口发送WMCLOSE消息

invokeFindWindow.addrszClassName.NULL;通过类名进行检测

.ifeax;找到

movhWinOD.eax

invokeMessageBox,NULL,ofsetszFound,offset

szCaption,MB_OKinvokeSendMessage,hWinOD,WM_CLOSE,NULL,NULL

.endif

(2)终止相关进程,根据窗口句柄获取进程ID,根据进程ID获取进程句柄,

_GetODProclDproc

LOCAL@hWinOD;窗口句柄

LOCAL@hProcessOD;进程句柄

LOCAL@idProcessOD;进程ID

invokeFindWindow,addrszClassName,NULL;通过类名进行检测

.ifeax俄到

mov@hWinOD,eax;窗口句柄

invokeGetWindowThreadProcessld,@hWinOD,addr@idProcessOD

;获取进程ID6,@idProcessOD里一

invokeOpenProcess,PROCESS_TERMINATE,TRUE,@idProcessOD

;获取进程句柄在返回值里

.ifeax;获取句柄成功

mov@hProcessOD,eax

invokeTerminateProcess,@hProcessOD,200;利用句柄终止进程

invokeCloseHandle,@hProcessOD;关闭进程句柄

invokeMessageBox,NULL,addrszClose.addrszMerry,MB_OK

,else;获取句柄失败,多因权限问题

invokeMessageBox,NULL,addrszFail,addrszCaption,MB_OK

.endif.

.endif

ret

GetODProclDendp

4•枚举进程列表,看是否有调试器进程

枚举进程列表,看是否有调试器进程(OLLYDBG.EXE,windbg.exe等)。

利用kemel32!ReadProcessMemory()读取进程内存,然后寻找调试器相关的字符串(如"OLLYDBG")

以防止逆向分析人员修改调试器的可执行文件名。

.386

.modelflat,stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includelibuser32,lib

includekernel32.inc

includelibkernel32.lib

.const

stSysProcdb'OLLYDBG.EXE,,0

szCaptiondb‘检测结果',0

szFounddb检测到调试器',0

szNotFounddb‘没有调试需,0

.code

_GetProcListproc

LOCAL@stProcessEntry:PROCESSENTRY32

LOCAL@hSnapShot

invokeCreateToolhelp32Snapshot!TH32CS_SNAPPROCESS,NULL

mov@hSnapShot,eax

mov@stProcessEntry.dwSize,sizeof@stProcessEntry

invokeProcess32First,@hSnapShot,addr@stProcessEntry

.whileeax

invokelstrcmp,addr@stProcessEntry.szExeFile,addrstSysProc

.ifeax==0;为0,说明进程名相同

push20

invokeMessageBox,NULL,addrszFound,addrszCapticn,MB_OK

.endif

invokeProcess32Next,@hSnapShot,addr@stProcessEntry

.endw

popeax

.ifeax!=20

invnkAMASsagpRnx^UII,addrs7NotFniind,addrs7Captinn1MR_OK

.endif

ret

_GetProcListendp

start:

invoke_GetProcList

invokeExitProcess.NULL

endstart

5.父进程是否是Explorer

原理:通常进程的父进程是explorer.exe(双击执行的情况下),否则可能程序被调试。

下面是实现这种检查的一种方法:

1.通过TEB(TEB.CIientld)或者使用GetCurrentProcessld()来检索当前进程的PID

2.用Process32First/Next(j得到所有进程的列表,注意eKplorer.exe的PID(通过

PROCESSENTRY32.szExeFile)和通过PROCESSENTRY32此32ParentProcesslD获得的当前进程的父进

程PID。Explorer进程ID也可以通过桌面窗口类和名称获得。

3.如果父进程的PID不是explorer.exe,cmd.exe,Services.exe的PID,则目标进程很可能被调试

对策:OllyAdvanced提供的方法是让Process32Next()总是返回fail,使进程枚举失效,PID检查将会被

跳过。这些是通过补丁kemel32!Process32NextW()的入口代码(将EAX值设为0然后直接返回)实现的。

(1)通过桌面类和名称获得Explorer的PID源码见附件

.data?

szDesktopCIassdb'Progman',0;桌面的窗口类

szDesktopWindowdbProgramManager'.O;桌面的窗口名称

dwProcessIDdd?;保存进程ID

dwThreadIDdd?;保存线程ID

.code

invokeFindWindow,addrszD9sktopClass,addrszDesktopWindow;获取桌面窗口句柄

invokeGetWindowThreadProcessld,eax,offsetdwProcesslD;获取匚XPLOI3匚n进程ID

movdwThreadID,eax;线程ID

(2)通过进程列表快照获得Explorer的PID源码见附件

szExplorerdb,EXPLORER.EXE'.O

dwParentIDdd?

dwExplorerlDdd?

_ProcTestproc

local@stProcess:PROCESSENTRY32海一个进程的信息

local@hSnapShot;快照句柄

pushad

invokeGetCurrentProcessId

movebx.eax;当前进程ID

invokeRtlZeroMemory,addr@stProcess,sizeof@stProcess;0初始化进程信息结构

mov@stProcess.dwSize,sizeof@stProcess;手工填写结构大小

invokeCreateToolhelp32Snapshot,TH32CS_SNAPPRQCESS,0;获取进程列表快照

mov@hSnapShot,eax;快照句柄

invokeProcess32First,@hSnapShot,addr@stProcess;第一个进程

.whileeax

.ifebx==@stProcess.th32ProcesslD;是当前进程吗?

moveax,@stProcess.th32ParentProcesslD;是,则保存父进程ID

movdwParentID,eax

.endif

invokelstrcmp,addr@stProcess.szExeFile,addrszExplorer;Explorer进程ID

.ifeax==0;为0,说明进程名相同

moveax,@stProcess.th32ProcesslD

movdwExplorerlD,eax

.endif

invokeProcess32Next,@hSnapShot,addr@stProcess;T•个进程

.endw

invokeCloseHandle,@hSnapShot;关闭快照

movebx,dwParentID

.ifebx==dwExplorerlD:父进程ID与EXPLORER进程ID比

较invokeMessageBox,NULL,offsetszNotFojnd,offsetszCaption,MB_OK

.else

invokeMessageEox,NULL,offsetszFound,offsetszCaption,MB_OK

.endif

popad

ret

_ProcTestendp

6.RDTSC/GetTickCount时间敏感程序段

当进程被调试时,调试器事件处理代码、步过指令等将占用CPU循环。如果相邻指令之间所花费的时间

如果大大超出常规,就意味着进程很可能是在被调试。

(1)RDTSC

将计算机启动以来的CPU运行周期数放到EDX:EAX里面,EDX是高位,EAX是低位。

如果CR4的TSD(timestampdisabled)置位,则rdtsc在ring3下运行会导致异常(特权指令),所以进入ringO,

把这个标记置上,然后Hook0D的WaitForDebugEvent,拦截异常事件,当异常代码为特权指令时,把异常处的

opcode读出检查,如果是rdtsc,把eip加2,SetThreadContext,edx:eax的返回由你了。

(2)GetTickCount源码见附件

invokeGetTickCount;第一次调用

movebx,eax;结果保存在ebx里

movecx,10;延时开始

movedx,6;单步走,放慢速度

movecx,10;延时结束

invokeGetTickCount;第二次调用

subeax,ebx;计算差值

.ifeax>1000;假定大于1000ms,就说明有调试器

jmpdebuggerjound

.endif

7.Startupinfo结构检测

原理:Windows操作系统中的explorer.exe创建进程的时候会把STARTUPINFO结构中的值设为0,而非

explorer.exe创建进程的时候会忽略这个结构中的值,也就是结构中的值不为0,所以可以利用这个来判断

OD是否在调试程序.

if(Info.dwXoO)or(lnfo.dwY<>Ojor(Info.dwXCountCharsoO)or(lnfo.dwYCountCharsoO)or(Info.dwFillAttributeoO)

or(Info.dwXSizeoO)or(lnfo.dwYSizeoO)then"有调试器”

*****♦**<**♦♦**•**«***♦**♦**<♦**♦**<**♦***♦**<***♦★*<•★*«***♦**♦**«******•**♦♦*

结构体

typedefstruct_STARTUPINFO

(

DWORDcb;0000

PSTRIpRcscrvcd;0004

PSTRIpDesktop;0008

PSTRIpTitle;000D

DWORDdwX;0010

DWORDdwY;0014

DWORDdwXSize;0018

DWORDdwYSize;001D

DWORDdwXCountChars;0C20

DWORDdwYCountChars;0024

DWORDdwFillAttribute;0028

DWORDdwFlags;002D

WORDwShowWindow;0030

WORDcbReserved2;0034

PBYTElpReserved2;0038

HANDLEhStdlnput;003D

HANDLEhStdOutput;0040

HANDLEhStdError;0044

}STARTUPINFO,*LPSTARTUPINFO;

ProcTestproc

LOCAL@stStartuplnfo:STARTUPINFO

pushad

invokeGetStartupInfo.addr@stStartuplnfo

cmp@stStartuplnfo.dwX,0

jnzfoundDebugger

cmp@stStartuplnfo.dwY,0

jnzfoundDebugger

cmp@stStartuplnfo.dwXGountChars,0

jnzfoundDebugger

cmp@stStartuplnfo.dwYCountChars,0

jnzfoundDebugger

cmp@stStartuplnfo.dwFillAttribute,0

jnzfoundDebugger

cmp@stStartuplnfo.dwXSize,0

jnzfoundDebugger

cmp@stStartuplnfo.dwYSize,0

jnzfoundDebugger

noDebugger:"无调试器”

jmpTestOver

foundDebugger:"有调试器”

TfistOvpr:

popad

ret

_ProcTestendp

8.BeingDebugged

kernel32!lsDebuggerPresent()API检测进程环境块(PEB)中的BeingDebugged标忐检查这个标志以确

定进程是否正在被用户模式的调试器调试。

每个进程都有PEB结构,一般通过TEB间接得到PEB地址

Fs:⑼指向当前线程的TEB结构,偏移为0处是线程信息块结构TIB

T旧偏移18H处是self字段,是「B的反身指针,指向TIB(也是PEB)首地址

TEB偏移30H处是指向PEB结构的指针

PEB偏移2H处,就是BeingDebugged字段,Uchar类型

(1)调用IsDebuggerPresent函数,间接读BeingDebugged字段

(2)利用地址直接读BeingDebugged字段

对策:

(1)数据窗口中Ctrl+Gfs:[30]查看PEB数据,将PEB.BeingDebugged标志置0

(2)Ollyscript命令"dbh"可以补「这个标志

.386

.modelflat,stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includekernel32.inc

includelibuser32.lib

includelibkernel32.lib

.const

szCaptiondb,检测结果:0

szFounddb'检测到调试器',0

szNotFounddb'没有调试密',0

.code

start:

;调用函数IsDebuggerPresent

invokeIsDebuggerPresent

.ifeax

invokeMessageBox,NULL,addrszFound,addrszCaption,MB_OK

.else

invokeMessageBox,NULL,addrszNotFound,addrszCaption,MB_OK

.endif

;直接去读字段

assumefs:nothing

moveax,fs:[30h]

movzxeax,byteptr[eax+2]

.ifeax

invokeMessageBox,NULL,addrszFound,addrszCaption,MB_OK

.else

invokeMessageBox,NULL,addrszNotFound,addrszCaption,MB_OK

.endif

invokeExitProcess,NULL

endstart

9.PEB.NtGlobalFlag,Heap.HeapFlags,Heap.ForceFlags

(1)通常程序没有被调试时,PEB另一个成员NtGlobalFlag(偏移0x68)值为0,如果进程被调试通常值为

0x70(代表下述标志被设置):

FLG_HEAP_ENABLE_TAIL_CHECK(0X10)

FLG_HEAP_ENABLE_FREE_CHECK(0X20)

FLG_HEAP_VALIDATE_PARAMETERS(0X40)

这些标志是在ntdll!Ldrpln计ializeExecutionOptions()里设置的。请注意PEB.NtGlobalFlag的默认值可以

通过gflags.exe工具或者在注册表以下位置创建条目来修改:

HKLM\Software\Microsoft\WindowsNt\CurrentVersion\lmageFileExecutionOptions

assumefs:nothing

moveax,fs:[30h]

moveax,[eax+68h'

andeax,70h

(2)由于NtGlobalFlag标志的设置,堆也会打开几个标志,这个变化可以在ntdlllRtlCreateHeap()里观

测到。正常情况下系统为进程创建第一个堆时会将Flags和FoxeFlags分别设为2(HEAP_GROWABLE)

和0。当进程被调试时,这两个标志通常被设为50000062(取决于NtGlobalFlag)和0x40300060(等于

FlagsAND0x6001007D)。

assumefs:nothing

movebx,fs:[30h];ebx指向PEB

moveax,[ebx+18h];PEB.ProcessHeap

cmpdwordptr[eax+0ch],2;PEB.ProcessHeap.Flags

jnedebuggerjound

cmpdwordptr[eax+10h],0;PED.ProcesslleapTorceFlags

jnedebuggerjound

这些标志位都是因为BeingDebugged引起的。系统创建进程的时候设置.BeingDebugged=TRUE,后来

NtGlobalFlag根据这个标记设置FLG_VALIDATE_PARAMETERS等标记。在为进程创建堆时,又由于

NtGlobalFlag的作用,堆的Flags被设置了一些标记,这个Flags随即被填充到ProcessHeap的Flags和

ForceFlags中,同时堆中被填充了很多BAADF00D之类的东西(HeapMagic,也可用来检测调试)。

一次性解决这些状态见加密解密P413

.386

.modelflat,stdcail

optioncasemap:none

includewindows.inc

includeuser32.inc

includekernel32.inc

includelibuser32.lib

includelibkernel32.lib

.const

szCaptiondb'检测结果',0

szFounddb'检测到调试器',0

szNotFounddb'没有调试器',0

.code

start:

assumefs:nothing

movebx,fs:[30h];ebx指向PEB

;PEB.NtGlobalFlag

moveax,[ebx+68h]

cmpeax,70h

jedebuggerjound

;PEB.ProcessHeap

moveax,[ebx+18h]

;PEB.ProcessHeap.Flags

cmpdwordptr[eax+0ch],2

jnedebuggerfound

;PEB.ProcessHeap.ForceFlags

cmpdwordptr[eax+10h],0

jnedebuggerjound

invokeMessageBox,NULL,addrszNotFound,addrszCaption,MB_OK

jmpexit

debugger_found:invokeMessageBox,NULL,addrszFound,addrszCaption,MBOK

exit:invokeExitProcess,NULL

endstart

10.EPROCESS的DebugPort成员

Kemel32!CheckRemoteDebuggerPresent()是用于确定是否有调试器被附力II至ij进程。

BOOLCheckRemoteDebuggerPresent(

HANDLEhProcess,

PBOOLpbDebuggerPresent

)

Kemel32!CheckRemoteDebuggerPresent()接受2个参数,第1个参数是进程句柄,第2个参数是一个

指句boolean变量的指针,如果进程被调试,该变量将包含TRUE返回值。

这个API内部调用了ntdll!NtQuerylnformationProcess(),由它完成检测工作。

.386

.modelflat,stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includekernel32.inc

includAlihusAr32.lib

includelibkernel32.lib

.data?

dwResultdd?

.const

szCaption'检测结果',0

szFounddb'检测到调试器',0

szNotFounddb'没有调试谭,0

.code

start:

invokeGetCurrentProcessId

invokeOpenProcess,PROCESS_ALL_ACCESS,NULL,eax

invokeCheckRemoteDebuggerPresent,eax,addrdwResult

cmpdwordptrdwResult,0

jnedebuggerjound

invokeMessageBox,NULL,addrszNotFound,addrszCaption,MB_OK

jmpexit

debuggerjound:invokeMessageBox,NULL,addr

szFound,addrszCaption.MBOKexit:invokeExitProcess,NULL

endstart

ntdll!NtQuerylnformationPrccess()W5个参数。

为了检测调试器的存在,需要将Processinformationclass参数设为ProcessDebugPort(7)o

NtQuerylnformationProcess。检索内核结构EPROCESS5的DebugPort成员,这个成员是系统用来与

调试器通信的端口句柄。非0的DebugPort成员意味着进程正在被用户模式的调试器调试。圻果是这样的话,

Processinformation将被置为OxFFFFFFFF,否则Processinformation将被置为0。

ZwQuerylnformationProcess(

INHANDLEProcessHandle,

INPROCESSINFOCLASSProcessInformationClass,

OUTPVOIDProcessInformation,

INULONGProcessInformationLength,

OUTPULONGReturnLengthOPTIONAL

);

.386

.modelflat,stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includelibuser32.lib

includekernel32.inc

includelibkernel32.lib

includentdll.inc;这两个

includelibntdll.lib

.data?

dwResultdd?

.const

szCaptiondb检测结果',0

szFounddb'检测到调试器',0

szNotFounddb‘没有调试器',0

.code

start:

invokeGetCurrentProcessId

invokeOpenProcess,PROCESS_ALL_ACCESS,NULL,eax

invokeZwQuerylnformationProcess,eax,7,offsetdwResult,4,NULL

cmpdwResult,0

jnedebugger_found

invokeMessageBox,NULL,addrszNotFound,addrszCaption,MB_OK

jmpexit

debugger_found:invokeMessageBox,NULL,addrszFound:addrszCaption,MB_OK

exit:invokeExitProcess,NULL

endstart

11.SetUnhandledExceptionFilter/DebuggerInterrupts

调试器中步过INT3和INT1指令的时候,由于调试器通常会处理这峻调试中断,所以设置的异常处理例

程默认情况下不会被调用,DebuggerInterrupts就利用了这个事实。这样我们可以在异常处理例程中设置标

志,通过INT指令后如果这些标志没有被设置则意味着进程正在被调试。另外,kernel32!DebugBreak()内部

是调用了INT3来实现的,有些壳也会使用这个APL注意测试时,在异常处理里取消选中INT3

breaks和Singal-stepbreak

.386

.modelflat.stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includelib।user32.lib

includekernel32.inc

includelib(kernel32.lib

.data

IpOldHandlerdd?

.const

szCaptiondb‘检测结果',0

szFoundd<b'检测到调试器',0

szNotFounddb'没有调试器',0

.code

;ExceptionHandler异常处理程序

HandlerprocIpExceptionPoint

pushad

movesi,—IpExceptionPoint

assumeesi:ptrEXCEPTION_POINTERS

movedi,[esi].ContextRecord

assumeedi:ptrCONTEXT

mov[edi].regEax,OFFFFFFFFH;设置EAX

mov[Adi].regEip,offsetSaffiPlacA

assumeesi:nothing,edi:nothing

popad

moveax,EXCEPTION_CONTINUE_EXECUTION

ret

_Handlerendp

start:

invokeSetUnhandledExceptionFilter,addr_Handler

movIpOldHandler,cax

xoreax,eax;清零eax

int3;产生异常,然后—Handler被调用

SafePlace:

testeax,eax

jedebugger_found

invokeMessageBox,NULL,addrszNotFound,addrszCaption,MB_OK

]mpexit

debugger_found:invokeMessageBox,NULL,addrszFound,addr

szCaption,MB_OKexit:invokeSetUnhandledExceptionFilterJpOldHandler;取消异常处

理函数

invokeExitProcess,NULL

endstart

由于调试中断而导致执行停止时,在QlyDbg中识别出异常处理例程(通过视图,SEH锥)并下断点,

然后SZft+F9将调试中断/异常传递给异常处理例程,最终异常处理例程中的断点会断下来,这时就可以跟踪

了,

另一个方法是允许调试中断自动地传递给异常处理例程。在OllyDbg中可以通过选项->调试选项->异

常->忽略下列异常选项卡中钩选”INT3中断”和“单步中断”复:选框来完成设置。

12.单步标志异常

TF=1的时候,会触发单步异常。该方法属于异常处理,不过比较特殊:未修改的OD无论是F9还是F8

都不能处理异常,有插件的OD在F9时能正确处理,F8时不能正确处理。

.386

.modelflat,stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includelibiuser32.lib

includekernel32.inc

includelibikernel32.lib

.data

IpOldHandlerdd?

szCaptiondb‘检测结果',0

szFounddb'程序未收到异常,说明有调试器’,0

szNotFounddb’程序处理了异常而到达安全位置,没有调试器',0

nodA

_HandlerprocJpExceptionPoint

pushad

movesi,JpExceptionPoint

assumeesi:ptrEXCEPTION_POINTERS

movedi,[esi].ContextRecord

assumeedi:ptrCONTEXT

mov[edi].regEip,offsetSafePlace

assumeesi:nothing,edi:nothing

popad

moveax,EXCEPTION_CONTINUE_EXECUTION

ret

_Handlerendp

start:

invokeSetUnhandledExceptionFilter,addrHandler

movIpOldHandlereax

pushfd;pusheflags

ordwordptr[esp],i00h;TF=1

popfd

nop

jmpdie

SafePlace:

invokeMessageBox.NULL.addrszNotFound,addrszCaption,MB_OK

jmpexit

die:invokeMessageBox,NULL,addrszFound,addrszCaption,MB_OK

exit:invokeSetUnhandledExceptionFilterJpOldHandler;取消异常处理函数

invokeExitProcess,NULL

endstart

13.SeDebugPrivilege进程权限

默认情况下进程没有SeDebugPrivilege权限,调试时,会从调试器继承这个权限,可以通过打开

CSRSS.EXE进程间接地使用SeDebugPrivilege确定进程是否被调试。注意默认情况下这一权限仅仅授予/

Administrators组的成员。可以使用ntdll!CsrGetProcessld()API获取CSRSS.EXE的PID,也可以通过枚举

进程来得到CSRSS.EXE的PID:.

实例测试中,0D载入后,第一次不能正确检测,第二次可以,不知为何。

.386

.modelflat.stdcall

optioncasemap:none

includewindows.inc

includeuser32.inc

includekernel32.inc

includentdll.inc

includelibuser32.lib

includelibkernel32.lib

includelibntdll.lib

.const

szCaptiondb,检测结果',0

szFounddb'检测到调试器10

szNotFounddb'没有调试器'Q

.code

start:

invokeCsrGetProcessId;ntdll!CsrGetProcessld获取CSRSS.EXE的PID

invokeOpenProcess,PROCESS_QUERY_INFORMATION,NULL,eax

testeax,eax

jnzdebugger_found

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论