看雪上看到的 ==》》
我最近调试程序才发觉,CloseHandle这个函数的一个怪异的行为。我突然想到可以利用这个行为来欺骗或者对抗调试器。
这个怪异的行为是这样的,调用CloseHandle释放一个无效句柄,如果进程在调试器之外,那么函数返回FALSE,而GetLastError得到ERROR_INVALID_HANDLE;但是如果进程在调试器内,那么系统将抛出异常C0000008H。
一、首先说如何得到一个无效句柄呢?
方法一:一个句柄释放多次次
这个方法看似没问题,但实际有很大的隐患。因为新创建的内核对象是总是寻找句柄表的空白记录,因此有可能存在已经释放过的句柄又被利用,而这时再次释放会很不安全。
方法二:硬编码一个足够大的句柄值
用procexp.exe看一下就会发现进程里的句柄值很少大于1000h,因此赌一下也是可以的
方法三:释放一个不可关闭的句柄
这个我认为是最安全的一种方法,首先自己随便调用一个CreateXXX得到一个句柄h1,再调用SetHandleInformation将句柄h1设为不可关闭的,最后调用CloseHandle释放它就行了
二、再说如何利用这个行为
方法一:利用SEH欺骗
这样利用CloseHandle根据调试器抛出异常,就可以通过SEH使得程序内外有不同的行为
方法二:隐式调用CloseHandle引发调试异常
关于方法范围比较广泛,大家八仙过海各显神通吧
偶然发现而已,文章发的比较匆忙,现在只是发现FindClose有相同的现象,我估计很多带Close字段都有这种现象,也不知道其它API是否有相似的现象,希望大家不吝指教。
这个怪异的行为是这样的,调用CloseHandle释放一个无效句柄,如果进程在调试器之外,那么函数返回FALSE,而GetLastError得到ERROR_INVALID_HANDLE;但是如果进程在调试器内,那么系统将抛出异常C0000008H。
一、首先说如何得到一个无效句柄呢?
方法一:一个句柄释放多次次
这个方法看似没问题,但实际有很大的隐患。因为新创建的内核对象是总是寻找句柄表的空白记录,因此有可能存在已经释放过的句柄又被利用,而这时再次释放会很不安全。
方法二:硬编码一个足够大的句柄值
用procexp.exe看一下就会发现进程里的句柄值很少大于1000h,因此赌一下也是可以的
方法三:释放一个不可关闭的句柄
这个我认为是最安全的一种方法,首先自己随便调用一个CreateXXX得到一个句柄h1,再调用SetHandleInformation将句柄h1设为不可关闭的,最后调用CloseHandle释放它就行了
二、再说如何利用这个行为
方法一:利用SEH欺骗
这样利用CloseHandle根据调试器抛出异常,就可以通过SEH使得程序内外有不同的行为
方法二:隐式调用CloseHandle引发调试异常
关于方法范围比较广泛,大家八仙过海各显神通吧
偶然发现而已,文章发的比较匆忙,现在只是发现FindClose有相同的现象,我估计很多带Close字段都有这种现象,也不知道其它API是否有相似的现象,希望大家不吝指教。