在 IDA Python 中自动执行解密函数调用

逆向工程 艾达 蟒蛇
2021-06-19 02:33:21

我试图找出一种方法来自动解密由函数多次调用的二进制文件中的某些字符串。

该函数接受三个参数,是一个简单的异或解密。但是,它为要解密的每个唯一字符串使用不同的密钥。

char* decrypt(char* string_to_decrypt, uint string_len, char xor_byte)

我试图做的是获取这个函数的外部参照列表(这是我得到的)并读入它想要在二进制文件中解密的当前字符串,并获取 xor 密钥和长度,然后修补binary 显示明文字符串。

该函数是这样调用的

.text:0040168A push    83h
.text:0040168F push    9
.text:00401691 mov     eax, esi
.text:00401693 mov     edx, offset unk_406D58
.text:00401698 call    decrypt_string

其中EDX,始终持有加密字符串的地址,两次push是key和length(9个长度,0x83个key)

mov eax, esi 并不总是存在。有没有办法读取反汇编并获取此函数,然后动态解密数据库中的所有字符串?

为了完整起见,这就是我开始的地方。

import idaapi
import idc

ea = here()
print hex(ea)

xrefs = CodeRefsTo(ea,0)
    for xref in xrefs:
    print "Ref Addr: {}".format(hex(xref))

# for all xrefs, get string offset, length and key,
# decrypt that string, and patch or rename it to display decrypted string)

谢谢你。

编辑:ws 响应让我朝着正确的方向前进。这是我想出的。这很粗糙,我知道:)

import idaapi
import idc
import idautils

ea = here()
xrefs = CodeRefsTo(ea,0)

data = []
decrypted = []

for xref in xrefs:
    current_x = xref
    d = {}
    d['addr'] = hex(current_x)
    n_pushes = 0
    n_movedx = 0

    for i in xrange(6):
        if n_pushes == 2 and n_movedx == 1:
            break

        current_x = idc.PrevHead(current_x)
        instr = idautils.DecodeInstruction(current_x)
        if instr.itype == idaapi.NN_push:

            if n_pushes < 1:
                d['len'] = int(GetOperandValue(current_x, 0))

            if n_pushes == 1:
                d['key'] = int( hex(GetOperandValue(current_x, 0)), 16)

            n_pushes += 1

        if instr.itype == idaapi.NN_mov:
            if GetOpnd(current_x, 0) == 'edx':
                d['string_offset'] = GetOperandValue(current_x, 1)

    data.append(d)
......
2个回答

当我做类似的事情时,我曾经idc.PrevHead(ea)从调用站点迭代回来,所以它类似于:

# Not tested, even not runned even once
# expect errors, use on your own risk 
# probably parsing and analysing disassembly is not best way to do it

import idc
import idautils


ea = idc.ScreenEA()

xrefs = CodeRefsTo(ea,0)
for xref in xrefs:
    current = xref
    numOfPushes = 0
    numOfEdx = 0
    edxes = []
    pushes = []
    for i in range(10): # taking 10 instructions back
        if numOfEdx == 1 and numOfPushes == 2:
            break
        current = idc.PrevHead(current)
        s = idc.GetDisasm(current)
        if s.find("mov edx") != -1: #copy-paste exact string here, tabulation expected
            edxes.append(s)
            numOfEdx += 1
        if s.find("push")!= -1: 
            pushes.append(s)
            numOfPushes += 1
    if len(edxes) > 0:
        print "%08x : EDX instruction " % xref, edxes[0]
    else:
        print "%08x : EDX mov not found" % xref
    if len(pushes) >= 2:
        print "%08x : Push[0] " % xref, pushes[0]
        print "%08x : Push[1] " % xref, pushes[1]
    else:
        print "%08x : can not find appropriate push instructions" %  xref

当你有你的指示时,其余的都应该是显而易见的。祝你好运。

您可以使用 Sark 来完成大部分工作。它应该与此类似:

import sark

def get_edx_values_at_calls_to(function):
    for xref in function.xrefs_to:
        if not xref.type.is_call:
            continue

        # start at the function call (`xref.frm`) and go back to the beginning of
        # the basic-block (`sark.CodeBlock(xref.frm).startEA`).
        for line in sark.lines(start=sark.CodeBlock(xref.frm).startEA,
                               end=xref.frm, 
                               reverse=True):
            if line.insn.mnem == 'mov' and line.insn.operands[0].reg == 'edx':
                yield line.insn.operands[1].value

decrypt_function = sark.Function()
for value in get_edx_values_at_calls_to(decrypt_function):
    pass  # decryption code