内容简介:终于是把这个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: https://www.microchip.com/mplab/avr-support/avr-and-sam-downloads-archive
- hapsim: http://www.helmix.at/hapsim/#hapsimdownload
两者安装好了之后,运行 AVR Studio
,直接open我们的 CustomShell
,然后如下
这里选择 AVR Simulator
平台,关于设备的选择,首先我们的程序是8位的,所以查阅资料之后选择 ATmega128
设备,是ATMEL公司的8位系列单片机的最高配置的一款单片机。
大概这样的界面:
然后打开 hapsim
, File - New Control - Terminal
,打开一个新的终端,随后 Options - Terminal Settings
,勾选 Local Echo
,然后就可以选择串口,尝试了一下之后,发现程序从 USART1
输出,这里可以看出需要输入一个用户名和一个密码:
然后需要定位到登录验证的地方,我们先单步简单跟一下,单步到第一个循环:
利用 Run to Cursor
跳出循环,然后进入到第二个循环:
同理利用 Run to Cursor
跳出循环,进而执行地址 0x61
处的一个跳转指令 RCALL
,该指令跳转到了地址为 0x7E5
处的子函数 sub_7E5
执行,然后依次 sub_729-sub_6EC
可以看到 sub_729
中连续调用两次 sub_6EC
,猜测是用来分别获取用户名和密码的输入,经过调试确实是的,而且其中的两个 sub_89A
则是打印 login
和 password
字符串的,
再往下看:
很自然就会想到这个 sub_920
是一个验证函数,
这里的 ld r24,X+
以及 ld r0,Z+
明显的查表操作,调试跟进一下
即是读取我们的输入,然后和 0x659
地址进行对比,
所以用户名就是 revkr12
,然后密码,跟进之后发现有点复杂,尤其是在 sub_729
中调用了 sub_20C
对输入的密码进行了一大堆操作:
具体 sub_20C
就不截图了。
这样有点麻烦,重新整理一下思路,我们回到最开始的 sub_7E5
函数中调用 sub_729
的地方:
看到调用完了 sub_729
之后就进行了一个判断, r24-1
是否等于0,调试的时候发现 r24
其实等于0(因为密码输错了),所以我们先不管密码怎么处理的,我们直接下断点,然后手动修改 r24=1
然后运行:
登陆成功!
然后简单操作之后发现读出的文件都是乱码,应该是存在某种加密方式,另外在尝试读 tmp
目录下的 readme
的时候报错了:
就这个文件说不存在,那我们跟一下看看到底是怎么回事。
单步跟进之后定位到了一个关键函数 sub_6EC
,它调用了 sub_6F8
,单步跟进之后了解到 sub_6EC
的作用就是获取我们输入的命令。
继续往下看:
之前说了 sub_920
是验证函数,所以这里对我们的输入进行验证,截图这一段函数就是遍历几个命令和我们输入的命令进行对比,
一旦匹配之后,便跳转到 loc_860
,然后在地址 0x871
处进行了一个 icall
间接寻址到 寄存器Z
所指向的地址,以 cat
为例, cat
最后跳转到了 0x1b5
。
然后调到 0x4e5
,在这里从 0x4f9-0x516
进行了 cat
要读取的文件名的对比:
这个时候在内存中才看到猫腻为啥读不了 readme
readme
后面有个空格 \x20
,但是重新尝试之后发现即使输了空格也不行,那就只能修改内存了,把这个 \x20
改成 \x00
,发现可以成功读取 readme
:
但是是乱码,跟进之后知道打印的是地址 0x582
处的字符串, readme
的内容为:
但是一路跟下来没发现加密之类的地方,在ida中看到静态也是同样的值:
然后猛然想起来为啥要研究这个 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码的总和,所以我们爆破分层,先爆破总和,再逐位爆破。
那至于结果的哪一位受输出的哪一位影响,我们可以分析,连个线就行了,我这里举例把方法是说一下就行了:
比如前四位输入, r1-1
代表 round1
的第一轮,根据 round1
的算法, round1
共8轮,第一轮input[1]影响第四位,但是到第二轮之后 input[1]
影响第1位,这样依次分析,发现 round1
的输出就是和输入一一对应,再分析 round2
和 round3
,可以得到最终结果:
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]
然后就可以开始逐位爆破,
得到结果如下:
这里附上全部的爆破代码:
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)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。