Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions examples/hello_mips64_linux_customapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

# NOTE: this runs a dynamically-linked glibc MIPS64 (n64) binary, which links
# above the 2GB useg and uses ll/sc locks. It requires the MIPS64 virtual-TLB
# fixes in unicorn (https://github.com/unicorn-engine/unicorn issues #2272 /
# the ll-AdEL fix); on an unfixed unicorn it raises a spurious RI/AdEL.

import sys
sys.path.append("..")

from qiling import Qiling
from qiling.os.const import STRING
from qiling.const import QL_VERBOSE

def my_puts(ql: Qiling):
params = ql.os.resolve_fcall_params({'s': STRING})

print(f'puts("{params["s"]}")')

if __name__ == "__main__":
ql = Qiling(["rootfs/mips64_linux/bin/mips64_hello"], "rootfs/mips64_linux", verbose=QL_VERBOSE.DEBUG)
ql.os.set_api("puts", my_puts)
ql.run()
26 changes: 26 additions & 0 deletions examples/hello_mips64el_linux_customapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

# NOTE: this runs a dynamically-linked glibc MIPS64 (n64, little-endian) binary,
# which links above the 2GB useg and uses ll/sc locks. It requires the MIPS64
# virtual-TLB fixes in unicorn (https://github.com/unicorn-engine/unicorn issues
# #2272 / the ll-AdEL fix); on an unfixed unicorn it raises a spurious RI/AdEL.

import sys
sys.path.append("..")

from qiling import Qiling
from qiling.os.const import STRING
from qiling.const import QL_VERBOSE

def my_puts(ql: Qiling):
params = ql.os.resolve_fcall_params({'s': STRING})

print(f'puts("{params["s"]}")')

if __name__ == "__main__":
ql = Qiling(["rootfs/mips64el_linux/bin/mips64el_hello"], "rootfs/mips64el_linux", verbose=QL_VERBOSE.DEBUG)
ql.os.set_api("puts", my_puts)
ql.run()
17 changes: 17 additions & 0 deletions examples/multithreading_mips64_linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import sys
sys.path.append("..")

from qiling import Qiling
from qiling.const import QL_VERBOSE

def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG, multithread=True)
ql.run()

if __name__ == "__main__":
my_sandbox(["rootfs/mips64_linux/bin/mips64_multithreading"], "rootfs/mips64_linux")
53 changes: 53 additions & 0 deletions examples/shellcode_mips64_run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import sys

sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_ARCH, QL_OS, QL_ENDIAN, QL_VERBOSE

# MIPS64 n64 shellcode: write(1, "MIPS64 hello\n", 13) then exit_group(0).
#
# it uses the n64 syscall numbers (write=5001, exit_group=5205) and computes the
# address of the message with a bal/daddiu pc-relative trick. assembled with
# binutils mips64-linux-gnuabi64-as:
#
# .set noreorder
# __start:
# li $v0, 5001 # __NR_write
# li $a0, 1 # fd = stdout
# bal load
# nop
# load:
# daddiu $a1, $ra, (msg - load) # a1 = &msg
# li $a2, 13 # len
# syscall
# li $v0, 5205 # __NR_exit_group
# li $a0, 0
# syscall
# msg:
# .ascii "MIPS64 hello\n"
MIPS64EB_LIN = bytes.fromhex('''
2402138924040001041100010000000067e500182406000d0000000c24021455
240400000000000c4d49505336342068656c6c6f0a
''')

# little-endian counterpart of MIPS64EB_LIN: the instruction words are
# byte-swapped while the trailing string is left as-is
MIPS64EL_LIN = bytes.fromhex('''
891302240100042401001104000000001800e5670d0006240c00000055140224
000004240c0000004d49505336342068656c6c6f0a
''')


if __name__ == "__main__":
print("\nLinux MIPS 64bit EB (big-endian) Shellcode")
ql = Qiling(code=MIPS64EB_LIN, archtype=QL_ARCH.MIPS64, ostype=QL_OS.LINUX, endian=QL_ENDIAN.EB, verbose=QL_VERBOSE.DEFAULT)
ql.run()

print("\nLinux MIPS 64bit EL (little-endian) Shellcode")
ql = Qiling(code=MIPS64EL_LIN, archtype=QL_ARCH.MIPS64, ostype=QL_OS.LINUX, endian=QL_ENDIAN.EL, verbose=QL_VERBOSE.DEFAULT)
ql.run()
11 changes: 7 additions & 4 deletions examples/src/linux/multithreading.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ void thread_2(void) {
int main(void) {
pthread_t id_1, id_2;
int ret;
/* pthread_join writes a void* (8 bytes on 64-bit); using an int* here makes
the store unaligned on strict-alignment 64-bit targets (e.g. MIPS64) */
void *retval;

/*Create pthread 1*/
ret=pthread_create(&id_1, NULL, (void *) thread_1, NULL);
Expand All @@ -44,9 +47,9 @@ int main(void) {
}

/*wait thread ending*/
pthread_join(id_1, &ret);
printf("thread 1 ret val is : %d\n", ret);
pthread_join(id_2, &ret);
printf("thread 2 ret val is : %d\n", ret);
pthread_join(id_1, &retval);
printf("thread 1 ret val is : %d\n", (int)(long)retval);
pthread_join(id_2, &retval);
printf("thread 2 ret val is : %d\n", (int)(long)retval);
return 0;
}
36 changes: 36 additions & 0 deletions examples/src/linux/statx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Regression test for statx struct serialization (esp. byte order on
// big-endian guests). statx() fills a `struct statx` whose stx_mode must carry
// the S_IFDIR bit for a directory; if the struct is emitted with the wrong
// endianness the type bits are lost and the directory looks like a plain file.
//
// Build (big-endian ARM, static so it needs no rootfs loader):
// armeb-linux-gnueabi-gcc -O2 -static -o armeb_statx statx.c
//
// Prints "DIR" when statx correctly reports the path as a directory.
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
const char *path = (argc > 1) ? argv[1] : "/";
struct statx stx;

memset(&stx, 0, sizeof(stx));

if (syscall(SYS_statx, AT_FDCWD, path, 0, STATX_BASIC_STATS, &stx) != 0) {
perror("statx");
return 1;
}

if (S_ISDIR(stx.stx_mode))
printf("DIR\n");
else
printf("NOTDIR mode=%o\n", (unsigned) stx.stx_mode);

return 0;
}
108 changes: 108 additions & 0 deletions qiling/arch/mips64.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from functools import cached_property
from typing import Optional

from unicorn import (
Uc, UC_ARCH_MIPS, UC_MODE_MIPS64, UC_MODE_BIG_ENDIAN, UC_MODE_LITTLE_ENDIAN,
UC_HOOK_TLB_FILL, UC_TLB_VIRTUAL, UC_PROT_ALL
)
from capstone import Cs, CS_ARCH_MIPS, CS_MODE_MIPS64, CS_MODE_BIG_ENDIAN, CS_MODE_LITTLE_ENDIAN
from keystone import Ks, KS_ARCH_MIPS, KS_MODE_MIPS64, KS_MODE_BIG_ENDIAN, KS_MODE_LITTLE_ENDIAN

from qiling import Qiling
from qiling.arch.arch import QlArch
from qiling.arch import mips_const
from qiling.arch.models import MIPS64_CPU_MODEL
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN


class QlArchMIPS64(QlArch):
type = QL_ARCH.MIPS64
bits = 64

def __init__(self, ql: Qiling, *, cputype: Optional[MIPS64_CPU_MODEL], endian: QL_ENDIAN):
# unicorn's default MIPS64 core is an old MIPS III (R4000-class) that lacks
# MIPS IV / MIPS64 instructions such as movn/movz, which glibc emits freely;
# executing them traps as a reserved instruction. default to a generic
# MIPS64 Release 2 core (what mips64/mips64r2 toolchains target) so the full
# ISA is available.
if cputype is None:
cputype = MIPS64_CPU_MODEL.MIPS64_MIPS64R2_GENERIC

super().__init__(ql, cputype=cputype)

self._init_endian = endian

@cached_property
def uc(self) -> Uc:
endian = {
QL_ENDIAN.EB: UC_MODE_BIG_ENDIAN,
QL_ENDIAN.EL: UC_MODE_LITTLE_ENDIAN
}[self.endian]

uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS64 + endian)

if self.cpu is not None:
uc.ctl_set_cpu_model(self.cpu.value)

# unicorn's MIPS64 CPU-MMU only executes within the low 2GB (useg); any
# access at or above 0x80000000 raises an address exception. MIPS64 ELFs
# link at 0x120000000 and Qiling lays the stack/mmap out well above 4GB,
# so we switch to unicorn's virtual-TLB mode and supply an identity
# mapping (paddr == vaddr). that bypasses the CPU-MMU segment checks while
# keeping virtual == physical, so the rest of Qiling's flat memory model
# (mem.map/read/write, loader, syscall pointer reads) keeps working as-is.
uc.ctl_set_tlb_mode(UC_TLB_VIRTUAL)
uc.hook_add(UC_HOOK_TLB_FILL, self.__tlb_fill_identity)

return uc

@staticmethod
def __tlb_fill_identity(uc: Uc, vaddr: int, access: int, entry, user_data) -> bool:
# identity-map every page to itself (paddr == vaddr); accesses to pages
# that are not actually backed still fault, since there is no physical
# memory behind them
entry.paddr = vaddr & ~0xfff
entry.perms = UC_PROT_ALL

return True

@cached_property
def regs(self) -> QlRegisterManager:
# the register names are shared with MIPS32; unicorn widens them to
# 64 bits under UC_MODE_MIPS64
regs_map = dict(
**mips_const.reg_map
)

pc_reg = 'pc'
sp_reg = 'sp'

return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)

@cached_property
def disassembler(self) -> Cs:
endian = {
QL_ENDIAN.EL: CS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB: CS_MODE_BIG_ENDIAN
}[self.endian]

return Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + endian)

@cached_property
def assembler(self) -> Ks:
endian = {
QL_ENDIAN.EL: KS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB: KS_MODE_BIG_ENDIAN
}[self.endian]

return Ks(KS_ARCH_MIPS, KS_MODE_MIPS64 + endian)

@property
def endian(self) -> QL_ENDIAN:
return self._init_endian
18 changes: 18 additions & 0 deletions qiling/arch/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,22 @@ class MIPS_CPU_MODEL(Enum):
MIPS32_I7200 = 15


class MIPS64_CPU_MODEL(Enum):
MIPS64_R4000 = 0
MIPS64_VR5432 = 1
MIPS64_5KC = 2
MIPS64_5KF = 3
MIPS64_20KC = 4
MIPS64_MIPS64R2_GENERIC = 5
MIPS64_5KEC = 6
MIPS64_5KEF = 7
MIPS64_I6400 = 8
MIPS64_I6500 = 9
MIPS64_LOONGSON_2E = 10
MIPS64_LOONGSON_2F = 11
MIPS64_MIPS64DSPR2 = 12


class PPC_CPU_MODEL(Enum):
PPC32_401 = 0
PPC32_401A1 = 1
Expand Down Expand Up @@ -429,6 +445,7 @@ class RISCV64_CPU_MODEL(Enum):
ARM_CPU_MODEL,
ARM64_CPU_MODEL,
MIPS_CPU_MODEL,
MIPS64_CPU_MODEL,
PPC_CPU_MODEL,
RISCV_CPU_MODEL,
RISCV64_CPU_MODEL
Expand All @@ -440,6 +457,7 @@ class RISCV64_CPU_MODEL(Enum):
'ARM_CPU_MODEL',
'ARM64_CPU_MODEL',
'MIPS_CPU_MODEL',
'MIPS64_CPU_MODEL',
'PPC_CPU_MODEL',
'RISCV_CPU_MODEL',
'RISCV64_CPU_MODEL',
Expand Down
3 changes: 2 additions & 1 deletion qiling/arch/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from functools import lru_cache

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_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_MIPS64, KS_MODE_PPC32, KS_MODE_16, KS_MODE_32, KS_MODE_64,
KS_MODE_LITTLE_ENDIAN, KS_MODE_BIG_ENDIAN)

from qiling import Qiling
Expand Down Expand Up @@ -126,6 +126,7 @@ def assembler(arch: QL_ARCH, endianness: QL_ENDIAN, is_thumb: bool) -> Ks:
QL_ARCH.ARM: (KS_ARCH_ARM, KS_MODE_ARM + endian + thumb),
QL_ARCH.ARM64: (KS_ARCH_ARM64, KS_MODE_ARM),
QL_ARCH.MIPS: (KS_ARCH_MIPS, KS_MODE_MIPS32 + endian),
QL_ARCH.MIPS64: (KS_ARCH_MIPS, KS_MODE_MIPS64 + 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),
Expand Down
16 changes: 15 additions & 1 deletion qiling/cc/mips.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework

from unicorn.mips_const import UC_MIPS_REG_V0, UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3
from unicorn.mips_const import (
UC_MIPS_REG_V0, UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3,
UC_MIPS_REG_T0, UC_MIPS_REG_T1, UC_MIPS_REG_T2, UC_MIPS_REG_T3
)

from qiling.cc import QlCommonBaseCC, make_arg_list

Expand All @@ -25,3 +28,14 @@ def getNumSlots(argbits: int):
def unwind(self, nslots: int) -> int:
# TODO: stack frame unwiding?
return self.arch.regs.ra


class mips64n64(mipso32):
# n64 passes the first 8 arguments in registers: a0-a3 followed by a4-a7,
# which are the physical registers $8-$11 (unicorn names them t0-t3). unlike
# o32, n64 reserves no shadow space on the stack.
_argregs = make_arg_list(
UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3,
UC_MIPS_REG_T0, UC_MIPS_REG_T1, UC_MIPS_REG_T2, UC_MIPS_REG_T3
)
_shadow = 0
1 change: 1 addition & 0 deletions qiling/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class QL_ARCH(IntEnum):
RISCV = 110
RISCV64 = 111
PPC = 112
MIPS64 = 113


class QL_OS(IntEnum):
Expand Down
Loading
Loading