人生は勉強ブログ

https://github.com/dooooooooinggggg

ptraceを用いて、発行されるシステムコールを確認する

動機

学校の研究室にて、自分が今学期やったことをまとめて発表することになった際、

テーマが決まらなかったため、先輩方にアドバイスをもらった。

自分は、セキュリティに興味を持っている。

そのため、数ヶ月前に、pythonを用いて、セキュリティについて勉強してみた。

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考

しかし、進めて行くうちに、高いレイヤーでのプログラミングでは、セキュリティの本質がわかることはないと考え(今もよくわかっていない)、

とりあえず、2冊目として、

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際

この本を読むことにした。それと並行して、発表する内容を、アドバイスに基づいて、

「ptraceを用いて、発行されるシステムコールを確認する」

というテーマを選ぶことにした。

また、以上の2冊についてのまとめは、別の記事で書こうと思う。

背景

動機の章でも述べたが、今自分は、セキュリティに興味がある。いずれ、CTFなどにも挑戦してみたいと考えている。

しかし、これは漠然とした考えであり、今はまだ、セキュリティという言葉のイメージがぼんやりしている状態だ。

ただ、一つわかるのは、C言語はもとより、アセンブリや、OS、システムコールについて、精通しなければならないということだ。

そこで今回は、そのまず第一歩として、ptraceを用いて、発行されるシステムコールを確認することにした。

目的

今学期、自分が所属させてもらっている研究会では、コンピュータアーキテクチャについて、輪講を行った。

ディジタル回路設計とコンピュータアーキテクチャ[ARM版]

ディジタル回路設計とコンピュータアーキテクチャ[ARM版]

この本では、コンピュータアーキテクチャ物理層から、どんどん上のレイヤーに上がって行く形式で進んでいった。

正直、聞いているときは訳が分からず、未だによくわかっていないが、改めて俯瞰してみると、読む前より、少しだけコンピュータの仕組みがわかるようになった。

しかし、この本を読んで行く過程で、レジスタという単語が出てきた。しかし、その役割について、よくわからなかった、というのもあって、

このテーマを通じて、少しでもレジスタに関するイメージができるようになればいいなと思った。

そこで、今回は、コードをコピペするのではなく、ググった情報などを参考にしつつ、あくまで自分の手で書いていこうと思っていた。

システムコールとは

システムコールとは、OSのカーネルの機能を呼び出すために使われる機構である。

例えば、C言語で使う、fopen()や、malloc()などの関数はその関数内において、システムコールを呼び出している。

OSとの架け橋というイメージを持っている。

ptraceとは

ptraceは、上述の、システムコールのうちの一つであり、別のプロセスを自分の子プロセスとしてトレースし、監視や変更を行うためのものである。

マニュアルは、http://surf.ml.seikei.ac.jp/~nakano/JMwww/html/LDP_man-pages/man2/ptrace.2.html←ここにある

このシステムコールは、デバッガなどを実装する際に用いられている。

f:id:dooooooooinggggg:20170830022844p:plain

目標

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, &reg_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

これを実行してみると、

f:id:dooooooooinggggg:20170830024438p:plain

このようになる。

次に、scanfなどで、どのようなシステムコールが発行されているのかを調べてみる。

#include <stdio.h>
#include <string.h>

int main(){
    char message[20];
    int i;

    printf("どんな文字列を出力しますか?");
    scanf("%s", &message);
    printf("%s", message);

    return 0;
}

このプログラムを同じように、アタッチし、トレースしてみる。

f:id:dooooooooinggggg:20170830024415p:plain

このような結果が得られる。

とりあえず、それっぽい実装ができた。しかし、この発表をしたところ、

出力されているシステムコールの値が不自然なのではないかとのご指摘を受けた。

あまりよく分からないが、どうやら、waitpidの使い方がおかしいらしい。(衝突している?)

もし詳しい方がいらっしゃったら、ご指摘していただけると大変助かります。

まとめ

この実装を通じて、ぼんやりだが、OSがどのようなことをしているのか、わかったような気がする。

今後も、この分野に関する勉強を進めていきたい。