内容简介:终于是把这个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)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Designing Data-Intensive Applications
Martin Kleppmann / O'Reilly Media / 2017-4-2 / USD 44.99
Data is at the center of many challenges in system design today. Difficult issues need to be figured out, such as scalability, consistency, reliability, efficiency, and maintainability. In addition, w......一起来看看 《Designing Data-Intensive Applications》 这本书的介绍吧!