把断点 断在 SetUnhandledExceptionFilter 的异常处理函数上 !!!

一、问题来源

在做题的时候发现了一个奇怪的异常处理函数:
SetUnhandledExceptionFilter
微软给出的定义是这样的:
使应用程序能够取代进程的每个线程的顶级异常处理程序。
调用此函数后,如果在未调试的进程中发生异常,并且异常会将其设置为未处理的异常筛选器,
该筛选器将调用 lpTopLevelExceptionFilter 参数指定的异常筛选器函数
也就是说,当我们使用OD或者IDA甚至VS进行调试的时候,我们是断不到异常处理函数中的,就算指定让程序接管异常也并不可以,程序会直接奔溃,停止运行(在没有其他异常接管处理程序的情况下)
0
 
那我们该如何处理呢
这里,我找到了两个方法,让我一一细细道来

二、利用Frida Hook NtQueryInformationProcess 函数

Hook NtQueryInformationProcess 函数的原因,下面会慢慢揭晓
经过查阅资料发现 UnhandledExceptionFilter 函数是检测调试器从而决定要不要走异常处理的关键函数,微软呀对他的定义如下:
如果正在调试进程,则应用程序定义的函数将未经处理的异常传递给调试器。 否则,它可以选择显示 应用程序错误消息 框,并导致执行异常处理程序。 只能从异常处理程序的筛选器表达式中调用此函数。
而该函数内部又调用了BasepIsDebugPortPresent 函数进行调试器附加的检查
0
BasepIsDebugPortPresent函数内部实际上是调用了 NtQueryInformationProcess 函数
0
 
那么,我们只需要Hook 掉NtQueryInformationProcess 函数,就可以欺骗 UnhandledExceptionFilter 函数的调试器检查了
实现脚本如下:
# 针对 SetUnhandledExceptionFilter 函数实现的反调试进行处理
import frida
import sys
# 0x41193B
JsScrip_Hook_and_xch_arg = '''      
var pUnhandledExceptionFilter = Module.findExportByName("ntdll.dll", 'NtQueryInformationProcess');  // KernelBase
console.log(pUnhandledExceptionFilter);
var flag = 0;
Interceptor.attach(pUnhandledExceptionFilter, {
    
    onEnter: function (args) {
        if (args[1].toInt32() == 7){
            flag = 1;
        }
    },
    onLeave: function(retval){
        if (flag == 1){
            retval.replace(0xffffffff); 
            console.log("Hook_Success_!!!");  
        }
        
    }

});
''' 

local = frida.get_local_device()
session = local.attach("matrix.exe")
script = session.create_script(JsScrip_Hook_and_xch_arg)
script.load()
sys.stdin.read()

或者

# 针对 SetUnhandledExceptionFilter 函数实现的反调试进行处理
# 这里采用条件断点的方式,多线程执行frida Hook函数(不然IDA会卡死)
import frida
import sys
import threading
# 0x41193B
def Frida_Hook():
    JsScrip_Hook_and_xch_arg = '''      
    var pUnhandledExceptionFilter = Module.findExportByName("ntdll.dll", 'NtQueryInformationProcess');  // KernelBase
    //console.log(pUnhandledExceptionFilter);
    var flag = 0;
    Interceptor.attach(pUnhandledExceptionFilter, {
        
        onEnter: function (args) {
            if (args[1].toInt32() == 7){
                flag = 1;
            }
        },
        onLeave: function(retval){
            if (flag == 1){
                retval.replace(0xffffffff);
                console.log("Hook_Success_!!!");
            }
            
        }

    });
    ''' 

    local = frida.get_local_device()
    session = local.attach("matrix.exe")
    script = session.create_script(JsScrip_Hook_and_xch_arg)
    script.load()
    print("Hook_Success_!!!")
    sys.stdin.read()


if __name__ == "__main__":
    thread = threading.Thread(target = Frida_Hook)
    thread.start()
    print("111")

 

经过测试,成功断到了异常处理函数上!!!

0

三、在异常处理函数上Patch 出循环后,用调试器attach

实现流程如下:
0
我们直接把这里的memset函数patch成循环:
 
0
在循环上下好断点
点击运行
 
0
 
程序就会被断到这里,之后使用IDA 进行Attach就可以了
 
0
程序成功断到了断点上!!
 

四、总结

对于这两种方法,我个人更倾向于用frida 处理,比较简单、方便,一个脚本可以应对无数次这种情况,而Patch循环这种方法则显的比较麻烦,我们需要在断到循环的位置后恢复被Patch的代码块,每次运行都需要恢复,比较麻烦。但都是可行的方法
posted @ 2022-11-12 23:35  TLSN  阅读(406)  评论(0)    收藏  举报