IDA Python入门(todo)
ida pro7.7已经基本普及了,所以本篇文章是基于ida pro77和python3环境编写。
不可否认的是ida pro在静态分析上极为强大,不过动调方面还是比起其他工具稍显不足,而idapython提供了大量的ida的api,能一定程度上缓解动调的缺陷,这里就给大家简单介绍一下(
PS:参考了Q神的 http://www.qfrost.com/posts/ctf/idapython ,并且修正为了新版ida python的api
指令相关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| from idaapi import * from ida_dbg import * from ida_bytes import *
prev_1 = prev_head(0x00007FF6A1AA2577, 1) prev_2 = prev_head(0x00007FF6A1AA257a, 0x00007FF6A1AA2578) print(hex(prev_1), hex(prev_2)) >> 0x7ff6a1aa2573 0xffffffffffffffff
next_1 = next_head(0x00007FF6A1AA257a, 1) next_2 = next_head(0x00007FF6A1AA257a, 0x00007FF6A1AA2580) >> 0xffffffffffffffff 0x7ff6a1aa257b
a = generate_disasm_line(0x00007FF6A1AA2585) print(a) >> b'\x01\x05movzx\x02\x05 \x01)\x01!eax\x02!\x02)\x01\t,\x02\t \x01*\x01 byte ptr\x02 \x01\t[\x02\t\x01!r10\x02!\x01\t]\x02\t\x02*'
a = print_insn_mnem(0x00007FF6A1AA2585) print(a) >> movzx
a = print_operand(0x00007FF6A1AA2585, 0) print(a) >> b'\x01)\x01!eax\x02!\x02)'
print(hex(get_operand_value(0x00007FF6A1AA2577, 1))) print(type(get_operand_value(0x00007FF6A1AA2577, 1))) >> 0x10 >> <class 'int'>
a = get_strlit_contents(0x00007FF6A1AC28A8, 4, 0) b = get_strlit_contents(0x00007FF6A1AC28A8, 4, 1) print(a) print(b) >> b'Erro' >> b'\xe7\x89\x85\xe6\xbd\xb2'
a = set_cmt(0x00007FF6A1AA2577, "this is a comment", True) print(a) >> True
a = set_name(0x00007FF6A1AC28A8, "err") print(a) >> True
print(hex(get_screen_ea())) >> 0x7ff6a1ac28a8
print(FlowChart(get_func(0x00007FF6A1AA21B0)).size) >> 149
|
功能+调试相关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| add_bpt(0x00007FF6A1AA2577)
del_bpt(0x00007FF6A1AA2577)
enable_bpt(0x00007FF6A1AA2577, False)
load_and_run_plugin("idapython", 3)
def condition(): print(">>> rip:", get_reg_val("rip")) return True add_bpt(0x00007FF6011D2581)
set_bpt_cond(0x00007FF6011D2581, "condition()")
a = XrefsTo(0x00007FF6011D2577, flags=0) for addr in a: print(hex(addr.frm))
get_byte(addr) get_word(addr) get_dword(addr) get_qword(addr) get_wide_byte(addr) get_wide_word(addr) get_wide_dword(addr) get_wide_qword(addr) patch_byte(addr, val) patch_word(addr, val) patch_dword(addr, val) patch_qword(addr, val)
get_reg_val("rip")
set_reg_val("rax", 0x10)
start_process()
continue_process()
run_to(addr)
wait_for_next_event(EVENT_TYPE, flags)
step_over() wait_for_next_event(WFNE_SUSP, -1)
wait_for_next_event(WFNE_SUSP, -1) wait_for_next_event(WFNE_ANY | WFNE_CONT, -1)
|
实战:使用IDApython编写爆破脚本
PS:使用我给SICTF Round3出的Re题Closeme作为例子
附件: 点击下载附件
这题说实话预期就是爆破(x
大概回顾一下题目,每次创建几个窗口后都会弹出一个messagebox,点击yes/no会被储存为1/0,然后长度为16时就check一次,所以先把CreateWindowsEx和MessageBoxW那里的call xxx给nop掉(你也不想开几千上万个窗口把cpu干烧了吧x),然后就需要手动添加各种值了。
因为每16次循环才能使长度达到16进而check,太影响效率了,所以直接在原来call messagebox那里patch成jmp到cmp rax, 10h这里。
rax是长度,直接jmp过来就没有调用获取长度的函数,我们需要手动设置0x10
1 2 3 4 5 6 7
| breakpoint_addr = 0x00007FF6A1AA2577 add_bpt(breakpoint_addr)
rip = get_reg_val("RIP")
if rip == breakpoint_addr: set_reg_val("rax", 0x10)
|
但是再执行两步就会把一个局部变量取出来解一层引用给r10,后续就是16个比较,所以r10应该是一个指向我们储存16个1或0的地址。
那么我们要伪造2层,一是先把那个局部变量的值修改为一个可写地址(同时也要保证运行时不会有其他的写者),然后二是在这个可写地址写入我们爆破的数据。
我直接找了.data段最末尾的0x10的空间
1 2 3
| buf_addr = 0x7FF6A1ACDFF0 patch_qword(get_reg_val("rbp") + 0x38, buf_addr)
|
然后写入要爆破的数据需要注意端序问题(
1 2 3 4 5 6 7
| send = left = '0' + '0'.join(send[:8][::-1]) left = int(left, 16) right = '0' + '0'.join(send[8:][::-1]) right = int(right, 16) patch_qword(buf_addr, left) patch_qword(buf_addr + 8, right)
|
然后就是爆破2^16次就行了!
完整爆破脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| from idaapi import * from ida_dbg import * from ida_bytes import *
this_input = 0
start = 0x00007FF6530D220F
breakpoint_addr = 0x00007FF6A1AA2577 add_bpt(breakpoint_addr) win_addr = 0x00007FF6A1AA2993 add_bpt(win_addr)
print("Start!") start_process()
print("Waiting for next event...") wait_for_next_event(WFNE_SUSP, -1)
buf_addr = 0x7FF6A1ACDFF0 patch_qword(get_reg_val("rbp") + 0x38, buf_addr)
while True: if this_input >= 0x10000: print("Burst Finished.") break send = bin(this_input)[2:].zfill(16) print("send: ", send) try: rip = get_reg_val("RIP") if rip == breakpoint_addr: set_reg_val("rax", 0x10)
left = '0' + '0'.join(send[:8][::-1]) left = int(left, 16) right = '0' + '0'.join(send[8:][::-1]) right = int(right, 16) patch_qword(buf_addr, left) patch_qword(buf_addr + 8, right) elif rip == win_addr: print("win") print("flag: SICTF{" + bin(this_input - 1)[2:].zfill(16) + "}") exit(0) continue_process() wait_for_next_event(WFNE_SUSP, -1) except Exception as e: print("err: ", e) break this_input += 1
print("out")
|