190321 逆向-花指令去除(脚本)

Pizza的脚本中是通过get_bytes和patch_bytes两个API来将所有机器码读出然后再Patch回去的
中间匹配pattern的过程是构造字符串然后find来控制
190321 逆向-花指令去除(脚本)_第1张图片

这样操作的优点是find的效率奇高,对于大量数据的处理非常的快
而我们之前使用的逐字符匹配pattern的优点是可控性比较强,例如通配符的存在
然而缺点就是速度很慢,对于数量级较大时格外明显,因此试图修复

刚开始以为速度在于多次取Byte,改成python同样用get_bytes取出后再匹配,发现仍然很慢
于是想到了通过find来快速找到pattern开头非?的部分,然后再用原来的matchBytes匹配,发现能得到一定的效率提升,但还是有点问题

from ida_bytes import get_bytes, patch_bytes
addr = 0x401000
end = 0x4b9cd0
buf = "".join(["%02x"%ord(i) for i in get_bytes(addr,end-addr)]).lower()
def patch_at(p, ln):
    global buf
    buf = buf[:p] + "90" * ln + buf[p+ln:]


def matchBytes(s, Condition):
    l = len(Condition)
    for i in range(len(Condition)):
        if(Condition[i]!="?" and s[i]!=Condition[i]):
            return False
    return True

Condition = "E801000000??83C404".lower()
l = len(Condition)/2
# for i in range(end-addr):
i = buf.find(Condition[:Condition.find("?")])
tmp = -1
while(i!=tmp):
    i = tmp
    if(matchBytes(buf[i:], Condition)):
        patch_at(i, l)
        print(i, l)
    tmp = i+1+buf[i+1:].find(Condition[:Condition.find("?")])
    
# print(buf)
buf = "".join([chr(int(buf[2*i]+buf[2*i+1], 16)) for i in range(len(buf)//2) ])
patch_bytes(addr,buf)
print("Done")

然后优化着优化着突然灵光一闪才想起来正则表达式
自己实现匹配pattern的都是傻哔-!

from ida_bytes import get_bytes, patch_bytes
import re
addr = 0x401000
end = 0x4b9cd0
# buf = "".join(["%02x"%ord(i) for i in get_bytes(addr,end-addr)])
buf = get_bytes(addr, end-addr)
def handler1(s):
    s = s.group(0)
    print("".join(["%02x"%ord(i) for i in s]))
    s = "\x90"*len(s)
    return s

def handler2(s):
    opcode1 = ord(s.group(1))
    opcode2 = ord(s.group(2))
    if(opcode1|1 == opcode2|1):
        return "\x90"*5
    else:
        return s.group(0)


pattern1 = r"\xE8[\x01-\x03]\x00\x00\x00((?!\xE8).){1,5}(\x83\xC4\x04|\x58|\x83\x04\x24\x06\xc3)"
pattern2 = r"([\x70-\x7f])\x03([\x70-\x7f])\x01."
buf = re.sub(pattern1, handler1, buf, flags=re.I)
# buf = "".join([chr(int(buf[2*i]+buf[2*i+1], 16)) for i in range(len(buf)//2) ])
patch_bytes(addr, buf)
print("Done")

又简单又快(╯‵□′)╯︵┻━┻现成的sub函数提供了替换功能,handler提供了截取通配符并有选择地修改替换内容的功能,简直量身定做啊这个

pattern里有一个((?!E8).)的部分,是(.*?)的对.的替换,要求中间部分不存在E8,否则可能会导致前一个E8衔接到后一个E8的83C404上,造成大范围的匹配

做完上述的以后突然想起来可以通过{1,5}来限定通配符.的匹配次数,进而控制大范围匹配的orz
另外还有\xE8之后的第二个字符用[\x01-\x03]做了限定

一共三处防止多余匹配的措施,对于其他题目可能会导致漏解,届时请自行修正~

另外还有(\x83\xc4\x01 | \x58 | \x83\x04\x24\x06\xc3)的多选结构,意味着以\x83\xc4\x01(add esp,4)或\x58(pop eax)或\x83\x04\x24\x06\xc3(add [esp], 6 ; retn)结尾的三种情况都会被匹配到并全部清除

这里可以想到的一种漏解可能情况是add [esp], 7; retn ; bad byte ; correct bytes;这种形式,因为它没有确定的结束字符所以相对麻烦一点
感觉可以通过多写几个通配符做冗余匹配,然后在handler中做判断

你可能感兴趣的:(CTF)