Python灰帽子学习笔记(二)——Immunity Debugger部署硬钩子

来源:互联网 发布:php 判断是否是移动端 编辑:程序博客网 时间:2024/05/21 17:11

一、引言

下面是学习python灰帽子第六章Immunity Debugger部署硬钩子部分的学习笔记。

目前对Immunity Debugger软件使用不熟悉,为了深入探索软件的功能,所以下面的部分并没有严格按照书本的套路走,而是利用书本上代码的框架,完成对另一软件(realese后的printf_loop.exe)的硬钩子部署工作。

首先有必要说明一下系统环境和工具版本:
- win10教育版
- VS2012
- python v2.7.9
- Immunity Debugger v1.85

二、实现及过程

首先在VS2012上编译如下C++代码,生成printf_loop.exe。

# printf_loop.cpp#include <iostream> using   namespace   std;int main(){    int counter = 0;    while(1)    {        printf("Loop iteration %d:\n", counter);        _sleep(2*1000);//延时2秒         counter += 1;    }    return 0;}

接下来对python灰帽子书上源代码进行修改,获取printf函数的入口和出口钩子。因为Immunity Debugger版本的不同,笔者发现书本上代码的一些方法名和实际Immunity Debugger库并不一致,存在一些方法名大小写不一致的问题。经过繁琐的查找和修改过程,初版的Pycommand代码如下(当然问题比较多,后面一步一步调试修改)。

# hippie_easy.py# -*- coding: utf-8 -*-  import immlibimport immutils# 搜索包含特定ret指令的基本块,# 寻找函数中合适的挂钩点def getRet(imm, printf_addr, max_opcodes = 300):    addr = printf_addr    for a in range(0, max_opcodes):        op = imm.disasmForward(addr)        if op.isRet():            if op.getImmConst() == 0xC:                op = imm.disasmBackward(addr, 3)                return op.getAddress()        addr = op.getAddress()    return 0x0# 打印钩子所记录下来的数据信息,def showresult(imm, a ,printfEndAddr, extra = ""):    if a[0] == printfEndAddr:        imm.log("PrintfStartAddr (0x%08x, 0x%08x) <- 0x%08x %s" %            (a[1][0], a[1][1], a[1][2], extra), address = a[1][2])        return "done"    else:        imm.log("PrintfEndAddr (0x%08x, 0x%08x) %s" %            (a[1][0], a[1][1], extra))def main(args):    imm = immlib.Debugger()    Name = "hippie"    fast = imm.getKnowledge(Name)    if fast:        # 记录我们之前设置下的钩子,打印钩子函数所记录下来的数据信息        hook_list = fast.getAllLog()        printfStartAddr, printfEndAddr = imm.getKnowledge("FuncNames")        for a in hook_list:            ret = showresult(imm, a, printfEndAddr)        return "Logged: %d hook hits. Results output to log window." %len(hook_list)    # 暂停调试器    imm.pause()    printfStartAddr = imm.getAddress("msvcrt.printf")    module = imm.getModule("msvcrt.dll")    if not module.isAnalysed():        imm.analyseCode(module.getCodebase())    # 寻找合适的函数出口点    printfEndAddr = getRet(imm, printfStartAddr, 1000)    imm.log("PrintfEnd hook: 0x%08x" % printfEndAddr)    # 保存挂钩点    imm.addKnowledge("FuncNames", (printfStartAddr, printfEndAddr))    # 构建钩子    fast = immlib.STDCALLFastLogHook(imm)    # 在尾部布置钩子    imm.log("Logging on PrintfEnd 0x%08x" %printfEndAddr)    fast.logFunction(printfEndAddr)    fast.logBaseDisplacement("EBP", 8)    fast.logBaseDisplacement("EBP", 0xc)    fast.logRegister("EAX")    #在头部布置钩子    imm.log("Logging on PrintfStart 0x%08x" % printfStartAddr)    fast.logFunction(printfStartAddr, 2)    # 设立钩子    fast.Hook()    #保存钩子对象,以便取回数据记录结果    imm.addKnowledge(Name, fast, force_add = 1)    return "Hooks set, press F9 to continue the process."

运行,出现了第一个值得研究的问题,如下图。

这里写图片描述

看提示,应该是module = imm.getModule(“msvcrt.dll”)这一句没有成功返回,是否msvcrt.dll不存在呢?我们不妨去Immunity Debbuger(太长了,后面简写为ID)里检查一下printf_loop.exe运行时是否导入 msvcrt.dll。

这里写图片描述

显然,程序运行没有导入 msvcrt.dll,但是我们发现有一个 MSVCR110.dll,和 msvcrt.all 很像。没错,就是它啦,这是VS12版本的运行库。所以,作如下修改:

    .................    # 暂停调试器    imm.pause()    printfStartAddr = imm.getAddress("MSVCR110.printf")    module = imm.getModule("MSVCR110.dll")    if not module.isAnalysed():        imm.analyseCode(module.getCodebase())    ................

再次运行,成功了。ID的log窗口显示如下:

这里写图片描述

显然,printf函数尾部的位置没有正确找出,table size 为1,表明只部署了一个钩子。出现这个问题是因为,初版代码里的寻找函数出口的代码直接copy了书本上的,没有针对printf函数的实际情况进行改写。

利用上面找到的printf函数入口的地址,查找汇编代码,可以找到printf函数出口处的汇编代码如下:

这里写图片描述

上图红框里的就是printf函数的最后五条汇编指令。实际上,IB要做的事情就是在633FEE76(函数出口倒数第五个指令地址)的位置上做点手脚。上面代码里的检索原理就是找到第一个 RETN ,然后判断后面的操作数是否为 OC(适用于 RtlAllocateHeap 函数),而对 printf 函数而言,RETN 的操作数为 0。因此,继续修改:

        ...............        if op.isRet():            if op.getImmConst() == 0x0:                op = imm.disasmBackward(addr, 3)                return op.getAddress()        addr = op.getAddress()        ..............

运行,成功。log输出如下:

这里写图片描述

可以发现在刚才提到的那个位置(printf函数汇编代码倒数第五个指令处)打上了钩子。恢复执行 printf_loop.exe 进程,一定时间后暂停进程,再次运行脚本,log输出如下:

这里写图片描述

printf函数入口与出口钩子获取的信息成对出现,且与 printf_loop.exe 进程输出一致。没毛病。

至此,实现了对 printf_loop.exe 的硬钩子部署。

三、总结

实现自己的功能远比照着别人的例程学习要难的多,期间会遇到各种奇怪的状况,但是一旦完成了这一过程,势必对使用的工具和脚本思路有更深刻的认识和理解。

本次尝试断断续续,历时七八个小时,感觉最大的收获在于ID软件的使用上,真心觉得这款软件很强大,而且一旦熟练,操作起来也很简单。

原创粉丝点击