Windows 32位-调试与反调试
反调试技术,恶意代码会用它识别自身是否被调试,或者让调试器失效,给反病毒工程师们制造麻烦,拉长提取特征码的时间线,本章将具体总结常见的反调试基础的实现原理以及如何过掉这些反调试手段,从而让我们能够继续分析恶意代码。
反调试技术的实现方式有很多,最简单的一种实现方式莫过于直接调用Windows系统提供给我们的API函数,这些API函数中有些专门用来检测调试器的,有些则是可被改造为用于探测调试器是否存在的工具,多数情况下,调用系统API函数实现反调试是不明智的,原因很简单,目标主机通常会安装主动防御系统,而作为主动防御产品默认会加载RootKit驱动挂钩这些敏感函数的使用,如果被非法调用则会提示错误信息,病毒作者通常会使用汇编自行实现这些类似于系统提供给我们的反调试函数,并不会使用系统的API,这样依附于API的主动防御的系统将会失效。
1.加载调试符号链接文件并放入d:/symbols目录下.
0:000> .sympath srv*d:\symbols*http://msdl.microsoft.com/download/symbols
0:000> .reload
Reloading current modules
2.位于fs:[0x30]的位置就是PEB结构的指针,接着我们分析如何得到的该指针,并通过通配符找到TEB结构的名称.
0:000> dt ntdll!*teb*
ntdll!_TEB
ntdll!_GDI_TEB_BATCH
ntdll!_TEB_ACTIVE_FRAME
ntdll!_TEB_ACTIVE_FRAME_CONTEXT
ntdll!_TEB_ACTIVE_FRAME_CONTEXT
3.接着可通过dt命令,查询下ntdll!_TEB结构,如下可看到0x30处ProcessEnvironmentBlock存放的正是PEB结构.
0:000> dt -rv ntdll!_TEB
struct _TEB, 66 elements, 0xfb8 bytes
+0x000 NtTib : struct _NT_TIB, 8 elements, 0x1c bytes # NT_TIB结构
+0x018 Self : Ptr32 to struct _NT_TIB, 8 elements, 0x1c bytes # NT_TIB结构
+0x020 ClientId : struct _CLIENT_ID, 2 elements, 0x8 bytes # 保存进程与线程ID
+0x02c ThreadLocalStoragePointer : Ptr32 to Void
+0x030 ProcessEnvironmentBlock : Ptr32 to struct _PEB, 65 elements, 0x210 bytes # PEB结构
偏移地址0x18是_NT_TIB结构,也就是指向自身偏移0x0的位置.
0:000> r $teb
$teb=7ffdf000
0:000> dd $teb+0x18
7ffdf018 7ffdf000 00000000 00001320 00000c10
7ffdf028 00000000 00000000 7ffd9000 00000000
而!teb地址加0x30正是PEB的位置,可以使用如下命令验证.
0:000> dd $teb+0x30
7ffdf030 7ffd9000 00000000 00000000 00000000
7ffdf040 00000000 00000000 00000000 00000000
0:000> !teb
TEB at 7ffdf000
ExceptionList: 0012fd0c
StackBase: 00130000
StackLimit: 0012e000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffdf000
EnvironmentPointer: 00000000
ClientId: 00001320 . 00000c10
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffd9000 # 此处teb地址
上方的查询结果可得知偏移位置fs:[0x18]正是TEB的基址TEB:7ffdf000
0:000> dd fs:[0x18]
003b:00000018 7ffdf000 00000000 000010f4 00000f6c
003b:00000028 00000000 00000000 7ffda000 00000000
0:000> dt _teb 0x7ffdf000
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID # 这里保存进程与线程信息
0:000> dt _CLIENT_ID 0x7ffdf000 # 查看进程详细结构
ntdll!_CLIENT_ID
+0x000 UniqueProcess : 0x0012fd0c Void # 获取进程PID
+0x004 UniqueThread : 0x00130000 Void # 获取线程PID
上方TEB首地址我们知道是fs:[0x18],接着我们通过以下公式计算得出本进程的进程ID.
在Windows系统中如果想要获取到PID进程号,可以使用NtCurrentTeb()这个系统API来实现,但这里我们手动实现该API的获取过程.
获取进程PID:
#include "stdafx.h"
#include <Windows.h>
DWORD GetPid(){
DWORD dwPid=0;
__asm
{
mov eax,fs:[0x18] // 获取PEB地址
add eax,0x20 // 加0x20得到进程PID
mov eax,[eax]
mov dwPid,eax
}
return dwPid;
}
int main()
{
printf("%d\n",GetPid());
return 0;
}
获取线程PID:
#include "stdafx.h"
#include <Windows.h>
DWORD GetPid(){
DWORD dwPid=0;
__asm
{
mov eax,fs:[0x18] // 获取PEB地址
add eax,0x20 // 加0x20得到进程PID
add eax,0x04 // 加0x04得到线程PID
mov eax,[eax]
mov dwPid,eax
}
return dwPid;
}
int main()
{
printf("%d\n",GetPid());
return 0;
}
通过标志反调试: 下方的调试标志BeingDebugged是Char类型,为1表示调试状态.为0表示没有调试.可以用于反调试.
0:000> dt _peb
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
#include "stdafx.h"
#include <Windows.h>
int main()
{
DWORD dwIsDebug = 0;
__asm
{
mov eax, fs:[0x18]; // 获取TEB
mov eax, [eax + 0x30]; // 获取PEB
movzx eax, [eax + 2]; // 获取调试标志
mov dwIsDebug,eax
}
if (1 == dwIsDebug)
{
printf("正在被调试");
}
else
{
printf("没有被调试");
}
return 0;
}
通过API反调试:
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int main()
{
STARTUPINFO temp;
temp.cb = sizeof(temp);
GetStartupInfo(&temp);
if (temp.dwFlags != 1)
{
ExitProcess(0);
}
printf("程序没有被反调试");
return 0;
}
反调试与绕过思路
BeingDebugged 属性反调试: 进程运行时,位置FS:[30h]指向PEB的基地址,为了实现反调试技术,恶意代码通过这个位置来检查BeingDebugged标志位是否为1,如果为1则说明进程被调试。
1.首先我们可以使用 dt _teb 命令解析一下TEB的结构,如下TEB结构的起始偏移为0x0,而0x30的位置指向的是 ProcessEnvironmentBlock 也就是指向了进程环境块。
0:000> dt _teb
ntdll!_TEB
+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
只需要在进程环境块的基础上 +0x2 就能定位到线程环境块TEB中 BeingDebugged 的标志,此处的标志位如果为1则说明程序正在被调试,为0则说明没有被调试。
0:000> dt _peb
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 BitField : UChar
+0x003 ImageUsesLargePages : Pos 0, 1 Bit
+0x003 IsProtectedProcess : Pos 1, 1 Bit
我们手动来验证一下,首先线程环境块地址是007f1000在此基础上加0x30即可得到进程环境快的基地址007ee000继续加0x2即可得到BeingDebugged的状态 ffff0401 需要 byte=1
0:000> r $teb
$teb=007f1000
0:000> dd 007f1000 + 0x30
007f1030 007ee000 00000000 00000000 00000000
007f1040 00000000 00000000 00000000 00000000
0:000> r $peb
$peb=007ee000
0:000> dd 007ee000 + 0x2
007ee002 ffff0401 0000ffff 0c400112 19f0775f
007ee012 0000001b 00000000 09e0001b 0000775f
梳理一下知识点我们可以写出一下反调试代码,本代码单独运行程序不会出问题,一旦被调试器附加则会提示正在被调试。
#include <stdio.h>
#include <windows.h>
int main()
{
BYTE IsDebug = 0;
__asm{
mov eax, dword ptr fs:[0x30]
mov bl, byte ptr [eax+ 0x2]
mov IsDebug, bl
}
/* 另一种反调试实现方式
__asm{
push dword ptr fs:[0x30]
pop edx
mov al, [edx + 2]
mov IsDebug,al
}
*/
if (IsDebug != 0)
printf("本程序正在被调试. %d", IsDebug);
else
printf("程序没有被调试.");
getchar();
return 0;
}
如果恶意代码中使用该种技术阻碍我们正常调试,该如何绕过呢?如下我们只需要在命令行中执行dump fs:[30]+2来定位到BeingDebugged的位置,并将其数值改为0然后运行程序,会发现反调试已经被绕过了。

ProcessHeap 属性反调试: 该属性是一个未公开的属性,它被设置为加载器为进程分配的第一个堆的位置,ProcessHeap位于PEB结构的0x18处,第一个堆头部有一个属性字段,这个属性叫做ForceFlags和Flags属性偏移为10,该属性为0说明程序没有被调试,非0则说明被调试。
0:000> dt !_peb
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
0:000> r $peb
$peb=006e1000
0:000> dd 006e1000+18
006e1018 00ca0000 775f09e0 00000000 00000000
0:000> dd 00ca0000 + 10
00ca0010 00ca00a4 00ca00a4 00ca0000 00ca0000
要实现反反调试,只需要将 00ca0000 + 10 位置的值修改为0即可,执行dump ds:[fs:[30] + 0x18] + 0x10 定位到修改即可。















Windows 32位-调试与反调试的更多相关文章
- Visual Studio远程调试监视器(MSVSMON.EXE)的32位版本不能用于调试64位进程或64位转储
在VS2013中调试Silverlight项目时,提示:无法附加.Visual Studio远程调试监视器(MSVSMON.EXE)的32位版本不能用于调试64位进程或64位转储.请改用64位版本. ...
- windows(32位 64位)下python安装mysqldb模块
windows(32位 64位)下python安装mysqldb模块 www.111cn.net 编辑:mengchu9 来源:转载 本文章来给各位使用在此windows系统中的python来安装一个 ...
- Redis 3.2.100 Windows 32位下载
因为公司的老服务器用的是Windows 2008 32位,不得不安装Redis32位.可在微软的Github上有64位的MSI安装包,前天开始在不同的群里寻找32位的安装包,一直没找到,索性自己下载源 ...
- [笔记]--Oracle 10g在Windows 32位系统使用2G以上内存
1.修改c:\boot.ini文件 打开boot.ini文件,我的电脑->属性->高级->启动和恢复->编辑,设置在最后一行末尾添加/PAE选项后如下: [boot loade ...
- windows 32位系统中进程最大可用内存空间为3GB (转)
http://msdn.microsoft.com/zh-cn/library/ms189334.aspx 进程地址空间 所有 32 位应用程序都有 4 GB 的进程地址空间(32 位地址最多可以映射 ...
- windows 32位以及64位的inline hook
Tips : 这篇文章的主题是x86及x64 windows系统下的inline hook实现部分. 32位inline hook 对于系统API的hook,windows 系统为了达成hotpatc ...
- Windows:32位程序运行在64位系统上注册表会重定向
参考资料 微软注册表英文文档 StackOverflow社区回答 1.注册表位置 64bit系统(Windows Server 2008 R2只有64bit系统)的注册表分32 位注册表项和64位注册 ...
- VS本地调试 Visual Studio远程调试监视器(MSVSMON.EXE)的32位版本不能用于调试64位进程或64位转储
vs2017 调试一致都没啥问题,今天莫名报这个错误,感觉好奇怪,网上搜索了半天也没解决,最后看着错误信息感觉很诡异,我本地调试你给我启动远程调试监测器干嘛,localhost也访问不了,ping了一 ...
- Delphi_OD_代码_调试_Delphi反调试技术(以OD为例附核心原代码) (转)
1.程序窗口[chuang kou]句柄[ju bing]检测原理:用FindWindow函数[han shu]查找[cha zhao]具有相同窗口[chuang kou]类名和标题的窗口[chuan ...
随机推荐
- macbook配置flutter环境变量
打开命令窗口,如果没有文件的,可以手动创建文件 code ~/.bash_profile 打开的文件内容如下,如果新增的空文件,肯定是空白的 如果将flutter存放到了应用中,可以如下操作,如果不是 ...
- C# ASP.NET 控制windows服务的 开启和关闭 以及重启
用ASP.NET控制Windows服务的开启与关闭效果如图 代码 首页页面需要添加引用 页面的pageload中 实例化windows服务 protected void Page_Load(objec ...
- laravel不同用户对应的同名的session是独立的
laravel不同用户对应的同名的session是独立的 一.总结 一句话总结: laravel中 不同用户会根据不同的laravel_session从而将session存在不同的session文件里 ...
- 【Python】把文件名命名成canlendar.py竟然导致无法使用canlendar模块 附赠2020年月历
这个bug困扰了我一阵,直到在 http://www.codingke.com/question/15489 找到了解决问题的钥匙,真是没想到居然是这个原因导致的. 下面是出错信息,可以看到只要目录下 ...
- Linux笔记整理
[随时更新] ps aux | grep mysql 检测MySQL服务是否在运行 Linux查看某个进程的线程:ps -T -p <pid> 列出了由进程号为<pid>的进程 ...
- MySQL数据库之sql_mode解释
在MySQL5.6中,默认的SQL模式为:NO_ENGINE_SUBSTITUTION, 而在MySQL5.7中默认的SQL模式为:ONLY_FULL_GROUP_BY, STRICT_TRANS_T ...
- Linux -- 在多线程程序中避免False Sharing
1.什么是false sharing 在对称多处理器(SMP)系统中,每个处理器均有属于自己的本地高速缓存区. 如图,CPU0和CPU1有各自的本地高速缓存区(cache).线程0和线程1会用到不同的 ...
- Django之form表单验证顺序
概述 django框架提供了一个forms类,来处理web开发中的表单相关事项.众所周知,form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证支持. 验证过程 ...
- LNMP一键环境安装多PHP版本共存的方法
多PHP版本只支持LNMP模式,LNMPA.LAMP模式下不支持!要使用多PHP先安装多PHP版本,在lnmp1.4源码(lnmp1.3的不行哦)目录下运行:./install.sh mphp 按提示 ...
- netstat -anp/ss -t里的Send-Q和Recv-Q含义
Send-Q 对方没有收到的数据或者说没有Ack的,还在本地缓冲区 Recv-Q 数据已经在本地接收缓冲区,但是还没有recv() The count of bytes not copied by t ...