動機
学校の研究室にて、自分が今学期やったことをまとめて発表することになった際、
テーマが決まらなかったため、先輩方にアドバイスをもらった。
自分は、セキュリティに興味を持っている。
そのため、数ヶ月前に、pythonを用いて、セキュリティについて勉強してみた。

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考
- 作者: Justin Seitz,青木一史,新井悠,一瀬小夜,岩村誠,川古谷裕平,星澤裕二
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (11件) を見る
しかし、進めて行くうちに、高いレイヤーでのプログラミングでは、セキュリティの本質がわかることはないと考え(今もよくわかっていない)、
とりあえず、2冊目として、

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際
- 作者: Jon Erickson,村上雅章
- 出版社/メーカー: オライリージャパン
- 発売日: 2011/10/22
- メディア: 単行本(ソフトカバー)
- 購入: 9人 クリック: 163回
- この商品を含むブログ (19件) を見る
この本を読むことにした。それと並行して、発表する内容を、アドバイスに基づいて、
「ptraceを用いて、発行されるシステムコールを確認する」
というテーマを選ぶことにした。
また、以上の2冊についてのまとめは、別の記事で書こうと思う。
背景
動機の章でも述べたが、今自分は、セキュリティに興味がある。いずれ、CTFなどにも挑戦してみたいと考えている。
しかし、これは漠然とした考えであり、今はまだ、セキュリティ
という言葉のイメージがぼんやりしている状態だ。
ただ、一つわかるのは、C言語はもとより、アセンブリや、OS、システムコールについて、精通しなければならないということだ。
そこで今回は、そのまず第一歩として、ptraceを用いて、発行されるシステムコールを確認する
ことにした。
目的
今学期、自分が所属させてもらっている研究会では、コンピュータアーキテクチャについて、輪講を行った。
![ディジタル回路設計とコンピュータアーキテクチャ[ARM版] ディジタル回路設計とコンピュータアーキテクチャ[ARM版]](https://images-fe.ssl-images-amazon.com/images/I/51HrwEwLrGL._SL160_.jpg)
- 作者: デイビッド・M・ハリスサラ・L・ハリス,天野英晴
- 出版社/メーカー: 星雲社
- 発売日: 2016/04/25
- メディア: 大型本
- この商品を含むブログ (1件) を見る
この本では、コンピュータアーキテクチャの物理層から、どんどん上のレイヤーに上がって行く形式で進んでいった。
正直、聞いているときは訳が分からず、未だによくわかっていないが、改めて俯瞰してみると、読む前より、少しだけコンピュータの仕組みがわかるようになった。
しかし、この本を読んで行く過程で、レジスタ
という単語が出てきた。しかし、その役割について、よくわからなかった、というのもあって、
このテーマを通じて、少しでもレジスタに関するイメージができるようになればいいなと思った。
そこで、今回は、コードをコピペするのではなく、ググった情報などを参考にしつつ、あくまで自分の手で書いていこうと思っていた。
システムコールとは
システムコールとは、OSのカーネルの機能を呼び出すために使われる機構である。
例えば、C言語で使う、fopen()
や、malloc()
などの関数はその関数内において、システムコールを呼び出している。
OSとの架け橋というイメージを持っている。
ptraceとは
ptraceは、上述の、システムコールのうちの一つであり、別のプロセスを自分の子プロセスとしてトレースし、監視や変更を行うためのものである。
マニュアルは、http://surf.ml.seikei.ac.jp/~nakano/JMwww/html/LDP_man-pages/man2/ptrace.2.html←ここにある
このシステムコールは、デバッガなどを実装する際に用いられている。
目標
ptraceというシステムコールを用いて、
レジスタの状態をuser_regs_struct構造体にコピーし、 取得できるシステムコール番号をorig_raxフィールドから取得し、これを表示するためのプログラムを、C言語で実装する。
環境: Kali Linux 64bit
実装
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <sys/user.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <asm/unistd.h> #include <sys/syscall.h> // とんでもないことになっているが、手っ取り早くやるためにこうなってしまった。 char *syscall_lookup_table[333] = { "read","write","open","close","stat","fstat","lstat","poll","lseek","mmap","mprotect""mummap", "brk","rt_sigaction","rt_sigprocmask","rt_sigreturn","ioctl","pread","pwrite","readv","writev", "access","pipe","select","sched_yiled","mremap","msync","mincore","madvise","shmget","shmat", "shmctl","dup","dup","pause","nanosleep","getitimer","alarm","setitimer","getpid","sendfile", "socket","connect","accept","sendto","recvfrom","sendmsg","recvmsg","shutdown","bind","listen", "getsockname","getpeername","socketpair","setsockopt","getsockopt","clone","fork","vfork","execve", "exit","wait4","kill","uname","semget","semop","semctl","shmdt","msgget","msgsnd","msgrcv","msgctl", "fcntl","flock","fsync","fdatasync","truncate","ftruncate","getdents","getcwd","chdir","fchdir","rename", "mkdir","rmdir","creat","link","unlink","symlink","readlink","chmod","fchmod","chown","fchown","lchown", "umask","gettimeofday","getrlimit","getrusage","sysinfo","times","ptrace","getuid","syslog","getgid", "setuid","setgid","geteuid","getegid","setpgid","getppid","getpgrp","setsid","setreuid","setregid", "getgroups","setgroups","setresuid","getresuid","setresgid","getresgid","getpgid","setfsuid","setfsgid", "getsid","capget","capset","rt_sigpending","rt_sigtimedwait","rt_sigqueueinfo","rt_sigsuspend","sigaltstack", "utime","mknod","uselib","personality","ustat","statfs","fstatfs","sysfs","getpriority","setpriority", "sched_setparam","sched_getparam","sched_setscheduler","sched_getscheduler","sched_get_priority_max", "sched_get_priority_min","sched_rr_get_interval","mlock","munlock","mlockall","munlockall","vhangup", "modify_ldt","pivot_root","_sysctl","prctl","arch_prctl","adjtimex","setrlimit","chroot","sync","acct", "settimeofday","mount","umount","swapon","swapoff","reboot","sethostname","setdomainname","iopl","ioperm", "create_module","init_module","delete_module","get_kernel_syms","query_module","quotactl","nfsservctl", "getpmsg","putpmsg","afs_syscall","tuxcall","security","gettid","readahead","setxattr","lsetxattr", "fsetxattr","getxattr","lgetxattr","fgetxattr","listxattr","llistxattr","flistxattr","removexattr", "lremovexattr","fremovexattr","tkill","time","futex","sched_setaffinity","sched_getaffinity","set_thread_area", "io_setup","io_destroy","io_getevents","io_submit","io_cancel","get_thread_area","lookup_dcookie","epoll_create", "epoll_ctl_old","epoll_wait_old","remap_file_pages","getdents64","set_tid_address","restart_syscall","semtimedop", "fadvise64","timer_create","timer_settime","timer_gettime","timer_getoverrun","timer_delete","clock_settime", "clock_gettime","clock_getres","clock_nanosleep","exit_group","epoll_wait","epoll_ctl","tgkill","utimes","vserver", "mbind","set_mempolicy","get_mempolicy","mq_open","mq_unlink","mq_timedsend","mq_timedreceive","mq_notify", "mq_getsetattr","kexec_load","waitid","add_key","request_key","keyctl","ioprio_set","ioprio_get","inotify_init", "inotify_add_watch","inotify_rm_watch","migrate_pages","openat","mkdirat","mknodat","fchownat","futimesat", "newfstatat","unlinkat","renameat","linkat","symlinkat","readlinkat","fchmodat","faccessat","pselect6","ppoll", "unshare","set_robust_list","get_robust_list","splice","tee","sync_file_range","vmsplice","move_pages","utimensat", "epoll_pwait","signalfd","timerfd","eventfd","fallocate","timerfd_settime","timerfd_gettime","accept4","signalfd4", "eventfd","epoll_create","dup3","pipe","inotify_init","preadv","pwritev","rt_tgsigqueueinfo","perf_event_open", "recvmmsg","fanotify_init","fanotify_mark","prlimit64","name_to_handle_at","open_by_handle_at","clock_adjtime", "syncfs","sendmmsg","setns","getcpu","process_vm_readv","process_vm_writev","kcmp","finit_module","sched_setattr", "sched_getattr","renameat","seccomp","getrandom","memfd_create","kexec_file_load","bpf","execveat","userfaultfd", "membarrier","mlock","copy_file_range","preadv","pwritev","pkey_mprotect","pkey_alloc","pkey_free","statx" }; // プロセスにアタッチしてシステムコールが実行されようとしている直前でレジスタの状態を補足してそれを表示することが目的 int main( int argc, char* *argv[] ){ assert( argc == 2 ); struct user_regs_struct reg_state; pid_t pid = atoi(argv[1]); int st; // long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); assert( ptrace(PTRACE_ATTACH, pid, NULL, NULL) == 0 ); printf("attached to pid: %d\n", pid); unsigned int syscall_nr; while(1){ waitpid(pid, &st, 0); // 子プロセスが正常に終了した場合に真を返す。 if( WIFEXITED(st) ){ break; }else if( WIFSTOPPED(st)){ // WIFSTOPPED - 子プロセスがシグナルの配送により停止した場合に真を返す。 ptrace(PTRACE_GETREGS, pid, NULL, ®_state); syscall_nr = reg_state.orig_rax; if(syscall_nr >= 0 && syscall_nr <= 333) {// syscall_lookup_tableに入っているものに収まっている、かつ、NULLではないもの if( syscall_nr != 231 ){ printf("[orig_rax]Syscall: Syscall(%d) = %s called\n", syscall_nr, syscall_lookup_table[syscall_nr]); }else{ break ; } } } ptrace(PTRACE_SYSCALL, pid, NULL, NULL); } assert( ptrace(PTRACE_DETACH, pid, NULL, 0) == 0 ); printf("detached from pid: %d\n", pid); return 0; }
このコードを実行する。 まず、対象のコードを書いてみる。
while(true){ printf(“hello, world!”); }
このプログラムをアタッチし、トレースしてみる。
まずは、プロセスIDを調べる
root@kali: \# ps a | grep hello # 出力結果 # 3683 pts/0 S+ 0:01 ./helloworld root@kali: \# ./watchingsyscall 3683
これを実行してみると、
このようになる。
次に、scanfなどで、どのようなシステムコールが発行されているのかを調べてみる。
#include <stdio.h> #include <string.h> int main(){ char message[20]; int i; printf("どんな文字列を出力しますか?"); scanf("%s", &message); printf("%s", message); return 0; }
このプログラムを同じように、アタッチし、トレースしてみる。
このような結果が得られる。
とりあえず、それっぽい実装ができた。しかし、この発表をしたところ、
出力されているシステムコールの値が不自然なのではないかとのご指摘を受けた。
あまりよく分からないが、どうやら、waitpid
の使い方がおかしいらしい。(衝突している?)
もし詳しい方がいらっしゃったら、ご指摘していただけると大変助かります。
まとめ
この実装を通じて、ぼんやりだが、OSがどのようなことをしているのか、わかったような気がする。
今後も、この分野に関する勉強を進めていきたい。