4.6 x64dbg 内存扫描与查壳实现

x64dbg,内存,扫描,实现 · 浏览次数 : 145

小编点评

**程序分析** **程序名称**:程序OEP **版本**:0x40153e **功能**:定位程序的真正入口地址 **步骤**: 1. **获取程序OEP的指令地址**:使用 scan_memory_one() 函数匹配特征码。 2. **设置断点**:使用 set_breakpoint() 函数设置断点。 3. **运行程序**:使用 run() 函数运行程序。 4. **设置调试**:使用 set_debug() 函数设置调试功能。 5. **断点**:程序在断点处停止运行。 6. **获取断点位置**:使用 get_breakpoint() 函数获取断点位置。 7. **自动跳转**:程序自动跳转到程序的真正入口地址。 **输出** 程序OEP 的指令地址。 **注意** * 此程序需要在程序运行之前设置断点。 * 程序的真正入口地址可能会有所变化。 * 可以使用其他方法设置断点,例如使用 set_breakpoint() 函数或使用 breakpoints() 函数。

正文

LyScript 插件中默认提供了多种内存特征扫描函数,每一种扫描函数用法各不相同,在使用扫描函数时应首先搞清楚不同函数之间的差异,本章内容将分别详细介绍每一种内存扫描函数是如何灵活运用,并实现一种内存查壳脚本,可快速定位目标程序加了什么壳以及寻找被加壳程序的入口点。

计算机中的壳定义

加壳通常指对可执行文件或者动态链接库等二进制文件进行加密或压缩等处理,以使得这些文件难以被反汇编或破解。通常情况下,加壳会增加二进制文件的大小,并在程序运行时增加一定的开销。加壳技术通常被用于保护软件的知识产权和防止软件被盗版。通过加壳,软件开发者可以使得软件更难被破解和复制,从而保护自己的商业利益。

计算机中查壳的原理

软件查壳的实现原理可以分为静态分析和动态分析两种方式。静态分析是指在不运行被加壳程序的情况下,通过对程序的二进制代码进行解析,识别出程序是否被加壳,以及加壳的种类和方法。动态分析是指通过运行被加壳程序,并观察程序运行时的行为,识别程序是否被加壳,以及加壳的种类和方法。

静态分析的实现原理通常包括以下几个步骤:

  • 提取被分析程序的二进制代码;
  • 识别程序的文件格式,并解析文件头等元数据信息;
  • 根据文件格式,从程序代码中提取节区、导入表、导出表等重要信息;
  • 分析程序的代码段,并检查代码中是否存在加壳相关的特征,如代码的执行流程、加密算法等;
  • 根据分析结果,判断程序是否被加壳,以及加壳的种类和方法。

静态分析的优点是分析速度快,不需要运行程序,可以有效地识别出程序中的加壳。但是它也有一些缺点,例如无法识别动态加载的加壳、易受代码混淆和反调试等技术的影响。

动态分析的实现原理通常包括以下几个步骤:

  • 启动被分析程序,并在程序运行期间捕捉程序的行为;
  • 跟踪程序的执行流程,并分析程序的内存、寄存器、堆栈等状态信息;
  • 检查程序的内存中是否存在加壳相关的特征,如解密函数、加壳程序等;
  • 根据分析结果,判断程序是否被加壳,以及加壳的种类和方法。

动态分析的优点是可以识别动态加载的加壳,并且可以有效地避免代码混淆和反调试等技术的影响。但是它也有一些缺点,例如需要运行被分析程序,因此可能会影响程序的性能和稳定性,并且可能会被加壳程序的反调试技术所绕过。

本例中将采用scan_memory_all()函数对特定内存进行动态扫描,该函数用来扫描当前进程内EIP所指向位置处整个内存段中符合条件的特征,如果找到了则返回一个列表,如果没有找到则返回False,该函数与scan_memory_one()函数原理是一致的,唯一的不同是all以列表形式返回所有匹配到的行,one则只返回匹配到的第一条记录,这两个函数都支持??模糊匹配。

如果载入一个程序,默认停留在系统领空,则调用该函数你所能得到的特征记录只能是系统领空当前dll内的特征集。

例如在程序被载入后,其EIP指针默认会停留在ntdll.dll模块上,需要注意的是,函数scan_memory_one用于扫描并返回第一段符合条件的内存,函数scan_memory_all则用于扫描并输出当前所有符合条件的内存地址;

>>> from LyScript32 import MyDebug
>>>
>>> dbg = MyDebug()
>>> conn = dbg.connect()
>>>
>>> ref_one = dbg.scan_memory_one("55 8b ec")
>>> ref_one
1995793090
>>>
>>> ref_all = dbg.scan_memory_all("55 8b ec")
>>> ref_all
[1995793090, 1995793298, 1995793455, 1995793799, 1995794214]

而有时,我们需要指定扫描某个模块,例如扫描进程内的msvcr120.dll模块里面的特征,则此时读者可调用scan_memory_any()函数,该函数无需切换到模块入口处即可实现扫描特定模块内的特征,不过该函数只能返回找到的第一条记录,且需要传入扫描起始位置以及扫描长度,不过得到这些参数并不难。

from LyScript32 import MyDebug

if __name__ == "__main__":
    dbg = MyDebug()
    conn = dbg.connect()

    # 得到进程模块
    local_module = dbg.get_all_module()[0]

    # 得到模块参数
    module_base = local_module.get("base")
    module_size = local_module.get("size")
    print("基地址: {} 长度: {} 结束地址: {}".format(hex(module_base),hex(module_size),hex(module_base+module_size)))

    # 扫描内存
    ref = dbg.scan_memory_any(module_base,module_size,"55 8b ec ??")
    if ref != False:
        print("找到内存: {}".format(hex(ref)))
    dbg.close()

运行如上所示的代码片段,则默认会扫描进程内第一个模块也就是主程序模块内的,特征55 8b ec ??指令集,当找到后会输出如下图所示的提示信息;

如上代码中的内存扫描方法如果能被读者理解,那么查壳这个功能就变得很简单了,市面上的查壳软件PEID等基本都是采用特征码定位的方式,所以我们想要实现查壳以及检测编译器特征可以采用特征码扫描法,只需要将上述代码更改一下,即可实现查壳功能。

from LyScript32 import MyDebug

# 查壳功能
def Scan(dbg, string):
    # 得到进程模块
    local_module = dbg.get_all_module()[0]

    # 得到模块参数
    module_base = local_module.get("base")
    module_size = local_module.get("size")
    # print("基地址: {} 长度: {} 结束地址: {}".format(hex(module_base),hex(module_size),hex(module_base+module_size)))

    # 扫描内存
    ref = dbg.scan_memory_any(module_base,module_size,string)
    if ref != False:
        return True
    return False

if __name__ == "__main__":
    dbg = MyDebug()
    conn = dbg.connect()

    # 存储特征码
    signs = [
        {"key": "Microsoft Visual C++ 2013", "value": "e8 ?? ?? ?? ?? e9 ?? ?? ?? ?? 55 8b ec"},
        {"key": "UPX 3.96w", "value": "60 be ?? ?? ?? ?? 8d be 00 90 ff ff 57"}
    ]

    for index in signs:
        check = Scan(dbg, index.get("value"))
        if check == True:
            print("编译特征: {}".format(index.get("key")))

    dbg.close()

当运行上述代码片段,则将会寻找特定指令集,如果找到则说明采用的是特定的加壳程序或特定编译器编译生成的,输出效果图如下所示;

通过运用scan_memory_one()函数,读者何以很容易的实现对特定内存区域的寻找,以32为代码为例,通过get_register函数获取到当前EIP的内存地址,并通过read_memory_word读入前四个字节的数据,当前四个字节的数据为0xBE60此时说明是UPX壳的开头,通过scan_memory_one("83 EC ?? E9")匹配特征码,当找到后顺势在此处set_breakpoint(patternAddr)下一个断点,程序通过不断地运行,最终即可定位到程序真正的入口地址;

from LyScript32 import MyDebug

if __name__ == "__main__":
    # 初始化
    dbg = MyDebug()

    # 连接到调试器
    connect_flag = dbg.connect()
    print("连接状态: {}".format(connect_flag))

    # 检测套接字是否还在
    ref = dbg.is_connect()
    print("是否在连接: ", ref)

    is_64 = False

    # 判断是否时64位数
    if is_64 == False:
        currentIP = dbg.get_register("eip")

        if dbg.read_memory_word(currentIP) != int(0xBE60):
            print("[-] 可能不是UPX")
            dbg.close()

        patternAddr = dbg.scan_memory_one("83 EC ?? E9")
        print("匹配到的地址: {}".format(hex(patternAddr)))

        dbg.set_breakpoint(patternAddr)
        dbg.set_debug("Run")
        dbg.set_debug("Wait")
        dbg.delete_breakpoint(patternAddr)

        dbg.set_debug("StepOver")
        dbg.set_debug("StepOver")
        print("[+] 程序OEP = 0x{:x}".format(dbg.get_register("eip")))

    else:
        currentIP = dbg.get_register("rip")

        if dbg.read_memory_dword(currentIP) != int(0x55575653):
            print("[-] 可能不是UPX")
            dbg.close()

        patternAddr = dbg.scan_memory_one("48 83 EC ?? E9")
        print("匹配到的地址: {}".format(hex(patternAddr)))

        dbg.set_breakpoint(patternAddr)
        dbg.set_debug("Run")
        dbg.set_debug("Wait")
        dbg.delete_breakpoint(patternAddr)

        dbg.set_debug("StepOver")
        dbg.set_debug("StepOver")
        print("[+] 程序OEP = 0x{:x}".format(dbg.get_register("eip")))

    dbg.close()

运行上述代码片段,则会看到如下图所示的输出效果,并以自动跳转到了程序的真正入口位置0x40153e,此时用户只需要脱壳转存即可;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/58999418.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与4.6 x64dbg 内存扫描与查壳实现相似的内容:

4.6 x64dbg 内存扫描与查壳实现

LyScript 插件中默认提供了多种内存特征扫描函数,每一种扫描函数用法各不相同,在使用扫描函数时应首先搞清楚不同函数之间的差异,本章内容将分别详细介绍每一种内存扫描函数是如何灵活运用,并实现一种内存查壳脚本,可快速定位目标程序加了什么壳以及寻找被加壳程序的入口点。软件查壳的实现原理可以分为静态分析和动态分析两种方式。静态分析是指在不运行被加壳程序的情况下,通过对程序的二进制代码进行解析,识别出

4.6 C++ Boost 函数绑定回调库

Boost库中提供了函数对象库,可以轻松地把函数的参数和返回值进行绑定,并用于回调函数。这个库的核心就是bind函数和function类。bind函数可以将一个函数或函数对象和其参数进行绑定,返回一个新的函数对象。通过这个新的函数对象,我们就可以将原有的函数或函数对象当做参数传来传去,并可以传递附加的参数,方便实现参数绑定和回调函数。function类用于表示一种特定的函数签名,可以在不知道具体函

oop课程4-6次作业小结

目录(1)前言(2)设计与分析第四次作业(答题判题程序-4)新增多选类新增填空类第五次作业(家居强电电路模拟程序-1)Element类控制设备开关#分档调速器#受控设备白炽灯#日光灯#吊扇#计算电压(家庭电路类)第六次作业(家居强电电路模拟程序-2)Element类Light类Fan类(3)采坑心得

FolkMq v1.4.6 发布(可以内嵌的消息中间件)

功能简表 角色 功能 生产者(客户端) 发布消息、定时消息(或叫延时)、顺序消息、可过期消息、事务消息。支持 Qos0、Qos1 消费者(客户端) 订阅、取消订阅。消费-ACK(自动、手动) 服务端 发布-Confirm、订阅-Confirm、取消订阅-Confirm、派发-Retry、派发-Del

【jetson nano】yolov5环境配置tensorrt部署加速

安装pytorch Nano上预装的Jetpack版本为4.6.1,Python为3.6.9,CUDA环境为10.2。在PyTorch for Jetson中可以下载不同版本的torch,torch<=1.10.0。 1 安装torch1.8.0 # substitute the link URL

C#.NET与JAVA互通之DES加密V2024

C#.NET与JAVA互通之DES加密V2024 配置视频: 环境: .NET Framework 4.6 控制台程序 JAVA这边:JDK8 (1.8) 控制台程序 注意点: 1.由于密钥、明文、密文的输入输出参数,都是byte数组(byte[]),所以:字符串转byte数组(byte[])环节,

OPPO主题组件开发 - 组件内容自适应

OPPO桌面有 3*5、3*6、4*5、4*6、5*5、5*6 等布局,随着布局不同,组件大小也会发生改变;不同型号手机分辨率不同,组件大小也不一致。这就要求组件内容做到自适应。 说明 OPPO主题组件自适应有两种表现方式,如下图所示。可以很明显的看到,第一种是根据宽高等比例缩放内容,第二种是固定内

IEC103设备数据 转 IEC61850项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关采集103设备数是 2 5 用IEC61850协议转发数据 4 6 网关使用多个逻辑设备和逻辑节点的方法 6 7 IEC103协议说明 8 8 案例总结 9 1 案例说明 设置网关采集IEC103设备数据

【Azure Developer】Github Action使用Azure/login@v1插件登录遇见错误的替代方案

问题描述 在使用 Github Action - Azure/login@v1 的插件时候,登录中国区Azure遇见了问题。 Login YAML 内容: - name: 'Login via Azure CLI' uses: Azure/login@v1.4.6 with: creds: ${{

基于神经网络的呼吸音分类算法

简介 在过去的几十年里,许多机器学习(ML)方法被引入来分析呼吸周期的声音,包括爆裂声、咳嗽声和喘息声[1-6]。然而,几乎所有传统的ML模型都完全依赖于手工制作的功能。此外,需要高度复杂的预处理步骤来利用设计的特征[4-6]。因此,仅仅基于ML的模型可能对肺部声音中的外部/内部噪声不具有鲁棒性,并