Sydney

Lockitall LockIT Pro, rev a.02,作为前一个的更新版本,我们有必要浏览一下显示的手册:

DETAILS

​ ...

​ This is Software Revision 02. We have received reports that the ​ prior version of the lock was bypassable without knowing the ​ password. We have fixed this and removed the password from memory.

大概意思是从内存中删除了密码,密码不会在内存中以硬编码的形式存在了。

首先查看main函数,显然,并没有之前create_password函数。main中仍然有put函数打印字符串输出,check_password函数检查密码是否正确,以及INT函数。

4438 <main>
4438:  3150 9cff      add    #0xff9c, sp
443c:  3f40 b444      mov    #0x44b4 "Enter the password to continue.", r15
4440:  b012 6645      call    #0x4566 <puts>
4444:  0f41           mov    sp, r15
4446:  b012 8044      call    #0x4480 <get_password>
444a:  0f41           mov    sp, r15
444c:  b012 8a44      call    #0x448a <check_password>
4450:  0f93           tst    r15
4452:  0520           jnz    #0x445e <main+0x26>
4454:  3f40 d444      mov    #0x44d4 "Invalid password; try again.", r15
4458:  b012 6645      call    #0x4566 <puts>
445c:  093c           jmp    #0x4470 <main+0x38>
445e:  3f40 f144      mov    #0x44f1 "Access Granted!", r15
4462:  b012 6645      call    #0x4566 <puts>
4466:  3012 7f00      push    #0x7f
446a:  b012 0245      call    #0x4502 <INT>
446e:  2153           incd    sp
4470:  0f43           clr    r15
4472:  3150 6400      add    #0x64, sp

根据静态分析,在check_password函数调用后,根据之前的经验,函数返回值存放在r15寄存器。返回后下一条指令”tst r15“,检查r15寄存器也就是的值是否为零。我们查看check_password函数进一步分析。

448a <check_password>
448a:  bf90 2c41 0000 cmp    #0x412c, 0x0(r15)
4490:  0d20           jnz    $+0x1c
4492:  bf90 3c67 0200 cmp    #0x673c, 0x2(r15)
4498:  0920           jnz    $+0x14
449a:  bf90 3c65 0400 cmp    #0x653c, 0x4(r15)
44a0:  0520           jne    #0x44ac <check_password+0x22>
44a2:  1e43           mov    #0x1, r14
44a4:  bf90 6b63 0600 cmp    #0x636b, 0x6(r15)
44aa:  0124           jeq    #0x44ae <check_password+0x24>
44ac:  0e43           clr    r14
44ae:  0f4e           mov    r14, r15
44b0:  3041           ret

可以看到,check_password中一共有4个cmp指令,将源操作数与r15寻址的内存中的内容比较,且目的操作数之后都是以两个字节偏移递增。若是经过4次cmp比较,r15将会被赋值为0x1,也就是能通过密码检查。

需要注意的是这里使用的是cmp,与上一等级的cmp.b相比,少了.b扩展名也叫助记符,所以操作数不再是一字节(byte);cmp虽然省略了.w扩展名,但其相当于cmp[.w],操作数是一个字(word)。在msp430用户指南中解释,如果不使用扩展名,指令是一个字指令

20210204200318
图 1.8.1.2.1 - 20210204200318

此时,我们需要知道r15所寻址的内存地址,我们回到main函数中可以发现,调用check_password函数前,将sp 当前栈指针移动到r15sp的值我们还不知道,那我们开始调试吧。

4438 <main>
...
444a:  0f41           mov    sp, r15
444c:  b012 8a44      call    #0x448a <check_password>
...

使用break 444a命令,在地址0x444a处设置断点,查看sp的值以及sp指向的栈空间的内容。首先会调用请求输入get_password函数,我们输入”test“,进行测试。

输入完毕后,在此c命令运行,执行道地址0x444a后中断,我们可以查看sp的值,栈空间(sp)的内容正是我们输入密码。

20210204200337
图 1.8.1.2.2 - 20210204200337

继续设置断点break check_password,然后c运行,进入check_password分析,我们已经知道r15寻址的内存中内容正是我们输入的密码,所有将cmp的源操作数提取出来,依次是0x412c、0x673c、0x653c、0x636b,这应该就是正确密码了。

448a:  bf90 2c41 0000 cmp    #0x412c, 0x0(r15)
4492:  bf90 3c67 0200 cmp    #0x673c, 0x2(r15)
449a:  bf90 3c65 0400 cmp    #0x653c, 0x4(r15)
44a4:  bf90 6b63 0600 cmp    #0x636b, 0x6(r15)

这里有一个问题,将以上16进制数组合起来:412c673c653c636b,若是直接作为输入肯定是会出错的,因为,我们还忽略了字节序的问题,MSP430的是小端存储(little-endian),所以我们需要将其高字节与低字节进行交换。

关于字节序,大家都不陌生,维基百科中关于字节序中介绍:

字节的排列方式有两个通用规则。例如,一个多位的整数,按照存储地址从低到高排序的字节中,如果该整数的最低有效字节(类似于最低有效位)在最高有效字节的前面,则称小端序;反之则称大端序

解锁

勾选16进制编码输入复选框,以16进制编码输入:2c413c673c656b63。

20210204200416
图 1.8.1.2.3 - 20210204200416

解锁成功!

20210204200425
图 1.8.1.2.4 - 20210204200425

results matching ""

    No results matching ""