#!/usr/bin/python

from pyoida import Plugin
from pyoida.analysis import RenderingOptions
from pyoida.arch import ArchProcessorType


class SysCallDB():
    """xxx"""

    def __init__(self):

        self.__syscalls = [
            "__NR_restart_syscall",
            "__NR_exit",
            "__NR_fork",
            "__NR_read",
            "__NR_write",
            "__NR_open",
            "__NR_close",
            "__NR_waitpid",
            "__NR_creat",
            "__NR_link",
            "__NR_unlink",
            "__NR_execve",
            "__NR_chdir",
            "__NR_time",
            "__NR_mknod",
            "__NR_chmod",
            "__NR_lchown",
            "__NR_break",
            "__NR_oldstat",
            "__NR_lseek",
            "__NR_getpid",
            "__NR_mount",
            "__NR_umount",
            "__NR_setuid",
            "__NR_getuid",
            "__NR_stime",
            "__NR_ptrace",
            "__NR_alarm",
            "__NR_oldfstat",
            "__NR_pause",
            "__NR_utime",
            "__NR_stty",
            "__NR_gtty",
            "__NR_access",
            "__NR_nice",
            "__NR_ftime",
            "__NR_sync",
            "__NR_kill",
            "__NR_rename",
            "__NR_mkdir",
            "__NR_rmdir",
            "__NR_dup",
            "__NR_pipe",
            "__NR_times",
            "__NR_prof",
            "__NR_brk",
            "__NR_setgid",
            "__NR_getgid",
            "__NR_signal",
            "__NR_geteuid",
            "__NR_getegid",
            "__NR_acct",
            "__NR_umount2",
            "__NR_lock",
            "__NR_ioctl",
            "__NR_fcntl",
            "__NR_mpx",
            "__NR_setpgid",
            "__NR_ulimit",
            "__NR_oldolduname",
            "__NR_umask",
            "__NR_chroot",
            "__NR_ustat",
            "__NR_dup2",
            "__NR_getppid",
            "__NR_getpgrp",
            "__NR_setsid",
            "__NR_sigaction",
            "__NR_sgetmask",
            "__NR_ssetmask",
            "__NR_setreuid",
            "__NR_setregid",
            "__NR_sigsuspend",
            "__NR_sigpending",
            "__NR_sethostname",
            "__NR_setrlimit",
            "__NR_getrlimit",
            "__NR_getrusage",
            "__NR_gettimeofday",
            "__NR_settimeofday",
            "__NR_getgroups",
            "__NR_setgroups",
            "__NR_select",
            "__NR_symlink",
            "__NR_oldlstat",
            "__NR_readlink",
            "__NR_uselib",
            "__NR_swapon",
            "__NR_reboot",
            "__NR_readdir",
            "__NR_mmap",
            "__NR_munmap",
            "__NR_truncate",
            "__NR_ftruncate",
            "__NR_fchmod",
            "__NR_fchown",
            "__NR_getpriority",
            "__NR_setpriority",
            "__NR_profil",
            "__NR_statfs",
            "__NR_fstatfs",
            "__NR_ioperm",
            "__NR_socketcall",
            "__NR_syslog",
            "__NR_setitimer",
            "__NR_getitimer",
            "__NR_stat",
            "__NR_lstat",
            "__NR_fstat",
            "__NR_olduname",
            "__NR_iopl",
            "__NR_vhangup",
            "__NR_idle",
            "__NR_vm86old",
            "__NR_wait4",
            "__NR_swapoff",
            "__NR_sysinfo",
            "__NR_ipc",
            "__NR_fsync",
            "__NR_sigreturn",
            "__NR_clone",
            "__NR_setdomainname",
            "__NR_uname",
            "__NR_modify_ldt",
            "__NR_adjtimex",
            "__NR_mprotect",
            "__NR_sigprocmask",
            "__NR_create_module",
            "__NR_init_module",
            "__NR_delete_module",
            "__NR_get_kernel_syms",
            "__NR_quotactl",
            "__NR_getpgid",
            "__NR_fchdir",
            "__NR_bdflush",
            "__NR_sysfs",
            "__NR_personality",
            "__NR_afs_syscall",
            "__NR_setfsuid",
            "__NR_setfsgid",
            "__NR__llseek",
            "__NR_getdents",
            "__NR__newselect",
            "__NR_flock",
            "__NR_msync",
            "__NR_readv",
            "__NR_writev",
            "__NR_getsid",
            "__NR_fdatasync",
            "__NR__sysctl",
            "__NR_mlock",
            "__NR_munlock",
            "__NR_mlockall",
            "__NR_munlockall",
            "__NR_sched_setparam",
            "__NR_sched_getparam",
            "__NR_sched_setscheduler",
            "__NR_sched_getscheduler",
            "__NR_sched_yield",
            "__NR_sched_get_priority_max",
            "__NR_sched_get_priority_min",
            "__NR_sched_rr_get_interval",
            "__NR_nanosleep",
            "__NR_mremap",
            "__NR_setresuid",
            "__NR_getresuid",
            "__NR_vm86",
            "__NR_query_module",
            "__NR_poll",
            "__NR_nfsservctl",
            "__NR_setresgid",
            "__NR_getresgid",
            "__NR_prctl",
            "__NR_rt_sigreturn",
            "__NR_rt_sigaction",
            "__NR_rt_sigprocmask",
            "__NR_rt_sigpending",
            "__NR_rt_sigtimedwait",
            "__NR_rt_sigqueueinfo",
            "__NR_rt_sigsuspend",
            "__NR_pread64",
            "__NR_pwrite64",
            "__NR_chown",
            "__NR_getcwd",
            "__NR_capget",
            "__NR_capset",
            "__NR_sigaltstack",
            "__NR_sendfile",
            "__NR_getpmsg",
            "__NR_putpmsg",
            "__NR_vfork",
            "__NR_ugetrlimit",
            "__NR_mmap2",
            "__NR_truncate64",
            "__NR_ftruncate64",
            "__NR_stat64",
            "__NR_lstat64",
            "__NR_fstat64",
            "__NR_lchown32",
            "__NR_getuid32",
            "__NR_getgid32",
            "__NR_geteuid32",
            "__NR_getegid32",
            "__NR_setreuid32",
            "__NR_setregid32",
            "__NR_getgroups32",
            "__NR_setgroups32",
            "__NR_fchown32",
            "__NR_setresuid32",
            "__NR_getresuid32",
            "__NR_setresgid32",
            "__NR_getresgid32",
            "__NR_chown32",
            "__NR_setuid32",
            "__NR_setgid32",
            "__NR_setfsuid32",
            "__NR_setfsgid32",
            "__NR_pivot_root",
            "__NR_mincore",
            "__NR_madvise",
            "__NR_madvise1",
            "__NR_getdents64",
            "__NR_fcntl64",
            "__NR_unused",
            "__NR_gettid",
            "__NR_readahead",
            "__NR_setxattr",
            "__NR_lsetxattr",
            "__NR_fsetxattr",
            "__NR_getxattr",
            "__NR_lgetxattr",
            "__NR_fgetxattr",
            "__NR_listxattr",
            "__NR_llistxattr",
            "__NR_flistxattr",
            "__NR_removexattr",
            "__NR_lremovexattr",
            "__NR_fremovexattr",
            "__NR_tkill",
            "__NR_sendfile64",
            "__NR_futex",
            "__NR_sched_setaffinity",
            "__NR_sched_getaffinity",
            "__NR_set_thread_area",
            "__NR_get_thread_area",
            "__NR_io_setup",
            "__NR_io_destroy",
            "__NR_io_getevents",
            "__NR_io_submit",
            "__NR_io_cancel",
            "__NR_fadvise64",
            "__NR_unused",
            "__NR_exit_group",
            "__NR_lookup_dcookie",
            "__NR_epoll_create",
            "__NR_epoll_ctl",
            "__NR_epoll_wait",
            "__NR_remap_file_pages",
            "__NR_set_tid_address",
            "__NR_timer_create",
            "__NR_timer_settime",
            "__NR_timer_gettime",
            "__NR_timer_getoverrun",
            "__NR_timer_delete",
            "__NR_clock_settime",
            "__NR_clock_gettime",
            "__NR_clock_getres",
            "__NR_clock_nanosleep",
            "__NR_statfs64",
            "__NR_fstatfs64",
            "__NR_tgkill",
            "__NR_utimes",
            "__NR_fadvise64_64",
            "__NR_vserver",
            "__NR_mbind",
            "__NR_get_mempolicy",
            "__NR_set_mempolicy",
            "__NR_mq_open",
            "__NR_mq_unlink",
            "__NR_mq_timedsend",
            "__NR_mq_timedreceive",
            "__NR_mq_notify",
            "__NR_mq_getsetattr",
            "__NR_kexec_load",
            "__NR_waitid",
            "__NR_sys_setaltroot",
            "__NR_add_key",
            "__NR_request_key",
            "__NR_keyctl",
            "__NR_ioprio_set",
            "__NR_ioprio_get",
            "__NR_inotify_init",
            "__NR_inotify_add_watch",
            "__NR_inotify_rm_watch",
            "__NR_migrate_pages",
            "__NR_openat",
            "__NR_mkdirat",
            "__NR_mknodat",
            "__NR_fchownat",
            "__NR_futimesat",
            "__NR_fstatat64",
            "__NR_unlinkat",
            "__NR_renameat",
            "__NR_linkat",
            "__NR_symlinkat",
            "__NR_readlinkat",
            "__NR_fchmodat",
            "__NR_faccessat",
            "__NR_pselect6",
            "__NR_ppoll",
            "__NR_unshare",
            "__NR_set_robust_list",
            "__NR_get_robust_list",
            "__NR_splice",
            "__NR_sync_file_range",
            "__NR_tee",
            "__NR_vmsplice",
            "__NR_move_pages",
            "__NR_getcpu",
            "__NR_epoll_pwait",
            "__NR_utimensat",
            "__NR_signalfd",
            "__NR_timerfd_create",
            "__NR_eventfd",
            "__NR_fallocate",
            "__NR_timerfd_settime",
            "__NR_timerfd_gettime",
            "__NR_signalfd4",
            "__NR_eventfd2",
            "__NR_epoll_create1",
            "__NR_dup3",
            "__NR_pipe2",
            "__NR_inotify_init1",
            "__NR_preadv",
            "__NR_pwritev",
            "__NR_rt_tgsigqueueinfo",
            "__NR_perf_event_open"
        ]

        self.__args = {
            "sys_restart_syscall" : [],
            "sys_exit" : ["int error_code"],
            "sys_write" : ["unsigned int fd", "const char __user *buf", "size_t count"]
        }

    def get_syscall_name(self, index):
        """Convert a syscall index into a human name."""

        return self.__syscalls[index]


    def get_syscall_args(self, name):
        """Provide the function name, the needed registers and the used arguments of a syscall."""

        name = name.replace("__NR_", "sys_")

        try:
            args = self.__args[name]
        except:
            return None

        targets = {}

        if len(args) >= 4:

            pass

        else:

            registers = ["ebx", "ecx", "edx", "esi", "edi"]

            i = 0

            for val in args:
                targets[registers[i]] = val
                i = i + 1

        return [name, targets]




class LinuxSysCallsPlugin(Plugin):
    """A simple example class"""



    def __rendering_options(self, binary):
        """Build the options needed to get the ASM code only."""

        exe = binary.get_format()

        ropts = RenderingOptions(exe)

        ropts.show_code(True)
        ropts.show_address = False

        return ropts

    def __is_syscall(self, code):
        """Tell weither the ASM line of code is a syscall or not."""

        result = False

        if code.find("int") != -1:

            list = code.split("\t")

            if len(list) == 2:
                result = list == ["int", "0x80"]

        return result

    def __find_used_register(self, ropts, start, target):
        """Look for a given register used to supply argument for a syscall."""

        for found in reversed(start):

            code = found.get_text(ropts)
            parts = code.expandtabs(1).replace(",", "").split(" ")

            # Next registers are used by a new interruption
            if parts[0] == "int":
                continue

            # No operand
            if len(parts) == 1:
                continue

            # Only simple cases are processed...
            if parts[1] == target:
                if len(parts) == 2:
                    return None
                else:
                    return [found, parts[2]]

        return None

    def run(self, binary):
        """Resolve all registers / interruptions involved in a syscall."""

        db = SysCallDB()

        ropts = self.__rendering_options(binary)

        lines = binary.get_lines()

        for line in lines:
            code = line.get_text(ropts)
            if self.__is_syscall(code):

                # What kind of syscall ?

                line_eax = self.__find_used_register(ropts, line, "eax")

                if line_eax is None:
                    line.comment = "== UNIX Syscall =="
                    continue

                index = int(line_eax[1], 16)
                name = db.get_syscall_name(index)

                line_eax[0].comment = name

                # What function / arguments ?

                targets = db.get_syscall_args(name)

                if targets == None:

                    line.comment = "== UNIX Syscall =="
                    continue

                else:

                    line.comment = "== UNIX Syscall to %s() ==" % (targets[0])

                    for reg, arg in targets[1].items():

                        found = self.__find_used_register(ropts, line, reg)

                        if found != None:
                            found[0].comment = arg






def get_instance(binary):

    print " --- toto from Python ---"



    a = LinuxSysCallsPlugin()

    a.run(binary)

    return Plugin.PGA_CODE_PROCESS