课程主页: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

https://github.com/HeLiangHIT/Hardware_Software_Interface/blob/f223721d39c88ffb30a109922fbb7db761e6a3ee/README.md

这次完成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

这部分依然使用偷鸡的方法,直接给寄存器赋值,得到如下结果:

参考资料:https://github.com/HeLiangHIT/Hardware_Software_Interface/blob/f223721d39c88ffb30a109922fbb7db761e6a3ee/README.md

(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