




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
反调试技巧总结-原理和实现
总结:
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
提交评论