4.9 x64dbg 内存处理与差异对比

LyScript 插件中针对内存读写函数的封装功能并不多,只提供了最基本的内存读取内存写入系列函数的封装,本章将继续对API接口进行封装,实现一些在软件逆向分析中非常实用的功能,例如ShellCode代码写出与置入,内存交换,内存区域对比,磁盘与内存镜像比较,内存特征码检索等功能,学会使用这些功能对于后续漏洞分析以及病毒分析都可以起到事半功倍的效果,读者应重点关注这些函数的使用方式。

4.9.1 实现ShellCode的灵活注入

Shellcode 是一种特殊类型的恶意代码,通常用于利用系统漏洞、执行恶意软件等攻击性行为。Shellcode 通常是一段二进制代码,没有可执行文件头,并且设计用于利用操作系统的特定漏洞,从而使攻击者能够获得对系统的控制或执行其他恶意操作。

由于 Shellcode 通常是在内存中执行的,因此它不需要像可执行文件一样拥有完整的文件结构。攻击者通常通过利用漏洞将Shellcode注入到受攻击系统的进程中,并使其在内存中执行,从而达到攻击目的。由于Shellcode是一种非常灵活的攻击工具,攻击者可以使用它来执行各种攻击行为,例如提权、执行远程命令、下载恶意软件等。因此,Shellcode已成为黑客和攻击者的常用工具之一。

通常读者应该自行准备好如下文本案例中所规范的ShellCode格式,这类格式通常经过处理后即可直接注入到远程进程内。

"\xfc\xe8\x8f\x00\x00\x00\x60\x31\xd2\x89\xe5\x64\x8b\x52\x30"
"\x8b\x52\x0c\x8b\x52\x14\x0f\xb7\x4a\x26\x31\xff\x8b\x72\x28"
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49"
"\x75\xef\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78"
"\x85\xc0\x74\x4c\x01\xd0\x8b\x58\x20\x01\xd3\x50\x8b\x48\x18"
"\x85\xc9\x74\x3c\x31\xff\x49\x8b\x34\x8b\x01\xd6\x31\xc0\xac"

接着笔者将带大家实现一个将文件内的ShellCode注入到进程远程堆空间内的案例,既然要注入到远程堆中,那么第一步则是通过create_alloc(1024)在对端开辟一段堆空间,如果读者需要让该空间可被执行则需要调用set_local_protect(address,32,1024)将该地址设置为32也就是读写执行,设置长度为1024字节,接着通过read_shellcode()函数从文本中读取ShellCode代码,并作压缩处理,最后通过循环write_memory_byte写内存的方式将其逐字节写出,总结起来核心代码如下所示;

from LyScript32 import MyDebug# 将shellcode读入内存
def read_shellcode(path):shellcode_list = []with open(path, "r", encoding="utf-8") as fp:for index in fp.readlines():shellcode_line = index.replace('"', "").replace(" ", "").replace("\n", "").replace(";", "")for code in shellcode_line.split("\\x"):if code != "" and code != "\\n":shellcode_list.append("0x" + code)return shellcode_listif __name__ == "__main__":dbg = MyDebug()dbg.connect()# 开辟堆空间address = dbg.create_alloc(1024)print("开辟堆空间: {}".format(hex(address)))if address == False:exit()# 设置内存可执行属性dbg.set_local_protect(address, 32, 1024)# 从文本中读取shellcodeshellcode = read_shellcode("c://shellcode.txt")# 循环写入到内存for code_byte in range(0, len(shellcode)):bytef = int(shellcode[code_byte], 16)dbg.write_memory_byte(code_byte + address, bytef)# 设置EIP位置dbg.set_register("eip", address)input()dbg.delete_alloc(address)dbg.close()

运行这段程序,则读者应该能看到如下图所示的输出结果,这说明我们的数据已经写出到对端堆中了;

而有时我们还需要将这段代码反写,将一段我们挑选好的指令集保存到本地,此时就需要使用read_memory_byte依次循环读入数据,并动态写出到文件中,代码如下所示;

from LyScript32 import MyDebug# 将特定内存保存到文本中
def write_shellcode(dbg,address,size,path):with open(path,"a+",encoding="utf-8") as fp:for index in range(0, size - 1):# 读取机器码read_code = dbg.read_memory_byte(address + index)if (index+1) % 16 == 0:print("\\x" + str(read_code))fp.write("\\x" + str(read_code) + "\n")else:print("\\x" + str(read_code),end="")fp.write("\\x" + str(read_code))if __name__ == "__main__":dbg = MyDebug()dbg.connect()eip = dbg.get_register("eip")write_shellcode(dbg,eip,128,"d://shellcode.txt")dbg.close()

如上代码运行后,并可将EIP位置出的指令集前128字节动态写出到d://shellcode.txt文件内,输出效果图如下图所示;

4.9.2 内存区域交换与对比

区域交换的原理是通过第三方变量依次交换内存两端的数据,例如将如下图中的0x5B00100x5B0070的前四个字节进行交换,则可调用memory_xchage(dbg, 5963792,5963792,4)传递参数实现,在调用前该内存区域如下图所示;

通过运行如下代码片段,则可实现数据交换。

from LyScript32 import MyDebug# 交换两个内存区域
def memory_xchage(dbg,memory_ptr_x,memory_ptr_y,bytes):ref = Falsefor index in range(0,bytes):# 读取两个内存区域read_byte_x = dbg.read_memory_byte(memory_ptr_x + index)read_byte_y = dbg.read_memory_byte(memory_ptr_y + index)# 交换内存ref = dbg.write_memory_byte(memory_ptr_x + index,read_byte_y)ref = dbg.write_memory_byte(memory_ptr_y + index, read_byte_x)return refif __name__ == "__main__":dbg = MyDebug()dbg.connect()eip = dbg.get_register("eip")# 内存交换flag = memory_xchage(dbg, 5963792,5963888,4)print("内存交换状态: {}".format(flag))dbg.close()

交换后的内存区域如下图所示;

4.9.3 内存与磁盘机器码对比

在某些时候我们还需要对比某个特定程序内存与磁盘之间的数据差异,这类需求的实现前提是实现两个特殊的读写函数,一般而言get_memory_hex_ascii函数可用于读出内存中的机器码数据,而get_file_hex_ascii则可用于读出磁盘中的机器码数据,将两者最进一步对比从而获取某些字节是否发生了改变。

首先实现get_memory_hex_ascii函数,该函数用于从给定的内存地址开始,读取指定长度的二进制数据,并将其转换为十六进制形式输出。具体解释如下:

  • 函数接收三个参数:内存地址address,偏移量offset,和要读取的长度len。
  • 定义变量count用于计算已经读取的字节数,并定义ref_memory_list用于存储读取的数据的十六进制形式。
  • 使用for循环读取指定范围内的二进制数据。
  • 调用dbg.read_memory_byte方法读取内存中的每个字节,并将其赋值给变量char。
  • 将读取的字节的十六进制表示输出到控制台。
  • 将读取的字节的十六进制形式存储到ref_memory_list列表中。
  • 如果已经读取了16个字节,就换行输出。
  • 如果字节的十六进制表示只有一位,则在前面添加一个0以保证两个字符宽度。
  • 最后返回ref_memory_list列表,包含了所有读取字节的十六进制形式。
# 得到程序的内存镜像中的机器码
def get_memory_hex_ascii(address,offset,len):count = 0ref_memory_list = []for index in range(offset,len):# 读出数据char = dbg.read_memory_byte(address + index)count = count + 1if count % 16 == 0:if (char) < 16:print("0" + hex((char))[2:])ref_memory_list.append("0" + hex((char))[2:])else:print(hex((char))[2:])ref_memory_list.append(hex((char))[2:])else:if (char) < 16:print("0" + hex((char))[2:] + " ",end="")ref_memory_list.append("0" + hex((char))[2:])else:print(hex((char))[2:] + " ",end="")ref_memory_list.append(hex((char))[2:])return ref_memory_list

其次实现get_memory_hex_ascii函数,该函数用于从给定的文件路径中读取指定长度的二进制数据,并将其转换为十六进制形式输出。具体解释如下:

  • 函数接收三个参数:文件路径path,偏移量offset,和要读取的长度len。
  • 定义变量count用于计算已经读取的字节数,并定义ref_file_list用于存储读取的数据的十六进制形式。
  • 使用with open语句打开指定路径的文件,并使用rb模式以二进制方式读取。
  • 使用fp.seek方法将文件指针移动到指定的偏移量offset处。
  • 使用for循环读取指定长度的二进制数据。
  • 使用fp.read(1)方法读取一个字节的数据,并将其赋值给变量char。
  • 将读取的字节的十六进制表示输出到控制台。
  • 将读取的字节的十六进制形式存储到ref_file_list列表中。
  • 如果已经读取了16个字节,就换行输出。
  • 如果字节的十六进制表示只有一位,则在前面添加一个0以保证两个字符宽度。
  • 最后返回ref_file_list列表,包含了所有读取字节的十六进制形式。
# 读取程序中的磁盘镜像中的机器码
def get_file_hex_ascii(path,offset,len):count = 0ref_file_list = []with open(path, "rb") as fp:# file_size = os.path.getsize(path)fp.seek(offset)for item in range(offset,offset + len):char = fp.read(1)count = count + 1if count % 16 == 0:if ord(char) < 16:print("0" + hex(ord(char))[2:])ref_file_list.append("0" + hex(ord(char))[2:])else:print(hex(ord(char))[2:])ref_file_list.append(hex(ord(char))[2:])else:if ord(char) < 16:print("0" + hex(ord(char))[2:] + " ", end="")ref_file_list.append("0" + hex(ord(char))[2:])else:print(hex(ord(char))[2:] + " ", end="")ref_file_list.append(hex(ord(char))[2:])return ref_file_list

有了这两个函数读者就可以实现依次输出内存与磁盘中的机器码功能,

import binascii,os,sys
from LyScript32 import MyDebug# 得到程序的内存镜像中的机器码
def get_memory_hex_ascii(address,offset,len):pass# 读取程序中的磁盘镜像中的机器码
def get_file_hex_ascii(path,offset,len):passif __name__ == "__main__":dbg = MyDebug()connect_flag = dbg.connect()print("连接状态: {}".format(connect_flag))module_base = dbg.get_base_from_address(dbg.get_local_base())print("模块基地址: {}".format(hex(module_base)))# 得到内存机器码memory_hex_byte = get_memory_hex_ascii(module_base,0,100)# 得到磁盘机器码file_hex_byte = get_file_hex_ascii("d://lyshark.exe",0,100)# 输出机器码print("\n内存机器码: ",memory_hex_byte)print("\n磁盘机器码: ",file_hex_byte)dbg.close()

如上代码片段的输出效果如下图所示,分别得到该进程的内存与磁盘机器码格式,取前100个字节作比较;

至于如何做对比,读者只需要通过for循环输出其参数即可得到,这里就不做截图演示了,效果同理;

    # 输出机器码for index in range(0,len(memory_hex_byte)):# 比较磁盘与内存是否存在差异if memory_hex_byte[index] != file_hex_byte[index]:# 存在差异则输出print("\n相对位置: [{}] --> 磁盘字节: 0x{} --> 内存字节: 0x{}".format(index,memory_hex_byte[index],file_hex_byte[index]))dbg.close()

原文地址

https://www.lyshark.com/post/1875a6c.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18852.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nativefier 将网站打包成桌面程序

Nativefier是一个命令行工具&#xff0c;仅仅通过一行代码就可以轻松地为任何的网站创建桌面应用程序 1 Mac 电脑 安装Nativefier 打开终端 npm install nativefier -g2 创建一个桌面应用程序&#xff1a; nativefier "https://biglead.blog.csdn.net/article/details…

Pytorch基本使用—激活函数

✨1 介绍 ⛄ 1.1 概念 激活函数是神经网络中的一种数学函数&#xff0c;它被应用于神经元的输出&#xff0c;以决定神经元是否应该被激活并传递信号给下一层。常见的激活函数包括Sigmoid函数、ReLU函数、Tanh函数等。 &#x1f384; 1.2 性质 激活函数是神经网络中的一种重…

RFID系统的部署流程和关键环节

RFID系统的部署与集成对于实现高效的物流管理、提升运营效益和实现自动化过程至关重要。本文将为您解释RFID系统的部署流程和关键环节&#xff0c;并探讨RFID系统与现有IT基础设施的集成方式。我们还将提供RFID系统部署和集成中常见问题的解决方案&#xff0c;帮助您制定RFID系…

ELK日志分析系统

目录 开始 第一台安装elasticsearch-head插件 第一台node1安装logstash 配置收集系统日志 第一台 安装kibana第一台 第三台 简述&#xff1a; 日志分析是运维工程师解决系统故障&#xff0c;发现问题的主要手段。日志主要包括系统日志、应用程序日志和安全日志。系统运…

cesium 实现多颗卫星与多颗地面站雷达通信效果

最主要的部分是计算空间内两点之间的距离以及卫星对地点是否在雷达扫描范围内 先看效果 1.计算空间内两点之间的距离 //计算距离 function distance(point1, point2) {var point1cartographic = Cesium.Cartographic.fromCartesian(point1);var</

基于STM32智能窗帘控制系统仿真设计(含源程序+proteus仿真+讲解视频)

# 基于STM32智能窗帘设计&#xff08;含源程序proteus仿真&#xff09; 仿真&#xff1a;proteus8.11 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 编号C0007 资料下载链接 讲解视频 基于STM32的简易智能窗帘控制系统仿真设计 功能说明&#xff1a; 实现功能&a…

Kubernetes:快速入门指南

原文作者&#xff1a;NGINX 原文链接&#xff1a;Kubernetes&#xff1a;快速入门指南 转载来源&#xff1a;NGINX 官方网站 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.cn 什么是 Kubernetes&#xff1f; Kubernetes又称为 k8s&#xff0c;是一个开源平台&#xff…

Java虚拟机(JVM)

Java虚拟机&#xff08;JVM&#xff09; 类加载 类加载 Java类加载的过程可以分为以下几个步骤&#xff1a; 加载&#xff08;Loading&#xff09;&#xff1a;类加载的第一步是将类的字节码文件加载到内存中。 通过类的全名&#xff0c;全限定名&#xff08;包括包名和类名&…

【STM32MP135】移除stm32mp135d-atk-u-boot.dtsi设备树config节点,否则会进入fastboot下载模式

文件路径&#xff1a;u-boot-stm32mp-v2021.10-stm32mp1-r1/arch/arm/dts/stm32mp135d-atk-u-boot.dtsi

flutter开发实战-Theme主题切换

flutter开发实战-Theme主题切换 之前做的应用中有用到Theme主题切换&#xff0c;一直没有整理&#xff0c;这里整理一下。 使用的是Android studio 一、效果图 二、创建ThemeModel // 提供五套可选主题色 const _themes <MaterialColor>[Colors.blue,Colors.cyan,Co…

PWM 输出实验(stm32)

目录 PWM的代码pwm.cpwm.h main.c 说明&#xff1a;以下内容参考正点原子的资料 PWM的代码 pwm.c //TIM3 PWM部分初始化 //PWM输出初始化 //arr&#xff1a;自动重装值 //psc&#xff1a;时钟预分频数 void TIM3_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitSt…

C语言进阶之字符串函数和内存函数的介绍及部分函数的模拟实现

字符串函数和内存函数 1.字符串函数介绍1.1 strlen1.2 strcpy1.3 strcat1.4 strcmp1.5 strncpy1.6 strncat1.7 strncpy1.8 strstr1.9 strtok1.10 strerror1.11 字符分类函数 2.内存函数2.1 memcpy2.2 memmove2.3 memcmp 3.函数的模拟实现3.1 模拟实现strlen3.2 模拟实现strcpy3…