The Hardware Software Interface Lab 2
课程主页:https://courses.cs.washington.edu/courses/cse351/16sp/
课程资料:
实验部分:https://github.com/vuquangtrong/HW-SW_Interface_Course
实验说明:https://courses.cs.washington.edu/courses/cse351/13sp/lab-0.html
课件:http://academictorrents.com/details/b63a566df824b39740eb9754e4fe4c0140306f4b
课程视频:https://www.bilibili.com/video/BV1Zt411s7Gg?from=search&seid=8781593976070799647
参考资料:https://www.runoob.com/cprogramming/c-function-strtol.html
这次完成Lab2,这次的内容是Buffer Overflows。
x86-64参考资料:
https://web.stanford.edu/class/archive/cs/cs107/cs107.1166/guide_x86-64.html
https://cs.brown.edu/courses/cs033/docs/guides/x64_cheatsheet.pdf
答案
Science isn't about why, it's about why not?
1 1 1 1 1 1
0 535
9
7 93
600
secret_phase需要特殊处理,输入99,107都可。
准备工作
反汇编:
objdump -t bomb >> bomb1.txt
objdump -d bomb >> bomb2.txt
strings -t x bomb >> bomb3.txt
phase_1
gdb bomb
break *0x400e74
print (char *) 0x401af8
得到结果为
$1 = 0x401af8 "Science isn't about why, it's about why not?"
所以输入为
Science isn't about why, it's about why not?
phase_2
整体说明如下,程序主要工作是判断相邻两个数是否相同:
400eac: 48 89 e5 mov %rsp,%rbp (初始化工作)#R[rbp]=R[rsp]
400eaf: 4c 8d 6c 24 0c lea 0xc(%rsp),%r13 #R[r13]=M[R[rsp]+0xc](上限位置)
400eb4: 41 bc 00 00 00 00 mov $0x0,%r12d #R[r12d]=0(初始化)
400eba: 48 89 eb mov %rbp,%rbx (循环开始)#R[rbx]=R[rbp]
400ebd: 8b 45 0c mov 0xc(%rbp),%eax #R[eax]=M[R[rbp]-0xc](前一个数字的值)
400ec0: 39 45 00 cmp %eax,0x0(%rbp) #M[R[rbp]] - R[eax](判断当且数字和之前数字是否相等)
400ec3: 74 05 je 400eca <phase_2+0x3e>
400ec5: e8 73 07 00 00 callq 40163d <explode_bomb>
400eca: 44 03 23 add (%rbx),%r12d #R[r12d]+=M[R[rbx]]
400ecd: 48 83 c5 04 add $0x4,%rbp #R[rbp]+=0x4(更新前一个数字的位置)
400ed1: 4c 39 ed cmp %r13,%rbp #R[rbp]-R[r13](判断)
所以输入为
1 1 1 1 1 1
phase_3
和CMU Bomb lab phase_3类似,分析见:
https://doraemonzzz.com/2020/06/02/CMU%2015-213%20Lab2%20Bomb%20Lab/
输入为
0 535
phase_4
func4对应的Python程序为
def func4(a, b):
b[0] = 1
if a > 1:
func4(a - 1, b)
else:
return
c = b[0]
func4(a - 2, b)
b[0] += c
我们需要比较$b[0]$和$0\text{x}37=55$的值,所以使用如下方式遍历即可
i = 0
while True:
b = [0]
func4(i, b)
if (b[0] == 55):
print(i)
break
i += 1
结果为
9
phase_5
首先查看输入格式:
(gdb) print (char *) 0x401ebe
$28 = 0x401ebe "%d %d"
所以输入为两个整型数,其次查看数组中存储的元素:
(gdb) print *(int *) (0x401ba0+4*0)
$10 = 10
(gdb) print *(int *) (0x401ba0+4*1)
$11 = 2
(gdb) print *(int *) (0x401ba0+4*2)
$12 = 14
(gdb) print *(int *) (0x401ba0+4*3)
$13 = 7
(gdb) print *(int *) (0x401ba0+4*4)
$14 = 8
(gdb) print *(int *) (0x401ba0+4*5)
$15 = 12
(gdb) print *(int *) (0x401ba0+4*6)
$16 = 15
(gdb) print *(int *) (0x401ba0+4*7)
$17 = 11
(gdb) print *(int *) (0x401ba0+4*8)
$18 = 0
(gdb) print *(int *) (0x401ba0+4*9)
$19 = 4
(gdb) print *(int *) (0x401ba0+4*10)
$20 = 1
(gdb) print *(int *) (0x401ba0+4*11)
$21 = 13
(gdb) print *(int *) (0x401ba0+4*12)
$22 = 3
(gdb) print *(int *) (0x401ba0+4*13)
$23 = 9
(gdb) print *(int *) (0x401ba0+4*14)
$24 = 6
(gdb) print *(int *) (0x401ba0+4*15)
$25 = 5
(gdb) print *(int *) (0x401ba0+4*16)
$26 = 2032168787
将原始汇编码转换为python代码:
array = [10, 2, 14, 7, 8, 12, 15, 11, 0, 4, 1, 13, 3, 9, 6, 5]
for i in range(15):
eax = i
ecx = 0
for edx in range(1, 16):
eax = array[eax]
ecx += eax
if (eax == 15):
break
if ((edx == 12) and (eax == 15)):
print(i, ecx)
最终得到如下结果:
7 93
phase_6
这里使用比较偷鸡的办法,直接打印如下结果:
(gdb) break *0x40110e
(gdb) print *(int *) $rax
$4 = 600
所以输入为
600
secret_phase
这部分依然使用偷鸡的方法,直接给寄存器赋值,得到如下结果:
(gdb) break *0x401177
Breakpoint 1 at 0x401177
(gdb) break *0x40118f
Breakpoint 2 at 0x40118f
(gdb) break *0x4017b0
Breakpoint 3 at 0x4017b0
(gdb) break *0x4017c4
Breakpoint 4 at 0x4017c4
(gdb) run
Starting program: /home/qz/桌面/Labs/lab2/bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Science isn't about why, it's about why not?
Phase 1 defused. How about the next one?
1 1 1 1 1 1
That's number 2. Keep going!
0 535
Halfway there!
9
So you got that one. Try this one.
7 93
Congratulations! You've (mostly) defused the bomb!
Hit Control-C to escape phase 6 (for free!), but if you want to
try phase 6 for extra credit, you can continue. Just beware!
600
Breakpoint 3, 0x00000000004017b0 in phase_defused ()
(gdb) print $rax=2
$1 = 2
(gdb) continue
Continuing.
Breakpoint 4, 0x00000000004017c4 in phase_defused ()
(gdb) print $rax=0
$2 = 0
(gdb) continue
Continuing.
Curses, you've found the secret phase!
But finding it and solving it are quite different...
4
Breakpoint 1, 0x0000000000401177 in secret_phase ()
(gdb) print $rax=0x3e8
$3 = 1000
(gdb) continue
Continuing.
Breakpoint 2, 0x000000000040118f in secret_phase ()
(gdb) print $rax=3
$4 = 3
(gdb) continue
Continuing.
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb! Again!
[Inferior 1 (process 14133) exited normally]
接着分析代码部分如何工作,首先秘密阶段是由secret_phase函数控制,该函数由phase_defused调用,该函数只有在完成phase_6时才会调用,然后查看如下汇编码:
40179c: be c4 1e 40 00 mov $0x401ec4,%esi #%d %s
4017a1: bf 30 30 60 00 mov $0x603030,%edi #0x603030 <input_strings+240> ""
4017a6: b8 00 00 00 00 mov $0x0,%eax
4017ab: e8 00 f3 ff ff callq 400ab0 <__isoc99_sscanf@plt>
4017b0: 83 f8 02 cmp $0x2,%eax #eax=0x2
4017b3: 75 31 jne 4017e6 <phase_defused+0x61>
4017b5: be ca 1e 40 00 mov $0x401eca,%esi #"austinpowers"
4017ba: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
4017bf: e8 79 fa ff ff callq 40123d <strings_not_equal>
4017c4: 85 c0 test %eax,%eax #eax=0
这部分首先调用sscanf函数,edi是我们的输入字符,格式为%d %s,然后调用sscanf,如下语句判断是否成功:
cmp $0x2,%eax
后续的语句
4017b5: be ca 1e 40 00 mov $0x401eca,%esi #"austinpowers"
4017ba: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
4017bf: e8 79 fa ff ff callq 40123d <strings_not_equal>
判断rdi处的字符串(即sscanf的结果)是否和austinpowers相同,所以我们需要在0x4017a1处设置断点,然后运行如下指令
set {char [30]} 0x603030 = "6301152 austinpowers"
这样可以通过上述测试。
通过该测试后命令行让我们输入一个参数,然后进入secret_phase,如下指令判断参数是否小于等于1001:
401172: 89 c3 mov %eax,%ebx #R[ebx]=R[eax]
401174: 8d 43 ff lea -0x1(%rbx),%eax #R[eax]=R[rbx]-0x1
401177: 3d e8 03 00 00 cmp $0x3e8,%eax #1000
通过该指令后进入fun7,最后判断返回结果是否为3,该函数C版本如下(参考https://github.com/HeLiangHIT/Hardware_Software_Interface/blob/f223721d39c88ffb30a109922fbb7db761e6a3ee/README.md)
int fun7(const int *a, int b)
{
if (a == NULL)
return -1;
int ret = 0;
if (*a - b > 0)
{
ret = fun7(*(a + 4), b);
ret *= 2
}
else if (*a - b == 0)
return 0;
else
{
ret = fun7(*(a + 8), b);
ret = ret * 2 + 1;
}
return ret;
}
所以求解该问题的python版本如下:
address = []
data = []
with open("data.txt") as f:
i = 0
for line in f.readlines():
num = int(line.split()[2])
if i % 2 == 0:
address.append(num)
else:
data.append(num)
i += 1
mp = dict(zip(address, data))
def func7(esi, index):
if (index not in mp):
return -1
tmp = mp[index] - esi
if tmp <= 0:
res = 0
if tmp == 0:
return 0
else:
index += 16
if index in mp:
index = mp[index]
else:
index = 0
res = func7(esi, index)
return 2 * res + 1
else:
index += 8
if index in mp:
index = mp[index]
else:
index = 0
res = func7(esi, index)
return res * 2
for esi in range(1002):
index = 6301088
res = func7(esi, index)
if (res == 3):
print(esi, res)
最后的结果为
99 3
107 3
所以输入99,107即可。
补充:data.txt中数据格式如下:
$2621 = 6301088
$2622 = 36
$2623 = 6301096
$2624 = 6301120
第一行等式右边表示地址,第二行等式右边表示值。
上述程序中的data.txt存储内存中的数据存放情况,该数组的初始位置为0x6025a0=6301088,这可以由如下命令得到:
401183: 89 de mov %ebx,%esi #R[esi]=R[ebx]=R[eax]
401185: bf a0 25 60 00 mov $0x6025a0,%edi #R[edi]=0x6025a0,M[0x6025a0]=36
然后使用如下命令得到结果:
set logging file data.txt
set logging on
set $i=6301088
set $j=0
set $k=0
while ($j<2000)
set $k=$i+8*$j
print $k
print *(int *) $k
set $j=$j+1
end
set logging off
完整的流程如下:
(gdb) break *0x4017a1
Breakpoint 1 at 0x4017a1
(gdb) run
Starting program: /home/qz/桌面/Labs/lab2/bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Science isn't about why, it's about why not?
Phase 1 defused. How about the next one?
1 1 1 1 1 1
That's number 2. Keep going!
0 535
Halfway there!
9
So you got that one. Try this one.
7 93
Congratulations! You've (mostly) defused the bomb!
Hit Control-C to escape phase 6 (for free!), but if you want to
try phase 6 for extra credit, you can continue. Just beware!
600
Breakpoint 1, 0x00000000004017a1 in phase_defused ()
(gdb) set {char [30]} 0x603030 = "6301152 austinpowers"
(gdb) set logging file data.txt
(gdb) set logging on
Copying output to data.txt.
Copying debug output to data.txt.
(gdb) set $i=6301088
(gdb) set $j=0
(gdb) set $k=0
(gdb) while ($j<2000)
> set $k=$i+8*$j
> print $k
> print *(int *) $k
> set $j=$j+1
>end
$1 = 6301088
$2 = 36
$3 = 6301096
$4 = 6301120
$5 = 6301104
$6 = 6301152
$7 = 6301112
$8 = 0
$9 = 6301120
$10 = 8
$11 = 6301128
$12 = 6301248
$13 = 6301136
$14 = 6301184
$15 = 6301144
$16 = 0
$17 = 6301152
$18 = 50
$19 = 6301160
$20 = 6301216
$21 = 6301168
$22 = 6301280
$23 = 6301176
--Type <RET> for more, q to quit, c to continue without paging--q
Quit
(gdb) set logging off
Done logging to data.txt.
(gdb) continue
Continuing.
Curses, you've found the secret phase!
But finding it and solving it are quite different...
107
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb! Again!
[Inferior 1 (process 18341) exited normally]
总结
gbd中给寄存器赋值:
print $rax=1
gdb赋值字符串:
set {char [30]} 0x603030 = "6301152 austinpowers"
循环:
set $i=6301088
set $j=0
set $k=0
while ($j<2000)
set $k=$i+8*$j
print $k
print *(int *) $k
set $j=$j+1
end
将gdb的输出结果重定向:
(gdb) set logging file <file name>
(gdb) set logging on
(gdb) info functions
(gdb) set logging off