探索黑客技术攻防,实战研究与安全创新

导航菜单

借尸还魂——谈主线程劫持术与伪句柄

在编程的时候我们经常会使用GetCurreutTluead()或者GetCurrentProcess()来得到当前进程或者主线程的句柄,这很方便,但如果我们想通过子线程来挂起主线程,通过对主线程的实现一种Anti-debug的效果,那就需要理解伪句柄了,本文就通过一些实例来加深大家对于伪句柄的理解,同时也学会如何正确的使用主线程术!

一、伪句柄的基本操作

1、伪句柄使用一例

这段程序执行后就会自动挂起主线程,所以我们无法看到“hello world”字符的输出,伪句柄在当前的主线程中是有效的,暂时可以这么理解,下面我们会分析这种所谓的“有效”指的是什么。

2、使用伪句柄通过子线程对主线程进行“劫持”

上面的程序试图通过子线程来挂起主线程,但是程序的执行结果并没有挂起主线程,因为我们看到了“hello world”字符串,伪句柄在于线程中无效,暂时我们就理解为无效!

3、通过DuplicateHandle()对主线程进程“劫持”

程序的执行效果是非常不错的,主线程已经被“劫持”成功了!

二、主线程“术”

通过上面这个例子,我们知道是完全可以把主线程挂起,然后把所有的程序功能加到子线程中的,而这个过程需要的条件又是多种多样的,比如发现文件大小、日期、CRC检验出现异常等,就可以让子线程执行相应的操作,有一种Anti-Deubg就是通过子线程来主线程实现的,而且这里只能使用挂起的方法,如果结束掉主线程,那么子进程也就没有了。

QQ截图20170302140224.png

三、用户态分析DuplicateHandle函数及伪句柄的实现机制

因为本人一直处于“饥饿”(学习)状态,在用户态下暂时刚刚解决“温饱”(可以实现),而内核态的操作估计以后“发肓”(深入掌握)一下才能搞,因此我就通过对比的方法在用户态中对伪句柄进行分析以加深对这种现象的理解!

1、DuplicateHandle作了什么

测试程序是在上文二中所写的利用程序,进入主函数后执行到如下位置“00401168 FF1578514100 cal dword ptr ds:[<&KERNEL32.Dplic>; keme132.uplicaeHandle”,在当前栈中显示了该函数所调用的参数:

“//”后面的阿拉伯数字表示该函数的参数在Duplicate中使用的次序,按F7跟进DuplicateHandle函数。

 Duplicate函数先对“hSource=GeturrentThread()”进行了一个类似switch......case结构的分析,得出了一个值,这个值作为ZwDulicateObject的第一个参数,然后依次从右到左的将Duplicate函数的参数入栈(_stdcall调用),在7C80DED上按F7跟过ZwDupiicatObject函数。

在OD的命令行窗口中执行“dd 7FFE0300”,就会发现“7FFE0300 7C92E4F0 ntil.KiastSystmCall”,也就是说这个地址其实就是KiFastSysemCall函数,在7C92D28A上按F7跟进KiFastSystemCall函数,来到如下代码:      

Syscntcr指令代替Windows中的“int 2e”指令,到这里我们调用的DuplicateHandle函数进入内核态进行操作,在OD中虽然无法知道是怎样的操作,但是通过MSDN的说明和一般的逻辑也能猜想出来一定是打开主线程的内核对象获得其句柄,在MSDN中会发现一个有意思的现象——没有类似OenPrcess之类的OpenThread函数!

2、分析伪句柄在当前中的使用机制

程序在执行后直接挂起主线程,该程序的主要反汇编代码如下:

将这部分代码帖出来只是便于下面的描述,相信很多朋友看到源程序后,汇编代码也都想象出来了!程序在执行到0040103A后,按F7跟进GetCurrentThread函数,发现如下代码:

程序没有进入内核态,这就是为什么叫伪句柄的原因。在00401050中按F7跟进SuspednThread(),我们来看看它都做了些什么?

也就是说SuspendThread()的本质是调用了ZwSuspendThread(),这个函数的两个参数一个是伪句柄的地址,一个是伪句柄的值,也就是FFFFFFFE。

 对于上面的代码是不是很熟悉呢,这和上面的DuplicateHandle函数最后的调用是一样的。

3、结论

(1)伪句柄之所以称为“伪”,是因为用户在编程的时候只是感觉它是可用的,其实在每一个针对句柄操作时都需要得到其真正的句柄。

(2)伪句柄之所以在当前进程或者线程中可用,是因为针对该伪句柄操作会使用相关的函数得到真正的句柄并进行相应的操作。

(3)伪句柄在不同的进程或者线程中不可用,这是因为子线程无法得到主线程的伪句柄地址,真正入栈的只是当前子进程的伪句柄地址,所以在上文一中的2中实现不了对主线程的劫持。

好了,文章就写到这里,当大家碰到类似劫持主线程的Anti-debug时,就可以结合本文所介绍的内容,下—个比较合适的断点就可以了!

本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法破解行为。