Reversing.kr Writeup(27)

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

内容简介:终于是把这个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)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

网络传播学

网络传播学

吴风 / 中国广播电视出版社 / 2004-6-1 / 22.00元

本书把网络传播置于构型与解构的双重语境中,全面而深入地梳理了网络传播的概念、发展背景与现状、传播模式、传播物征、传播学意义,并从文化学、舆论学、政治学、心理学、符号学、法学、伦理学等视角,对网络传播对于国家民族进步、社会文明与个体发展等方面所带来的影响,作了理性审视。最后,作者指出网络传播在目前的新发展中,尚存在着侵犯个人隐私权、网络著作侵权、公共信息安全、网络色情、虚假信息等诸多的问题,对于这些......一起来看看 《网络传播学》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具