关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
ida python 脚本开发
无
916
0
0
mut3p1g
https://www.hex-rays.com/products/ida/support/idapython_docs/ https://www.hex-rays.com/products/ida/support/idapython_docs/idc-module.html 很多函数都更新了:https://hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml # 0x01 demo ``` import idaapi class routerEXP(idaapi.plugin_t): flags = idaapi.PLUGIN_UNL comment = "no comment" help = "no help" wanted_name = "routerEXP" wanted_hotkey = "Alt-F8" def init(self): idaapi.msg("init() called!\n") return idaapi.PLUGIN_OK def run(self, arg): idaapi.msg("run() called with %d!\n" % arg) def term(self): idaapi.msg("term() called!\n") def PLUGIN_ENTRY(): return routerEXP() ``` 保存到`ida`目录下的`plugins`即可,之后重启`ida`即可在`edit->plugins`中看到插件 还可以添加热键调用对应函数: ``` AddHotkey("Alt+x","xxx") ``` # 0x02 用到的函数 ## 1. 输入获取 http://nullege.com/codes/search/idc.AskStr ``` AskStr('1','2') => 1: 默认值 2: 提示值 返回为输入 ``` 类似的函数还有 ``` AskYN(1,'2') # 询问是或否 1:默认值 2:提示之 返回值为真/假 AskFile() ``` ## 2. 消息 ``` Warning() Message() ``` ## 3. 信息获取 ``` get_name_ea_simple('name'): 通过输入的name获取对应的地址 get_next_cref_to(To, current): 获取current地址后调用To的地址 get_name(ea): 获取地址ea对应的函数名或变量名 get_func_name(ea): 获取地址ea所在的函数名 get_func_off_str(ea):获取地址ea所在的函数名及偏移 ``` ## 4. 获取引用 首先就是要获得某函数引用它的地方,代码如下: ``` def getXref(self, addr): idx = 0 xrefs = [] while True: xref = get_next_cref_to(addr, idx) if xref != self.failed and xref not in xrefs: xrefs.append(xref) else: break idx = xref return xrefs ``` ## 5. 创建函数 我这里的目标是`mips`,一般函数都是以下面这样开头 ``` la $gp, xxxx ``` 那么可以先尝试通过地址获取函数名,如果失败了就向上寻找开头,然后在对应位置创建函数 ``` add_func(start): 创建函数 ``` 而向上寻找的办法就是获取上条指令的地址就行了,然后一直向上搜索: ``` PrevHead: 上条指令的地址 NextHead: 下条指令的地址 get_item_head: 当前指令的地址 ``` 代码如下: ``` def createFunc(self, addr): while True: disasm = GetDisasm(addr) if re.match("la *\$gp, unk_[0-9a-fA-F]*", disasm): break addr = PrevHead(addr) add_func(addr) ``` ## 6. 获取函数汇编 获取了函数起始结束地址,然后一个一个获取就行了 ``` GetFunctionAttr(ea,FUNCATTR_END) ``` 代码如下: ``` def getFuncDisasm(func): addr = get_name_ea_simple(func) endaddr = get_func_attr(addr, FUNCATTR_END) funcdisasm = [] while addr < endaddr: funcdisasm.append(GetDisasm(addr)) addr = next_head(addr) return funcdisasm ``` ## 7. 参数判断 于是,我们需要通过`strcpy`的参数,判断是否合法 1. 对于`$a0`,其来源必须是`$sp` 2. 对于`$a1`,其来源必须是`find_val`的结果`$v0` ### 1) $a0 所以关键就是向上溯源了,先是`$a0`,这个比较简单,直接向上遍历,如果 ``` def findback(self, pc, aim, source, limit, type=0, visited=[], h=0): ''' pc: now pc aim&souce: try to find string like "aim=source", they are register. limit: pc should in limit. type: exp or input visited: a list to record visited pc. h: height ''' while True: #print(hex(pc)[:-1],aim,source,h) if self.disasm.isok(pc): # 判断当前地址是否在对应函数中 instruction = self.disasm.ins[pc] else: return False if pc in visited: # 判断当前地址是否访问过 return False else: visited.append(pc) match = re.findall("[l|a|m][a-z]* *%s, (\$[avtsk][0-9p])" % aim.replace("$", "\\$"), instruction) # 寻找能给aim赋值的寄存器#register if not match: # 如果发现aim被常亮赋值了,则直接返回 if re.findall("[l|a|m][a-z]* *%s, ([0-9A-Fx]*)" % aim.replace("$", "\$"), instruction): # num return False if match: aim = match[0] if aim == source: # 找到了目标寄存器 if limit[0]<=pc<=limit[1]: # 限制目标的地址范围,若满足则记录关键信息 #print("ok",aim,source, hex(pc)[:-1],h) if type: self.inputsource = pc else: self.expsource = pc return True return False xref = self.getXref(pc) # 获取能走到当前位置的地址 for refs in xref: # 遍历来路,这里出现分支 try: if self.findback(refs, aim, source, limit, type, copy.copy(visited), h+1): return True except: return False pc = PrevHead(pc) # 向上继续寻找 return False ``` 这里有几个关键点: 1. 分支:先判断当前是否满足条件,不满足则从引用先走一遍,最后再从当前路径继续向上搜索 2. 已访问:已访问不能是全局的,因为可能出现分支,所以需要将已访问数组从父节点继承;同时也不能不设置这个数组,否则可能出现死循环。 3. 向上搜索:只要搜索如`$aim=$source`,那么就不用管`$aim`了,继续搜索`$souce`的来历 ### 3) $a1 这里多了一步,就是获得的`$v0`必须是我们需要的`find_val`的返回值,所以需要寻找`find_val`之后还调用函数的位置: ``` def findNextCall(self, pc, filter): while True: pc = NextHead(pc) if not self.disasm.isok(pc): return self.disasm.end else: instruction = self.disasm.ins[pc] if pc in filter: return NextHead(pc) if instruction.startswith('jalr'): return pc ``` 这样就能获取从`find_val`到下一个调用函数的地址区间,并设置为`limit`即可 # other gdb里有符号但ida里面没有,所以写了个脚本爬取下来并设置一下: ida里获取没有名字的函数: ``` def get_unknow_func(): for func in Functions(): name = get_func_name(func) if name.startswith("sub_"): print(hex(func)) get_unknow_func() ``` gdb里获取函数名: ``` import gdb import re import json def execute(gdb_command, silent=False): result = None logname = "/tmp/gdb.log" logfd = open(logname) gdb.execute('set logging off') # prevent nested call gdb.execute('set height 0') # disable paging gdb.execute('set logging file %s' % logname) gdb.execute('set logging overwrite on') gdb.execute('set logging redirect on') gdb.execute('set logging on') try: gdb.execute(gdb_command) gdb.flush() gdb.execute('set logging off') if not silent: logfd.flush() result = logfd.read() except Exception as e: gdb.execute('set logging off') #to be sure if config.Option.get("debug") == "on": msg('Exception (%s): %s' % (gdb_command, e), "red") traceback.print_exc() logfd.close() return result ans = {} for addr in open("/tmp/func.txt").readlines(): addr = addr.strip() ret = execute("x /i {}".format(addr)) try: func_name = re.findall("\<(.*?)\>", ret)[0] ans[addr] = func_name except Exception as e: pass print(json.dumps(ans)) ``` 最后再设置回去: ``` import json content = open("C:\\Users\\ai\\Desktop\\func.txt", 'rb').read().strip() funcs = json.loads(content) for item in funcs: set_name(eval(item), funcs[item]) ```
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
0
条评论
More...
文章目录
No Leanote account ? Sign up now.