LyScript x64dbg 自动化调试插件
为 x64dbg 调试器开发的自动化测试插件,用于快速构建基于 Python 的测试脚本,以加速漏洞利用程序的开发,协助漏洞挖掘和分析恶意软件。
参考:https://www.52pojie.cn//thread-1631953-1-1.html
补充一下测试环境 Python 版本 3.11.9,x64dbg 版本 Mar 3 2025 (TitanEngine), x64dbg LyScript插件版本 v1 (2025年5月6日从官网下载的)
下面是根据官网的内存读写比较例子写的一段根据自定义断点数组自动下断,根据断点位置获取相应数据的测试代码。大概流程就是自定义一个断点数组 bpList
(当然断点需要你先找到),进入主程序后根据断点数组下断所有断点,然后进入一个死循环进行判断当前调试状态是否为被中断,如果被中断了就进入断点检查函数看看是否是自己所需的断点被命中,根据不同的断点获取需要寄存器或所在函数变量、参数等数据,然后继续执行。也可以使用插件的dbg.check_breakpoint(0x0000)
来判断是否为自己指定的断点被命中。
import time
from x32dbg import Debugger
bpList = []
CountAll = 0
def get_memory_hex_ascii(address,offset,len):
count = 0
ref_memory_list = []
for index in range(offset,len):
char = dbg.get_memory_byte(address + index)
count = count + 1
if 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:])
print("")
return ref_memory_list
def bp_all_address():
for i in range(0,len(bpList)):
if dbg.get_breakpoint_type(bpList[i]) == 0 or dbg.is_breakpoint_disabled(bpList[i]):
dbg.set_breakpoint(bpList[i])
print("BP disabled State:{}:{}".format(hex(bpList[i]),dbg.is_breakpoint_disabled(bpList[i])))
return
def checkEIPInBplist():
match dbg.get_eip():
case 0x4AC8BA:
print("SRC Address:{} Index:{} Length:{} Data:".format(hex(dbg.get_eip()),CountAll,dbg.get_memory_byte(dbg.get_ebp()-4)))
tea_key_data = get_memory_hex_ascii(dbg.get_ecx(),0,dbg.get_memory_byte(dbg.get_ebp()-4))
case 0x4AC8D2:
print("CRC Address:{} Index:{} Length:{} Data:".format(hex(dbg.get_eip()),CountAll,dbg.get_memory_byte(dbg.get_ebp()-4)))
tea_key_data = get_memory_hex_ascii(dbg.get_ecx(),0,dbg.get_memory_byte(dbg.get_ebp()-4))
case 0x4AC908:
print("TEA Address:{} Index:{} Length:{} Data:".format(hex(dbg.get_eip()),CountAll,dbg.get_eax()))
tea_key_data = get_memory_hex_ascii(dbg.get_ecx(),0,dbg.get_eax())
case 0x4AC93C:
print("SRC Address:{} Index:{} Length:{} Data:".format(hex(dbg.get_eip()),CountAll,dbg.get_memory_byte(dbg.get_ebp()-4)))
tea_key_data = get_memory_hex_ascii(dbg.get_eax(),0,dbg.get_memory_byte(dbg.get_ebp()-4))
case 0x4AC961:
print("CRC Address:{} Index:{} Length:{} Data:".format(hex(dbg.get_eip()),CountAll,dbg.get_memory_byte(dbg.get_ebp()-4)))
print("ECX:{} EBP+8:{} EBP-8:{}".format(hex(dbg.get_ecx()),hex(dbg.get_memory_ptr(dbg.get_ebp()+8)),hex(dbg.get_memory_ptr(dbg.get_ebp()-8))))
tea_key_data = get_memory_hex_ascii(dbg.get_memory_ptr(dbg.get_ebp()-8),0,dbg.get_memory_byte(dbg.get_ebp()-4))
case 0x4AC998:
length = dbg.get_eax()
print("TEA Address:{} Index:{} Length:{} Data:".format(hex(dbg.get_eip()),CountAll,length))
dbg.debug_stepover()
tea_key_data = get_memory_hex_ascii(dbg.get_eax(),0,length)
dbg.debug_run()
if __name__ == "__main__":
dbg = Debugger(address="127.0.0.1", port=6589)
if not dbg.connect():
exit()
bpList = [0x4AC8BA,0x4AC8D2,0x4AC908,0x4AC93C,0x4AC961,0x4AC998]
module_base = dbg.get_base_from_address(dbg.get_memory_localbase())
print("Module base address = {}".format(hex(module_base)))
print("Tea_Key address = {}".format(hex(0x789248)))
tea_key_data = get_memory_hex_ascii(0x789248,0,16)
bp_all_address()
while True:
if dbg.debug_pause():
CountAll = CountAll + 1
checkEIPInBplist()
time.sleep(0.001)
input("按下回车键继续...")
dbg.close()
执行结果
PS C:\Users\Administrator\Desktop> c:; cd 'c:\Users\Administrator\Desktop'; & 'd:\Python\Python311\python.exe' 'c:\Users\Administrator\.vscode\extensions\ms-python.debugpy-2025.6.0-win32-x64\bundled\libs\debugpy\launcher' '9868' '--' 'C:\Users\Administrator\Desktop\test.py'
Module base address = 0x0040000
Tea_Key address = 0x789248
31 33 94 30 7c e2 58 90 af 04 c4 4d 00 00 00 00
BP disabled State:0x4ac8ba:False
BP disabled State:0x4ac8d2:False
BP disabled State:0x4ac908:False
BP disabled State:0x4ac93c:False
BP disabled State:0x4ac961:False
BP disabled State:0x4ac998:False
CRC Address:0x4ac961 Index:7 Length:32 Data:
ECX:0x789234 EBP+8:0x789234 EBP-8:0x2e0f020
00 00 00 20 77 00 d7 c6 00 00 00 00 00 00 00 00
07 00 00 00 33 2e 31 31 2e 30 31 00 00 00 00 00
TEA Address:0x4ac998 Index:8 Length:40 Data:
00 00 00 28 77 00 d7 c6 90 b6 07 a8 7e a4 45 d0
dc fb 1a d0 28 db a7 b8 fb 83 19 ad eb a7 38 5f
SRC Address:0x4ac93c Index:9 Length:16 Data:
CRC Address:0x4ac961 Index:10 Length:16 Data:
ECX:0x789234 EBP+8:0x789234 EBP-8:0x2e0f020
00 00 00 10 20 27 89 a2 00 00 00 00 28 04 04 00
TEA Address:0x4ac998 Index:11 Length:24 Data:
00 00 00 18 20 27 89 a2 4f aa fd aa db 24 15 dd
56 0f 79 01 4d 93 a6 1c
部分断点对应的汇编代码
004AC920 | 55 | push ebp |
004AC921 | 8BEC | mov ebp,esp |
004AC923 | 83EC 08 | sub esp,8 |
004AC926 | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC929 | E8 D220F7FF | call backmir.41EA00 |
004AC92E | 8945 FC | mov dword ptr ss:[ebp-4],eax |
004AC931 | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC934 | E8 1725FBFF | call backmir.45EE50 |
004AC939 | 8945 F8 | mov dword ptr ss:[ebp-8],eax | [ebp-08]:WindowFromPoint+600
004AC93C | 8B45 FC | mov eax,dword ptr ss:[ebp-4] |
004AC93F | 50 | push eax |
004AC940 | E8 35CC1400 | call <JMP.&htonl> |
004AC945 | 8B4D F8 | mov ecx,dword ptr ss:[ebp-8] | ecx:NtUserGetMessage+C, [ebp-08]:WindowFromPoint+600
004AC948 | 8901 | mov dword ptr ds:[ecx],eax | ecx:NtUserGetMessage+C
004AC94A | 0FB655 0C | movzx edx,byte ptr ss:[ebp+C] |
004AC94E | 85D2 | test edx,edx |
004AC950 | 74 3A | je backmir.4AC98C |
004AC952 | 8B45 08 | mov eax,dword ptr ss:[ebp+8] |
004AC955 | 50 | push eax |
004AC956 | E8 D53DFCFF | call backmir.470730 |
004AC95B | 83C4 04 | add esp,4 |
004AC95E | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC961 | 51 | push ecx | ecx:NtUserGetMessage+C
004AC962 | B9 48927800 | mov ecx,backmir.789248 | ecx:NtUserGetMessage+C
004AC967 | E8 643DFCFF | call backmir.4706D0 |
004AC96C | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC96F | E8 8C20F7FF | call backmir.41EA00 |
004AC974 | 3B45 FC | cmp eax,dword ptr ss:[ebp-4] |
004AC977 | 74 13 | je backmir.4AC98C |
004AC979 | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC97C | E8 7F20F7FF | call backmir.41EA00 |
004AC981 | 50 | push eax |
004AC982 | E8 F3CB1400 | call <JMP.&htonl> |
004AC987 | 8B55 F8 | mov edx,dword ptr ss:[ebp-8] | [ebp-08]:WindowFromPoint+600
004AC98A | 8902 | mov dword ptr ds:[edx],eax |
004AC98C | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC98F | E8 6C20F7FF | call backmir.41EA00 |
004AC994 | 50 | push eax |
004AC995 | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] | ecx:NtUserGetMessage+C
004AC998 | E8 B324FBFF | call backmir.45EE50 |
004AC99D | 50 | push eax |
004AC99E | B9 603FC900 | mov ecx,backmir.C93F60 | ecx:NtUserGetMessage+C, C93F60:"攺s"
004AC9A3 | E8 28441300 | call backmir.5E0DD0 |
004AC9A8 | 8BE5 | mov esp,ebp |
004AC9AA | 5D | pop ebp |
004AC9AB | C3 | ret |
以下内容来自官网
对 Python 的依赖
通过利用 Python 语言丰富的第三方库并将其与调试器相结合,可以提高效率。
无第三方依赖项
使用 Python 语言快速构建功能脚本,无需安装任何第三方依赖文件,开箱即用。
本机脚本调用
支持调用 x64dbg 提供的原生脚本和可自定义的组合函数。
插件用户手册
欢迎使用 LyScript,这是一个强大的插件,允许您通过 Python 控制 x64dbg 进行自动作。在逆向工程领域,分析大量病毒样本或寻找漏洞通常需要大量的时间和精力。LyScript 插件的出现解放了逆向工程师的双手,并为他们提供了高效的自动化解决方案。无论是远程动态调试、漏洞检索,还是自动化二进制样本分析,LyScript 都可以成为您的得力助手。
该插件结合了 Python 的灵活性和丰富的第三方库,为攻击者、漏洞挖掘和恶意软件分析提供了便利。通过使用插件,您可以快速开发各种自定义工具,加速您的工作流程,并提高工作效率。
概述
INFO 信息
-
将开发时间缩短 50%
-
简单易懂的界面
-
用于自动化智能调试的强大脚本语言
-
轻量级和快速调试可以防止复杂分析过程中的损坏
-
连接到 PyPi 和开发工具
无论您是逆向工程师、防病毒专家还是漏洞分析师,此插件都将是您不可或缺的工具。让我们一起探索 LyScript 的强大功能,将繁重的工作转化为轻松愉快的体验!
安装插件
在使用这个插件之前,你需要自己下载对应版本的 x64dbg 调试器。可以确认该插件对 debugger 没有版本限制。如果已安装调试器,则可以忽略此步骤。
接下来,您需要单击下载按钮以在本地下载插件。下载的文件包含两个文件夹,其中 x32 是 32 位插件,x64 是 64 位插件。该插件由两部分组成,LyScript.dp32
是主程序,LyScript.ini
是配置文件。将这两个文件拖到 x64dbg 的 plugins 目录下,即可安装插件。
打开插件配置文件,你会看到三行配置信息,可以用来指定插件是否在运行,以及绑定地址和端口信息。出于安全考虑,新版插件默认只允许本地用户访问。如果想实现远程调试,可以将 Address 修改为 0.0.0.0
或者任意网卡地址进行监听。
[Setting]
Enabled=1
Address="127.0.0.1"
Port=6589
其次,需要安装相应版本的 Python 包。由于 LyScript 插件已集成到官方 PYPI 存储库中,因此您可以轻松安装它。以 32 位为例,打开控制台,输入 pip install x32dbg
进行安装。如果是 64 位,则需要执行 pip install x64dbg
来安装。建议同时安装这两个软件包。
Microsoft Windows [12.0.19999.888]
(c) 2024 Microsoft Corporation。
C:\Users\admin> pip install x32dbg
Installing collected packages: x32dbg
Successfully installed x32dbg-1.1.0
C:\Users\admin> pip install x64dbg
Installing collected packages: x64dbg
Successfully installed x64dbg-1.1.0
导入和使用
一切准备就绪后,您就可以正式使用它了。首先,运行 x32dbg 调试器。插件加载成功后,我们打开 Python 控制台,输入 from x32dbg import Debugger
导入调试界面。在 Debugger
函数中,您可以指定相应的 IP 地址和端口信息。如果未指定,则默认为本地地址。通过使用 connect
,您可以连接到调试器的内部,is_connect
可用于确定调试器的状态,close_connect
可用于关闭本地套接字。
C:\> python
Python 3.12.0 [MSC v.1935 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from x32dbg import Debugger
>>>
>>> dbg = Debugger(address="127.0.0.1",port=6589)
>>> dbg
<x32dbg.Debugger object at 0x00323BA0>
>>>
>>> connect = dbg.connect()
>>> connect
True
>>> is_connect = dbg.is_connect()
>>> is_connect
True
>>> dbg.close_connect()
True
通用寄存器
通用寄存器是一种具有广泛应用和功能的寄存器,在计算机体系结构中发挥着重要作用。它们通常用于存储数据、地址和临时结果,以及执行算术和逻辑运算。通过使用通用寄存器,程序可以更有效地管理数据并执行各种计算任务。
标准 API 接口可用于读取通用寄存器。以读取通用寄存器 eax
、ebx
、ecx
和 edx
为例,该函数不带参数传入,调用后会以十进制无符号整数的形式输出给用户。我们可以使用 hex()
将其转换为十六进制格式,如下例所示;
>>> eax = dbg.get_eax()
>>> ebx = dbg.get_ebx()
>>> ecx = dbg.get_ecx()
>>> edx = dbg.get_edx()
>>>
>>> print("eax={} ebx={} ecx={} edx={}".format(eax,ebx,ecx,edx))
eax=0 ebx=5242880 ecx=706543616 edx=0
>>>
>>> print("ebx={} ecx={}".format(hex(ebx),hex(ecx)))
ebx=0x500000 ecx=0x2a1d0000
其次,通用 registers 支持长度分割。例如,ebx 寄存器通常可以分为三种类型:bx
、bh
和 bl
。如果是 64 位的rbx,也可以分为四种类型:ebx
、bx
、bh
和 bl
。以 32 位为例,我们通常可以直接调用 output,如下例所示;
>>> ebx = dbg.get_ebx()
>>> bx = dbg.get_bx()
>>> bh = dbg.get_bh()
>>> bl = dbg.get_bl()
>>>
>>> print("ebx={} bx={} bh={} bl={}".format(hex(ebx),hex(bx),hex(bh),hex(bl)))
ebx=0x500000 bx=0x0 bh=0x0 bl=0x0
标准接口还提供了一个 get_register
函数,通过传入 register 的名称,从对应的 register 中读取参数。如果该函数成功执行,它将返回一个十进制无符号整数。如果失败,则返回 false。
>>> eax = dbg.get_register("eax")
>>> eip = dbg.get_register("eip")
>>>
>>> print("eax = {} | eip = {}".format(hex(eax),hex(eip)))
eax = 0x0 | eip = 0x77e0f127
>>>
>>> cip = dbg.get_register("CIP")
>>> cip
2011230503
>>> dr1 = dbg.get_register("dr1")
>>> dr1
0
此方法的可用寄存器范围包括:
- DR0, DR1, DR2, DR3, DR6, DR7
- EAX, AX, AH, AL
- EBX, BX, BH, BL
- ECX, CX, CH, CL
- EDX, DX, DH, DL
- EDI, DI
- ESI, SI
- EBP, BP
- ESP, SP
- EIP, RAX, RBX, RCX, RDX, RSI, SIL, RDI, DIL, RBP, BPL, RSP, SPL, RIP
- CIP, CSP, CAX, CBX, CCX, CDX, CDI, CSI, CBP, CFLAGS
对于 64 位进程,已添加 register set,如下所示。
- R8, R8D, R8W, R8B
- R9, R9D, R9W, R9
- R10, R10D, R10W, R10B
- R11, R11D, R11W, R11B
- R12, R12D, R12W, R12B
- R13, R13D, R13W, R13B
- R14, R14D, R14W, R14B
- R15, R15D, R15W, R15B
对于设置通用寄存器,可以使用 set_
,也可以使用 set_register
。要设置 set 方法,需要传入一个十进制整数参数,而对于后者,需要两个参数。参数 1 使用字符串,参数 2 使用十进制无符号整数。如果函数执行成功,则返回十进制无符号整数,如果执行失败,则返回 false。
>>> eax = dbg.get_register("eax")
>>> eax
0
>>> dbg.set_eax(0x1000)
True
>>> eax = dbg.get_register("eax")
>>> eax
4096
>>> hex(eax)
'0x1000'
>>>
>>> dbg.set_register("eax",0x2000)
True
>>> eax = dbg.get_register("eax")
>>> hex(eax)
'0x2000'
标志寄存器
标志寄存器是计算机体系结构中的一种特殊类型的寄存器,用于存储处理器作期间生成的状态信息和标志位。标志寄存器通常用于存储各种条件代码,这些代码记录最近算术或逻辑运算的结果或状态。程序可以使用标志寄存器的状态信息来做出条件判断,控制程序流程,实现各种算法和逻辑运算。标志寄存器是计算机体系结构的重要组成部分,它对指令的执行和程序的正确性有重大影响。
读取标志寄存器可以使用标准 API 接口实现,类似于通用寄存器。可以使用 get_
为前缀的一系列函数或 get_flag_register
函数读取标志。前者不需要传入参数,而后者需要传入标志寄存器字符串,如以下示例所示;
>>> zf = dbg.get_zf()
>>> of = dbg.get_of()
>>> pf = dbg.get_pf()
>>>
>>> print("zf = {} of ={} pf = {}".format(zf,of,pf))
zf = True of =False pf = True
>>>
>>> zf = dbg.get_flag_register("zf")
>>> of = dbg.get_flag_register("of")
>>> pf = dbg.get_flag_register("pf")
>>> print("zf = {} of ={} pf = {}".format(zf,of,pf))
zf = True of =False pf = True
此方法的可用寄存器范围包括:
- ZF, OF, CF, PF, SF, TF, AF, DF, IF
与通用寄存器一致,可以使用 set_
为前缀一系列的函数或直接使用 set_flag_register
函数来实现标志寄存器的设置。这一系列函数只传入 1 或 0 作为参数,分别表示 true 或 false。这两个函数都返回布尔值,如以下示例所示;
>>> dbg.set_zf(1)
True
>>> dbg.set_zf(0)
True
>>> dbg.set_flag_register("pf",1)
True
>>> dbg.set_flag_register("tf",0)
True
>>>
>>> dbg.get_zf()
False
>>> dbg.get_pf()
True
>>> dbg.get_tf()
False