抖音資料采集教程,Unicorn 模擬 CPU 指令并 Hook CPU 執行狀態
短視頻、直播資料實時采集介面,請查看檔案: TiToData
免責宣告:本檔案僅供學習與參考,請勿用于非法用途!否則一切后果自負,
添加記憶體訪問 hook 回呼
引數
- type: 記憶體操作型別 READ, or WRITE
- address: 當前指令地址
- size: 讀或寫的長度
- value: 寫入的值(type=read時無視)
- user_data: hook_add 設定的 user_data 引數
添加記憶體訪問例外處理 hook 回呼
引數
- type: 記憶體操作型別 READ, or WRITE
- address: 當前指令地址
- size: 讀或寫的長度
- value: 寫入的值(type=read時無視)
- user_data: hook_add 設定的 user_data 引數
- 回傳值: 真(繼續模擬執行) 假(停止模擬執行)
模擬 cpu 執行 mov 指令
目標:
執行普通的 匯編代碼, 模擬讓他跑起來.text:00008ACA 0A 46 MOV R2, R1 ; Rd = Op2 // 將 R1 放到 R2.text:00008ACC 03 46 MOV R3, R0 ; Rd = Op2 // 將 R0 放到 R3
上面的指令代碼為: \x0A\x46\x03\x46
import unicorn
import capstone
import binascii
CODE = b'\x0A\x46\x03\x46' # 測驗指令 IDA 中拉來的, mov 命令,未涉及到記憶體讀寫命令
def print_result(mu):
"""輸出除錯結果, 原始碼中,這些都是常量
UC_ARM_REG_R0 = 66
UC_ARM_REG_R1 = 67
UC_ARM_REG_R2 = 68
UC_ARM_REG_R3 = 69
UC_ARM_REG_R4 = 70
"""
msg = """
暫存器輸出 --------
暫存器[R0], 值:{}
暫存器[R1], 值:{}
暫存器[R2], 值:{}
暫存器[R3], 值:{}
""".format(
mu.reg_read(66), # UC_ARM_REG_R0 原始碼對應常量 66
mu.reg_read(67),
mu.reg_read(68),
mu.reg_read(69),
)
print(msg.strip().replace(' ', ''))
def capstone_print(code):
"""capstone 測驗"""
print("\033[1;32m-------- capstone 輸出--------\033[0m")
CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) # 指定 THUMB 指令集
for i in CP.disasm(code, 0, len(code)):
print('\033[1;32m地址: %s | 操作碼: %s | 內容: %s\033[0m'%(i.address, i.mnemonic, i.op_str))
def uni_test():
"將匯編片段,映射到 unicorn 虛擬記憶體中,將 pc 指向第一條指令處并執行"
print('-------- unicorn 執行前--------')
# 1. 創建實體
mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) # 要指定架構和模式, 這里用 arm 架構, 指定 THUMB 指令集
# 2. 將代碼片段映射到模擬器的虛擬地址
ADDRESS = 0x1000 # 映射開始地址
SIZE = 1024 # 分配映射大小
# 3. 開始映射
mu.mem_map(ADDRESS, SIZE) # 初始化映射 引數1:地址 引數2:空間大小 默認初始化后默認值:0
mu.mem_write(ADDRESS, CODE) # 寫入指令 引數1: 寫入位置 引數2:寫入內容
# 4. 測驗讀取 [測驗]
bytes=mu.mem_read(ADDRESS, 10) # 引數1: 讀出位置 引數2:讀出位元組數
print('地址:%x, 內容:%s'%(ADDRESS, binascii.b2a_hex(bytes))) # 讀出來是 bates, 要用 binascii 轉換一下
# 寫入暫存器
# 5. 暫存器初始化 指令集涉及到 R0,R1,R2,R3 4個暫存器
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x100) # 在 r0 暫存器上寫入 0x100
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x200) # 在 r1 暫存器上寫入 0x200
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, 0x300) # 在 r2 暫存器上寫入 0x100
mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3, 0x400) # 在 r3 暫存器上寫入 0x200
# 6. pc 指標指向地址開始執行
follow_cpu(mu) # 跟蹤 CPU 進行 hook
print_result(mu) # 輸出
mu.emu_start(ADDRESS+1, ADDRESS+4) # THUMB 指令集所以要 ADDRESS +1, 引數1:起始位置,引數2:結束位置
print('-------- unicorn 執行后--------')
print_result(mu) # 輸出
def follow_cpu(mu):
"""跟蹤 cpu 執行狀態進行 hook 堆疊顯示"""
mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) # 這里默認跟蹤所有,具體也可以配置
def hook_code(mu, address, size, user_data):
"""定義回呼函式, 在進入匯編指令之前就會先運行這里
mu: 模擬器
address: 執行地址
size: 匯編指令大小
user_data: 通過 hook_add 添加的引數
"""
code=mu.mem_read(address,size) # 讀取
print('\033[1;32m=== Hook cpu ===\033[0m')
capstone_print(code)
return
if __name__ == "__main__":
capstone_print(CODE)
uni_test()
Python
_ 復制_
執行 STR 等記憶體操作命令
在上面例子中加入一條 STR 指令.text:00008B04 04 92 STR R2, [SP,#0x40+var_30] ; Store to Memory // 存暫存器
整個指令代碼變成\x0A\x46\x03\x46\x04\x92
這種情況下如果只是像上面那樣寫就不行了,因為需要記憶體操作,與提前映射,否則 hook cpu 操作的時候就會報錯,
需要自行添加一個回呼函式來主動映射
def hook_mem_write_unmapped(mu, type, address, size, value, user_data):
print('\033[1;32m=== Hook cpu ===\033[0m')
if type==unicorn.UC_MEM_WRITE_UNMAPPED:
mu.mem_map(0x0,0x1000) # 主動映射進去 注意:映射的時候需要對齊!!和最開始設定的位置和大小!
print("\033[1;32m[主動映射]地址: 0x%x | hook_mem 型別: %d | 大小:%d | 值:0x%x\033[0m" % (address, type, size, value))
return True # 回傳 True 繼續執行,回傳 False 則不執行后面的
Python
_ 復制_
如此才能執行 STR 指令并成功 Hook CPU 狀態
執行系統指令與基本塊
如果要模擬執行匯編系統指令與基本塊的 Hook, 那么我們也要自行添加回呼函式
def hook_syscall(mu,intno,user_data):
print("\033[1;36mhook 系統呼叫 系統呼叫號: 0x%d"%intno)
if intno==2: # 例子 2 是退出
print("系統呼叫退出!!")
print_result(mu)
print("\033[0m")
return
def hook_block(mu, address, size, user_data):
# code = mu.mem_read(address,size)
print("\033[1;36mhook 基本塊")
print_result(mu)
print("\033[0m")
return
Python
_ 復制_
當然這只是個例子代碼,執行推出的
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/252568.html
標籤:其他
下一篇:binlog的刷盤策略
