[原创]Immunity Debugger内存搜素python脚本
近日使用Immunity Debugger工具调试程序,发现它的内存搜索方面不太好,不知道是不是我找错地方了。于是自己写了个用于内存搜索的脚本。核心功能是search_code_string函数,它在内存中搜索一段内存数据,实现了简单的模糊查询。
比如搜索:
FF00*0104EEAAeeaa
其中*号表示 后面的02 和04是个范围的上下限,表示不确定数据的范围为[1,4]。于是FF 00 ** EE AA EE AA和FF 00 ** ** EE AA EE AA 、FF 00 ** ** ** ** EE AA EE AA 均能被搜索。
FF00-03AA
其中的-表示这个范围固定后面的03是个值,只能搜索 FF 00 -- -- -- AA
注意这个范围上下限和固定值必须是两位数,个位数就凑成两位数吧。而且数值是16机制的。也就是*1a2b 是可以的,表示范围[26, 43]
自己随便定义的。 * 和 -是可以自定义的。。通过RD_CHAR = "*" FD_CHAR ="-" 两个变量定义。
search_code_Long、search_code_short、search_code_byte、search_data_str等函数都是转换一下,然后调用search_code_string函数 来搜索的
search_asmcode函数也支持简单的替换 R32表示32位的寄存器,R16表示16为寄存器,R8表示8为寄存器。注意搜索的汇编语句中,只能有两个 寄存器未知,比如mov R8, byte ptr ds:[R32+0x10]可以被搜索,但是mov R8, byte ptr ds:[R32+R32+10]就有错误了,不想搞那么复杂。
如果两未知寄存器均为32位,则分别以R32和R_32表示。(R8 R_8 和 R16 R_16 同理)
原理就是一顿替换。。。。
search_asmcode_infun函数需要提供函数地址,它在函数调用了search_asmcode函数。具体看脚本吧。
change_special函数是查找汇编代码中的16进制值 转换为带0x 比如fa-->0xfa
之所以这么做是因为op.getDisasm() 返回的 十六进制值没有带0x。在调用imm.assemble 时会出错。
代码中的变量命名命得不好,大家凑合着看吧。。不对。。。是 凑合着用吧。。。
Immunity Debugger可以用的资料太少了,但是很喜欢这个Debugger 因为可以用python。。
import struct
import math
import libanalyze
RD_CHAR = "*"
FD_CHAR ="-"
#get max end function
def func_max_end(imm, addr):
fun = libanalyze.Function(imm, addr)
end1 = fun.getFunctionEnd()
if len(end1) == 0:
return 0
end_max = end1[0]
for e in end1:
if e>end_max:
end_max = e
return end_max
#search asmcode in function
#["mov byte ptr ss:[R32+100], R8", 3,"mov byte ptr ss:[R32+101], R8"] R32 R_32 R16 R_16 R8 R_8
#ret []
def search_asmcode_infun(imm, fun_begin_addr, c_list):
end_max = func_max_end(imm, fun_begin_addr)
if end_max==0 :
return []
ret_addr = []
space = -1
begin_addr = fun_begin_addr
imm.log("")
imm.log("start to search asm !", highlight = 1)
imm.log("-----------------------------------------------------")
for c in c_list:
if type(c)== type(1):
space = c
else:
s_ret = search_asmcode(imm, begin_addr, end_max, c, space)
if len(s_ret) !=0:
begin_addr = s_ret[0]
imm.log("get address at 0x%x" % begin_addr)
ret_addr.append(begin_addr)
space = -1
imm.log("-----------------------------------------------------")
return ret_addr
#"lea eax, dword ptr ds:[ecx+edx-FA]" --> "lea eax, dword ptr ds:[ecx+edx-0xFA]"
def change_special(asm_code):
def change_list_chrsp(st, chr_sp):
st_tmp = st
for chr_elt in chr_sp:
l_flt = change_hex(st_tmp, chr_elt)
st_tmp= l_flt
return st_tmp
def change_hex(st, chr_sp):
l = st.split(chr_sp)
xx = []
for ii in l:
try:
xx.append("0x%x" %int(ii, 16))
except (ValueError, TypeError):
xx.append("%s" % ii)
return chr_sp.join(xx)
fit = asm_code.find("[")
sec = asm_code.find("]")
aft = ""
if (fit!=-1) and (sec !=-1) and (sec>fit):
ll = asm_code[fit+1: sec]
aft += change_list_chrsp(ll, "+-*")
asm_code = asm_code.replace(asm_code[fit+1: sec], aft)
asm_code = change_list_chrsp(asm_code, ", ")
if asm_code.startswith("0x"):
asm_code = asm_code[2:]
return asm_code
def search_asmcode(imm, start_addr, end_addr, asmcode, space = -1):
imm.log("asmreg to search: %s space: %d addr: 0x%x" % (asmcode, space, start_addr), highlight = 1)
register_32 = ["EAX", "EBX", "ECX", "EDX", "EBP", "ESI", "EDI", "ESP"]
register_16 = ["AX", "BX", "CX", "DX", "BP", "SI", "DI", "SP"]
register_8 = ["AH", "AL", "BH", "BL", "CL", "CH", "DL", "DH"]
ret_list = []
ret_list.append(asmcode)
ret_list.extend([xx.replace("R32", r) for r in register_32 for xx in ret_list if (xx.find("R32")!= -1)])
ret_list.extend([xx.replace("R_32", r) for r in register_32 for xx in ret_list if (xx.find("R_32")!= -1)])
ret_list.extend([xx.replace("R16", r) for r in register_16 for xx in ret_list if (xx.find("R16")!= -1)])
ret_list.extend([xx.replace("R_16", r) for r in register_16 for xx in ret_list if (xx.find("R_16")!= -1)])
ret_list.extend([xx.replace("R_8", r) for r in register_8 for xx in ret_list if (xx.find("R_8")!= -1)])
ret_list.extend([xx.replace("R8", r) for r in register_8 for xx in ret_list if (xx.find("R8")!= -1)])
ret_list = [r for r in ret_list if ((r.find("R32")==-1) and (r.find("R_32")==-1) and (r.find("R16")==-1) \
and (r.find("R_16")==-1) and (r.find("R8")==-1) and (r.find("R_8")==-1))]
code = ""
for r in ret_list :
code += imm.assemble(change_special(r)) # get code list
cnt = 0
while True:
op = imm.disasm(start_addr)
start_addr = start_addr + op.getSize()
new_asm = change_special(op.getDisasm())
this_code = imm.assemble(new_asm)
if (this_code in code) and (cnt<=space if space>0 else True):
imm.log("hit addr 0x%x asm:%s"%(op.getAddress(), op.getDisasm()), highlight = 1)
return [op.getAddress(), op.getDisasm()]
if start_addr > end_addr:
break
cnt = cnt +1
return []
#list[0x12345678, 0x12345678]
def search_code_Long(imm, begin, end, code_list, min, max):
st = ""
if (min<0) or (min>max):
return []
if type(code_list) != type([]):
return []
for i in range(len(code_list)-1):
st += "%02X%02X%02X%02X"%(code_list[i]&0xff, (code_list[i]>>0x8)&0xff, (code_list[i]>>0x10)&0xff, (code_list[i]>>0x18)&0xff)
st += "%s%02x%02x" % (RD_CHAR, min, max) if max>0 else ""
last_num = code_list[len(code_list)-1]
st += "%02X%02X%02X%02X"%(last_num&0xff, (last_num>>0x8)&0xff, (last_num>>0x10)&0xff, (last_num>>0x18)&0xff)
return search_code_string(imm, begin, end, st)
#list[0x1234, 0x1234]
def search_code_short(imm, begin, end, code_list, min, max):
st = ""
if (min<0) or (min>max):
return []
if type(code_list) != type([]):
return []
for i in range(len(code_list)-1):
st += "%02X%02X"%(code_list[i]&0xff, (code_list[i]>>0x8)&0xff)
st += "%s%02x%02x" % (RD_CHAR, min, max) if max>0 else ""
last_num = code_list[len(code_list)-1]
st += "%02X%02X"%(last_num&0xff, (last_num>>0x8)&0xff)
return search_code_string(imm, begin, end, st)
#list[0x34, 0x34]
def search_code_byte(imm, begin, end, code_list, min, max):
st = ""
if (min<0) or (min>max):
return []
if type(code_list) != type([]):
return []
for i in range(len(code_list)-1):
st += "%02X"%(code_list[i]&0xff)
st += "%s%02x%02x" % (RD_CHAR, min, max) if max>0 else ""
last_num = code_list[len(code_list)-1]
st += "%02X"%(last_num&0xff)
return search_code_string(imm, begin, end, st)
#str "GUOJICHONGYUN" "\xff\xff\xff\xff"
def search_data_str(imm, begin, end, str_search, min, max):
st = ""
if (min<0) or (min>max):
return []
if type(str_search) != type(""):
return []
for i in range(len(str_search)-1):
st += "%02X"%(ord(str_search[i]))
st += "%s%02x%02x"%(RD_CHAR, min, max) if max>0 else ""
st += "%02X"%(ord(str_search[len(str_search)-1]))
return search_code_string(imm, begin, end, st)
#search code string
#ef00*0209EabA*0304eeaa FF00-04AA 53f966*0000E000008ab70183C40899F7*00007C24
def search_code_string(imm, begin, end, code_str):
st1 = code_str.upper()
ret_error = 0
ret = []
imm.log("the code to search:%s"%st1, highlight = 1)
start = 0
x = 0
for aa in st1:
if (aa ==RD_CHAR) or (aa == FD_CHAR):
break
else:
x += 1
y = 0
min_n = []
max_n = []
s1 = []
s2 = []
if x == len(st1):
s1.append(st1)
else:
count_ = 0
while True:
count_ = 0
if st1[x] == FD_CHAR:
min_max_tmp = int(st1[x+1:x+3], 16)
min_n.append(min_max_tmp*2)
max_n.append(min_max_tmp*2)
count_ = 3
else:
min_tmp = int(st1[x+1:x+3], 16)
max_tmp = int(st1[x+3:x+5], 16)
if (min_tmp>max_tmp) or (min_tmp<0):
return []
min_n.append(min_tmp*2)
max_n.append(max_tmp*2)
count_ = 5
if start != x:
s1.append(st1[start:x])
y = 0
for aa in st1[x+count_:]:
if (aa ==RD_CHAR) or (aa == FD_CHAR):
break
else:
y += 1
if y == len(st1[x+count_:]):
s2.append(st1[x+count_:])
break
else:
s2.append(st1[x+count_:y+x+count_])
x = y + x + count_
start = x
#'''
imm.log("min_n %s " % min_n, highlight = 1)
imm.log("max_n %s " % max_n, highlight = 1)
imm.log("s1 %s " % s1, highlight = 1)
imm.log("s2 %s " % s2, highlight = 1) #'''
len1 = len(s1[0])
mun1_str = ""
for i in range(len1/2):
x = ((int(s1[0][2*i])) if s1[0][2*i].isdigit() else (int(s1[0][2*i], 16)))*math.pow(16, 1)
x += ((int(s1[0][2*i+1])) if s1[0][2*i+1].isdigit() else (int(s1[0][2*i+1], 16)))*math.pow(16, 0)
mun1_str += struct.pack("B", x)
first_ = imm.search(mun1_str)
byte_num = 0
for i in max_n:
byte_num += i
for si in s2:
byte_num += len(si)
for fx in first_:
if (fx<begin) or (fx>end):
continue
buf = imm.readMemory(fx, byte_num)
mem_read_str = ''.join(['%02X' % ord(x) for x in buf[0:]])
imm.log("addr 0x%x mem_read_str %s" %(fx, mem_read_str), highlight = 1)
start = len(s1[0])
i = 0
flag = 0
for s2_tmp in s2:
y = mem_read_str[start:].find(s2_tmp)
if y == -1:
flag = 0
break
elif (y>=min_n[i]) and (y<=max_n[i]):
flag += 1
start = start + y + len(s2_tmp)
else:
break
i = i +1
#imm.log("flag %d len1 %d i %d"%(flag, len1, i))
#imm.log("flag %d len1%d"%(flag, len1))
if flag == len(min_n):
ret.append(fx)
return ret
测试代码(以window xp的记事本程序为例):
import immlib
import libanalyze
from libhook import *
import z_search
import struct
imm = immlib.Debugger()
def main(agrs):
#x = z_search.search_code_Long(imm, 0x01012000, 0x01022000, [0x03, 0x0a], 2, 9)
#-->"03000000*02090a000000"
#x = z_search.search_code_string(imm, 0x01012000, 0x01022000, "03000000-080a000000")
#x = z_search.search_code_string(imm, 0x01012000, 0x01022000, "03000000-080a000000*02050a0b")
#搜索是可以分为N段的
#x = z_search.search_code_short(imm, 0x01012000, 0x01022000, [0x58, 0x025c], 02, 02)
#-->"5800*0202025c" == "5800-02025c"
#x = z_search.search_code_byte(imm, 0x01012000, 0x01022000, [0x53, 0x63, 0x69, 0x43], 0,3)
#-->53*000363*000369*000343
#x = z_search.search_data_str(imm, 0x01012000, 0x01022000, "\x53\x63\x69\x43", 0,1)
#-->-->53*000163*000169*000143
#010127E3 |. 8D6C24 10 lea ebp, dword ptr ss:[esp+10] sub esp, eax
#x = z_search.search_asmcode_infun(imm, 0x010127c8, ["lea ebp, dword ptr ss:[esp+0x10]","sub esp,eax"])
#x = z_search.search_asmcode_infun(imm, 0x010127c8, ["lea ebp, dword ptr ss:[esp+0x10]", 6, "mov eax, dword ptr ss:[ebp-0x8]"])
x = z_search.search_asmcode_infun(imm, 0x010127c8, ["lea R32, dword ptr ss:[R_32+0x10]", 6, "mov R32, dword ptr ss:[R_32-0x8]"])
#6表示两条汇编指令间 间隔的指令数少于6
imm.log("hit number:%d"%len(x))
for x_t in x:
imm.log("hit address:0x%08x"%x_t)
return "sccuess"
#!z_search_test
