gatesXgame

阅读量230553

|

发布时间 : 2021-01-04 16:30:26

 

脱UPX壳

ida一打开发现是个标准的UPX的代码

找到最后一个jmp跳过去即可

后面修IAT啥的就不说了

 

去除花指令

首先进入start 然后找到main函数

这个函数只是简单的判断了下是否是64位系统的环境,不是则直接退出

可以看到通过jmp call ret 这样的指令把我们的IDA F5功能给搞了

往下面看又可以看出一种混淆

使用OD插件来去除混淆
添加模式

去除混淆后保存,这里就不细看了主要是比较了下输入是否是npointer{开头

继续看这个函数申请了一块内存地址然后把参数一里面的东西拷贝过去了并且执行了,这个函数里面还有个check windbg的函数,感兴趣可以看看

 

32切换64

首先断点下载到这里

输入准备的key

代码拷贝到0x4a0000里面了

继续在这里下断点

可以看出ebx是输入的长度 ecx现在要取的位置

这两句执行完就是 push 0x33

call这个地方就是一个混淆了

F7一下,发现用到了esp 所以这个混淆的去除不能全给nop掉,因为call 会psuh

edi就是之前申请的空间地址

继续往后面走就会改变CS为0x33了切64位

为什么0x33会切64位呢?因为保护模式下段选择子可以拆分为

00110 0 11

可以知道RPL=3

TI=0

Index=6

可以去查GDT[6]里面的内容就知道为啥了

切换到64位那么OD就不能调试了,但是可以使用Windbg(x64)的来进行调试

32位和64位的解析是不一样的所以需要打开两份ida看了,还有就是ida其实已经帮我们把混淆识别出来了

不过我还是用OD去除混淆了,然后dump下来用ida观察,先看32位的发现先比较了长度和现在比较到了字符串index,因为刚进来所以第一个字符如果不是0x66也就是f的话函数返回0,也就表示失败了。下面的代码切换到64位的0x49e地址

这段代码可以看出有3条路径和0x30比较的是会返回之前的0地址,和0x34比较的会去到0x558的地址,都不是的话就直接抛异常了????(黑人问号这个题是不是出的有点子问题)

输入如下图的就会抛异常了,所以我们只需要关注每个块的jz指令即可

 

取节点

然后编写了个ida脚本,由于没有考虑到三分支的情况导致出错了,这个脚本也放出来来吧,虽然有问题,主要思路就是判断jz 和mov 指令 ,如果mov 后面跟着的指令不是回去的,或者是走过的块则走下面一个jz

import  idc
import  idautils
import  idaapi
flag_map={}
Is_first=True
eip=0
block_address=0
oldjz_eip=0
while True:
    if GetMnem(eip)=='jz': 
        oldjz_eip=eip
        address=PrevHead(eip)
        c=chr((Byte(address+1))) 
        eip=eip+Byte(eip+1)+2
        if Is_first:
            flag_map[block_address]=c
            Is_first=False
        #print (GetMnem(eip))
        #print("jz:"+hex(eip))    
        while True:
            if GetMnem(eip)=='mov':
                if Byte(eip)==0x36:
                    eip=Dword(eip+5)
                else:
                    eip=Dword(eip+3)
                block_address=eip
                #print("mov:"+hex(block_address))
                if block_address in flag_map.keys():
                    #print ("same")
                    eip=oldjz_eip+1
                    break
                flag_map[block_address]=c
                print c
                break 
            else:
                eip+=1
    else:
        eip+=1

    if eip>=0x30d0:
        break

三分支,最后一个比较是回到上一个块,上面个比较对于脚本来说都是可以走的,所以走错了,因为写的逻辑比较简单

因为上一个块连接下一个块,下一个块又能回到上一个块,并且有的还有三个分支,所以应该是图,

结束的节点是0x30d0,搜索这个块可以先搜索retn这个text,再没搜到就搜索C3

取节点代码已经有人写好了参考链接

from capstone import *
from struct import *
md_64 = Cs(CS_ARCH_X86, CS_MODE_64)
md_32 = Cs(CS_ARCH_X86, CS_MODE_32)
nodes = {}
with open("code", "rb") as f:
    code = f.read()
def dis32(addr):
    global code, md_32
    tmp = code[addr:addr + 10]
    return md_32.disasm(tmp, addr).__next__()
def dis64(addr):
    global code, md_64
    tmp = code[addr:addr + 10]
    return md_64.disasm(tmp, addr).__next__()
def exp32(addr):
    now = dis32(addr)
    assert now.bytes == b'9\xd9'        #39 d9 开头是32位
    addr += now.size
    now = dis32(addr)
    ret_addr = int(now.op_str, 16)
    ret = dis32(ret_addr)
    assert ret.bytes == b'1\xc0'

    addr += now.size                    # mov al, byte ptr [edx + ecx]
    now = dis32(addr)
    assert now.bytes == b'\x8a\x04\n' 
    node = []
    addr += now.size
    while True:
        if addr == ret_addr:
            break
        now = dis32(addr)
        assert now.mnemonic == 'cmp'
        next_ch = chr(int(now.op_str[4:], 16))

        addr += now.size
        now = dis32(addr)
        jz_addr = int(now.op_str, 16)
        next_addr = unpack("<I", code[jz_addr+15:jz_addr+15+4])[0]
        next = [next_addr, next_ch]
        node.append(next)
        while True:
            addr += now.size
            now = dis32(addr)
            if now.mnemonic == 'cmp' or addr == ret_addr:
                break
    return node
def exp64(addr):
    now = dis64(addr)
    assert now.bytes == b'H9\xd9'  # 48 39 d9 开头表示64位代码块
    addr += now.size
    now = dis64(addr)
    ret_addr = int(now.op_str, 16)
    ret = dis64(ret_addr)
    assert ret.bytes == b'H1\xc0'

    addr += now.size                    # mov al, byte ptr [edx + ecx]
    now = dis64(addr)
    assert now.bytes == b'g\x8a\x04\n'
    node = []
    addr += now.size
    while True:
        if addr == ret_addr:
            break
        now = dis64(addr)
        assert now.mnemonic == 'cmp'
        next_ch = chr(int(now.op_str[4:], 16))

        addr += now.size
        now = dis64(addr)

        jz_addr = int(now.op_str, 16)
        next_addr = unpack("<I", code[jz_addr+21:jz_addr+21+4])[0]
        next = [next_addr, next_ch]
        node.append(next)
        while True:
            addr += now.size
            now = dis64(addr)
            if now.mnemonic == 'cmp' or addr == ret_addr:
                break
    return node
def show(addr, node):
    print("0x%04x ->"%addr, end=' ')
    for i in node:
        print('\''+i[1]+'\'', "0x%04x"%i[0], end=', ')
    print()

def get_graph(addr, switch):
    if addr == 0x30d0:
        return
    global nodes
    if switch == 0:
        node = exp32(addr)
    else:
        node = exp64(addr)
    switch ^= 1
    nodes[addr] = node
    show(addr, node)

    for i in node:
        if i[0] not in nodes.keys():
            get_graph(i[0], switch)
get_graph(0, 0)

跑出的结果

0x0000 -> 'f' 0x049e, 
0x049e -> '0' 0x0000, '4' 0x0558, 
0x0558 -> 'f' 0x09cc, '9' 0x049e,
0x09cc -> '2' 0x0558, 'c' 0x098a,
0x098a -> 'e' 0x0e60, '6' 0x09cc,
0x0e60 -> '8' 0x098a, '9' 0x0f1a, 
0x0f1a -> '5' 0x1372, 'f' 0x0e60,
0x1372 -> 'e' 0x0f1a, 'b' 0x192c,
0x192c -> 'c' 0x1372, '6' 0x1838,
0x1838 -> '0' 0x134c, '3' 0x1dd4, '5' 0x192c, 
0x134c -> '7' 0x1838,
0x1dd4 -> '2' 0x1838, 'f' 0x22c0,
0x22c0 -> 'a' 0x1dd4, '5' 0x27ac,
0x27ac -> '2' 0x22c0, '7' 0x2c02, 
0x2c02 -> 'f' 0x27ac, '1' 0x2cbc,
0x2cbc -> '8' 0x2cfe, '9' 0x2c02,
0x2cfe -> '7' 0x2db8, '0' 0x2cbc,
0x2db8 -> 'c' 0x2dfa, 'b' 0x2cfe,
0x2dfa -> '9' 0x2eb4, '8' 0x2db8, 
0x2eb4 -> '4' 0x29b4, 'a' 0x2dfa,
0x29b4 -> '5' 0x2eb4, '2' 0x2a6e,
0x2a6e -> '4' 0x25b4, 'a' 0x29b4,
0x25b4 -> '7' 0x2a6e, 'a' 0x266e,
0x266e -> 'e' 0x210a, '5' 0x25b4,
0x210a -> '8' 0x266e, '0' 0x21c4, 
0x21c4 -> '6' 0x1cbc, 'b' 0x210a,
0x1cbc -> 'e' 0x21c4, 'c' 0x1d76,
0x1d76 -> '8' 0x177e, 'f' 0x2206, 'e' 0x1cbc,
0x177e -> '0' 0x130a, '2' 0x1d76, 
0x130a -> '4' 0x177e, 'd' 0x1250,
0x1250 -> '3' 0x173c, '0' 0x130a, 
0x173c -> '1' 0x1250, '2' 0x1682,
0x1682 -> 'a' 0x120e, '3' 0x173c,
0x120e -> '9' 0x1682, 'e' 0x1154,
0x1154 -> 'b' 0x1640, 'c' 0x120e,
0x1640 -> '4' 0x1154, 'e' 0x1ba2,
0x1ba2 -> 'f' 0x1640, '9' 0x20c8, '2' 0x1c96,
0x20c8 -> '5' 0x1ba2, '1' 0x200e,
0x200e -> '8' 0x2572, 'd' 0x20c8,
0x2572 -> '0' 0x200e, '5' 0x24b8, 
0x24b8 -> 'd' 0x2972, '5' 0x2572,
0x2972 -> 'e' 0x24b8, 'c' 0x28b8,
0x28b8 -> 'e' 0x2476, 'd' 0x2972,
0x2476 -> 'c' 0x1f2e, '7' 0x28b8,
0x1f2e -> '7' 0x2476, 'e' 0x1ed0,
0x1ed0 -> '2' 0x196e, 'd' 0x1f2e, '6' 0x1e16,
0x196e -> '3' 0x142c, '2' 0x1ed0,
0x142c -> '9' 0x196e, 'f' 0x146e,
0x146e -> '1' 0x1016, '7' 0x142c, 
0x1016 -> '9' 0x146e, '5' 0x0f5c,
0x0f5c -> 'c' 0x0a86, 'f' 0x1016,
0x0a86 -> '1' 0x059a, '5' 0x0f5c,
0x059a -> '3' 0x00ae, 'a' 0x0a86,
0x00ae -> 'c' 0x059a, '6' 0x010c, '7' 0x0026, 
0x010c -> 'b' 0x0654, 'e' 0x00ae,
0x0654 -> '9' 0x010c, 'c' 0x0696,
0x0696 -> '7' 0x01c6, '6' 0x0654,
0x01c6 -> '0' 0x0696, 'f' 0x0208,
0x0208 -> 'b' 0x0750, 'c' 0x01c6,
0x0750 -> 'd' 0x0208, 'c' 0x0792,
0x0792 -> '5' 0x02c2, '9' 0x0750,
0x02c2 -> '3' 0x0792, 'c' 0x0304,
0x0304 -> '3' 0x084c, 'd' 0x02c2, 
0x084c -> 'd' 0x0304, '9' 0x088e,
0x088e -> 'e' 0x0d48, '2' 0x084c,
0x0d48 -> 'd' 0x088e, 'b' 0x0da6, '3' 0x0c8e,
0x0da6 -> '0' 0x0948, '5' 0x0d48,
0x0948 -> '2' 0x03e4, 'c' 0x0da6,
0x03e4 -> 'f' 0x0948, '1' 0x03be, 
0x03be -> '5' 0x03e4,
0x0c8e -> '9' 0x0d48, '5' 0x0c4c,
0x0c4c -> 'c' 0x0c8e, '9' 0x0b92,
0x0b92 -> '4' 0x1112, '3' 0x0c4c,
0x1112 -> '9' 0x0b92, 'f' 0x1586,
0x1586 -> '2' 0x1112, '9' 0x1528,
0x1528 -> '0' 0x1058, 'e' 0x1a4e, '5' 0x1586,
0x1058 -> 'b' 0x0b50, 'e' 0x1528,
0x0b50 -> 'b' 0x1058, '8' 0x0ac8,
0x0ac8 -> 'd' 0x0b50,
0x1a4e -> '8' 0x1528, '7' 0x1fe8, '0' 0x1b7c, '1' 0x1a28, 
0x1fe8 -> '3' 0x1a4e,
0x1b7c -> '9' 0x1a4e,
0x1a28 -> '0' 0x1a4e,
0x0026 -> '3' 0x00ae,
0x1e16 -> 'b' 0x237a, 'c' 0x1ed0,
0x237a -> '2' 0x1e16, '5' 0x23bc,
0x23bc -> '9' 0x2876, '6' 0x237a, 
0x2876 -> '2' 0x23bc, 'e' 0x27ee,
0x27ee -> '3' 0x2876,
0x1c96 -> 'a' 0x1ba2,
0x2206 -> 'e' 0x1d76, '1' 0x276a,
0x276a -> '9' 0x2206, 'a' 0x26b0,
0x26b0 -> '8' 0x2b38, 'e' 0x276a,
0x27ee -> '3' 0x2876,
0x1c96 -> 'a' 0x1ba2,
0x2206 -> 'e' 0x1d76, '1' 0x276a,
0x276a -> '9' 0x2206, 'a' 0x26b0,
0x26b0 -> '8' 0x2b38, 'e' 0x276a,
0x2b38 -> '7' 0x26b0, '6' 0x2fdc,
0x2fdc -> 'a' 0x2b38, 'f' 0x30d0, 'c' 0x2f7e,
0x2f7e -> '3' 0x2ab0, '5' 0x2fdc, '9' 0x2ef6,
0x2ab0 -> '5' 0x2f7e,
0x2ef6 -> '8' 0x2f7e,

 

图搜算法

接下来就是图搜索算法了,需要找到一条从0到0x30d0的路径。

def dfs(addr, flag, path):
    if addr == 0x30d0:
        print("found!")
        print("npointer{"+flag+"}")
        return
    node = nodes[addr]
    for i in node:
        if i[0] not in path:
            get_path(i[0], flag+i[1], path+[i[0]])
dfs(0, "", [])

输出结果

found!
npointer{f4fce95b63f57187c9424ae06cf1a86f}

本文由NATIVE原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/226747

安全客 - 有思想的安全新媒体

分享到:微信
+14赞
收藏
NATIVE
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66