一、调用系统的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. Java &、&&、|、||、^、<<、>>、~、>>>等运算符

    &(按位与) 运算规则:两个为真才为真 (1&1=1 , 1&0=0 , 0&1=0 , 0&0=0) 例:3&5=1 3的二进制位是0000 0011 ...

  2. spring——依赖注入的三种方式

    1 构造器注入(与构造器有直接关系) 默认无参构造 3种构造方式:通过<contructor-arg>调用类中的构造器 下标 <bean id="userService&q ...

  3. 部署YUM源仓库与NFS共享存储服务

    镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 一.YUM YUM (Yellow dog Updater Modified) ●基于RPM包构建的软件更新机制 ●可以自动解决依赖关系 ●所有 ...

  4. java对配置文件properties的操作

    1.读取配置文件的键值对,转为Properties对象:将Properties(键值对)对象写入到指定文件. package com.ricoh.rapp.ezcx.admintoolweb.util ...

  5. Python knife 一款伪菜刀

    Python knife ​ 一款伪菜刀. ​ 设计之初,本想只写个命令行的就可以了,但又想与众不同,想用python写代码,又不想用c#写前端(c#太卡了),万分无奈之下,找到一个替代品,Pyqt, ...

  6. MySQL — 数据查询语言

    目录 1.基础查询 2.条件查询 3.分组查询 4.排序查询 5.分页查询 6.多表查询 6.1.连接查询 6.1.1.内连接 6.1.2.外连接 6.1.3.自连接 6.1.4.联合查询 6.2.子 ...

  7. Android BLE 蓝牙开发——扫码枪基于BLESSED

    一.蓝牙模式HID与BLE 当扫码枪与手机连接时,通常采用的是蓝牙HID(Human Interface Device)模式.本质上是一个把扫码枪作为一个硬件键盘,按照键盘协议把扫码后的结果逐个输入到 ...

  8. Prometheus自定义监控告警项-3

    prometheus 编写告警规则 将自定义的告警规则写到独立的文件中,prometheus.yml中引用如下: rule_files: - "rules/*.yml" [root ...

  9. C#中的类型转换-自定义隐式转换和显式转换

    目录 前言 基础知识 示例代码 实际应用 问题 答案 报错 用户定义的转换必须是转换成封闭类型,或者从封闭类型转换 参考 其他 应用和设计 读音 参考 前言 有时我们会遇到这么一种情况:在json数据 ...

  10. 哪一个 bash 内置命令能够进行数学运算?

    bash shell 的内置命令 let 可以进行整型数的数学运算. #! /bin/bash--let c=a+b--