• 我们在哪一颗星上见过 ,以至如此相互思念 ;我们在哪一颗星上相互思念过,以至如此相互深爱
  • 我们在哪一颗星上分别 ,以至如此相互辉映 ;我们在哪一颗星上入睡 ,以至如此唤醒黎明
  • 认识世界 克服困难 洞悉所有 贴近生活 寻找珍爱 感受彼此

恶意代码分析实战:第十五章 对抗反汇编

恶意代码分析实战 云涯 4年前 (2021-02-22) 1889次浏览

4.4 滥用结构化异常处理

结构化异常处理(SEH)提供提供一种控制流的方法,该方法不能被反汇编器采用,但可以用来欺骗反汇编器。

SEH是一个函数列表,目的是处理线程中的异常。列表中的每个函数,要么处理异常,要么将异常传递给下一个函数。如果一个异常总是被传递到最后一个异常处理函数处,就会被认为是一个不能处理的异常。这种情况下,最后一个异常处理函数会负责弹出一个消息提示框“an unhadled execption has occurred”。在大多数进程中,异常是有规律产生的,但在到达最后状态之前,异常都会被静悄悄处理掉 。

为了查询SEH链,操作系统会检查FS段寄存器。这个寄存器会包含一个段选择子,使用段选择子可以得到线程环境块(TEB),TEB中的第一个数据结构是线程信息块(TIB),TIB中的第一个元素即第一个字节就是SEH链指针。

当线程运行在 用户空间 下时, FS 指向的段是 GDT 中的 0x3B 段。该段的长度为 4K ,基地址为当前线程的线程环境块( TEB ),所以该段也被称为“ TEB 段”。
因为 Windows 中线程是不停切换的,所以该段的基地址值将随线程切换而改变的。

寄存器FS和GS是段寄存器。 它们没有处理器定义的目的,而是由操作系统运行它们来实现目的。 
FS用于指向Windows进程上的线程信息块(TIB)。一个典型示例是(SEH),它在FS:[0x00]中存储指向回调函数的指针。GS通常用作指向线程本地存储(TLS)的指针。

FS寄存器指向当前活动线程的TEB结构(线程结构)
偏移  说明
000  指向SEH链指针
004  线程堆栈顶部
008  线程堆栈底部
00C  SubSystemTib
010  FiberData
014  ArbitraryUserPointer
018  FS段寄存器在内存中的镜像地址
020  进程PID
024  线程ID
02C  指向线程局部存储指针
030  PEB结构地址(进程结构)
034  上个错误号

得到KERNEL32.DLL基址的方法
assume fs:nothing          ;打开FS寄存器
mov eax,fs:[30h]           ;得到PEB结构地址
mov eax,[eax + 0ch]        ;得到PEB_LDR_DATA结构地址
mov esi,[eax + 1ch]        ;InInitializationOrderModuleList
lodsd                      ;得到KERNEL32.DLL所在LDR_MODULE结构的InInitializationOrderModuleList地址
mov edx,[eax + 8h]         ;得到BaseAddress,既Kernel32.dll基址 

SEH链是一个简单的8字节数据结构链表,叫做EXCEPTION_TEGISTRATION记录。


struct _EXCETION_REGISTRATION{

DWORD prev;      指向前一个记录的指针

DWORD hadler;  指向异常处理函数的指针

}


这个链表以栈的方式进行操作,第一个调用的是最后一个加入链表的记录。由于子进程的调用与嵌套的异常处理块的原因,SEH链的增长和缩小都等同于程序中异常处理层的改变,所以SEH记录总是建在栈上。

实现变相控制程序流,需要了解怎么将自己的异常处理添加到链表头部。

为了将一条记录添加到这个链中,我们需要在栈上构造一条新的记录。因为记录结构有两个DWORD变量组成,所有需要使用两个PUSH来完成。第一个push进栈的是异常处理函数指针,第二个push进栈的是下一条记录的指针。当添加一条记录到链表头部是,下一条记录需要完成的异常处理是当前栈顶,由fs[0]指针给出。


push ExceptionHandler

push fs:[0]

mov fs:[0],esp


异常发生时,首先调用函数ExceptionHandler。这个动作受到微软的软件数据执行保护机制(软件DEP, 也叫SafeSEH)的限制。

软件DEP是一项安全功能,目的是阻止程序运行过程中添加第三方异常处理。对于硬编码的代码,有几种方法能够绕过这种技术。

例如使用支持SafeSEH指令的汇编器。另外使用微软的C编译器也能达到此目的,添加/SAFESEH:NO到链接器命令行,就可使这种限制无效。

当调用ExceptionHandler函数时,栈将被大幅改变。

要达到目的,不必检查添加到栈中的所有数据。

我们必须知道怎么返回异常发生前的栈位置。当异常被调用时,操作系统添加了其他的SEH处理

为了让程序恢复正常操作,不仅要将我们的异常处理从异常处理链中断开,还要将系统添加的异常处理冲异常处理链中断开。因此需要从esp+8处而不是esp处取出原始指针。


这是为了让程序恢复正常。

mov esp, [esp+8]

mov eax, fs:[0]       fs:[0]代表上一条记录指针prev,存储prev的地址

mov eax, [eax]       赋值后eax为prev的值,值是上一条记录prev的地址

mov eax, [eax]       赋值后eax为上一条记录prev的值

mov fs:[0], eax

add esp, 8


②处eax被设置为40106C,通过这次操作秘密地安装一个异常处理,然后将EAX加14h,构造404080函数指针。通过xor ecx,ecx将ECX寄存器设置为0,随后div ecx③,触发除0异常。

在401080处按C键

 

 

 

 

 

 

 


云涯历险记 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:恶意代码分析实战:第十五章 对抗反汇编
喜欢 (0)