Reversing.kr Writeup(27)

栏目: Ruby · 发布时间: 4年前

内容简介:终于是把这个reversing.kr做完了。。。。AVR架构的单片机程序,去除了符号表在做题之前先记录一下环境搭建的过程:

终于是把这个reversing.kr做完了。。。。

➜  file CustomShell 
CustomShell: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, stripped

AVR架构的单片机程序,去除了符号表

在做题之前先记录一下环境搭建的过程:

两者安装好了之后,运行 AVR Studio ,直接open我们的 CustomShell ,然后如下

Reversing.kr Writeup(27)

这里选择 AVR Simulator 平台,关于设备的选择,首先我们的程序是8位的,所以查阅资料之后选择 ATmega128 设备,是ATMEL公司的8位系列单片机的最高配置的一款单片机。

大概这样的界面:

Reversing.kr Writeup(27)

然后打开 hapsimFile - New Control - Terminal ,打开一个新的终端,随后 Options - Terminal Settings ,勾选 Local Echo ,然后就可以选择串口,尝试了一下之后,发现程序从 USART1 输出,这里可以看出需要输入一个用户名和一个密码:

Reversing.kr Writeup(27)

然后需要定位到登录验证的地方,我们先单步简单跟一下,单步到第一个循环:

Reversing.kr Writeup(27)

利用 Run to Cursor 跳出循环,然后进入到第二个循环:

Reversing.kr Writeup(27)

同理利用 Run to Cursor 跳出循环,进而执行地址 0x61 处的一个跳转指令 RCALL ,该指令跳转到了地址为 0x7E5 处的子函数 sub_7E5 执行,然后依次 sub_729-sub_6EC

Reversing.kr Writeup(27)

可以看到 sub_729 中连续调用两次 sub_6EC ,猜测是用来分别获取用户名和密码的输入,经过调试确实是的,而且其中的两个 sub_89A 则是打印 loginpassword 字符串的,

再往下看:

Reversing.kr Writeup(27)

很自然就会想到这个 sub_920 是一个验证函数,

Reversing.kr Writeup(27)

这里的 ld r24,X+ 以及 ld r0,Z+ 明显的查表操作,调试跟进一下

Reversing.kr Writeup(27)

即是读取我们的输入,然后和 0x659 地址进行对比,

Reversing.kr Writeup(27)

所以用户名就是 revkr12 ,然后密码,跟进之后发现有点复杂,尤其是在 sub_729 中调用了 sub_20C 对输入的密码进行了一大堆操作:

Reversing.kr Writeup(27)

具体 sub_20C 就不截图了。

这样有点麻烦,重新整理一下思路,我们回到最开始的 sub_7E5 函数中调用 sub_729 的地方:

Reversing.kr Writeup(27)

看到调用完了 sub_729 之后就进行了一个判断, r24-1 是否等于0,调试的时候发现 r24 其实等于0(因为密码输错了),所以我们先不管密码怎么处理的,我们直接下断点,然后手动修改 r24=1

Reversing.kr Writeup(27)

然后运行:

Reversing.kr Writeup(27)

登陆成功!

然后简单操作之后发现读出的文件都是乱码,应该是存在某种加密方式,另外在尝试读 tmp 目录下的 readme 的时候报错了:

Reversing.kr Writeup(27)

就这个文件说不存在,那我们跟一下看看到底是怎么回事。

单步跟进之后定位到了一个关键函数 sub_6EC ,它调用了 sub_6F8 ,单步跟进之后了解到 sub_6EC 的作用就是获取我们输入的命令。

继续往下看:

Reversing.kr Writeup(27)

之前说了 sub_920 是验证函数,所以这里对我们的输入进行验证,截图这一段函数就是遍历几个命令和我们输入的命令进行对比,

Reversing.kr Writeup(27)

一旦匹配之后,便跳转到 loc_860 ,然后在地址 0x871 处进行了一个 icall 间接寻址到 寄存器Z 所指向的地址,以 cat 为例, cat 最后跳转到了 0x1b5

Reversing.kr Writeup(27)

然后调到 0x4e5 ,在这里从 0x4f9-0x516 进行了 cat 要读取的文件名的对比:

Reversing.kr Writeup(27)

这个时候在内存中才看到猫腻为啥读不了 readme

Reversing.kr Writeup(27)

readme 后面有个空格 \x20 ,但是重新尝试之后发现即使输了空格也不行,那就只能修改内存了,把这个 \x20 改成 \x00 ,发现可以成功读取 readme

Reversing.kr Writeup(27)

但是是乱码,跟进之后知道打印的是地址 0x582 处的字符串, readme 的内容为:

Reversing.kr Writeup(27)

但是一路跟下来没发现加密之类的地方,在ida中看到静态也是同样的值:

Reversing.kr Writeup(27)

然后猛然想起来为啥要研究这个 readme 为啥读不了…..再回到shell,又研究了一番发现其中没有什么有用的信息,所以再整理一下,应该还是需要破解密码。

然后焦点又回到了最开始跳过的 sub_20c ….

这个函数分成三个部分,关键地址: 0xAD2-0XADB ,10位由8位的用户输入生成,也是变换之后的最终值存储的地方,

第一部分 0x23B-0X302 ,循环8次,将8位的输入变成10位值,具体算法变成 python 如下:

def round1(i):
    #print([hex(i) for i in data_ad2])
    global r4
    r12=i<<2
    r12=r12&0xff
    #if i==1:import ipdb;ipdb.set_trace()
    last_round_ad2=data_ad2.copy()
    data_ad2[0]=data_100[0+r4]^last_round_ad2[2]
    r24=data_ae4[i]&0x05

    data_ad2[1]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[4]    
    r24=data_ae4[i]&0x0a
    r24=lsr(r24)

    data_ad2[2]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[7]
    r24=data_ae4[i]&0x50
    r24=swap(r24)

    data_ad2[3]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[3]
    r24=data_ae4[i]&0xa0
    r24=swap(r24)
    r24=lsr(r24)

    data_ad2[4]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[1]
    r24=data_ae4[i]&0x05

    data_ad2[5]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[5]
    r24=data_ae4[i]&0x0a
    r24=lsr(r24)

    data_ad2[6]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[6]
    r24=data_ae4[i]&0x50
    r24=swap(r24)

    data_ad2[7]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[2]
    r24=data_ae4[i]&0xa0
    r24=swap(r24)
    r24=lsr(r24)

    data_ad2[8]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[8]
    r24=data_ae4[i]&0x05

    data_ad2[9]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[3]
    r4+=4

这部分代码比较简单很容易看懂,看懂之后直接回复就行。

第二大部分, 0X304-0X3D2 ,这部分和上一部分类似甚至比上一部分简单,代码如下:

def round2(i):
    #print([hex(i) for i in data_ad2])
    r12=i<<2
    r12=r12&0xff
    #if i==0:import ipdb;ipdb.set_trace()

    last_round_ad2=data_ad2.copy()

    r24=data_ae4[i]&0x42

    data_ad2[0]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[0]
    r24=data_ae4[i]&0x81

    data_ad2[1]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[4]
    r24=data_ae4[i]&0x42

    data_ad2[2]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[1]
    r24=data_ae4[i]&0x24

    data_ad2[3]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[8]
    r24=data_ae4[i]&0x18

    data_ad2[4]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[2]
    r24=data_ae4[i]&0x81

    data_ad2[5]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[5]
    r24=data_ae4[i]&0x42

    data_ad2[6]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[6]
    r24=data_ae4[i]&0x24

    data_ad2[7]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[7]
    r24=data_ae4[i]&0x18

    data_ad2[8]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[3]
    r24=data_ae4[i]&0x81

    data_ad2[9]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[4]

最后一部分 0X3DD-0X3F6 ,如下:

def round3():
    data_ad2[1] = ror(data_ad2[1],8-getrorindex(all,8,(all & 0xff00) >> 8 )[1])
    data_ad2[2] = ror(data_ad2[2],8-getrorindex(all,7,(all & 0xff00) >> 8 )[1])
    data_ad2[3] = ror(data_ad2[3],8-getrorindex(all,6,(all & 0xff00) >> 8 )[1])
    data_ad2[4] = ror(data_ad2[4],8-getrorindex(all,5,(all & 0xff00) >> 8 )[1])
    data_ad2[5] = ror(data_ad2[5],8-getrorindex(all,4,(all & 0xff00) >> 8 )[1])
    data_ad2[6] = ror(data_ad2[6],8-getrorindex(all,3,(all & 0xff00) >> 8 )[1])
    data_ad2[7] = ror(data_ad2[7],8-getrorindex(all,2,(all & 0xff00) >> 8 )[1])

那一个完整的加密流程如下:

data_ad2=[0]+input+[0] #输入

for i in range(8):
    round1(i)

data_ad2[0]=all&0xff

for i in range(8):
    round2(i)

data_ad2[9]=(all&0xff00)>>8

round3()

正向恢复完了就得考虑逆向或者爆破解决的问题了。

那这里其实分析完算法之后很容易知道每一位是独立计算的,每一位结果只受一位输入的影响,所以可以逐位爆破,但逐位爆破又需要考虑输入ascii码的总和,所以我们爆破分层,先爆破总和,再逐位爆破。

那至于结果的哪一位受输出的哪一位影响,我们可以分析,连个线就行了,我这里举例把方法是说一下就行了:

Reversing.kr Writeup(27)

比如前四位输入, r1-1 代表 round1 的第一轮,根据 round1 的算法, round1 共8轮,第一轮input[1]影响第四位,但是到第二轮之后 input[1] 影响第1位,这样依次分析,发现 round1 的输出就是和输入一一对应,再分析 round2round3 ,可以得到最终结果:

input[1] <- result[4]
input[2] <- result[1]
input[3] <- result[3]
input[4] <- result[2]
input[5] <- result[5]
input[6] <- result[6]
input[7] <- result[7]
input[8] <- result[8]

然后就可以开始逐位爆破,

得到结果如下:

Reversing.kr Writeup(27)

这里附上全部的爆破代码:

data_624=[0x01,0x00,0x00,0x02,0x03]
data_ae4=[0x4a,0x18,0xaf,0xf7,0x81,0x6a,0xd7,0x3a]
data_100=[0x4A,0x16,0x71,0x2C,0x11,0xBB,0xAF,0x1E,0xB8,0x9F,0x68,0xD3,0x37,0xCD,0x55,0x1B,0xB7,0xA8,0x02,0xBD,0x0B,0xFF,0xEE,0x8E,0x30,0xC9,0xD7,0x12,0xE8,0x60,0x0A,0x4B,0x01,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x58,0x05,0x15,0x00,0x66,0x05,0x16,0x00,0x73,0x05,0x1F,0x00,0x82,0x05,0x28,0x00,0x10,0x06,0x29,0x00,0x10,0x06,0x2A,0x00,0x10,0x06,0x33,0x00,0x91,0x05,0x08,0x00,0x01,0x00,0x01,0x05,0x00,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0A,0x01,0x01,0x05,0x00,0x00,0x00,0x00,0x65,0x74,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x01,0x01,0x05,0x00,0x00,0x00,0x00,0x74,0x6D,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x01,0x01,0x05,0x00,0x00,0x00,0x00,0x62,0x69,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0x01,0x01,0x05,0x00,0x00,0x00,0x00,0x76,0x61,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
r4=0
def go(data_ad2,all,guess,index):

    global r4
    r4=0
    def swap(val):
        hexray="{:0>2}".format(hex(val)[2:])
        tmp1=hexray[0]
        tmp2=hexray[1]
        return int("0x"+tmp2+tmp1,16)

    def lsr(val):
        return val>>1

    def sub_1DC(val):
        if val<=0:
            return 0
        if val-1<5:
            return data_624[val-1]
        else:
            return 0

    def sub_1EC(val):
        if val==0x18: 
            return 3
        elif val<0x18:
            if val==4:#4
                return 1
            elif val<4:
                if 1<=val<=2:#12
                    return 1
                else:#03
                    return 0
            elif val>4:
                if val==8:
                    return 1
                else:
                    if val==0x10:
                        return 2
                    else:
                        return 0
        elif val>0x18:
            if val==0x40:
                return 3
            elif val<0x40:
                if val==0x20:
                    return 2
                elif val==0x24:
                    return 3
                else:
                    return 0
            elif val>0x40:
                if val==0x80:
                    return 2
                elif val==0x81:
                    return 3
                elif val==0x42:
                    return 3
                else:
                    return 0

    def round1(i):
        #print([hex(i) for i in data_ad2])
        global r4
        r12=i<<2
        r12=r12&0xff
        #if i==1:import ipdb;ipdb.set_trace()
        last_round_ad2=data_ad2.copy()
        data_ad2[0]=data_100[0+r4]^last_round_ad2[2]
        r24=data_ae4[i]&0x05

        data_ad2[1]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[4]    
        r24=data_ae4[i]&0x0a
        r24=lsr(r24)

        data_ad2[2]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[7]
        r24=data_ae4[i]&0x50
        r24=swap(r24)

        data_ad2[3]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[3]
        r24=data_ae4[i]&0xa0
        r24=swap(r24)
        r24=lsr(r24)

        data_ad2[4]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[1]
        r24=data_ae4[i]&0x05

        data_ad2[5]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[5]
        r24=data_ae4[i]&0x0a
        r24=lsr(r24)

        data_ad2[6]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[6]
        r24=data_ae4[i]&0x50
        r24=swap(r24)

        data_ad2[7]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[2]
        r24=data_ae4[i]&0xa0
        r24=swap(r24)
        r24=lsr(r24)

        data_ad2[8]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[8]
        r24=data_ae4[i]&0x05

        data_ad2[9]=data_100[0+r12+sub_1DC(r24)]^last_round_ad2[3]
        r4+=4



    def round2(i):
        #print([hex(i) for i in data_ad2])
        r12=i<<2
        r12=r12&0xff
        #if i==0:import ipdb;ipdb.set_trace()

        last_round_ad2=data_ad2.copy()

        r24=data_ae4[i]&0x42

        data_ad2[0]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[0]
        r24=data_ae4[i]&0x81

        data_ad2[1]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[4]
        r24=data_ae4[i]&0x42

        data_ad2[2]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[1]
        r24=data_ae4[i]&0x24

        data_ad2[3]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[8]
        r24=data_ae4[i]&0x18

        data_ad2[4]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[2]
        r24=data_ae4[i]&0x81

        data_ad2[5]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[5]
        r24=data_ae4[i]&0x42

        data_ad2[6]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[6]
        r24=data_ae4[i]&0x24

        data_ad2[7]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[7]
        r24=data_ae4[i]&0x18

        data_ad2[8]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[3]
        r24=data_ae4[i]&0x81

        data_ad2[9]=data_100[0+r12+sub_1EC(r24)]^last_round_ad2[4]

    def ror(a,b):
        return ((a>>b) ^ (a<<(8-b)))&0xff

    def getrorindex(r24,r22,r9):
        carry = 0
        r25 = r9
        r26 = 0
        r23 = 0
        r27 = 0
        i = 0x11
        while(1):
            r24 = (r24 << 1) + carry
            carry = (r24 & 0x100) >> 8
            r24 &= 0xff

            r25 = (r25 << 1) + carry
            carry = (r25 & 0x100) >> 8
            r25 &= 0xff
            i -= 1
            if i == 0:
                break
            r26 = (r26 << 1) + carry
            carry = (r26 & 0x100) >> 8
            r26 &= 0xff

            r27 = (r27 << 1) + carry
            carry = (r27 & 0x100) >> 8
            r27 &= 0xff

            tmp = r26 - r22
            carry = (tmp & 0x100) >> 8
            tmp &= 0xff

            tmp = r27 - r23 - carry
            carry = (tmp & 0x100) >> 8
            tmp &= 0xff

            if carry == 0:
                r26 = (r26 - r22) & 0xff
        r24 = (0xff-r24)&0xff
        r25 = (0xff-r25)&0xff
        return (r24,r26)

    def round3():
        data_ad2[1] = ror(data_ad2[1],8-getrorindex(all,8,(all & 0xff00) >> 8 )[1])
        data_ad2[2] = ror(data_ad2[2],8-getrorindex(all,7,(all & 0xff00) >> 8 )[1])
        data_ad2[3] = ror(data_ad2[3],8-getrorindex(all,6,(all & 0xff00) >> 8 )[1])
        data_ad2[4] = ror(data_ad2[4],8-getrorindex(all,5,(all & 0xff00) >> 8 )[1])
        data_ad2[5] = ror(data_ad2[5],8-getrorindex(all,4,(all & 0xff00) >> 8 )[1])
        data_ad2[6] = ror(data_ad2[6],8-getrorindex(all,3,(all & 0xff00) >> 8 )[1])
        data_ad2[7] = ror(data_ad2[7],8-getrorindex(all,2,(all & 0xff00) >> 8 )[1])


    for i in range(8):
        round1(i)

    data_ad2[0]=all&0xff

    for i in range(8):
        round2(i)

    data_ad2[9]=(all&0xff00)>>8

    round3()

    print([hex(i) for i in data_ad2])
    if data_ad2[index]==guess:
        return True
    else:
        return False

all_ans=[]
for all in range(0x200,0x300):
    ans=[]
    #result=[0x9a,0x7d,0x72,0x57,0xd5,0x78,0x49,0xe6,0xf2,0x02]

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0xd5,4):
            ans.append(i)
            break
    if len(ans)!=1: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0x7d,1):
            ans.append(i)
            break
        else: continue
    if len(ans)!=2: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0x57,3):
            ans.append(i)
            break
        else: continue
    if len(ans)!=3: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0x72,2):
            ans.append(i)
            break
        else: continue
    if len(ans)!=4: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0x78,5):
            ans.append(i)
            break
        else: continue
    if len(ans)!=5: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0x49,6):
            ans.append(i)
            break
        else: continue
    if len(ans)!=6: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0xe6,7):
            ans.append(i)
            break
        else: continue
    if len(ans)!=7: continue

    for i in range(0x7f):
        data_ad2=[0]+[i]*8+[0]
        if go(data_ad2,all,0xf2,8):
            ans.append(i)
            break
        else: continue
    if len(ans)!=8: continue
    tmp=0
    for i in ans:
        tmp+=i
    if tmp != all:
        continue
    print('=============')
    all_ans.append("".join(chr(i) for i in ans))

print(all_ans)

但是提交发现这并不是flag。。有点迷茫,结果登录进去之后发现文件都可以读了不是乱码了,直接读 /tmp/readme 就拿到flag:

Reversing.kr Writeup(27)

具体读的方式开始已经说了需要下断点修改一下内存,这里不赘述了,具体的怎么加密的我就没有进行分析了。。。到此为止。


以上所述就是小编给大家介绍的《Reversing.kr Writeup(27)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网络英雄传

网络英雄传

郭羽、刘波 / 江苏凤凰文艺出版社 / 2018-6 / 59.80元

“商战鬼才郭羽、营销奇才刘波强强联手,凝集十年实战经验,倾力打造商战巨作。” 这是一个商业竞争和资本激战交织的惊心动魄的创业交锋故事。 由郭天宇、刘帅共同创立的在线旅游公司万全天盛凭借其出色的商业模式异军突起,与老牌巨头“51旅游网”两强相争,但国际巨头通远来势汹汹,国内在线旅游市场进入战火纷飞的“三国杀”时代,分踞杭、沪、京三地互相“搏杀”。中国新兴的互联网公司面对国际巨头的入侵,毫不退缩......一起来看看 《网络英雄传》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具