# 攻擊與防禦 HW3 Q1
###### tags: `攻擊與防禦`
## Ver. Simple
```python=
import angr
p = angr.Project("./stack1")
initial_state = p.factory.entry_state()
sm = p.factory.simulation_manager(initial_state, save_unconstrained=True)
while sm.active:
sm.step()
if sm.unconstrained:
for un in sm.unconstrained: # 1-1
print("stdout:\n", un.posix.dumps(1))
print("stdin:\n", un.posix.dumps(0), "\n")
```
### 1. Describe the overall code structure of the script.
```python=1
import angr
```
import angr module ,此 module 為操作 angr 的 API 。
```python=3
p = angr.Project("./stack1")
initial_state = p.factory.entry_state()
sm = p.factory.simulation_manager(initial_state, save_unconstrained=True)
```
取得一個 simulation manager ,令其初始狀態為 stack1 的 entry point(即變數 `initial_state`)。
設定 `save_unconstrained` ,這樣的話會崩壞的 state 才會被存在 `uncinstrained` 中。
```python=7
while sm.active:
sm.step()
```
持續探索 state 。
`active` 是一個存放當前狀態的陣列,若該陣列非空,函數 step 會枚舉 `active` 的所有子節點並更新到 `active` 。
```python=9
if sm.unconstrained:
for un in sm.unconstrained: # 1-1
print("stdout:\n", un.posix.dumps(1))
print("stdin:\n", un.posix.dumps(0), "\n")
```
若存在被搜索到的 unconstrained 的 state ,則印出之。
官方 document 中提到 uncinstrained 可能指
> with the instruction pointer controlled by user data or some other source of symbolic data
因此當 buffer overflow 動到 PC 時,就會發生 uncinstrained 。
### 2. Explain the purpose of the code on the lines marked with comment symbol.
#### #1-1
如 1. 所言,設定 `save_unconstrained` ,這樣的話會發生 buffer overflow 的 state 才會被存在 `uncinstrained` 中。
### 3. Test the exploit(input) on the C program and show your results.
執行腳本:

將此 input 餵給 stack1。

顯然是因為很長的 input 造成 buffer overflow。這樣的狀態因為有設定 `save_unconstrained` 的關係所以會存到 `unconstrained` 陣列中。
## Ver. Full
### 1. Describe the overall code structure of the script.
#### main part
```python=73
if __name__ == '__main__':
filename="stack1"
p = angr.Project(filename,auto_load_libs=False)
state=p.factory.entry_state()
state.globals['rbp_list']={}
simgr = p.factory.simulation_manager(state,save_unconstrained=True)
while simgr.active:
for act in simgr.active:
check_head(act)
check_end(act)
simgr.step()
```
上述程式碼試圖分析 stack1 ,遍歷其所有經過的 state 並以函數 check_head 和 check_end 分析之。
#### 函數 check_head
```python=57
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
ins0=insns[0].insn
ins1=insns[1].insn
if len(ins0.operands)==1 and len(ins1.operands)==2:
ins0_name=ins0.mnemonic
ins0_op0=ins0.reg_name(ins0.operands[0].reg)
ins1_name=ins1.mnemonic
ins1_op0=ins1.reg_name(ins1.operands[0].reg)
ins1_op1=ins1.reg_name(ins1.operands[1].reg)
if ins0_name=="push" and ins0_op0=="rbp" and ins1_name=="mov" and ins1_op0=="rbp" and ins1_op1=="rsp": # 1-2
pre_target=state.callstack.ret_addr
state.globals['rbp_list'][hex(pre_target)]=state.regs.rbp
```
上方 code 先是分析 state 的頭兩個 instrcution ,若其為
```asm
PUSH rbp
MOV rbp, rsp
```
則通常代表即將進入一個函數,此時記錄一個以返回地址與 rbp 值的 key-value pair 。
#### 函數 check_end
```python=25
if state.addr==0:
return
insns=state.project.factory.block(state.addr).capstone.insns
if len(insns)>=2:
flag=0
for ins in insns:
if ins.insn.mnemonic=="leave":
flag+=1
if ins.insn.mnemonic=="ret":
flag+=1
if flag==2: # 1-3
```
上方 code 先是檢查當前 state 是否含有 `leave` 和 `ret` op code 。這通常代表函數的結束。
```python=36
rsp=state.regs.rsp
rbp=state.regs.rbp
byte_s=state.arch.bytes
stack_rbp=state.memory.load(rbp,endness=angr.archinfo.Endness.LE)
stack_ret=state.memory.load(rbp+byte_s,endness=angr.archinfo.Endness.LE)
pre_target=state.callstack.ret_addr
pre_rbp=state.globals['rbp_list'][hex(pre_target)]
```
上方 code 取得該函數即將進入前和即將離開前的 rbp 值和返回地址值。
```python=44
if stack_ret.symbolic: # 1-4
num=check_symbolic_bits(state,stack_ret)
print_pc_overflow_msg(state,num//byte_s)
state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
state.memory.store(rbp+byte_s, state.solver.BVV(pre_target, 64),endness=angr.archinfo.Endness.LE)
return
```
上方 code 比對前後返回值。若當前返回值被標記 symbolic ,表示此值來自 input ,乃是因為 buffer overflow ,然後輸出訊息。
```python=51
if stack_rbp.symbolic: # 1-5
num=check_symbolic_bits(state,stack_rbp)
print_bp_overflow_msg(state,num//byte_s)
state.memory.store(rbp,pre_rbp,endness=angr.archinfo.Endness.LE)
```
上方 code 比對前後 rbp 值。若當前返回值被標記 symbolic ,表示此值來自 input ,乃是因為 buffer overflow ,然後輸出訊息。
因為覆蓋返回地址比覆蓋 rbp 值要來得困難(前者需要較長的溢位),故優先檢測前者。
### 2. Explain the purpose of the code on the lines marked with comment symbol.
#### #1-2
如 1. 所言,用以判斷是否即將進入新的函數。若是,記錄其 rbp 值和預期返回地址。
#### #1-3
如 1. 所言,判斷是否含 `leave` 和 `ret` 表即將離開函數。
#### #1-4
如 1. 所言,判斷返回地址是否被修改過。若有,印出訊息。
#### #1-5
如 1. 所言,判斷 rbp 是否被修改過。若有,印出訊息。
### 3. Test the exploit(input) on the C program and show your results.
執行腳本:

測試造成第一個 overflow 的 input:

測試造成第二個 overflow 的 input:

兩者都造成了 buffer overflow 。