静态补丁脚本 - 修改 libtolua.so
直接改arm64的so, 使用python脚本。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
静态补丁脚本 - 修改 libtolua.so
主要功能:
1. 修改 luaL_loadbuffer 函数,将跳转目标从 luaL_loadbufferx 改为 luaL_loadfilex
2. 在 luaL_loadfilex 函数中实现跳转到自定义的 my_custom_luaL_loadbufferx 函数
"""import struct
import os
import shutil
from pathlib import Pathclass LibtoluaPatcher:def __init__(self, target_so_path, custom_so_path):self.target_so_path = Path(target_so_path)self.custom_so_path = Path(custom_so_path)self.backup_path = self.target_so_path.with_suffix('.so.backup')# 关键地址定义 (基于提供的信息)self.luaL_loadbuffer_offset = 0x8A728 # luaL_loadbuffer 函数偏移self.luaL_loadfilex_offset = 0x8A530 # luaL_loadfilex 函数偏移 (未使用的函数)def backup_original(self):"""备份原始文件"""if not self.backup_path.exists():shutil.copy2(self.target_so_path, self.backup_path)print(f"已备份原始文件到: {self.backup_path}")else:print(f"备份文件已存在: {self.backup_path}")def calculate_branch_instruction(self, from_addr, to_addr):"""计算ARM64分支指令"""# 计算相对偏移offset = to_addr - from_addr# 检查是否在B指令范围内 (±128MB)if abs(offset) < (1 << 27):# 使用B指令 (无条件分支)# B指令格式: 0x14000000 | ((offset >> 2) & 0x3FFFFFF)offset_encoded = (offset >> 2) & 0x3FFFFFFinstruction = 0x14000000 | offset_encodedreturn struct.pack('<I', instruction)else:# 超出B指令范围,需要使用间接跳转raise ValueError(f"分支距离太远: {offset}, 需要使用间接跳转")def patch_luaL_loadbuffer(self, data):"""修改 luaL_loadbuffer 函数"""print("正在修改 luaL_loadbuffer 函数...")# 原始代码:# .text:000000000008A728 MOV X4, #0# .text:000000000008A72C B luaL_loadbufferx# 新代码:# .text:000000000008A728 MOV X4, #0 (保持不变)# .text:000000000008A72C B luaL_loadfilex (修改跳转目标)# 计算从 luaL_loadbuffer+4 到 luaL_loadfilex 的分支指令from_addr = self.luaL_loadbuffer_offset + 4 # +4 因为要修改第二条指令to_addr = self.luaL_loadfilex_offsettry:branch_instruction = self.calculate_branch_instruction(from_addr, to_addr)# 修改数据patch_offset = self.luaL_loadbuffer_offset + 4data[patch_offset:patch_offset+4] = branch_instructionprint(f"已修改 luaL_loadbuffer+4 (0x{patch_offset:X}) 的跳转目标")except ValueError as e:print(f"修改 luaL_loadbuffer 失败: {e}")return Falsereturn Truedef patch_luaL_loadfilex(self, data):"""在 luaL_loadfilex 函数中实现跳转到自定义函数"""print("正在修改 luaL_loadfilex 函数...")# 创建一个简化的hook代码,直接跳转到我们预设的地址# 在实际应用中,这个地址需要通过运行时解析获得# 创建hook代码桩stub_code = self.create_dynamic_dlopen_dlsym_and_call_stub()# 将代码写入 luaL_loadfilex 位置patch_offset = self.luaL_loadfilex_offsetif len(stub_code) <= 0x1F8: # 确保不超出函数空间 (到下一个函数的距离)data[patch_offset:patch_offset+len(stub_code)] = stub_codeprint(f"已在 luaL_loadfilex (0x{patch_offset:X}) 处植入hook代码")return Trueelse:print(f"Hook代码太长 ({len(stub_code)} 字节),超出可用空间")return False# def create_dynamic_loader_stub(self):# """创建SVC系统调用代码桩"""# # 使用SVC指令调用自定义的lua_loadbuffer系统调用# # 根据lua_file_writer.c中的定义:SVC_LUA_LOADBUFFER = 0x1001# instructions = []# # 函数参数已经在X0-X4寄存器中:# # X0 = lua_State *L# # X1 = const char *buff # # X2 = size_t sz# # X3 = const char *name# # X4 = const char *mode# # 直接调用SVC指令,无需保存/恢复寄存器# # SVC #0x1001 (SVC_LUA_LOADBUFFER)# svc_num = 0x1001# svc_instruction = 0xD4000001 | (svc_num << 5)# instructions.append(struct.pack('<I', svc_instruction))# # 返回到调用者# # RET# instructions.append(struct.pack('<I', 0xD65F03C0))# return b''.join(instructions)def create_dynamic_dlopen_dlsym_and_call_stub(self):"""创建动态加载和调用代码桩"""# 这个函数生成ARM64汇编代码,实现:# 1. dlopen("liblua_file_writer.so", RTLD_LAZY)# 2. dlsym(handle, "my_custom_luaL_loadbufferx")# 3. 跳转到获取的函数地址instructions = []#000000000008A6FC luaL_loadbufferx ; CODE XREF: sub_C1B80+110↓p
#000000000008A530 F4 FF FF 17 B luaL_loadbufferx (从0x00000000008A530跳转到0x8A6FC)# 正确计算从0x8A530跳转到0x8A6FC的B指令# 偏移量:0x8A6FC - 0x8A530 = 0x1CC = 460字节# 指令偏移:460 / 4 = 115# B指令编码:0x14000000 | 115 = 0x14000073#instructions.append(struct.pack('<I', 0x14000073)) # B luaL_loadbufferx# => 0x0000007f3d352530 <+0>: sub sp, sp, #0x80
# 0x0000007f3d352534 <+4>: stp x24, x23, [sp]
# 0x0000007f3d352538 <+8>: stp x22, x21, [sp,#16]
# 0x0000007f3d35253c <+12>: stp x20, x19, [sp,#32]
# 0x0000007f3d352540 <+16>: stp x29, x30, [sp,#48]
# 0x0000007f3d352544 <+20>: str x5, [sp,#56]# 保存参数寄存器 X0-X4 (逐个保存到不同栈位置)instructions.append(struct.pack('<I',0xD102C3FF)) # FF C3 02 D1 SUB SP, SP, #0xB0 instructions.append(struct.pack('<I',0xA9005FF8)) #F8 5F 00 A9 STP X24, X23, [SP]instructions.append(struct.pack('<I',0xA90157F6)) #F6 57 01 A9 STP X22, X21, [SP,#0x10]instructions.append(struct.pack('<I',0xA9024FF4)) #F4 4F 02 A9 STP X20, X19, [SP,#0x30]instructions.append(struct.pack('<I',0xA9037BFD)) #FD 7B 03 A9 STP X29, X30, [SP,#0x40]instructions.append(struct.pack('<I',0xF90057E5)) #E5 57 00 F9 STR X5, [SP,#0xA8]instructions.append(struct.pack('<I',0xAA0203F5)) #F5 03 02 AA MOV X21, X2instructions.append(struct.pack('<I',0xAA0103F6)) #F6 03 01 AA MOV X22, X1instructions.append(struct.pack('<I',0xAA0003F7)) #F7 03 00 AA MOV X23, X0instructions.append(struct.pack('<I',0xAA0403F3)) #F3 03 04 AA MOV X19, X4instructions.append(struct.pack('<I',0xAA0303F4)) #F4 03 03 AA MOV X20, X3# 准备dlopen参数# 需要将"liblua_file_writer.so"字符串地址加载到X0# RTLD_LAZY (1) 加载到X1# 使用原始SO文件中已存在的字符串# "liblua_file_writer.so" 在 0x133C20# 当前函数在 0x8A530,计算相对偏移lib_string_addr = 0x133C20#0000000000133C68# GROUP# lib_string_addr = 0x00DD668current_addr = self.luaL_loadfilex_offset + (len(instructions) * 4)lib_offset = lib_string_addr - current_addr# ADR X0, #lib_offset ; 获取"liblua_file_writer.so"地址if abs(lib_offset) < (1 << 20): # ADR指令范围检查adr_instruction = 0x10000000 | ((lib_offset >> 2) & 0x7FFFF) << 5instructions.append(struct.pack('<I', adr_instruction))else:# 如果超出ADR范围,使用ADRP+ADD组合page_offset = (lib_offset >> 12) & 0x1FFFFFadrp_instruction = 0x90000000 | (page_offset << 5)instructions.append(struct.pack('<I', adrp_instruction))# ADD X0, X0, #(lib_offset & 0xFFF)add_instruction = 0x91000000 | ((lib_offset & 0xFFF) << 10)instructions.append(struct.pack('<I', add_instruction))# MOV X1, #1 ; RTLD_LAZYinstructions.append(struct.pack('<I', 0xD2800021))# 调用dlopen# 这里需要通过GOT表调用dlopen,简化处理:# 假设dlopen在已知偏移处,实际应该通过PLT调用# BL dlopen ; 计算到dlopen的偏移# dlopen在.plt:0x17980,当前位置大约在0xB8AC0附近dlopen_plt_addr = 0x17980current_bl_addr = self.luaL_loadfilex_offset + (len(instructions) * 4)dlopen_offset = dlopen_plt_addr - current_bl_addr# BL指令格式: 0x94000000 | ((offset >> 2) & 0x3FFFFFF)bl_instruction = 0x94000000 | ((dlopen_offset >> 2) & 0x3FFFFFF)instructions.append(struct.pack('<I', bl_instruction))# 检查dlopen返回值,如果不为空则跳过异常触发指令# CBNZ X0, #8 ; 如果X0不为0,跳过下一条指令instructions.append(struct.pack('<I', 0xB5000040))# LDR X16, [X0] ; 如果X0为0,这里会触发空指针异常instructions.append(struct.pack('<I', 0xF9400010))# 准备dlsym参数# X0已经是handle,需要设置X1为符号名# "luaL_loadbufferx" 在 0x1336DEsymbol_string_addr = 0x1336DE;current_addr2 = self.luaL_loadfilex_offset + (len(instructions) * 4)symbol_offset = symbol_string_addr - current_addr2 +2# ADR X1, #symbol_offset ; 获取"luaL_loadbufferx"地址if abs(symbol_offset) < (1 << 20): # ADR指令范围检查adr_instruction2 = 0x10000000 | ((symbol_offset >> 2) & 0x7FFFF) << 5 | 1 # 目标寄存器X1instructions.append(struct.pack('<I', adr_instruction2))else:# 如果超出ADR范围,使用ADRP+ADD组合page_offset2 = (symbol_offset >> 12) & 0x1FFFFFadrp_instruction2 = 0x90000000 | (page_offset2 << 5) | 1 # 目标寄存器X1instructions.append(struct.pack('<I', adrp_instruction2))# ADD X1, X1, #(symbol_offset & 0xFFF)add_instruction2 = 0x91000000 | ((symbol_offset & 0xFFF) << 10) | (1 << 5) | 1instructions.append(struct.pack('<I', add_instruction2))# 调用dlsym# BL dlsym ; 计算到dlsym的偏移# dlsym在.plt:0x17A40dlsym_plt_addr = 0x17A40current_bl_addr2 = self.luaL_loadfilex_offset + (len(instructions) * 4)dlsym_offset = dlsym_plt_addr - current_bl_addr2# BL指令格式: 0x94000000 | ((offset >> 2) & 0x3FFFFFF)bl_instruction2 = 0x94000000 | ((dlsym_offset >> 2) & 0x3FFFFFF)instructions.append(struct.pack('<I', bl_instruction2))# 检查dlopen返回值,如果为空则访问其指针值造成异常# CBNZ X0, #8 ; 如果X0不为0,跳过下一条指令instructions.append(struct.pack('<I', 0xB5000040))# LDR X16, [X0] ; 如果X0为0,这里会触发空指针异常instructions.append(struct.pack('<I', 0xF9400010))# 保存dlsym返回的函数地址到X16# MOV X17, X0instructions.append(struct.pack('<I', 0xAA0003F1))instructions.append(struct.pack('<I',0xAA1703E0)) #E0 03 17 AA MOV X0, X23instructions.append(struct.pack('<I',0xAA1603E1)) #E1 03 16 AA MOV X1, X22instructions.append(struct.pack('<I',0xAA1503E2)) #E2 03 15 AA MOV X2, X21instructions.append(struct.pack('<I',0xAA1403E3)) #E3 03 14 AA MOV X3, X20instructions.append(struct.pack('<I',0xAA1303E4)) #E4 03 13 AA MOV X4, X19# 跳转到目标函数instructions.append(struct.pack('<I',0xA9424FF4)) #F4 4F 42 A9 LDP X20, X19, [SP,#0x30+var_10]instructions.append(struct.pack('<I',0xA94157F6)) #F6 57 41 A9 LDP X22, X21, [SP,#0x30+var_20]instructions.append(struct.pack('<I',0xA9405FF8)) #F8 5F 40 A9 LDP X24, X23, [SP]instructions.append(struct.pack('<I',0xA9437BFD)) #FD 7B 43 A9 LDP X29, X30, [SP,#0x30+var_s0]instructions.append(struct.pack('<I',0xF9401FE5)) #E5 1F 40 F9 LDR X5, [SP,#0x38]instructions.append(struct.pack('<I',0x9102C3FF)) #FF C3 02 91 ADD SP, SP, #0xB0 ;instructions.append(struct.pack('<I', 0xD61F0220)) #20 02 1F D6 BR X17 return b''.join(instructions)def apply_patches(self):"""应用所有补丁"""# 读取目标文件with open(self.target_so_path, 'rb') as f:data = bytearray(f.read())print(f"文件大小: {len(data)} 字节")# 应用补丁success = Trueif not self.patch_luaL_loadbuffer(data):success = Falseif not self.patch_luaL_loadfilex(data):success = Falseif success:# 写回修改后的文件with open(self.target_so_path, 'wb') as f:f.write(data)print(f"补丁应用成功!修改后的文件: {self.target_so_path}")else:print("补丁应用失败!")return successdef restore_backup(self):"""恢复备份文件"""if self.backup_path.exists():shutil.copy2(self.backup_path, self.target_so_path)print(f"已恢复备份文件: {self.target_so_path}")else:print("备份文件不存在!")def main():# 文件路径target_so = "libtolua.so"custom_so = "iblua_file_writer.so"# 检查文件是否存在if not os.path.exists(target_so):print(f"目标文件不存在: {target_so}")returnif not os.path.exists(custom_so):print(f"自定义SO文件不存在: {custom_so}")return# 创建补丁器patcher = LibtoluaPatcher(target_so, custom_so)# 应用补丁if patcher.apply_patches():print("\n补丁应用完成!")print("使用说明:")print("1. 修改后的libtolua.so会在luaL_loadbuffer调用时跳转到我们的hook函数")print("2. 如需恢复原始文件,请运行: patcher.restore_backup()")else:print("\n补丁应用失败!")if __name__ == "__main__":main()