diff --git a/CREDITS.md b/CREDITS.md index b1cba9447..b4e32331e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -53,6 +53,7 @@ - madprogrammer - danielmoos - sigeryang +- bet4it #### Legacy Core Developers diff --git a/README.md b/README.md index 48af44c75..ce7bee4b5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Qiling is an advanced binary emulation framework, with the following features: - Emulate multi-platforms: Windows, MacOS, Linux, BSD, UEFI, DOS, MBR, Ethereum Virtual Machine -- Emulate multi-architectures: X86, X86_64, Arm, Arm64, MIPS, 8086 +- Emulate multi-architectures: 8086, X86, X86_64, ARM, ARM64, MIPS, RISCV, PowerPC - Support multiple file formats: PE, MachO, ELF, COM, MBR - Support Windows Driver (.sys), Linux Kernel Module (.ko) & MacOS Kernel (.kext) via [Demigod](https://groundx.io/demigod/) - Emulates & sandbox code in an isolated environment diff --git a/examples/rootfs b/examples/rootfs index d8a9b0d6c..76780c9e9 160000 --- a/examples/rootfs +++ b/examples/rootfs @@ -1 +1 @@ -Subproject commit d8a9b0d6c52a3c5bc627c055d5f711dacbb1a1f6 +Subproject commit 76780c9e91471db1820b160d3b1d4a9ed6b13325 diff --git a/qiling/arch/ppc.py b/qiling/arch/ppc.py new file mode 100644 index 000000000..4e6d099db --- /dev/null +++ b/qiling/arch/ppc.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from functools import cached_property + +from unicorn import Uc, UC_ARCH_PPC, UC_MODE_PPC32, UC_MODE_BIG_ENDIAN +from capstone import Cs, CS_ARCH_PPC, CS_MODE_32, CS_MODE_BIG_ENDIAN +from keystone import Ks, KS_ARCH_PPC, KS_MODE_PPC32, KS_MODE_BIG_ENDIAN + +from qiling import Qiling +from qiling.arch.arch import QlArch +from qiling.arch import ppc_const +from qiling.arch.register import QlRegisterManager +from qiling.const import QL_ARCH, QL_ENDIAN + +class QlArchPPC(QlArch): + type = QL_ARCH.PPC + bits = 32 + + @cached_property + def uc(self) -> Uc: + return Uc(UC_ARCH_PPC, UC_MODE_PPC32 + UC_MODE_BIG_ENDIAN) + + @cached_property + def regs(self) -> QlRegisterManager: + regs_map = dict( + **ppc_const.reg_map, + **ppc_const.reg_float_map + ) + + pc_reg = 'pc' + sp_reg = 'r1' + + return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg) + + @cached_property + def disassembler(self) -> Cs: + return Cs(CS_ARCH_PPC, CS_MODE_32 + CS_MODE_BIG_ENDIAN) + + @cached_property + def assembler(self) -> Ks: + return Ks(KS_ARCH_PPC, KS_MODE_PPC32 + KS_MODE_BIG_ENDIAN) + + @property + def endian(self) -> QL_ENDIAN: + return QL_ENDIAN.EB + + def enable_float(self): + self.regs.msr = self.regs.msr | ppc_const.MSR.FP diff --git a/qiling/arch/ppc_const.py b/qiling/arch/ppc_const.py new file mode 100644 index 000000000..732cba1c9 --- /dev/null +++ b/qiling/arch/ppc_const.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from unicorn.ppc_const import * +from enum import IntEnum + +reg_map = { + "r0": UC_PPC_REG_0, + "r1": UC_PPC_REG_1, + "r2": UC_PPC_REG_2, + "r3": UC_PPC_REG_3, + "r4": UC_PPC_REG_4, + "r5": UC_PPC_REG_5, + "r6": UC_PPC_REG_6, + "r7": UC_PPC_REG_7, + "r8": UC_PPC_REG_8, + "r9": UC_PPC_REG_9, + "r10": UC_PPC_REG_10, + "r11": UC_PPC_REG_11, + "r12": UC_PPC_REG_12, + "r13": UC_PPC_REG_13, + "r14": UC_PPC_REG_14, + "r15": UC_PPC_REG_15, + "r16": UC_PPC_REG_16, + "r17": UC_PPC_REG_17, + "r18": UC_PPC_REG_18, + "r19": UC_PPC_REG_19, + "r20": UC_PPC_REG_20, + "r21": UC_PPC_REG_21, + "r22": UC_PPC_REG_22, + "r23": UC_PPC_REG_23, + "r24": UC_PPC_REG_24, + "r25": UC_PPC_REG_25, + "r26": UC_PPC_REG_26, + "r27": UC_PPC_REG_27, + "r28": UC_PPC_REG_28, + "r29": UC_PPC_REG_29, + "r30": UC_PPC_REG_30, + "r31": UC_PPC_REG_31, + "pc": UC_PPC_REG_PC, + "msr": UC_PPC_REG_MSR, + "cr": UC_PPC_REG_CR0, + "lr": UC_PPC_REG_LR, + "ctr": UC_PPC_REG_CTR, + "xer": UC_PPC_REG_XER, +} + +reg_float_map = { + "f0": UC_PPC_REG_FPR0, + "f1": UC_PPC_REG_FPR1, + "f2": UC_PPC_REG_FPR2, + "f3": UC_PPC_REG_FPR3, + "f4": UC_PPC_REG_FPR4, + "f5": UC_PPC_REG_FPR5, + "f6": UC_PPC_REG_FPR6, + "f7": UC_PPC_REG_FPR7, + "f8": UC_PPC_REG_FPR8, + "f9": UC_PPC_REG_FPR9, + "f10": UC_PPC_REG_FPR10, + "f11": UC_PPC_REG_FPR11, + "f12": UC_PPC_REG_FPR12, + "f13": UC_PPC_REG_FPR13, + "f14": UC_PPC_REG_FPR14, + "f15": UC_PPC_REG_FPR15, + "f16": UC_PPC_REG_FPR16, + "f17": UC_PPC_REG_FPR17, + "f18": UC_PPC_REG_FPR18, + "f19": UC_PPC_REG_FPR19, + "f20": UC_PPC_REG_FPR20, + "f21": UC_PPC_REG_FPR21, + "f22": UC_PPC_REG_FPR22, + "f23": UC_PPC_REG_FPR23, + "f24": UC_PPC_REG_FPR24, + "f25": UC_PPC_REG_FPR25, + "f26": UC_PPC_REG_FPR26, + "f27": UC_PPC_REG_FPR27, + "f28": UC_PPC_REG_FPR28, + "f29": UC_PPC_REG_FPR29, + "f30": UC_PPC_REG_FPR30, + "f31": UC_PPC_REG_FPR31, +} + +class MSR(IntEnum): + SF = 1 << 63 + TAG = 1 << 62 + ISF = 1 << 61 + HV = 1 << 60 + TS0 = 1 << 34 + TS1 = 1 << 33 + TM = 1 << 32 + CM = 1 << 31 + ICM = 1 << 30 + GS = 1 << 28 + UCLE = 1 << 26 + VR = 1 << 25 + SPE = 1 << 25 + AP = 1 << 23 + VSX = 1 << 23 + SA = 1 << 22 + KEY = 1 << 19 + POW = 1 << 18 + TGPR = 1 << 17 + CE = 1 << 17 + ILE = 1 << 16 + EE = 1 << 15 + PR = 1 << 14 + FP = 1 << 13 + ME = 1 << 12 + FE0 = 1 << 11 + SE = 1 << 10 + DWE = 1 << 10 + UBLE = 1 << 10 + BE = 1 << 9 + DE = 1 << 9 + FE1 = 1 << 8 + AL = 1 << 7 + EP = 1 << 6 + IR = 1 << 5 + DR = 1 << 4 + IS = 1 << 5 + DS = 1 << 4 + PE = 1 << 3 + PX = 1 << 2 + PMM = 1 << 2 + RI = 1 << 1 + LE = 1 << 0 diff --git a/qiling/arch/utils.py b/qiling/arch/utils.py index 2764bd72b..ab9583fb7 100644 --- a/qiling/arch/utils.py +++ b/qiling/arch/utils.py @@ -11,8 +11,8 @@ from os.path import basename from functools import lru_cache -from keystone import (Ks, KS_ARCH_ARM, KS_ARCH_ARM64, KS_ARCH_MIPS, KS_ARCH_X86, - KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_16, KS_MODE_32, KS_MODE_64, +from keystone import (Ks, KS_ARCH_ARM, KS_ARCH_ARM64, KS_ARCH_MIPS, KS_ARCH_X86, KS_ARCH_PPC, + KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_PPC32, KS_MODE_16, KS_MODE_32, KS_MODE_64, KS_MODE_LITTLE_ENDIAN, KS_MODE_BIG_ENDIAN) from qiling import Qiling @@ -104,7 +104,8 @@ def assembler(arch: QL_ARCH, endianess: QL_ENDIAN, is_thumb: bool) -> Ks: QL_ARCH.MIPS : (KS_ARCH_MIPS, KS_MODE_MIPS32 + endian), QL_ARCH.A8086 : (KS_ARCH_X86, KS_MODE_16), QL_ARCH.X86 : (KS_ARCH_X86, KS_MODE_32), - QL_ARCH.X8664 : (KS_ARCH_X86, KS_MODE_64) + QL_ARCH.X8664 : (KS_ARCH_X86, KS_MODE_64), + QL_ARCH.PPC : (KS_ARCH_PPC, KS_MODE_PPC32 + KS_MODE_BIG_ENDIAN) } if arch in asm_map: diff --git a/qiling/cc/ppc.py b/qiling/cc/ppc.py new file mode 100644 index 000000000..9f19e8818 --- /dev/null +++ b/qiling/cc/ppc.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework + +from qiling.cc import QlCommonBaseCC + +from unicorn.ppc_const import ( + UC_PPC_REG_3, UC_PPC_REG_4, UC_PPC_REG_5, + UC_PPC_REG_6, UC_PPC_REG_7, UC_PPC_REG_8, +) + + +class ppc(QlCommonBaseCC): + """Default calling convention for PPC + First 6 arguments are passed in regs, the rest are passed on the stack. + """ + + _retreg = UC_PPC_REG_3 + _argregs = (UC_PPC_REG_3, UC_PPC_REG_4, UC_PPC_REG_5, UC_PPC_REG_6, UC_PPC_REG_7, UC_PPC_REG_8) + (None, ) * 10 + + @staticmethod + def getNumSlots(argbits: int): + return 1 diff --git a/qiling/const.py b/qiling/const.py index 9548a7219..1f97e4cbb 100644 --- a/qiling/const.py +++ b/qiling/const.py @@ -21,6 +21,7 @@ class QL_ARCH(IntEnum): CORTEX_M = 109 RISCV = 110 RISCV64 = 111 + PPC = 112 class QL_OS(IntEnum): LINUX = 201 diff --git a/qiling/os/blob/blob.py b/qiling/os/blob/blob.py index 3f1b6fff5..02e6f94d3 100644 --- a/qiling/os/blob/blob.py +++ b/qiling/os/blob/blob.py @@ -4,7 +4,7 @@ # from qiling import Qiling -from qiling.cc import QlCC, intel, arm, mips +from qiling.cc import QlCC, intel, arm, mips, riscv, ppc from qiling.const import QL_ARCH, QL_OS from qiling.os.fcall import QlFunctionCall from qiling.os.os import QlOs @@ -26,11 +26,14 @@ def __init__(self, ql: Qiling): self.ql = ql cc: QlCC = { - QL_ARCH.X86 : intel.cdecl, - QL_ARCH.X8664 : intel.amd64, - QL_ARCH.ARM : arm.aarch32, - QL_ARCH.ARM64 : arm.aarch64, - QL_ARCH.MIPS : mips.mipso32 + QL_ARCH.X86 : intel.cdecl, + QL_ARCH.X8664 : intel.amd64, + QL_ARCH.ARM : arm.aarch32, + QL_ARCH.ARM64 : arm.aarch64, + QL_ARCH.MIPS : mips.mipso32, + QL_ARCH.RISCV : riscv.riscv, + QL_ARCH.RISCV64 : riscv.riscv, + QL_ARCH.PPC : ppc.ppc, }[ql.arch.type](ql.arch) self.fcall = QlFunctionCall(ql, cc) diff --git a/qiling/os/linux/function_hook.py b/qiling/os/linux/function_hook.py index 7b07c2e58..aa2e6cf64 100644 --- a/qiling/os/linux/function_hook.py +++ b/qiling/os/linux/function_hook.py @@ -79,6 +79,10 @@ def get_ret_pc(self): elif self.ql.arch.type == QL_ARCH.ARM64: return self.ql.arch.regs.x30 + # PPC + elif self.ql.arch.type== QL_ARCH.PPC: + return self.ql.arch.regs.lr + # X86 elif self.ql.arch.type == QL_ARCH.X86: return self.ql.unpack(self.ql.mem.read(self.ql.arch.regs.esp, self.ql.arch.pointersize)) @@ -98,6 +102,10 @@ def context_fixup(self): elif self.ql.arch.type == QL_ARCH.MIPS: pass + # PPC + elif self.ql.arch.type== QL_ARCH.PPC: + pass + # ARM64 elif self.ql.arch.type == QL_ARCH.ARM64: pass @@ -121,6 +129,10 @@ def set_ret(self, addr): elif self.ql.arch.type == QL_ARCH.MIPS: self.ql.arch.regs.ra = addr + # PPC + elif self.ql.arch.type== QL_ARCH.PPC: + self.ql.arch.regs.lr = addr + # ARM64 elif self.ql.arch.type == QL_ARCH.ARM64: self.ql.arch.stack_write(0, addr) @@ -173,29 +185,6 @@ def call_enter(self): else: self.context_fixup() - def ret(self): - # ARM - if self.ql.arch.type == QL_ARCH.ARM: - self.ql.arch.regs.arch_pc = self.ret_pc - - # MIPS32 - elif self.ql.arch.type == QL_ARCH.MIPS: - self.ql.arch.regs.arch_pc = self.ret_pc - - # ARM64 - elif self.ql.arch.type == QL_ARCH.ARM64: - self.ql.arch.regs.arch_pc = self.ret_pc - - # X86 - elif self.ql.arch.type == QL_ARCH.X86: - self.ql.arch.regs.arch_pc = self.ret_pc - - # X8664 - elif self.ql.arch.type == QL_ARCH.X8664: - self.ql.arch.regs.arch_pc = self.ret_pc - else: - raise - def call_exit(self): # if self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64: # self.ql.arch.regs.arch_pc = self.ql.arch.regs.arch_pc + 4 @@ -211,7 +200,7 @@ def call_exit(self): else: onexit_cb(self.ql, onexit_userdata) - self.ret() + self.ql.arch.regs.arch_pc = self.ret_pc class HookFuncRel(HookFunc): @@ -614,6 +603,14 @@ def __init__(self, ql, phoff, phnum, phentsize, load_base, hook_mem): ins = b'\x00\x01' self.add_function_hook = self.add_function_hook_relocation + # PowerPC + elif self.ql.arch.type== QL_ARCH.PPC: + self.GLOB_DAT = 21 + self.JMP_SLOT = 22 + # nop + ins = b'\x60\x00\x00\x00' + self.add_function_hook = self.add_function_hook_relocation + self._parse() if self.rel != None: self.show_relocation(self.rel) diff --git a/qiling/os/linux/linux.py b/qiling/os/linux/linux.py index 34a2a733e..ed95a7ecd 100644 --- a/qiling/os/linux/linux.py +++ b/qiling/os/linux/linux.py @@ -10,7 +10,7 @@ from qiling.arch.x86_const import GS_SEGMENT_ADDR, GS_SEGMENT_SIZE from qiling.arch.x86_utils import GDTManager, SegmentManager86, SegmentManager64 from qiling.arch import arm_utils -from qiling.cc import QlCC, intel, arm, mips, riscv +from qiling.cc import QlCC, intel, arm, mips, riscv, ppc from qiling.const import QL_ARCH, QL_OS from qiling.os.fcall import QlFunctionCall from qiling.os.const import * @@ -28,13 +28,14 @@ def __init__(self, ql: Qiling): self.ql = ql cc: QlCC = { - QL_ARCH.X86 : intel.cdecl, - QL_ARCH.X8664 : intel.amd64, - QL_ARCH.ARM : arm.aarch32, - QL_ARCH.ARM64 : arm.aarch64, - QL_ARCH.MIPS : mips.mipso32, - QL_ARCH.RISCV : riscv.riscv, - QL_ARCH.RISCV64: riscv.riscv, + QL_ARCH.X86 : intel.cdecl, + QL_ARCH.X8664 : intel.amd64, + QL_ARCH.ARM : arm.aarch32, + QL_ARCH.ARM64 : arm.aarch64, + QL_ARCH.MIPS : mips.mipso32, + QL_ARCH.RISCV : riscv.riscv, + QL_ARCH.RISCV64 : riscv.riscv, + QL_ARCH.PPC : ppc.ppc, }[ql.arch.type](ql.arch) self.fcall = QlFunctionCall(ql, cc) @@ -107,6 +108,11 @@ def load(self): self.ql.hook_intno(self.hook_syscall, 8) self.thread_class = None + elif self.ql.arch.type == QL_ARCH.PPC: + self.ql.arch.enable_float() + self.ql.hook_intno(self.hook_syscall, 8) + self.thread_class = None + # on fork or execve, do not inherit opened files tagged as 'close on exec' for i in range(len(self.fd)): if getattr(self.fd[i], 'close_on_exec', 0): diff --git a/qiling/os/linux/map_syscall.py b/qiling/os/linux/map_syscall.py index 19bb42695..3d0bcead8 100644 --- a/qiling/os/linux/map_syscall.py +++ b/qiling/os/linux/map_syscall.py @@ -18,8 +18,9 @@ def get_syscall_mapper(archtype: QL_ARCH): QL_ARCH.X8664 : x8664_syscall_table, QL_ARCH.X86 : x86_syscall_table, QL_ARCH.MIPS : mips_syscall_table, - QL_ARCH.RISCV : riscv32_syscall_table, - QL_ARCH.RISCV64 : riscv64_syscall_table + QL_ARCH.RISCV : riscv32_syscall_table, + QL_ARCH.RISCV64 : riscv64_syscall_table, + QL_ARCH.PPC : ppc_syscall_table }[archtype] def __mapper(syscall_num: int) -> str: @@ -2550,3 +2551,421 @@ def __mapper(syscall_num: int) -> str: 446: "landlock_restrict_self", 448: "process_mrelease", } + +ppc_syscall_table = { + 0: "restart_syscall", + 1: "exit", + 2: "fork", + 3: "read", + 4: "write", + 5: "open", + 6: "close", + 7: "waitpid", + 8: "creat", + 9: "link", + 10: "unlink", + 11: "execve", + 12: "chdir", + 13: "time", + 14: "mknod", + 15: "chmod", + 16: "lchown", + 17: "break", + 18: "oldstat", + 19: "lseek", + 20: "getpid", + 21: "mount", + 22: "umount", + 23: "setuid", + 24: "getuid", + 25: "stime", + 26: "ptrace", + 27: "alarm", + 28: "oldfstat", + 29: "pause", + 30: "utime", + 31: "stty", + 32: "gtty", + 33: "access", + 34: "nice", + 35: "ftime", + 36: "sync", + 37: "kill", + 38: "rename", + 39: "mkdir", + 40: "rmdir", + 41: "dup", + 42: "pipe", + 43: "times", + 44: "prof", + 45: "brk", + 46: "setgid", + 47: "getgid", + 48: "signal", + 49: "geteuid", + 50: "getegid", + 51: "acct", + 52: "umount2", + 53: "lock", + 54: "ioctl", + 55: "fcntl", + 56: "mpx", + 57: "setpgid", + 58: "ulimit", + 59: "oldolduname", + 60: "umask", + 61: "chroot", + 62: "ustat", + 63: "dup2", + 64: "getppid", + 65: "getpgrp", + 66: "setsid", + 67: "sigaction", + 68: "sgetmask", + 69: "ssetmask", + 70: "setreuid", + 71: "setregid", + 72: "sigsuspend", + 73: "sigpending", + 74: "sethostname", + 75: "setrlimit", + 76: "getrlimit", + 77: "getrusage", + 78: "gettimeofday", + 79: "settimeofday", + 80: "getgroups", + 81: "setgroups", + 82: "select", + 83: "symlink", + 84: "oldlstat", + 85: "readlink", + 86: "uselib", + 87: "swapon", + 88: "reboot", + 89: "readdir", + 90: "mmap", + 91: "munmap", + 92: "truncate", + 93: "ftruncate", + 94: "fchmod", + 95: "fchown", + 96: "getpriority", + 97: "setpriority", + 98: "profil", + 99: "statfs", + 100: "fstatfs", + 101: "ioperm", + 102: "socketcall", + 103: "syslog", + 104: "setitimer", + 105: "getitimer", + 106: "stat", + 107: "lstat", + 108: "fstat", + 109: "olduname", + 110: "iopl", + 111: "vhangup", + 112: "idle", + 113: "vm86", + 114: "wait4", + 115: "swapoff", + 116: "sysinfo", + 117: "ipc", + 118: "fsync", + 119: "sigreturn", + 120: "clone", + 121: "setdomainname", + 122: "uname", + 123: "modify_ldt", + 124: "adjtimex", + 125: "mprotect", + 126: "sigprocmask", + 127: "create_module", + 128: "init_module", + 129: "delete_module", + 130: "get_kernel_syms", + 131: "quotactl", + 132: "getpgid", + 133: "fchdir", + 134: "bdflush", + 135: "sysfs", + 136: "personality", + 137: "afs_syscall", + 138: "setfsuid", + 139: "setfsgid", + 140: "_llseek", + 141: "getdents", + 142: "_newselect", + 143: "flock", + 144: "msync", + 145: "readv", + 146: "writev", + 147: "getsid", + 148: "fdatasync", + 149: "_sysctl", + 150: "mlock", + 151: "munlock", + 152: "mlockall", + 153: "munlockall", + 154: "sched_setparam", + 155: "sched_getparam", + 156: "sched_setscheduler", + 157: "sched_getscheduler", + 158: "sched_yield", + 159: "sched_get_priority_max", + 160: "sched_get_priority_min", + 161: "sched_rr_get_interval", + 162: "nanosleep", + 163: "mremap", + 164: "setresuid", + 165: "getresuid", + 166: "query_module", + 167: "poll", + 168: "nfsservctl", + 169: "setresgid", + 170: "getresgid", + 171: "prctl", + 172: "rt_sigreturn", + 173: "rt_sigaction", + 174: "rt_sigprocmask", + 175: "rt_sigpending", + 176: "rt_sigtimedwait", + 177: "rt_sigqueueinfo", + 178: "rt_sigsuspend", + 179: "pread64", + 180: "pwrite64", + 181: "chown", + 182: "getcwd", + 183: "capget", + 184: "capset", + 185: "sigaltstack", + 186: "sendfile", + 187: "getpmsg", + 188: "putpmsg", + 189: "vfork", + 190: "ugetrlimit", + 191: "readahead", + 192: "mmap2", + 193: "truncate64", + 194: "ftruncate64", + 195: "stat64", + 196: "lstat64", + 197: "fstat64", + 198: "pciconfig_read", + 199: "pciconfig_write", + 200: "pciconfig_iobase", + 201: "multiplexer", + 202: "getdents64", + 203: "pivot_root", + 204: "fcntl64", + 205: "madvise", + 206: "mincore", + 207: "gettid", + 208: "tkill", + 209: "setxattr", + 210: "lsetxattr", + 211: "fsetxattr", + 212: "getxattr", + 213: "lgetxattr", + 214: "fgetxattr", + 215: "listxattr", + 216: "llistxattr", + 217: "flistxattr", + 218: "removexattr", + 219: "lremovexattr", + 220: "fremovexattr", + 221: "futex", + 222: "sched_setaffinity", + 223: "sched_getaffinity", + 225: "tuxcall", + 226: "sendfile64", + 227: "io_setup", + 228: "io_destroy", + 229: "io_getevents", + 230: "io_submit", + 231: "io_cancel", + 232: "set_tid_address", + 233: "fadvise64", + 234: "exit_group", + 235: "lookup_dcookie", + 236: "epoll_create", + 237: "epoll_ctl", + 238: "epoll_wait", + 239: "remap_file_pages", + 240: "timer_create", + 241: "timer_settime", + 242: "timer_gettime", + 243: "timer_getoverrun", + 244: "timer_delete", + 245: "clock_settime", + 246: "clock_gettime", + 247: "clock_getres", + 248: "clock_nanosleep", + 249: "swapcontext", + 250: "tgkill", + 251: "utimes", + 252: "statfs64", + 253: "fstatfs64", + 254: "fadvise64_64", + 255: "rtas", + 256: "sys_debug_setcontext", + 258: "migrate_pages", + 259: "mbind", + 260: "get_mempolicy", + 261: "set_mempolicy", + 262: "mq_open", + 263: "mq_unlink", + 264: "mq_timedsend", + 265: "mq_timedreceive", + 266: "mq_notify", + 267: "mq_getsetattr", + 268: "kexec_load", + 269: "add_key", + 270: "request_key", + 271: "keyctl", + 272: "waitid", + 273: "ioprio_set", + 274: "ioprio_get", + 275: "inotify_init", + 276: "inotify_add_watch", + 277: "inotify_rm_watch", + 278: "spu_run", + 279: "spu_create", + 280: "pselect6", + 281: "ppoll", + 282: "unshare", + 283: "splice", + 284: "tee", + 285: "vmsplice", + 286: "openat", + 287: "mkdirat", + 288: "mknodat", + 289: "fchownat", + 290: "futimesat", + 291: "fstatat64", + 292: "unlinkat", + 293: "renameat", + 294: "linkat", + 295: "symlinkat", + 296: "readlinkat", + 297: "fchmodat", + 298: "faccessat", + 299: "get_robust_list", + 300: "set_robust_list", + 301: "move_pages", + 302: "getcpu", + 303: "epoll_pwait", + 304: "utimensat", + 305: "signalfd", + 306: "timerfd_create", + 307: "eventfd", + 308: "sync_file_range2", + 309: "fallocate", + 310: "subpage_prot", + 311: "timerfd_settime", + 312: "timerfd_gettime", + 313: "signalfd4", + 314: "eventfd2", + 315: "epoll_create1", + 316: "dup3", + 317: "pipe2", + 318: "inotify_init1", + 319: "perf_event_open", + 320: "preadv", + 321: "pwritev", + 322: "rt_tgsigqueueinfo", + 323: "fanotify_init", + 324: "fanotify_mark", + 325: "prlimit64", + 326: "socket", + 327: "bind", + 328: "connect", + 329: "listen", + 330: "accept", + 331: "getsockname", + 332: "getpeername", + 333: "socketpair", + 334: "send", + 335: "sendto", + 336: "recv", + 337: "recvfrom", + 338: "shutdown", + 339: "setsockopt", + 340: "getsockopt", + 341: "sendmsg", + 342: "recvmsg", + 343: "recvmmsg", + 344: "accept4", + 345: "name_to_handle_at", + 346: "open_by_handle_at", + 347: "clock_adjtime", + 348: "syncfs", + 349: "sendmmsg", + 350: "setns", + 351: "process_vm_readv", + 352: "process_vm_writev", + 353: "finit_module", + 354: "kcmp", + 355: "sched_setattr", + 356: "sched_getattr", + 357: "renameat2", + 358: "seccomp", + 359: "getrandom", + 360: "memfd_create", + 361: "bpf", + 362: "execveat", + 363: "switch_endian", + 364: "userfaultfd", + 365: "membarrier", + 378: "mlock2", + 379: "copy_file_range", + 380: "preadv2", + 381: "pwritev2", + 382: "kexec_file_load", + 383: "statx", + 384: "pkey_alloc", + 385: "pkey_free", + 386: "pkey_mprotect", + 387: "rseq", + 388: "io_pgetevents", + 393: "semget", + 394: "semctl", + 395: "shmget", + 396: "shmctl", + 397: "shmat", + 398: "shmdt", + 399: "msgget", + 400: "msgsnd", + 401: "msgrcv", + 402: "msgctl", + 403: "clock_gettime64", + 404: "clock_settime64", + 405: "clock_adjtime64", + 406: "clock_getres_time64", + 407: "clock_nanosleep_time64", + 408: "timer_gettime64", + 409: "timer_settime64", + 410: "timerfd_gettime64", + 411: "timerfd_settime64", + 412: "utimensat_time64", + 413: "pselect6_time64", + 414: "ppoll_time64", + 416: "io_pgetevents_time64", + 417: "recvmmsg_time64", + 418: "mq_timedsend_time64", + 419: "mq_timedreceive_time64", + 420: "semtimedop_time64", + 421: "rt_sigtimedwait_time64", + 422: "futex_time64", + 423: "sched_rr_get_interval_time64", + 424: "pidfd_send_signal", + 425: "io_uring_setup", + 426: "io_uring_enter", + 427: "io_uring_register", + 428: "open_tree", + 429: "move_mount", + 430: "fsopen", + 431: "fsconfig", + 432: "fsmount", + 433: "fspick", +} diff --git a/qiling/os/posix/const.py b/qiling/os/posix/const.py index 51a03393e..7340a758a 100644 --- a/qiling/os/posix/const.py +++ b/qiling/os/posix/const.py @@ -515,6 +515,24 @@ 'O_LARGEFILE': None, } +linux_ppc_open_flags = { + 'O_RDONLY': 0x0, + 'O_WRONLY': 0x1, + 'O_RDWR': 0x2, + 'O_NONBLOCK': 0x800, + 'O_APPEND': 0x400, + 'O_ASYNC': 0x2000, + 'O_SYNC': 0x101000, + 'O_NOFOLLOW': 0x8000, + 'O_CREAT': 0x40, + 'O_TRUNC': 0x200, + 'O_EXCL': 0x80, + 'O_NOCTTY': 0x100, + 'O_DIRECTORY': 0x4000, + 'O_BINARY' : None, + 'O_LARGEFILE': 0x10000, +} + freebsd_x86_open_flags = { 'O_RDONLY': 0x0, 'O_WRONLY': 0x1, diff --git a/qiling/os/posix/const_mapping.py b/qiling/os/posix/const_mapping.py index 2dd15a21f..e04030c34 100644 --- a/qiling/os/posix/const_mapping.py +++ b/qiling/os/posix/const_mapping.py @@ -61,6 +61,8 @@ def flag_mapping(flags, mapping_name, mapping_from, mapping_to): f = linux_mips_open_flags elif ql.arch.type in (QL_ARCH.RISCV, QL_ARCH.RISCV64): f = linux_riscv_open_flags + elif ql.arch.type == QL_ARCH.PPC: + f = linux_ppc_open_flags elif virt_os == QL_OS.MACOS: if ql.arch.type in (QL_ARCH.X86, QL_ARCH.X8664): diff --git a/qiling/os/posix/posix.py b/qiling/os/posix/posix.py index d71069ba0..73278057a 100644 --- a/qiling/os/posix/posix.py +++ b/qiling/os/posix/posix.py @@ -19,9 +19,10 @@ UC_X86_REG_R9, UC_X86_REG_RAX ) from unicorn.riscv_const import UC_RISCV_REG_A7 +from unicorn.ppc_const import UC_PPC_REG_0 from qiling import Qiling -from qiling.cc import QlCC, intel, arm, mips, riscv +from qiling.cc import QlCC, intel, arm, mips, riscv, ppc from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT from qiling.exception import QlErrorSyscallNotFound from qiling.os.os import QlOs @@ -60,6 +61,9 @@ class riscv32(riscv.riscv): class riscv64(riscv.riscv): pass +class ppc(ppc.ppc): + pass + class QlFileDes: def __init__(self): @@ -115,7 +119,8 @@ def __init__(self, ql: Qiling): QL_ARCH.X86 : UC_X86_REG_EAX, QL_ARCH.X8664 : UC_X86_REG_RAX, QL_ARCH.RISCV : UC_RISCV_REG_A7, - QL_ARCH.RISCV64 : UC_RISCV_REG_A7 + QL_ARCH.RISCV64 : UC_RISCV_REG_A7, + QL_ARCH.PPC : UC_PPC_REG_0 }[self.ql.arch.type] # handle some special cases @@ -133,7 +138,8 @@ def __init__(self, ql: Qiling): QL_ARCH.X86 : intel32, QL_ARCH.X8664 : intel64, QL_ARCH.RISCV : riscv32, - QL_ARCH.RISCV64 : riscv64 + QL_ARCH.RISCV64 : riscv64, + QL_ARCH.PPC : ppc }[self.ql.arch.type](self.ql.arch) # select syscall mapping function based on emulated OS and architecture diff --git a/qiling/os/posix/syscall/stat.py b/qiling/os/posix/syscall/stat.py index 1c7130161..01efd092d 100644 --- a/qiling/os/posix/syscall/stat.py +++ b/qiling/os/posix/syscall/stat.py @@ -819,6 +819,108 @@ class LinuxRISCVStat(ctypes.Structure): _pack_ = 8 +# Srouce: https://elixir.bootlin.com/linux/latest/source/arch/powerpc/include/uapi/asm/stat.h#L30 +# struct stat { +# unsigned long st_dev; +# ino_t st_ino; +# #ifdef __powerpc64__ +# unsigned long st_nlink; +# mode_t st_mode; +# #else +# mode_t st_mode; +# unsigned short st_nlink; +# #endif +# uid_t st_uid; +# gid_t st_gid; +# unsigned long st_rdev; +# long st_size; +# unsigned long st_blksize; +# unsigned long st_blocks; +# unsigned long st_atime; +# unsigned long st_atime_nsec; +# unsigned long st_mtime; +# unsigned long st_mtime_nsec; +# unsigned long st_ctime; +# unsigned long st_ctime_nsec; +# unsigned long __unused4; +# unsigned long __unused5; +# #ifdef __powerpc64__ +# unsigned long __unused6; +# #endif +# }; + +class LinuxPPCStat(ctypes.BigEndianStructure): + _fields_ = [ + ("st_dev", ctypes.c_uint32), + ("st_ino", ctypes.c_uint32), + ("st_mode", ctypes.c_uint32), + ("st_nlink", ctypes.c_uint16), + ("st_uid", ctypes.c_uint32), + ("st_gid", ctypes.c_uint32), + ("st_rdev", ctypes.c_uint32), + ("st_size", ctypes.c_uint32), + ("st_blksize", ctypes.c_uint32), + ("st_blocks", ctypes.c_uint32), + ("st_atime", ctypes.c_uint32), + ("st_atime_ns", ctypes.c_uint32), + ("st_mtime", ctypes.c_uint32), + ("st_mtime_ns", ctypes.c_uint32), + ("st_ctime", ctypes.c_uint32), + ("st_ctime_ns", ctypes.c_uint32), + ("__unused4", ctypes.c_uint32), + ("__unused5", ctypes.c_uint32) + ] + + _pack_ = 8 + +# Srouce: https://elixir.bootlin.com/linux/latest/source/arch/powerpc/include/uapi/asm/stat.h#L60 +# struct stat64 { +# unsigned long long st_dev; /* Device. */ +# unsigned long long st_ino; /* File serial number. */ +# unsigned int st_mode; /* File mode. */ +# unsigned int st_nlink; /* Link count. */ +# unsigned int st_uid; /* User ID of the file's owner. */ +# unsigned int st_gid; /* Group ID of the file's group. */ +# unsigned long long st_rdev; /* Device number, if device. */ +# unsigned short __pad2; +# long long st_size; /* Size of file, in bytes. */ +# int st_blksize; /* Optimal block size for I/O. */ +# long long st_blocks; /* Number 512-byte blocks allocated. */ +# int st_atime; /* Time of last access. */ +# unsigned int st_atime_nsec; +# int st_mtime; /* Time of last modification. */ +# unsigned int st_mtime_nsec; +# int st_ctime; /* Time of last status change. */ +# unsigned int st_ctime_nsec; +# unsigned int __unused4; +# unsigned int __unused5; +# }; + +class LinuxPPCStat64(ctypes.BigEndianStructure): + _fields_ = [ + ("st_dev", ctypes.c_uint64), + ("st_ino", ctypes.c_uint64), + ("st_mode", ctypes.c_uint32), + ("st_nlink", ctypes.c_uint32), + ("st_uid", ctypes.c_uint32), + ("st_gid", ctypes.c_uint32), + ("st_rdev", ctypes.c_uint64), + ("__pad2", ctypes.c_uint16), + ("st_size", ctypes.c_uint64), + ("st_blksize", ctypes.c_uint32), + ("st_blocks", ctypes.c_uint64), + ("st_atime", ctypes.c_uint32), + ("st_atime_ns", ctypes.c_uint32), + ("st_mtime", ctypes.c_uint32), + ("st_mtime_ns", ctypes.c_uint32), + ("st_ctime", ctypes.c_uint32), + ("st_ctime_ns", ctypes.c_uint32), + ("__unused4", ctypes.c_uint32), + ("__unused5", ctypes.c_uint32) + ] + + _pack_ = 8 + # Source: openqnx lib/c/public/sys/stat.h # # struct stat { @@ -991,6 +1093,8 @@ def get_stat64_struct(ql: Qiling): return LinuxARMStat64() elif ql.arch.type in (QL_ARCH.RISCV, QL_ARCH.RISCV64): return LinuxRISCVStat() + elif ql.arch.type == QL_ARCH.PPC: + return LinuxPPCStat64() elif ql.os.type == QL_OS.MACOS: return MacOSStat64() elif ql.os.type == QL_OS.QNX: @@ -1034,6 +1138,8 @@ def get_stat_struct(ql: Qiling): return LinuxARM64EBStat() elif ql.arch.type in (QL_ARCH.RISCV, QL_ARCH.RISCV64): return LinuxRISCVStat() + elif ql.archtype == QL_ARCH.PPC: + return LinuxPPCStat() elif ql.os.type == QL_OS.QNX: if ql.arch.type == QL_ARCH.ARM64: return QNXARM64Stat() diff --git a/qiling/os/qnx/qnx.py b/qiling/os/qnx/qnx.py index 8c1b89c15..9f79f1d95 100644 --- a/qiling/os/qnx/qnx.py +++ b/qiling/os/qnx/qnx.py @@ -14,7 +14,7 @@ from qiling.os.qnx.helpers import QnxConn from qiling.os.qnx.structs import _thread_local_storage -from qiling.cc import QlCC, intel, arm, mips, riscv +from qiling.cc import QlCC, intel, arm, mips, riscv, ppc from qiling.const import QL_ARCH, QL_OS from qiling.os.fcall import QlFunctionCall from qiling.os.const import * @@ -29,13 +29,14 @@ def __init__(self, ql: Qiling): self.ql = ql cc: QlCC = { - QL_ARCH.X86 : intel.cdecl, - QL_ARCH.X8664 : intel.amd64, - QL_ARCH.ARM : arm.aarch32, - QL_ARCH.ARM64 : arm.aarch64, - QL_ARCH.MIPS : mips.mipso32, - QL_ARCH.RISCV : riscv.riscv, - QL_ARCH.RISCV64: riscv.riscv, + QL_ARCH.X86 : intel.cdecl, + QL_ARCH.X8664 : intel.amd64, + QL_ARCH.ARM : arm.aarch32, + QL_ARCH.ARM64 : arm.aarch64, + QL_ARCH.MIPS : mips.mipso32, + QL_ARCH.RISCV : riscv.riscv, + QL_ARCH.RISCV64 : riscv.riscv, + QL_ARCH.PPC : ppc.ppc, }[ql.arch.type](ql.arch) self.fcall = QlFunctionCall(ql, cc) diff --git a/qiling/utils.py b/qiling/utils.py index 08573a008..68607fe60 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -126,6 +126,7 @@ def __emu_env_from_elf(path: str) -> Tuple[Optional[QL_ARCH], Optional[QL_OS], O EM_X86_64 = 62 EM_AARCH64 = 183 EM_RISCV = 243 + EM_PPC = 20 endianess = { ELFDATA2LSB : (QL_ENDIAN.EL, 'little'), @@ -136,7 +137,8 @@ def __emu_env_from_elf(path: str) -> Tuple[Optional[QL_ARCH], Optional[QL_OS], O EM_386 : QL_ARCH.X86, EM_MIPS : QL_ARCH.MIPS, EM_ARM : QL_ARCH.ARM, - EM_RISCV : QL_ARCH.RISCV + EM_RISCV : QL_ARCH.RISCV, + EM_PPC : QL_ARCH.PPC } machines64 = { @@ -381,7 +383,8 @@ def select_arch(archtype: QL_ARCH, endian: QL_ENDIAN, thumb: bool) -> QlClassIni QL_ARCH.EVM : r'evm.evm', QL_ARCH.CORTEX_M : r'cortex_m', QL_ARCH.RISCV : r'riscv', - QL_ARCH.RISCV64 : r'riscv64' + QL_ARCH.RISCV64 : r'riscv64', + QL_ARCH.PPC : r'ppc' }[archtype] qlarch_path = f'.arch.{module}' diff --git a/tests/test_elf.py b/tests/test_elf.py index 49407a310..8ce4712a4 100644 --- a/tests/test_elf.py +++ b/tests/test_elf.py @@ -820,6 +820,12 @@ def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): del ql + def test_elf_linux_powerpc(self): + ql = Qiling(["../examples/rootfs/powerpc_linux/bin/powerpc_hello"], "../examples/rootfs/powerpc_linux", verbose=QL_VERBOSE.DEBUG) + ql.run() + del ql + + def test_elf_linux_arm_custom_syscall(self): def my_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): regreturn = 0