2018年强网杯初赛 逆向题目 hide writeup (超详细)

这道题有壳,strings搜索是upx3.91。但是不能用upx -d。只好手动脱壳了。

难点在于本题目有反调试,有些函数不能步过。

首先研究壳。


第一句call必须步入。

Sub_44f1c5必须步入。

 

Sub_44F1C5中,


0x44f214的rep指令把0x400000到0x44f248复制到0x800000位置。

0x44f22c处,rbp是0x84efb5。这句话是start处的第二条指令。这里不必步入。如果步入,

在44EFCB处的call    sub_44F020,再步过就会卡死(不知道为什么,奇怪!如果卡死,按ctrl+c可以只是终止而不退出gdb);步入之后可以使用finish命令运行到函数外,即0x44F22E处pop     rcx。

在0x44f22f retn之后,到达0x84f248:  call   0x84f2ac。

0x84f2ac是一开始没有复制的地址,可以考虑是0x84efb5的代码形成的新代码。此处内存开辟是在系统调用mmap处,刚开辟后此处全为0.

此时可以dump出来0x800000处的内存,见dump2.(我修改了ep,va,filesize)

接着运行到0x84f31c:  call   0x84f782,可以步过。

在0x84f339:  jmp    QWORD PTR [r15],跳转到0x40000c

此时可以dump处0x400000处的内存,见文件bindump.so。这就是脱壳后的文件了,但是不能运行,不清楚原因。可以看到ep是0x400890。

 

0x40000c:     syscall(调用号11,sys_munmap,解除内存映射)

0x40000e:     ret   

上面两处是elf头部的padding中存储的,感叹做的壳之精准。

ret之后到达:

0x400890:     xor    ebp,ebp

 

下一部分,分析主逻辑。

发现Enter the flag出现了两次。

按照程序执行过程只会执行上面一个。



从流程图上可以看到左枝非正常退出,猜测和反调试有关,进入43f380函数,看到调用了ptrace的系统调用,确实是反调试。由于没有脱壳成功,这里没办法修改判断条件,只好用gdb脚本修改eflags寄存器了。

接着分析0x4009AE函数:


这个时候就应该认识到这是错误的分支了,毕竟告诉你是错的flag了。但是我继续分析了,


具体函数还没有写入。根据动态调试,最后比较字符串的函数是42D820.


仔细分析逻辑,需要保证输入地址的64字节和目标地址的64字节相等,而只能输入32字节,根本不能实现。

废了很久时间之后,想到了另一处Enter the flag。


这里并未被ida识别成函数,0x4c8ef4处是我标记的函数,从这里标记可以使函数能使用f5.由于原程序并不会执行到此处,需要gdb脚本中修改pc值。

下面是可以快速进入关键位置的gdb脚本。

#!/bin/bash
file hide
b *0x44f22f
r
si
si
d
b *0x84f339  
#jmp    QWORD PTR [r15];0x40000c
c
d
si
si
si
b* 0x4009EF
c
set $rip=0x4C8EBC
b *0x4c8f11
c
set $eflags = $eflags |(1<<6)
5
b *0x4C8D09
c
c
d
b *0x4C8DFB
c

下面是本题目的exp

#coding=utf-8
import struct
import string
def u32(data):
    return struct.unpack("<I",data)[0]

def p32(data):
    return struct.pack("<I",data)

def u64(data):
    return struct.unpack("<Q",data)[0]

def p64(data):
    return struct.pack("<Q",data)

input1 = '1234567890123456'
input1 = bytearray(input1)
CONST_STR = 's1IpP3rEv3Ryd4Y3'
CONST = 0X676E696C
v4 = 0
v4_4=0
v4_4_arr = [0 for i in range(0,9)]
for i in range(1,9):
    v4_4_arr[i] = (v4_4_arr[i-1]+CONST)&0XFFFFFFFF

def re_block(byte_arr_8):
    i = 0
    v3 = u32(byte_arr_8[0:4])
    v4 = u32(byte_arr_8[4:8])
    print 'round', v3, v4
    for i in range(7,-1,-1):
        v30 = (v3 << 4) & 0xffffffff
        v2c = v3 >> 5
        edx = v30 ^ v2c
        v30 = (v3 + edx) & 0xffffffff

        v28 = (v4_4_arr[i+1] >> 11) & 3
        edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
        v2c = (v4_4_arr[i+1] + edx) & 0xffffffff  # xxxx
        # print 'v2c',hex(v2c)
        print v30 ^ v2c
        v4 = (v4+0x100000000-(v30 ^ v2c)) & 0xffffffff

        v30 = (v4 << 4) & 0xffffffff
        v2c = v4 >> 5
        edx = v30 ^ v2c

        v30 = (v4 + edx) & 0xffffffff

        v28 = v4_4_arr[i] & 3
        edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
        v2c = (v4_4_arr[i] + edx) & 0xffffffff

        v3 = (v3+0x100000000-(v30 ^ v2c)) & 0xffffffff

        print 'round',i,v3,v4
    byte_arr_8[0:4] = p32(v3)
    byte_arr_8[4:8] = p32(v4)
    return byte_arr_8
def xor16(byte_arr_16):
    for i in range(0,16):
        byte_arr_16[i]^=i
def re_all(str16):
    byte_arr = bytearray(str16)
    xor16(byte_arr)#传入整个bytearray,就是传入地址
    byte_arr[0:8] = re_block(byte_arr[0:8])#传入部分bytearray,就是复制之后再传入
    byte_arr[8:16] = re_block(byte_arr[8:16])
    xor16(byte_arr)
    byte_arr[0:8] = re_block(byte_arr[0:8])
    byte_arr[8:16] = re_block(byte_arr[8:16])
    xor16(byte_arr)
    byte_arr[0:8] = re_block(byte_arr[0:8])
    byte_arr[8:16] = re_block(byte_arr[8:16])
    return str(byte_arr)


def block(str8):
    i=0
    input1=bytearray(str8)
    v3 = u32(input1[8 * i:8 * i + 4])
    v4 = u32(input1[8 * i + 4:8 * (i + 1)])
    v4_4 = 0  ##0000
    for j in range(0, 8):

        v30 = (v4 << 4) & 0xffffffff
        v2c = v4 >> 5
        edx = v30 ^ v2c

        v30 = (v4 + edx) & 0xffffffff

        v28 = v4_4 & 3
        edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
        v2c = (v4_4 + edx) & 0xffffffff

        v3 = ((v30 ^ v2c) + v3) & 0xffffffff


        v4_4 = (v4_4 + CONST) & 0xffffffff

        v30 = (v3 << 4) & 0xffffffff
        v2c = v3 >> 5
        edx = v30 ^ v2c
        v30 = (v3 + edx) & 0xffffffff

        v28 = (v4_4 >> 11) & 3
        edx = u32(CONST_STR[v28 * 4:(v28 + 1) * 4])
        v2c = (v4_4 + edx) & 0xffffffff  # xxxx
        print v30 ^ v2c
        v4 = ((v30 ^ v2c) + v4) & 0xffffffff

        print 'round', j, v3, v4

    input1[8 * i:8 * i + 4] = p32(v3)
    input1[8 * i + 4:8 * (i + 1)] = p32(v4)
    str8_1=str(input1)
    return str8_1
def block2(str8):
    input1 = bytearray(str8)
    for i in range(0,8):
        input1[i]=input1[1]^i
    return str(input1)
des = ('52B8137F358CF21B'+'F46386D2734F1E31').decode('hex')
print len(des)

print block('12345678').encode('hex')
des1 = '5b90ef3f91b58fe6'.decode('hex')
print re_all(bytearray(des))


  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值