一、调用系统的IsDebuggerPresent函数

(1)实现程序

  最简单也是最基础的,Windows提供的API接口:IsDebuggerPresent(),这API实际上就是访问PEB的BeingDebugged标志来判断是否处于调试状态。

  使用vs调试此段代码,弹出"检测到调试器"。

#include <stdio.h>
#include <Windows.h> DWORD WINAPI ThreadFunctionCallBack(LPVOID lp)
{
while(true)
{
if (IsDebuggerPresent())
{
printf("检测到调试器\n");
}
}
} int main()
{ CreateThread(NULL, NULL, ThreadFunctionCallBack, NULL, NULL, NULL); // 启动一个线程进行实时检测
while(TRUE)
{
printf("主线程在执行");
}
system("pause");
return 0;
}

(2)分析IsDebuggerPresent原理:

  通过dbg进行简单地逆向分析,进入到 IsDebuggerPresent 函数内部。x86下 FS:[30] 指向PEB,访问 PEB 的 BeingDebugged 标志来判断是否处于调试状态。

   

  IsDebuggerPresent:

  

// x86下PEB结构

NTDLL_Test!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar   // (BOOL)被调试时,被置为1
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 SparePtr1 : Ptr32 Void
+0x024 SparePtr2 : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 ExecuteOptions : Pos 0, 2 Bits
+0x034 SpareBits : Pos 2, 30 Bits
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B   // 被调试时,会被置为x070
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 _RTL_CRITICAL_SECTION
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void 
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x208 MinimumStackCommit : Uint4B
+0x20c FlsCallback : Ptr32 Ptr32 Void
+0x210 FlsListHead : _LIST_ENTRY
+0x218 FlsBitmap : Ptr32 Void
+0x21c FlsBitmapBits : [4] Uint4B
+0x22c FlsHighIndex : Uint4B

(3)IsDebuggerPresent函数的反反调试

  方法1: 在dbg中直接对IsDebuggerPresent函数进行修改,让其返回即可。  

  方法2: 对Kernel32.dll中的IsDebuggerPresent函数实现MiniHook(或InlineHook),在FakeIsDebuggerPresent函数中返回FALSE即可。编写一个dll,在加载到进程时,执行MiniHook。这里因为MiniHook框架太大,就不贴出相关程序了,有需要可以留言,此处只附出DLL的编写。

#include "MiniHook.h"

typedef
BOOL
(WINAPI* LPFN_ISDEBUGGERPRESENT)(); LPFN_ISDEBUGGERPRESENT __OriginalIsDebuggerPresent = NULL; BOOL
WINAPI
FakeIsDebuggerPresent(VOID)
{
return FALSE;
} BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (SunInitialize() != STATUS_SUCCESS)
{
MessageBox(0,"初始化失败","提示",0);
}
if (SunCreateHook(&IsDebuggerPresent, &FakeIsDebuggerPresent,
reinterpret_cast<LPVOID*>(&__OriginalIsDebuggerPresent)) != STATUS_SUCCESS)
{
MessageBox(0,"初始化Hook失败","提示",0);
}
if (SunEnableHook(&IsDebuggerPresent) != STATUS_SUCCESS)
{
MessageBox(0, "创建Hook失败", "提示", 0);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
//SunRemoveHook(&IsDebuggerPresent);
break;
}
return TRUE;
}

二、自定义实现IsDebuggerPresent函数(x86)

自己用汇编实现实现IsDebuggerPresent,如何获得PEB呢? 在应用层,fs寄存器是指向当前线程的TEB结构的,而在内核层,fs寄存器指向PCR(Processor Control Region)的内存,其数据类型是KPCR。所以可以通过当前线程的TEB获得PEB,再取出BeingDebugged的值。

原理:

  mov   eax, fs:18h     // TEB Self指针

  mov   eax, [eax+30h]  // PEB

  movzx eax, [eax+2]    // PEB->BeingDebugged

实现:

_asm
{
push eax;
mov eax, fs:[0x30]; // PEB
movzx eax, byte ptr[eax + 2]; //BeingDebugged
mov dword ptr[Value], eax; //取值
pop eax;
} if (Value) //判断
{
MessageBox(0,L"检测到调试器",L"提示",0);
}
else
{
MessageBox(0,L"未检测到调试器",L"提示",0);
}

三、深度解析调试中各标志位的变化

这部分转载于:https://blog.csdn.net/qq_35713009/article/details/86603668/

  加密与解密中提供了从外部代码去除某进程BeingDebugged标志的方式,可以作为调试器的插件去除这种反调试方法。但BeingDebugged标记在生成时还留下了一些后患。

  那么是否可以简单认为将 PEB 的 BeingDebugged 标志篡改就可以达到越过IsDebuggerPresent函数的目的呢?当然不能。因为调试时不仅仅这一位被置值了,而是连锁反应... ...

(1) PEB 的 BeingDebugged 标志(BOOL型),被修改为1,表示正在被调试

(2)NtGlobalFlag的变化

  LdrpInitialize函数是一个新进程的初始线程开始在用户态执行的最早代码,在这个函数内部有一段这样的代码:

if (Peb->BeingDebugged)
{
Peb->NtGlobalFlag |= FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_VALIDATE_PARAMETERS; }

这说明BeingDebugged被设置为1后,NtGlobalFlag也被设置了一个标记,通过调试可发现这个标记为0x70。

(3)RtlCreateHeap中创建调试堆时的变化

  那么 NtGlobalFlag 有没有向 BeingDebugged 一样留下痕迹呢?是有的,在初始化堆的函数 RtlCreateHeap 中可以找到相应代码。

if (RtlpGetMode() == UserMode)
{
/* Also check these flags if in usermode */
if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL)
Flags |= HEAP_VALIDATE_ALL_ENABLED; if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS)
Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED; if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB)
Flags |= HEAP_CAPTURE_STACK_BACKTRACES;
} /* Call special heap */
if (RtlpHeapIsSpecial(Flags))
return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); FORCEINLINE BOOLEAN
RtlpHeapIsSpecial(ULONG Flags)
{
if (Flags & HEAP_SKIP_VALIDATION_CHECKS) return FALSE; if (Flags & (HEAP_FLAG_PAGE_ALLOCS |
HEAP_VALIDATE_ALL_ENABLED |
HEAP_VALIDATE_PARAMETERS_ENABLED |
HEAP_CAPTURE_STACK_BACKTRACES |
HEAP_CREATE_ENABLE_TRACING))
{
/* This is a special heap */
return TRUE;
} /* No need for a special treatment */
return FALSE;
}

  RtlDebugCreateHeap内部实际上又调用了RtlCreateHeap,不过这次Flags参数又多了几个属性,分别是HEAP_SKIP_VALIDATION_CHECKS, HEAP_TAIL_CHECKING_ENABLED, HEAP_FREE_CHECKING_ENABLED。第一个属性使得 RtlCreateHeap和RtlDebugCreateHeap不会再继续重复的相互调用。

/* All validation performed, now call the real routine with skip validation check flag */
Flags |= HEAP_SKIP_VALIDATION_CHECKS |
HEAP_TAIL_CHECKING_ENABLED |
HEAP_FREE_CHECKING_ENABLED; Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters);

与后两个标记有关的有这样一段代码,

if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
RtlFillMemoryUlong((PCHAR)(BusyBlock + 1), Size & ~0x3, ALLOC_HEAP_FILL); } if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
RtlFillMemory((PCHAR)ReturnValue + Size, CHECK_HEAP_TAIL_SIZE, CHECK_HEAP_TAIL_FILL) BusyBlock->Flags |= HEAP_ENTRY_FILL_PATTERN; } #define ALLOC_HEAP_FILL 0xBAADF00D #define FREE_HEAP_FILL 0xFEEEFEEE #define CHECK_HEAP_TAIL_FILL 0xAB

意思是会用这3种数据来填堆,即当堆中多次重复出现这3种数据时,就表示在调试状态中。这些东西通常被称为HeapMagic。

除此之外,Flags在RltCreateHeap函数后面还有一些感染操作,使得 Heap->Flags 和 Heap->ForceFlags 都附带了调试信息,通常在程序正常执行的情况下,Flags为2,ForceFlags为0;而在调试状态下,Flags为0x50000062,ForceFlags为0x40000060。

// x86下PEB结构
NTDLL_Test!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar // (BOOL)被调试时,被置为1
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 SparePtr1 : Ptr32 Void
+0x024 SparePtr2 : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 ExecuteOptions : Pos 0, 2 Bits
+0x034 SpareBits : Pos 2, 30 Bits
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B // 被调试时,会被置为x070
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 _RTL_CRITICAL_SECTION
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void 
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x1fc ProcessAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x200 SystemDefaultActivationContextData : Ptr32 _ACTIVATION_CONTEXT_DATA
+0x204 SystemAssemblyStorageMap : Ptr32 _ASSEMBLY_STORAGE_MAP
+0x208 MinimumStackCommit : Uint4B
+0x20c FlsCallback : Ptr32 Ptr32 Void
+0x210 FlsListHead : _LIST_ENTRY
+0x218 FlsBitmap : Ptr32 Void
+0x21c FlsBitmapBits : [4] Uint4B
+0x22c FlsHighIndex : Uint4B

IsDebuggerPresent的反调试与反反调试的更多相关文章

  1. 反编译apk + eclipse中调试smali

    1.对apk使用apktool反编译出可调试的smali代码到out文件夹 apktool -d d 定点加粉丝_com.mingniu.wxddjfs_440.apk -o out 这里必须使用-d ...

  2. [Android]反编译apk + eclipse中调试smali

    从来没有想过反编译apk是来的如此方便,并且还可以修改后重新编译运行,这比在win下修改pe容易多了,感谢apktool和smali工具的作者提供这么好的工具. 跟踪apk一般的做法是在反编译的sma ...

  3. Win7 x86内核调试与TP反调试的研究

    参考  这两天对某P双机调试的学习及成果 ,非常好的一篇分析贴. 本文在Win7 x86下的分析,在虚拟机中以/DEBUG模式启动TP游戏,系统会自动重启. 0x01 内核调试全局变量  根据软件调试 ...

  4. 爬虫(Spider),反爬虫(Anti-Spider),反反爬虫(Anti-Anti-Spider)

    爬虫(Spider),反爬虫(Anti-Spider),反反爬虫(Anti-Anti-Spider),这之间的斗争恢宏壮阔... Day 1小莫想要某站上所有的电影,写了标准的爬虫(基于HttpCli ...

  5. Android反编译(二)之反编译XML资源文件

    Android反编译(二) 之反编译XML资源文件 [目录] 1.工具 2.反编译步骤 3.重新编译APK 4.实例 5.装X技巧 6.学习总结 1.工具 1).反编译工具  apktool http ...

  6. Android反编译(一)之反编译JAVA源码

    Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具  dex2jar   http://code.go ...

  7. 转 谈谈android反编译和防止反编译的方法

    谈谈android反编译和防止反编译的方法   android基于java的,而java反编译工具很强悍,所以对正常apk应用程序基本上可以做到100%反编译还原. 因此开发人员如果不准备开源自己的项 ...

  8. 谈谈android反编译和防止反编译的方法(转)

    谈谈android反编译和防止反编译的方法(转) android基于java的,而java反编译工具很强悍,所以对正常apk应用程序基本上可以做到100%反编译还原. 因此开发人员如果不准备开源自己的 ...

  9. (转)unity3D 如何提取游戏资源 (反编译)+代码反编译

    原帖:http://bbs.9ria.com/thread-401140-1-1.html 首先感谢 雨松MOMO 的一篇帖子 教我们怎么提取 .ipa 中的游戏资源.教我们初步的破解unity3d资 ...

  10. python爬虫---详解爬虫分类,HTTP和HTTPS的区别,证书加密,反爬机制和反反爬策略,requests模块的使用,常见的问题

    python爬虫---详解爬虫分类,HTTP和HTTPS的区别,证书加密,反爬机制和反反爬策略,requests模块的使用,常见的问题 一丶爬虫概述       通过编写程序'模拟浏览器'上网,然后通 ...

随机推荐

  1. CF1228E题解

    设 \(f_{i,j}\) 为恰好 \(i\) 行 \(j\) 列不满足条件的矩阵个数, \(g_{i,j}\) 为钦定 \(i\) 行 \(j\) 列不满足条件的矩阵个数. 容易得到: \[g_{x ...

  2. Python之简单的用户名密码验证

    题目要求: 输入用户名密码 认证成功后显示欢迎信息 输错三次后锁定   #要求使用文件存储用户名和密码,每次从文件读入用户名和密码用来验证,如果输错三次密码该账户会被锁定,并讲锁定的用户名写入文件 # ...

  3. GitLab 常用命令

    1. 进入本地仓库访问位置之后执行命令 1) 远程仓库相关命令检出仓库:$ git clone git://github.com/jquery/jquery.git查看远程仓库:$ git remot ...

  4. 在西电使用校内Linux 开源软件镜像

    西电开源社区(linux.xidian.edu.cn)为全校师生提供开源镜像服务,由于其使用校内服务器,因此产生的流量不会计入校园网 打开镜像列表:https://linux.xidian.edu.c ...

  5. UVA1389 Hard Life (01分数规划+最大流)

    UVA1389 Hard Life (01分数规划+最大流) Luogu 题目描述略 题解时间 $ (\frac{\Sigma EdgeCount}{\Sigma PointCount})_{max} ...

  6. 玩转SpringBoot之定时任务@Scheduled线程池配置

    序言 对于定时任务,在SpringBoot中只需要使用@Scheduled 这个注解就能够满足需求,它的出现也给我们带了很大的方便,我们只要加上该注解,并且根据需求设置好就可以使用定时任务了. 但是, ...

  7. 七天接手react项目 系列 —— 尾篇(antd 和 mobx)

    其他章节请看: 七天接手react项目 系列 尾篇 前面我们依次学习了 react 基础知识.react 脚手架创建项目.react 路由,已经花费了不少时间,但距离接手 spug_web 项目还有一 ...

  8. C++ cout 数字之间进制的转换

    转换一个数变成8进制,则为 cout << oct << x << endl; 转换一个数变为16进制,为 cout << hex << x ...

  9. JDBC-Druid增删改查

    由于刚学完JDBC,所以来小练一下,通过Druid实现对数据库的增删改查操作 (现在是真正简单的纯数据库操作,等我学过前端,再做一个比较具体的!) •数据库 新建一个数据库tb,创建brand表,有主 ...

  10. IDEA 常用快捷键操作

    自定义设置及查询: 操作路径:file-setting-Keymap-Editor actions 右击需要修改的action操作,或者右击Editor actions,选择添加Add Keyboar ...