频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

15.1.1 BeingDebugged

作者: 段钢等编著 出处:电子工业出版社  2008-06-22 15:50    砖    好    评论   进入论坛
阅读提示:《加密与解密》第15章主要为大家讲述的是反跟踪技术,这一章将讨论一些常用的反跟踪方法,本节为大家介绍BeingDebugged。

第15章 反跟踪技术①

好的软件保护都要与反跟踪技术结合在一起。如果没有反跟踪技术,软件等于直接裸露在解密者的面前。这里所说的反跟踪是泛指,包括防调试器、防监视工具等内容。本章将讨论一些常用的反跟踪方法,读者可以根据实际情况在自己的软件中采用相关的技术和代码。

15.1  由BeingDebugged引发的蝴蝶效应

一个坏的微小的机制,如果不加以及时地引导、调节,会给社会带来非常大的危害,戏称为“龙卷风”或“风暴”;一个好的微小的机制,只要正确指引,经过一段时间的努力,将会产生轰动效应,或称为“革命”。

15.1.1  BeingDebugged

Win32 API为程序提供了IsDebuggerPresent判断自己是否处于调试状态,懒惰的程序员总是用它。来看一下实现代码:

// debug.c
BOOL
APIENTRY
IsDebuggerPresent(VOID)
{
return NtCurrentPeb()->BeingDebugged;
}
这个函数读取了当前进程PEB中的BeingDebugged标志,每个运行中的进程拥有一个名为PEB(Process Environment Block,进程环境块)的结构,对它有少许了解会有助于理解后面的内容。PEB结构的内容:
Offset Elements name Type 
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+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 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : 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
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
接下来的问题当然是如何找到PEB地址。它储存在另一个名为线程环境块(Thread Environment Block,TEB)的结构之内。
Windows在调入进程,创建线程时,操作系统均会为每个线程分配TEB,而且FS段寄存器总是被设置成使得地址FS:0指向当前线程的TEB数据(单CPU机器在任何时刻系统中只有一个线程在执行),这就为存取TEB数据提供了途径,如图15.1所示。
 
图15.1  执行线程块的结构
再来了解一下TEB的结构,请注意+30h处的偏移字段。
+0x000 NtTib :      :_NT_TIB
+0x01c EnvironmentPointer    : Ptr32 Void
+0x020 ClientId            : _CLIENT_ID
+0x028 ActiveRpcHandle     : Ptr32 Void
+0x02c ThreadLocalStoragePointer  : Ptr32 Void
+0x030 ProcessEnvironmentBlock   : Ptr32 _PEB
+0x034 LastErrorValue       : Uint4B
+0x038 CountOfOwnedCriticalSections  : Uint4B
+0x03c CsrClientThread      : Ptr32 Void
+0x040 Win32ThreadInfo      : Ptr32 Void
+0x044 User32Reserved       : [26] Uint4B
+0x0ac UserReserved         : [5] Uint4B
+0x0c0 WOW32Reserved        : Ptr32 Void
+0x0c4 CurrentLocale        : Uint4B
+0x0c8 FpSoftwareStatusRegister   : Uint4B
+0x0cc SystemReserved1      : [54] Ptr32 Void
+0x1a4 ExceptionCode        : Int4B
+0x1a8 ActivationContextStack   : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1          : [24] UChar
+0x1d4 GdiTebBatch          : _GDI_TEB_BATCH
+0x6b4 RealClientId         : _CLIENT_ID
+0x6bc GdiCachedProcessHandle   : Ptr32 Void
+0x6c0 GdiClientPID         : Uint4B
+0x6c4 GdiClientTID         : Uint4B
+0x6c8 GdiThreadLocalInfo    : Ptr32 Void
+0x6cc Win32ClientInfo      : [62] Uint4B
+0x7c4 glDispatchTable      : [233] Ptr32 Void
+0xb68 glReserved1          : [29] Uint4B
+0xbdc glReserved2          : Ptr32 Void
+0xbe0 glSectionInfo        : Ptr32 Void
+0xbe4 glSection            : Ptr32 Void
+0xbe8 glTable              : Ptr32 Void
+0xbec glCurrentRC          : Ptr32 Void
+0xbf0 glContext            : Ptr32 Void
+0xbf4 LastStatusValue      : Uint4B
+0xbf8 StaticUnicodeString    : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer    : [261] Uint2B
+0xe0c DeallocationStack   : Ptr32 Void
+0xe10 TlsSlots            : [64] Ptr32 Void
+0xf10 TlsLinks             : _LIST_ENTRY
+0xf18 Vdm                  : Ptr32 Void
+0xf1c ReservedForNtRpc    : Ptr32 Void
+0xf20 DbgSsReserved        : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled   : Uint4B
+0xf2c Instrumentation      : [16] Ptr32 Void
+0xf6c WinSockData          : Ptr32 Void
+0xf70 GdiBatchCount        : Uint4B
+0xf74 InDbgPrint           : UChar
+0xf75 FreeStackOnTermination   : UChar
+0xf76 HasFiberData         : UChar
+0xf77 IdealProcessor       : UChar
+0xf78 Spare3               : Uint4B
+0xf7c ReservedForPerf      : Ptr32 Void
+0xf80 ReservedForOle       : Ptr32 Void
+0xf84 WaitingOnLoaderLock    : Uint4B
+0xf88 Wx86Thread           : _Wx86ThreadState
+0xf94 TlsExpansionSlots    : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale    : Uint4B
+0xf9c IsImpersonating      : Uint4B
+0xfa0 NlsCache             : Ptr32 Void
+0xfa4 pShimData            : Ptr32 Void
+0xfa8 HeapVirtualAffinity    : Uint4B
+0xfac CurrentTransactionHandle   : Ptr32 Void
+0xfb0 ActiveFrame          : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall        : UChar
+0xfb5 BooleanSpare         : [3] UChar
00h处的TIB(Thread Information Block,线程信息块)结构为:
+0x000 ExceptionList        : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase            : Ptr32 Void
+0x008 StackLimit            : Ptr32 Void
+0x00c SubSystemTib         : Ptr32 Void
+0x010 FiberData             : Ptr32 Void
+0x010 Version               : Uint4B
+0x014 ArbitraryUserPointer    : Ptr32 Void
+0x018 Self                  : Ptr32 _NT_TIB,指向TEB结构的指针
每个进程都有自己的PEB,Windows一般通过TEB间接得到PEB的地址。即通过以下语句获得:
mov eax,fs:[18h]        //获得当前线程的TEB地址
mov eax,[eax+30h]       //在TEB偏移30h处获得PEB地址

TIB+18h处为Self,是TIB的反身指针,指向PEB首地址,因此可以省略而直接使用fs:[30h]得到自己进程的PEB。

为了免去繁冗的定义,这里给出一个内联汇编代码的简化版IsDebuggerPresent:

 BOOL MyIsDebuggerPresent(VOID)
{
__asm {
mov eax, fs:[0x30]            //在位于TEB偏移30h处获得PEB地址
movzx eax, byte ptr [eax+2] //获得PEB偏移2h处BeingDebugged的值
}
}

根据这个原理,OllyDbg可以用插件清除BeingDebugged以隐藏调试器。

虽然在Windows 2000/NT系统中PEB本身在大多数情况下被映射到7FFDF000h处,不过值得注意的是,从Windows XP SP2后系统引入了一个特性:PEB地址随机化。每个进程的PEB地址不固定,大概有14种可能。

系统创建进程时设置PEB的地址,调用NtCreateProcess/NtCreateProcessEx,依次转向PspCreateProcess/MmCreatePeb/MiCreatePebOrTeb,在MiCreatePebOrTeb函数中根据当前时间计算随机值:

 PVOID HighestVadAddress;
LARGE_INTEGER CurrentTime;
HighestVadAddress = (PVOID) ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1);
KeQueryTickCount (&CurrentTime);
CurrentTime.LowPart &= ((X64K >> PAGE_SHIFT) - 1);
if (CurrentTime.LowPart <= 1) {
CurrentTime.LowPart = 2;
}
HighestVadAddress = (PVOID) ((PCHAR)HighestVadAddress - (CurrentTime.LowPart<< PAGE_SHIFT));
所以不能认为PEB就在7FFDF000h,不同的进程PEB地址会不一样。当然也就不能用本进程FS:[18h]的指针去读写其他进程的内容。正确的方法是使用下面的函数,取得某个线程段选择子的线性地址:
BOOL GetThreadSelectorEntry(
HANDLE hThread,
DWORD dwSelector,
LPLDT_ENTRY lpSelectorEntry
);
如果喜欢Native API,也可以通过NtQueryInformationProcess获得PEB:
 ULONG GetPebBase(ULONG ProcessId)
{
HANDLE hProcess = NULL;
PROCESS_BASIC_INFORMATION pbi = {0}
ULONG peb = 0;       
ULONG cnt = 0;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessId);
if (hProcess != NULL) {
if (NtQueryInformationProcess(
hProcess,
ProcessBasicInformation,
&pbi,
sizeof(PROCESS_BASIC_INFORMATION),
&cnt) == 0) {
PebBase = (ULONG)pbi.PebBaseAddress;
}           
CloseHandle(hProcess);
}
}
这里采用GetThreadSelectorEntry,下面这段代码就可以清除BeingDebugged标记了:
BOOL HideDebugger( HANDLE hThread,HANDLE hProcess)
{
CONTEXT ctx;
LDT_ENTRY sel;
DWORD fs;
DWORD peb;
SIZE_T bytesrw;
WORD flag;
ctx.ContextFlags = CONTEXT_SEGMENTS;
if (!GetThreadContext(hThread, &ctx))
return FALSE;
if (!GetThreadSelectorEntry(hThread, ctx.SegFs, &sel))
return FALSE;
fs = (sel.HighWord.Bytes.BaseHi << 8 | sel.HighWord.
Bytes.BaseMid) << 16 | sel.BaseLow;   
if (!ReadProcessMemory(hProcess, (LPCVOID)(fs + 0x30),
&peb, 4, &bytesrw) || bytesrw != 4)
return FALSE;
if (!ReadProcessMemory(hProcess, (LPCVOID)(peb + 0x2),
&flag, 2, &bytesrw) || bytesrw != 2)
return FALSE;
flag = 0;
if (!WriteProcessMemory(hProcess, (LPCVOID)(peb + 0x2),
&flag, 2, &bytesrw) || bytesrw != 2)
return FALSE;
return TRUE;
}

现在读者一定认为这个标志太愚蠢了,事实上它比你想象的要复杂一些。BeingDebugged虽然被消灭了,但是问题并不是这么简单。
【责任编辑:夏书 TEL:(010)68476606】

回书目   上一节   下一节
专题
系统应用日志分析管理
主流防火墙性能对比分析
3G资讯全面跟踪
DDoS攻击防御与分析
深入了解PGP加密技术
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:SQL Server数据库管理精品黄皮书
简介:书中文章经过精挑细选,便于用户能根据自己的实际工作和学习,快速在本书寻找到相关资料。内容涵盖了SQL Server的安装与升级、语句查询、数据备份和恢复、自动化任务、数据同步、数据字典、安全和预防、性能和优化、集群等各方面应用信息,以及DBA管理人员在数据库管理工作中
名称:2007路由技术大全
简介:《2007路由技术大全》由51CTO.com网站特别策划制作,该书包括路由器技术、路由器产品、路由器配置、安全设置、路由器故障处理、路由器密码恢复,以及广大网友在实践使用中的心得经验和技巧文章,内容注重实用性,适用于初学者入门,也适合多年从业者提高,是一本实践和理论完
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SQL Server入门到精通
SQL Server入门到精通
网络工程师职业规划与现状
网络工程师职业规划与现状
· 网络工程师职业规划与..
· 浏览器的战国时代
· 运营商封堵ADSL共享 中..
· 微软出价446亿美元收购..
· 技术人求职简历完备手册
· 开源虚拟化技术Xen
· Windows Server 2008专..
· 隐私保护技术探讨
· 绿色IT人健康生存手册
· 杀毒软件评测专题
· 访问控制列表(ACL)介绍
· Vista SP1对决XP SP3
· 华为员工自杀频频拷问..
· 2008中国互联网大会
· 勇闯IT培训黑色围城
· CISSP认证成长之路
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SOA 面向服务架构
SOA 面向服务架构
· SOA 面向服务架构
· SQL Server 2008/2005..
· Apache技术专题
· 三层交换技术专题
· SQL Server入门到精通
· Apache技术专题
· 国际文档格式标准开战
· 路由器设置与口令恢复
· PHP开发应用手册
· SOA 面向服务架构
· 企业数据恢复指南
· 了解统一威胁管理(UTM)..
· 专题:AIX操作系统管理..
· 访问控制列表(ACL)介绍
· 反垃圾邮件技术应用
· ASP.NET 2.0基础开发指..
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SQL Server入门到精通
SQL Server入门到精通
· SQL Server入门到精通
· SQL Server 2008/2005..
· SOA 面向服务架构
· Apache技术专题
· 三层交换技术专题
· Apache技术专题
· 企业数据恢复指南
· 路由器设置与口令恢复
· SOA 面向服务架构
· 了解统一威胁管理(UTM)..
· 反垃圾邮件技术应用
· 访问控制列表(ACL)介绍
· PHP开发应用手册
· 专题:AIX操作系统管理..
· 交换机故障解决指南
· 三层交换技术专题