0x00 背景
在一次样本分析过程中,碰到了如下函数,此函数功能为解码字符串。
按照平常的思路一般都是动态调试出字符串,然后在ida上打上注释。
如果此时同类型的样本量非常大,显然一个个的调试是不现实的。
这时便可以使用idapython辅助我们来对字符串进行解码。
此篇记下学习idapython脚本的过程。
0x01 确定解码过程
根据ida F5的结果来看,是将每个字节数据减去13
我们很容易能编写出GenerateStr函数,如下:
def GenerateStr(data):
str_len = len(data)
decode_str = ""
if str_len != 0:
index = 0
while index < str_len:
decode_str += chr(ord(data[index]) - 13)
index += 1
return decode_str
0x02 确定调用GenerateStr的地址
在ida中,可以查找GenerateStr的引用来确定哪些地方调用了此函数
同样,在idapython中也提供了接口
XrefsTo(func_addr, flags = 0)
0x03 获取GenerateStr的参数
通过观察,我们发现每次调用GenerateStr函数时,传入参数都是esi,则我们可以定位call GenerateStr
,然后向上找mov esi, xxx
,直到找到此指令时,xxx就是我们需要的参数地址
idapython脚本如下:
def find_function_arg(addr):
while True:
# 获取上一条指令地址
addr = idc.PrevHead(addr)
# 判断当前指令的操作码为mov,且第一个操作数为esi
if GetMnem(addr) == "mov" and "esi" in GetOpnd(addr, 0):
# print "[+]Found it at 0x%x" % GetOperandValue(addr, 1)
break
# 返回第二个操作数
return GetOperandValue(addr, 1)
得到参数地址之后,我们要获取参数的内容,可以循环读直到空字符
def get_string(addr):
output = ""
while Byte(addr) != 0:
output += chr(Byte(addr))
addr += 1
return output
0x04 完整代码
def find_function_arg(addr):
while True:
addr = idc.PrevHead(addr)
if GetMnem(addr) == "mov" and "esi" in GetOpnd(addr, 0):
# print "[+]Found it at 0x%x" % GetOperandValue(addr, 1)
break
return GetOperandValue(addr, 1)
def get_string(addr):
output = ""
while Byte(addr) != 0:
output += chr(Byte(addr))
addr += 1
return output
def GenerateStr(data):
str_len = len(data)
decode_str = ""
if str_len != 0:
index = 0
while index < str_len:
decode_str += chr(ord(data[index]) - 13)
index += 1
return decode_str
if __name__ == '__main__':
func_addr = 0x00402F30
result = {}
for addr in XrefsTo(func_addr, flags = 0):
arg_addr = find_function_arg(addr.frm)
result[addr.frm] = (arg_addr, GenerateStr(get_string(arg_addr)))
# print result
for addr, (arg_addr, comm) in result.items():
# print "[+]CallFunc: ", hex(addr), "\tArgAddr: ", hex(arg_addr), "\tComm: ", comm
MakeComm(int(addr), comm) # 添加注释
MakeComm(int(arg_addr), comm) # 添加注释
print "done..."