自作ローダのためのlibc初期化ハック
Kernel/VM探検隊@東京 No16
Name | Akira Kawata |
Biography | |
mastodon |
自己紹介
ローダとは何か?
ローダの仕事
$ cat ./hoge.c
#include <stdio.h>
void hoge(){ puts("hoge\n"); }
$ cat ./hello.c
void hoge();
int main(){ hoge(); }
$ gcc -o libhoge.so -fPIC -shared hoge.c
$ gcc -o hello -fPIC hello.c libhoge.so
$ ./hello
hoge
ローダの仕事
$ ldd hello
linux-vdso.so.1
libhoge.so
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
/lib64/ld-linux-x86-64.so.2
$ ldd libhoge.so
linux-vdso.so.1
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
/lib64/ld-linux-x86-64.so.2
ローダの仕事
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
説明の簡略化のため実際の起動プロセスとは違う部分があります。より正確な情報は How To Write Shared Libraries を参照してください。
ローダの仕事
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
hoge();
…
hello
説明の簡略化のため実際の起動プロセスとは違う部分があります。より正確な情報は How To Write Shared Libraries を参照してください。
ローダの仕事
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
hoge();
…
hello
void hoge(){
puts(“hoge”);
}
libhoge.so
説明の簡略化のため実際の起動プロセスとは違う部分があります。より正確な情報は How To Write Shared Libraries を参照してください。
ローダの仕事
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
hoge();
…
…
putsの定義
…
hello
libc.so.6
void hoge(){
puts(“hoge”);
}
libhoge.so
説明の簡略化のため実際の起動プロセスとは違う部分があります。より正確な情報は How To Write Shared Libraries を参照してください。
ローダの仕事
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
hoge();
…
…
putsの定義
…
hello
libc.so.6
void hoge(){
puts(“hoge”);
}
libhoge.so
jmp命令の引数にhogeのアドレスを埋める
call命令の引数にputsのアドレスを埋める
説明の簡略化のため実際の起動プロセスとは違う部分があります。より正確な情報は How To Write Shared Libraries を参照してください。
ローダを自作する動機
sloader – Simple Loader
最近のsloader
自作ローダはまりポイント
libc.so.6 とは?
ほとんどのソフトウェアはlibc.so.6 に依存している
$ find /usr/bin -mindepth 1 -maxdepth 1 | wc -l
2602
$ find /usr/bin -mindepth 1 -maxdepth 1 \
| xargs ldd \
| grep libc.so.6 \
| wc -l
1949
libc.so.6 をロードするのは難しい
sloader での 対策
sloaderがhelloを起動する様子
$ cat ./hoge.c
#include <stdio.h>
void hoge(){puts("hoge\n"); }
$ cat ./hello.c
void hoge();
int main(){ hoge(); }
$ gcc -o libhoge.so -fPIC -shared hoge.c
$ gcc -o hello -fPIC hello.c libhoge.so
$ sloader ./hello
hoge
sloaderがhelloを起動する様子
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
…
putsの定義
sloader
…
…
putsの定義
sloader
sloaderがhelloを起動する様子
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
…
putsの定義
sloader
…
…
putsの定義
sloader
…
hoge();
…
hello
sloaderがhelloを起動する様子
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
…
putsの定義
sloader
…
…
putsの定義
sloader
…
hoge();
…
hello
void hoge(){
puts(“hoge”);
}
libhoge.so
sloaderがhelloを起動する様子
…
hoge();
…
…
putsの定義
…
ディスク
hello
libc.so.6
メモリ
void hoge(){
puts(“hoge”);
}
libhoge.so
…
…
putsの定義
sloader
…
…
putsの定義
sloader
…
hoge();
…
hello
void hoge(){
puts(“hoge”);
}
libhoge.so
jmp命令の引数にhogeのアドレスを埋める
jmp命令の引数にsloaderの中の
putsのアドレスを埋める
現状のsloaderの問題点
まとめ
予備スライド
Thread Local Storage (TLS)の初期化
Thread Local Storage (TLS) とは?
TLSにfsレジスタ経由でアクセスする例 (gotbolt)
#include <stdint.h>
thread_local uint64_t i0;
int main() {
i0 = 0xabcdabcdabcdabcd;
}
i0:
.zero 8
main:
push rbp
mov rbp, rsp
movabs rax, -6067004223159161907
mov QWORD PTR fs:i0@tpoff, rax
mov eax, 0
pop rbp
ret