反调试技术,恶意代码会用它识别自身是否被调试,或者让调试器失效,给反病毒工程师们制造麻烦,拉长提取特征码的时间线,本章将具体总结常见的反调试基础的实现原理以及如何过掉这些反调试手段,从而让我们能够继续分析恶意代码。

反调试技术的实现方式有很多,最简单的一种实现方式莫过于直接调用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结构,如下可看到0x30ProcessEnvironmentBlock存放的正是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位-调试与反调试的更多相关文章

  1. Visual Studio远程调试监视器(MSVSMON.EXE)的32位版本不能用于调试64位进程或64位转储

    在VS2013中调试Silverlight项目时,提示:无法附加.Visual Studio远程调试监视器(MSVSMON.EXE)的32位版本不能用于调试64位进程或64位转储.请改用64位版本. ...

  2. windows(32位 64位)下python安装mysqldb模块

    windows(32位 64位)下python安装mysqldb模块 www.111cn.net 编辑:mengchu9 来源:转载 本文章来给各位使用在此windows系统中的python来安装一个 ...

  3. Redis 3.2.100 Windows 32位下载

    因为公司的老服务器用的是Windows 2008 32位,不得不安装Redis32位.可在微软的Github上有64位的MSI安装包,前天开始在不同的群里寻找32位的安装包,一直没找到,索性自己下载源 ...

  4. [笔记]--Oracle 10g在Windows 32位系统使用2G以上内存

    1.修改c:\boot.ini文件 打开boot.ini文件,我的电脑->属性->高级->启动和恢复->编辑,设置在最后一行末尾添加/PAE选项后如下: [boot loade ...

  5. windows 32位系统中进程最大可用内存空间为3GB (转)

    http://msdn.microsoft.com/zh-cn/library/ms189334.aspx 进程地址空间 所有 32 位应用程序都有 4 GB 的进程地址空间(32 位地址最多可以映射 ...

  6. windows 32位以及64位的inline hook

    Tips : 这篇文章的主题是x86及x64 windows系统下的inline hook实现部分. 32位inline hook 对于系统API的hook,windows 系统为了达成hotpatc ...

  7. Windows:32位程序运行在64位系统上注册表会重定向

    参考资料 微软注册表英文文档 StackOverflow社区回答 1.注册表位置 64bit系统(Windows Server 2008 R2只有64bit系统)的注册表分32 位注册表项和64位注册 ...

  8. VS本地调试 Visual Studio远程调试监视器(MSVSMON.EXE)的32位版本不能用于调试64位进程或64位转储

    vs2017 调试一致都没啥问题,今天莫名报这个错误,感觉好奇怪,网上搜索了半天也没解决,最后看着错误信息感觉很诡异,我本地调试你给我启动远程调试监测器干嘛,localhost也访问不了,ping了一 ...

  9. Delphi_OD_代码_调试_Delphi反调试技术(以OD为例附核心原代码) (转)

    1.程序窗口[chuang kou]句柄[ju bing]检测原理:用FindWindow函数[han shu]查找[cha zhao]具有相同窗口[chuang kou]类名和标题的窗口[chuan ...

随机推荐

  1. linux 安装nginx -查看 linux的环境变量

    我发现在linux上面安装linux很简单 在CentOS release 6.5 上面先看一下操作系统的版本: lsb_release -a 直接执行 yum install nginx 系统自动的 ...

  2. legend3---19、要更多的从服务器端控制元素的显示和隐藏,而不要是页面端

    legend3---19.要更多的从服务器端控制元素的显示和隐藏,而不要是页面端 一.总结 一句话总结: 这样可以控制很多页面端的非法操作 1.html标签中data方式的数据,修改之后在标签上只显示 ...

  3. 连接池设置导致的“血案” 原创: 一页破书 一页破书 5月6日 这个问题被投诉的几个月了,一直没重视——内部客户嘛😿 问题现象: 隔几周就会出现 A服务调用B服务超时 脚趾头想就是防火墙的问题,A、B两服务之间有防火墙 找运维查看防火墙日志确实断掉了tcp连接,但是是因为B服务5分钟没有回包,下面这个表情就是我当时的心情——其实我们在防火墙、A服务、B服务都抓包了,几十个G的t

    连接池设置导致的“血案” 原创: 一页破书 一页破书 5月6日 这个问题被投诉的几个月了,一直没重视——内部客户嘛

  4. Matrix: android 中的Matrix (android.graphics.Matrix) (转)

    本篇博客主要讲解一下如何处理对一个Bitmap对象进行处理,包括:缩放.旋转.位移.倾斜等.在最后将以一个简单的Demo来演示图片特效的变换. 1. Matrix概述 对于一个图片变换的处理,需要Ma ...

  5. SQL-W3School-函数:SQL FORMAT() 函数

    ylbtech-SQL-W3School-函数:SQL FORMAT() 函数 1.返回顶部 1. FORMAT() 函数 FORMAT 函数用于对字段的显示进行格式化. SQL FORMAT() 语 ...

  6. Ubuntu + Apache2 环境下用C编写 一个简单的cgi脚本

    我只学习过c语言,没有学习过prel,网上很多教程都是针对prel的,很少有针对c的.自己在Ubuntu下鼓捣了一下午,也总算是用c成功编写了一个helloworld的cgi,算是cgi入门的第一步. ...

  7. Sass(Syntactically Awesome Stylesheets)——概述(待续)

    官网地址:http://sass.bootcss.com/ Sass(Syntactically Awesome Stylesheets) Sass 是成熟.稳定.强大的 CSS 扩展语言. 特征 兼 ...

  8. 123457123456#0#-----com.yuming.baoBaoAiXueXi01hanzi--前拼后广--幼儿园宝宝学汉字

    123457123457#0#-----com.yuming.baoBaoAiXueXi01hanzi--前拼后广--幼儿园宝宝学汉字

  9. 123457123456#0#---com.threeapp.ErTongShuXueKoSuan01----儿童宝宝数学口算01

    com.threeapp.ErTongShuXueKoSuan01----儿童宝宝数学口算01

  10. 【Leetcode_easy】687. Longest Univalue Path

    problem 687. Longest Univalue Path 参考 1. Leetcode_easy_687. Longest Univalue Path; 2. Grandyang; 完