mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
fix documents and minor changes
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
[CODE]
|
||||
load_address = 0x10000000
|
||||
entry_point = 0x10000008
|
||||
ram_size = 0xa00000
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
# Everything about the bug and firmware https://www.exploit-db.com/exploits/33863
|
||||
|
||||
import os,sys
|
||||
import sys
|
||||
sys.path.append("../../..")
|
||||
|
||||
from qiling import Qiling
|
||||
@@ -13,7 +13,7 @@ from qiling.const import QL_VERBOSE
|
||||
from qiling.extensions.afl import ql_afl_fuzz
|
||||
|
||||
|
||||
def main(input_file, enable_trace=False):
|
||||
def main(input_file: str):
|
||||
|
||||
env_vars = {
|
||||
"REQUEST_METHOD": "POST",
|
||||
@@ -24,40 +24,36 @@ def main(input_file, enable_trace=False):
|
||||
# "CONTENT_LENGTH": "8", # no needed
|
||||
}
|
||||
|
||||
ql = Qiling(["./rootfs/htdocs/web/hedwig.cgi"], "./rootfs",
|
||||
verbose=QL_VERBOSE.DEBUG, env=env_vars, console=enable_trace)
|
||||
ql = Qiling(["./rootfs/htdocs/web/hedwig.cgi"], "./rootfs", verbose=QL_VERBOSE.DISABLED, env=env_vars)
|
||||
|
||||
def place_input_callback(ql: Qiling, input: bytes, _: int):
|
||||
env_var = ("HTTP_COOKIE=uid=1234&password=").encode()
|
||||
env_vars = env_var + input + b"\x00" + (ql.path).encode() + b"\x00"
|
||||
ql.mem.write(ql.target_addr, env_vars)
|
||||
def place_input_callback(ql: Qiling, data: bytes, _: int) -> bool:
|
||||
# construct the payload
|
||||
payload = b''.join((b"HTTP_COOKIE=uid=1234&password=", bytes(data), b"\x00", ql_path, b"\x00"))
|
||||
|
||||
# patch the value of 'HTTP_COOKIE' in memory
|
||||
ql.mem.write(target_addr, payload)
|
||||
|
||||
# payload is in place, we are good to go
|
||||
return True
|
||||
|
||||
def start_afl(_ql: Qiling):
|
||||
|
||||
"""
|
||||
Callback from inside
|
||||
"""
|
||||
|
||||
ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])
|
||||
|
||||
addr = ql.mem.search("HTTP_COOKIE=uid=1234&password=".encode())
|
||||
ql.target_addr = addr[0]
|
||||
addr = ql.mem.search(b"HTTP_COOKIE=uid=1234&password=")
|
||||
target_addr = addr[0]
|
||||
ql_path = ql.path.encode()
|
||||
|
||||
main_addr = ql.loader.elf_entry
|
||||
ql.hook_address(callback=start_afl, address=main_addr)
|
||||
ql.hook_address(start_afl, ql.loader.elf_entry)
|
||||
|
||||
try:
|
||||
ql.run()
|
||||
os._exit(0)
|
||||
except:
|
||||
if enable_trace:
|
||||
print("\nFuzzer Went Shit")
|
||||
os._exit(0)
|
||||
ql.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 1:
|
||||
if len(sys.argv) < 2:
|
||||
raise ValueError("No input file provided.")
|
||||
if len(sys.argv) > 2 and sys.argv[1] == "-t":
|
||||
main(sys.argv[2], enable_trace=True)
|
||||
else:
|
||||
main(sys.argv[1])
|
||||
|
||||
main(sys.argv[1])
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
##############################################################################
|
||||
# This example is meant to demonstrate the modifications necessary
|
||||
# to enable code coverage when emulating small code snippets or bare-metal
|
||||
# code.
|
||||
##############################################################################
|
||||
from qiling import Qiling
|
||||
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE
|
||||
from qiling.extensions.coverage import utils as cov_utils
|
||||
from qiling.loader.loader import Image
|
||||
import os
|
||||
|
||||
BASE_ADDRESS = 0x10000000
|
||||
CHECKSUM_FUNC_ADDR = BASE_ADDRESS + 0x8
|
||||
END_ADDRESS = 0x100000ba
|
||||
DATA_ADDR = 0xa0000000 # Arbitrary address for data
|
||||
STACK_ADDR = 0xb0000000 # Arbitrary address for stack
|
||||
|
||||
# Python implementation of the checksum function being emulated
|
||||
# This checksum function is intended to have different code paths based on the input
|
||||
# which is useful for observing code coverage
|
||||
def checksum_function(input_data_buffer: bytes):
|
||||
expected_checksum_python = 0
|
||||
input_data_len = len(input_data_buffer)
|
||||
if input_data_len >= 1 and input_data_buffer[0] == 0xDE: # MAGIC_VALUE_1
|
||||
for i in range(min(input_data_len, 4)):
|
||||
expected_checksum_python += input_data_buffer[i]
|
||||
expected_checksum_python += 0x10
|
||||
elif input_data_len >= 2 and input_data_buffer[1] == 0xAD: # MAGIC_VALUE_2
|
||||
for i in range(input_data_len):
|
||||
expected_checksum_python ^= input_data_buffer[i]
|
||||
expected_checksum_python += 0x20
|
||||
else:
|
||||
for i in range(input_data_len):
|
||||
expected_checksum_python += input_data_buffer[i]
|
||||
expected_checksum_python &= 0xFF # Ensure it's a single byte
|
||||
return expected_checksum_python
|
||||
|
||||
def unmapped_handler(ql: Qiling, type: int, addr: int, size: int, value: int) -> None:
|
||||
print(f"Unmapped Memory R/W, trying to access {size:d} bytes at {addr:#010x} from {ql.arch.regs.pc:#010x}")
|
||||
|
||||
def emulate_checksum_function(input_data_buffer: bytes) -> None:
|
||||
print(f"\n--- Testing with input: {input_data_buffer.hex()} ---")
|
||||
|
||||
test_file = "rootfs/blob/example_raw.bin"
|
||||
|
||||
with open(test_file, "rb") as f:
|
||||
raw_code: bytes = f.read()
|
||||
|
||||
ql: Qiling = Qiling(
|
||||
code=raw_code,
|
||||
archtype=QL_ARCH.ARM,
|
||||
ostype=QL_OS.BLOB,
|
||||
profile="blob_raw.ql",
|
||||
verbose=QL_VERBOSE.DEBUG,
|
||||
thumb=True
|
||||
)
|
||||
|
||||
''' monkeypatch - Correcting the loader image name, used for coverage collection
|
||||
removing all images with name 'blob_code' that were created by the blob loader.
|
||||
This is necessary because some code coverage visualization tools require the
|
||||
module name to match that of the input file '''
|
||||
ql.loader.images = [img for img in ql.loader.images if img.path != 'blob_code']
|
||||
ql.loader.images.append(Image(ql.loader.load_address, ql.loader.load_address + ql.os.code_ram_size, os.path.basename(test_file)))
|
||||
|
||||
input_data_len: int = len(input_data_buffer)
|
||||
|
||||
# Map memory for the data and stack
|
||||
ql.mem.map(STACK_ADDR, 0x2000)
|
||||
ql.mem.map(DATA_ADDR, ql.mem.align_up(input_data_len + 0x100)) # Map enough space for data
|
||||
|
||||
# Write input data
|
||||
ql.mem.write(DATA_ADDR, input_data_buffer)
|
||||
|
||||
# Set up the stack pointer
|
||||
ql.arch.regs.sp = STACK_ADDR + 0x2000 - 4
|
||||
# Set up argument registers
|
||||
ql.arch.regs.r0 = DATA_ADDR
|
||||
ql.arch.regs.r1 = input_data_len
|
||||
|
||||
# Set the program counter to the function's entry point
|
||||
ql.arch.regs.pc = CHECKSUM_FUNC_ADDR
|
||||
|
||||
# Set the return address (LR) to a dummy address.
|
||||
ql.arch.regs.lr = 0xbebebebe
|
||||
|
||||
ql.hook_mem_unmapped(unmapped_handler)
|
||||
#ql.debugger="gdb:127.0.0.1:9999"
|
||||
|
||||
# Start emulation
|
||||
print(f"Starting emulation at PC: {hex(ql.arch.regs.pc)}")
|
||||
try:
|
||||
with cov_utils.collect_coverage(ql, 'drcov', 'output.cov'):
|
||||
ql.run(begin=CHECKSUM_FUNC_ADDR, end=END_ADDRESS)
|
||||
except Exception as e:
|
||||
print(f"Emulation error: {e}")
|
||||
|
||||
print(f"Emulated checksum: {hex(ql.arch.regs.r0)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
data = b"\x01\x02\x03\x04\x05" # Example input data
|
||||
emulate_checksum_function(data)
|
||||
+59
-45
@@ -8,68 +8,82 @@ sys.path.append("..")
|
||||
|
||||
from qiling.core import Qiling
|
||||
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE
|
||||
from qiling.os.const import STRING
|
||||
from qiling.os.const import STRING, SIZE_T, POINTER
|
||||
|
||||
|
||||
def get_kaimendaji_password():
|
||||
def my_getenv(ql: Qiling):
|
||||
env = {
|
||||
"ID" : b"000000000000000",
|
||||
"ethaddr" : b"11:22:33:44:55:66"
|
||||
}
|
||||
def my_getenv(ql: Qiling):
|
||||
env = {
|
||||
"ID" : b"000000000000000",
|
||||
"ethaddr" : b"11:22:33:44:55:66"
|
||||
}
|
||||
|
||||
params = ql.os.resolve_fcall_params({'key': STRING})
|
||||
value = env.get(params["key"], b"")
|
||||
params = ql.os.resolve_fcall_params({'key': STRING})
|
||||
value = env.get(params["key"], b"")
|
||||
|
||||
value_addr = ql.os.heap.alloc(len(value))
|
||||
ql.mem.write(value_addr, value)
|
||||
value_addr = ql.os.heap.alloc(len(value))
|
||||
ql.mem.write(value_addr, value)
|
||||
|
||||
ql.arch.regs.r0 = value_addr
|
||||
ql.arch.regs.arch_pc = ql.arch.regs.lr
|
||||
ql.arch.regs.r0 = value_addr
|
||||
ql.arch.regs.arch_pc = ql.arch.regs.lr
|
||||
|
||||
def get_password(ql: Qiling):
|
||||
password_raw = ql.mem.read(ql.arch.regs.r0, ql.arch.regs.r2)
|
||||
|
||||
password = ''
|
||||
for item in password_raw:
|
||||
if 0 <= item <= 9:
|
||||
password += chr(item + 48)
|
||||
else:
|
||||
password += chr(item + 87)
|
||||
def get_password(ql: Qiling):
|
||||
# we land on a memcmp call, where the real password is being compared to
|
||||
# the one provided by the user. we can follow the arguments to read the
|
||||
# real password
|
||||
|
||||
print("The password is: %s" % password)
|
||||
params = ql.os.resolve_fcall_params({
|
||||
'ptr1': POINTER, # points to real password
|
||||
'ptr2': POINTER, # points to user provided password
|
||||
'size': SIZE_T # comparison length
|
||||
})
|
||||
|
||||
def partial_run_init(ql: Qiling):
|
||||
# argv prepare
|
||||
ql.arch.regs.arch_sp -= 0x30
|
||||
arg0_ptr = ql.arch.regs.arch_sp
|
||||
ql.mem.write(arg0_ptr, b"kaimendaji")
|
||||
ptr1 = params['ptr1']
|
||||
size = params['size']
|
||||
|
||||
ql.arch.regs.arch_sp -= 0x10
|
||||
arg1_ptr = ql.arch.regs.arch_sp
|
||||
ql.mem.write(arg1_ptr, b"000000") # arbitrary password
|
||||
password_raw = ql.mem.read(ptr1, size)
|
||||
|
||||
ql.arch.regs.arch_sp -= 0x20
|
||||
argv_ptr = ql.arch.regs.arch_sp
|
||||
ql.mem.write_ptr(argv_ptr, arg0_ptr)
|
||||
ql.mem.write_ptr(argv_ptr + ql.arch.pointersize, arg1_ptr)
|
||||
def __hex_digit(ch: int) -> str:
|
||||
off = ord('0') if ch in range(10) else ord('a') - 10
|
||||
|
||||
ql.arch.regs.r2 = 2
|
||||
ql.arch.regs.r3 = argv_ptr
|
||||
return chr(ch + off)
|
||||
|
||||
with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
|
||||
uboot_code = f.read()
|
||||
# should be: "013f1f"
|
||||
password = "".join(__hex_digit(ch) for ch in password_raw)
|
||||
|
||||
ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="uboot_bin.ql", verbose=QL_VERBOSE.OFF)
|
||||
print(f'The password is: {password}')
|
||||
|
||||
image_base_addr = ql.loader.load_address
|
||||
ql.hook_address(my_getenv, image_base_addr + 0x13AC0)
|
||||
ql.hook_address(get_password, image_base_addr + 0x48634)
|
||||
|
||||
partial_run_init(ql)
|
||||
def partial_run_init(ql: Qiling):
|
||||
# argv prepare
|
||||
ql.arch.regs.arch_sp -= 0x30
|
||||
arg0_ptr = ql.arch.regs.arch_sp
|
||||
ql.mem.write(arg0_ptr, b"kaimendaji")
|
||||
|
||||
ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718)
|
||||
ql.arch.regs.arch_sp -= 0x10
|
||||
arg1_ptr = ql.arch.regs.arch_sp
|
||||
ql.mem.write(arg1_ptr, b"000000") # arbitrary password
|
||||
|
||||
ql.arch.regs.arch_sp -= 0x20
|
||||
argv_ptr = ql.arch.regs.arch_sp
|
||||
ql.mem.write_ptr(argv_ptr, arg0_ptr)
|
||||
ql.mem.write_ptr(argv_ptr + ql.arch.pointersize, arg1_ptr)
|
||||
|
||||
ql.arch.regs.r2 = 2
|
||||
ql.arch.regs.r3 = argv_ptr
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_kaimendaji_password()
|
||||
with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
|
||||
uboot_code = f.read()
|
||||
|
||||
ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="uboot_bin.ql", verbose=QL_VERBOSE.DEBUG)
|
||||
|
||||
imgbase = ql.loader.images[0].base
|
||||
|
||||
ql.hook_address(my_getenv, imgbase + 0x13AC0)
|
||||
ql.hook_address(get_password, imgbase + 0x48634)
|
||||
|
||||
partial_run_init(ql)
|
||||
|
||||
ql.run(imgbase + 0x486B4, imgbase + 0x48718)
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ def hook_StartServiceA(ql: Qiling, address: int, params):
|
||||
init_unseen_symbols(ql.amsint32_driver, ntoskrnl.base+0xb7695, b"NtTerminateProcess", 0, "ntoskrnl.exe")
|
||||
#ql.amsint32_driver.debugger= ":9999"
|
||||
try:
|
||||
ql.amsint32_driver.load()
|
||||
ql.amsint32_driver.run()
|
||||
return 1
|
||||
except UcError as e:
|
||||
print("Load driver error: ", e)
|
||||
|
||||
@@ -80,6 +80,7 @@ CALL :collect_dll32 setupapi.dll
|
||||
CALL :collect_dll32 shell32.dll
|
||||
CALL :collect_dll32 shlwapi.dll
|
||||
CALL :collect_dll32 sspicli.dll
|
||||
CALL :collect_dll32 symcryptk.dll
|
||||
CALL :collect_dll32 ucrtbase.dll
|
||||
CALL :collect_dll32 ucrtbased.dll
|
||||
CALL :collect_dll32 urlmon.dll
|
||||
@@ -94,6 +95,9 @@ CALL :collect_dll32 wininet.dll
|
||||
CALL :collect_dll32 winmm.dll
|
||||
CALL :collect_dll32 ws2_32.dll
|
||||
CALL :collect_dll32 wsock32.dll
|
||||
CALL :collect_dll32 msvcp140.dll
|
||||
CALL :collect_dll32 msvcp140_1.dll
|
||||
CALL :collect_dll32 msvcp140_2.dll
|
||||
|
||||
CALL :collect_dll32 downlevel\api-ms-win-core-fibers-l1-1-1.dll
|
||||
CALL :collect_dll32 downlevel\api-ms-win-core-localization-l1-2-1.dll
|
||||
@@ -122,6 +126,7 @@ CALL :collect_dll64 rpcrt4.dll
|
||||
CALL :collect_dll64 sechost.dll
|
||||
CALL :collect_dll64 shell32.dll
|
||||
CALL :collect_dll64 shlwapi.dll
|
||||
CALL :collect_dll64 symcryptk.dll
|
||||
CALL :collect_dll64 user32.dll
|
||||
CALL :collect_dll64 vcruntime140.dll
|
||||
CALL :collect_dll64 vcruntime140d.dll
|
||||
@@ -131,6 +136,9 @@ CALL :collect_dll64 win32u.dll
|
||||
CALL :collect_dll64 winhttp.dll
|
||||
CALL :collect_dll64 wininet.dll
|
||||
CALL :collect_dll64 ws2_32.dll
|
||||
CALL :collect_dll64 msvcp140.dll
|
||||
CALL :collect_dll64 msvcp140_1.dll
|
||||
CALL :collect_dll64 msvcp140_2.dll
|
||||
|
||||
CALL :collect_dll64 downlevel\api-ms-win-crt-heap-l1-1-0.dll
|
||||
CALL :collect_dll64 downlevel\api-ms-win-crt-locale-l1-1-0.dll
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Makefile for Bare-Metal ARM Checksum Calculator
|
||||
|
||||
# --- Toolchain Definitions ---
|
||||
TOOLCHAIN_PREFIX = arm-none-eabi
|
||||
|
||||
# Compiler, Linker, and Objcopy executables
|
||||
CC = $(TOOLCHAIN_PREFIX)-gcc
|
||||
LD = $(TOOLCHAIN_PREFIX)-gcc
|
||||
OBJCOPY = $(TOOLCHAIN_PREFIX)-objcopy
|
||||
|
||||
# --- Source and Output Files ---
|
||||
SRCS = example_raw.c
|
||||
OBJS = $(SRCS:.c=.o) # Convert .c to .o
|
||||
ELF = example_raw.elf
|
||||
BIN = example_raw.bin
|
||||
|
||||
# --- Linker Script ---
|
||||
LDSCRIPT = linker.ld
|
||||
|
||||
# --- Compiler Flags ---
|
||||
CFLAGS = -c -O0 -mcpu=cortex-a7 -mthumb -ffreestanding -nostdlib
|
||||
|
||||
# --- Linker Flags ---
|
||||
LDFLAGS = -T $(LDSCRIPT) -nostdlib
|
||||
|
||||
# --- Objcopy Flags ---
|
||||
OBJCOPYFLAGS = -O binary
|
||||
|
||||
# --- Default Target ---
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(BIN)
|
||||
|
||||
# Rule to build the raw binary (.bin) from the ELF file
|
||||
$(BIN): $(ELF)
|
||||
$(OBJCOPY) $(OBJCOPYFLAGS) $< $@
|
||||
@echo "Successfully created $(BIN)"
|
||||
|
||||
# Rule to link the object file into an ELF executable
|
||||
$(ELF): $(OBJS) $(LDSCRIPT)
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $@
|
||||
@echo "Successfully linked $(ELF)"
|
||||
|
||||
# Rule to compile the C source file into an object file
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
@echo "Successfully compiled $<"
|
||||
|
||||
# --- Clean Rule ---
|
||||
clean:
|
||||
rm -f $(OBJS) $(ELF) $(BIN)
|
||||
@echo "Cleaned build artifacts."
|
||||
@@ -0,0 +1,56 @@
|
||||
// example checksum algorithm to demonstrate raw binary code coverage in qiling
|
||||
// example_raw.c
|
||||
|
||||
// Define some magic values
|
||||
#define MAGIC_VALUE_1 0xDE
|
||||
#define MAGIC_VALUE_2 0xAD
|
||||
|
||||
// This function calculates a checksum with branches based on input data
|
||||
// It takes a pointer to data and its length
|
||||
// Returns the checksum (unsigned char to fit in a byte)
|
||||
unsigned char calculate_checksum(const unsigned char *data, unsigned int length) {
|
||||
unsigned char checksum = 0;
|
||||
|
||||
// Branch 1: Check for MAGIC_VALUE_1 at the start
|
||||
if (length >= 1 && data[0] == MAGIC_VALUE_1) {
|
||||
// If first byte is MAGIC_VALUE_1, do a simple sum of first 4 bytes
|
||||
// (or up to length if less than 4)
|
||||
for (unsigned int i = 0; i < length && i < 4; i++) {
|
||||
checksum += data[i];
|
||||
}
|
||||
// Add a fixed offset to make this path distinct
|
||||
checksum += 0x10;
|
||||
}
|
||||
// Branch 2: Check for MAGIC_VALUE_2 at the second byte
|
||||
else if (length >= 2 && data[1] == MAGIC_VALUE_2) {
|
||||
// If second byte is MAGIC_VALUE_2, do a XOR sum of all bytes
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
checksum ^= data[i];
|
||||
}
|
||||
// Add a fixed offset to make this path distinct
|
||||
checksum += 0x20;
|
||||
}
|
||||
// Default Branch: Standard byte sum checksum
|
||||
else {
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
checksum += data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
// Minimal entry point for bare-metal.
|
||||
// This function will not be called directly during Qiling emulation,
|
||||
// but it's needed for the linker to have an entry point.
|
||||
__attribute__((section(".text.startup")))
|
||||
void _start() {
|
||||
// In a real bare-metal application, this would initialize hardware,
|
||||
// set up stacks, etc. For this example, it's just a placeholder.
|
||||
// We'll call calculate_checksum directly from our Qiling script.
|
||||
|
||||
while (1) {
|
||||
// Do nothing, or perhaps put the CPU to sleep
|
||||
asm volatile ("wfi"); // Wait For Interrupt (ARM instruction)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* linker.ld */
|
||||
|
||||
ENTRY(_start) /* Define the entry point of our program */
|
||||
|
||||
/* Define memory regions - simple RAM region for this example */
|
||||
MEMORY
|
||||
{
|
||||
ram (rwx) : ORIGIN = 0x10000000, LENGTH = 64K /* 64KB of RAM for our program */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Define the start of our program in memory.
|
||||
*/
|
||||
. = 0x10000000;
|
||||
|
||||
.text : {
|
||||
KEEP(*(.text.startup)) /* Keep the _start function */
|
||||
*(.text) /* All other code */
|
||||
*(.text.*)
|
||||
*(.rodata) /* Read-only data */
|
||||
*(.rodata.*)
|
||||
. = ALIGN(4);
|
||||
} > ram /* Place .text section in the 'ram' region */
|
||||
|
||||
.data : {
|
||||
. = ALIGN(4);
|
||||
*(.data) /* Initialized data */
|
||||
*(.data.*)
|
||||
. = ALIGN(4);
|
||||
} > ram
|
||||
|
||||
.bss : {
|
||||
. = ALIGN(4);
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
. = ALIGN(4);
|
||||
} > ram
|
||||
}
|
||||
@@ -33,6 +33,7 @@ TARGETS = \
|
||||
x8664_hello_cpp \
|
||||
x8664_hello_cpp_static \
|
||||
x8664_cloexec_test \
|
||||
x8664_linux_onestraw \
|
||||
patch_test.bin
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -124,7 +125,10 @@ libpatch_test.so: patch_test.so.h patch_test.so.c
|
||||
$(CC) $(CPPFLAGS) -Wall -s -O0 -shared -fpic -o $@ patch_test.so.c
|
||||
|
||||
patch_test.bin: patch_test.bin.c libpatch_test.so
|
||||
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
|
||||
x8664_onestraw_server: x8664_linux_onestraw.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -m64 -o $@ $<
|
||||
|
||||
$(OBJS):%.o:%.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#define MAX_EVENTS 5
|
||||
#define READ_SIZE 10
|
||||
#include <stdio.h> // for fprintf()
|
||||
#include <unistd.h> // for close(), read()
|
||||
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event
|
||||
#include <string.h> // for strncmp
|
||||
|
||||
int main()
|
||||
{
|
||||
//setvbuf(stdin, NULL, _IONBF, 0);
|
||||
int running = 1, event_count, i;
|
||||
size_t bytes_read;
|
||||
char read_buffer[READ_SIZE + 1];
|
||||
struct epoll_event event, events[MAX_EVENTS];
|
||||
int epoll_fd = epoll_create1(0);
|
||||
|
||||
if (epoll_fd == -1) {
|
||||
fprintf(stderr, "Failed to create epoll file descriptor\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
event.events = EPOLLIN;
|
||||
event.data.fd = 0;
|
||||
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
|
||||
{
|
||||
fprintf(stderr, "Failed to add file descriptor to epoll\n");
|
||||
close(epoll_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (running) {
|
||||
printf("\nPolling for input...\n");
|
||||
event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, 30000);
|
||||
printf("%d ready events\n", event_count);
|
||||
for (i = 0; i < event_count; i++) {
|
||||
printf("Reading file descriptor '%d' -- ", events[i].data.fd);
|
||||
bytes_read = read(events[i].data.fd, read_buffer, READ_SIZE);
|
||||
printf("%zd bytes read.\n", bytes_read);
|
||||
read_buffer[bytes_read] = '\0';
|
||||
printf("Read '%s'\n", read_buffer);
|
||||
|
||||
if(!strncmp(read_buffer, "stop\n", 5))
|
||||
running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (close(epoll_fd)) {
|
||||
fprintf(stderr, "Failed to close epoll file descriptor\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Attention:
|
||||
* To keep things simple, do not handle socket/bind/listen/.../epoll_create/epoll_wait API error
|
||||
*/
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DEFAULT_PORT 8000
|
||||
#define MAX_CONN 16
|
||||
#define MAX_EVENTS 32
|
||||
#define BUF_SIZE 16
|
||||
#define MAX_LINE 256
|
||||
|
||||
void server_run();
|
||||
void client_run();
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
char role = 's';
|
||||
while ((opt = getopt(argc, argv, "cs")) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
role = 'c';
|
||||
break;
|
||||
case 's':
|
||||
break;
|
||||
default:
|
||||
printf("usage: %s [-cs]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (role == 's') {
|
||||
server_run();
|
||||
} else {
|
||||
client_run();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* register events of fd to epfd
|
||||
*/
|
||||
static void epoll_ctl_add(int epfd, int fd, uint32_t events)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
ev.events = events;
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
perror("epoll_ctl()\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_sockaddr(struct sockaddr_in *addr)
|
||||
{
|
||||
bzero((char *)addr, sizeof(struct sockaddr_in));
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = INADDR_ANY;
|
||||
addr->sin_port = htons(DEFAULT_PORT);
|
||||
}
|
||||
|
||||
static int setnonblocking(int sockfd)
|
||||
{
|
||||
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK) ==
|
||||
-1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* epoll echo server
|
||||
*/
|
||||
void server_run()
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
int epfd;
|
||||
int nfds;
|
||||
int listen_sock;
|
||||
int conn_sock;
|
||||
int socklen;
|
||||
char buf[BUF_SIZE];
|
||||
struct sockaddr_in srv_addr;
|
||||
struct sockaddr_in cli_addr;
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
|
||||
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
set_sockaddr(&srv_addr);
|
||||
bind(listen_sock, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
|
||||
setnonblocking(listen_sock);
|
||||
listen(listen_sock, MAX_CONN);
|
||||
|
||||
epfd = epoll_create(1);
|
||||
epoll_ctl_add(epfd, listen_sock, EPOLLIN | EPOLLOUT | EPOLLET);
|
||||
|
||||
socklen = sizeof(cli_addr);
|
||||
int a;
|
||||
for (a = 0; a < 2; a++) {
|
||||
nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
|
||||
for (i = 0; i < nfds; i++) {
|
||||
if (events[i].data.fd == listen_sock) {
|
||||
/* handle new connection */
|
||||
conn_sock =
|
||||
accept(listen_sock,
|
||||
(struct sockaddr *)&cli_addr,
|
||||
&socklen);
|
||||
|
||||
inet_ntop(AF_INET, (char *)&(cli_addr.sin_addr),
|
||||
buf, sizeof(cli_addr));
|
||||
printf("[+] connected with %s:%d\n", buf,
|
||||
ntohs(cli_addr.sin_port));
|
||||
|
||||
setnonblocking(conn_sock);
|
||||
epoll_ctl_add(epfd, conn_sock,
|
||||
EPOLLIN | EPOLLET | EPOLLRDHUP |
|
||||
EPOLLHUP);
|
||||
} else if (events[i].events & EPOLLIN) {
|
||||
/* handle EPOLLIN event */
|
||||
for (;;) {
|
||||
bzero(buf, sizeof(buf));
|
||||
n = read(events[i].data.fd, buf,
|
||||
sizeof(buf));
|
||||
if (n <= 0 /* || errno == EAGAIN */ ) {
|
||||
break;
|
||||
} else {
|
||||
printf("[+] data: %s\n", buf);
|
||||
write(events[i].data.fd, buf,
|
||||
strlen(buf));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("[+] unexpected\n");
|
||||
}
|
||||
/* check if the connection is closing */
|
||||
if (events[i].events & (EPOLLRDHUP | EPOLLHUP)) {
|
||||
printf("[+] connection closed\n");
|
||||
epoll_ctl(epfd, EPOLL_CTL_DEL,
|
||||
events[i].data.fd, NULL);
|
||||
close(events[i].data.fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* test clinet
|
||||
*/
|
||||
void client_run()
|
||||
{
|
||||
int n;
|
||||
int c;
|
||||
int sockfd;
|
||||
char buf[MAX_LINE];
|
||||
struct sockaddr_in srv_addr;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
set_sockaddr(&srv_addr);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) {
|
||||
perror("connect()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
printf("input: ");
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
c = strlen(buf) - 1;
|
||||
buf[c] = '\0';
|
||||
write(sockfd, buf, c + 1);
|
||||
|
||||
bzero(buf, sizeof(buf));
|
||||
while (errno != EAGAIN
|
||||
&& (n = read(sockfd, buf, sizeof(buf))) > 0) {
|
||||
printf("echo: %s\n", buf);
|
||||
bzero(buf, sizeof(buf));
|
||||
|
||||
c -= n;
|
||||
if (c <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sockfd);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// This is the default Hello World program generated by Visual Studio 2022.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "Hello World!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
In this folder: Sources for programs intended to help test C++ features and software exceptions.
|
||||
|
||||
Compile with MSVC (Visual Studio 2022)
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
/*
|
||||
* Test simple try..catch.
|
||||
*/
|
||||
void test1()
|
||||
{
|
||||
std::cout << "y";
|
||||
|
||||
try {
|
||||
std::cout << "y";
|
||||
throw (unsigned int)0x12345678;
|
||||
std::cout << "n";
|
||||
}
|
||||
catch(unsigned int n) {
|
||||
n;
|
||||
std::cout << "y";
|
||||
}
|
||||
|
||||
std::cout << "y";
|
||||
}
|
||||
|
||||
/*
|
||||
* Test simple try..catch with throw.
|
||||
*/
|
||||
void test2()
|
||||
{
|
||||
std::cout << "y";
|
||||
|
||||
try {
|
||||
std::cout << "y";
|
||||
throw (unsigned int)0x12345679;
|
||||
std::cout << "n";
|
||||
}
|
||||
catch (unsigned int n) {
|
||||
n;
|
||||
if (n == 0x12345679) {
|
||||
std::cout << "y";
|
||||
}
|
||||
else {
|
||||
std::cout << "n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "y";
|
||||
}
|
||||
|
||||
/*
|
||||
* Test nested try..catch with throw.
|
||||
*/
|
||||
void test3()
|
||||
{
|
||||
std::cout << "y";
|
||||
|
||||
try {
|
||||
std::cout << "y";
|
||||
|
||||
try {
|
||||
std::cout << "y";
|
||||
throw (unsigned int)0x1234567A;
|
||||
std::cout << "n";
|
||||
}
|
||||
catch (unsigned int n) {
|
||||
n;
|
||||
if (n == 0x1234567A) {
|
||||
std::cout << "y";
|
||||
}
|
||||
else {
|
||||
std::cout << "n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "y";
|
||||
}
|
||||
catch (unsigned int n) {
|
||||
n;
|
||||
std::cout << "n";
|
||||
}
|
||||
|
||||
std::cout << "y";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
/*
|
||||
* For this program, all subtests successful will print:
|
||||
* - 14 'y'
|
||||
* - 0 'n'
|
||||
*/
|
||||
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
|
||||
LONG WINAPI CustomExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
|
||||
printf("Inside exception filter (GOOD)\n");
|
||||
DWORD exceptionCode = (DWORD)ExceptionInfo->ExceptionRecord->ExceptionCode;
|
||||
printf("Exception Code: 0x%X\n", exceptionCode);
|
||||
|
||||
if (exceptionCode == 0xE06D7363) { // code for C++ exception
|
||||
printf("Exception code DOES match, GOOD\n");
|
||||
}
|
||||
else {
|
||||
printf("Exception code DOES NOT match, BAD\n");
|
||||
}
|
||||
|
||||
printf("Exception Address: 0x%llx\n", (ULONGLONG)ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
printf("After printing exception: (GOOD)\n");
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
int main() {
|
||||
/*
|
||||
* For this program, all subtests successful will print:
|
||||
* - 3 'GOOD'
|
||||
* - 0 'BAD'
|
||||
*
|
||||
* It is expected that the program terminates abnormally
|
||||
* with status code 0xE06D7363 (C++ exception)
|
||||
*/
|
||||
|
||||
// Set the custom top-level exception filter
|
||||
SetUnhandledExceptionFilter(CustomExceptionFilter);
|
||||
|
||||
// Throw an unhandled exception.
|
||||
// It should be caught by our filter.
|
||||
throw (unsigned int)5;
|
||||
|
||||
// We should never reach this point, because the exception
|
||||
// dispatcher should terminate the program after our unhandled
|
||||
// exception filter is called.
|
||||
printf("After exception filter (BAD)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
|
||||
int main()
|
||||
{
|
||||
/*
|
||||
* For this program, all subtests successful will print:
|
||||
* - 1 'GOOD'
|
||||
* - 0 'BAD'
|
||||
*
|
||||
* It is expected that the program terminates abnormally
|
||||
* with status code 0xC0000409 (stack buffer overrun/security
|
||||
* check failure)
|
||||
*/
|
||||
|
||||
printf("Before throw (GOOD)\n");
|
||||
|
||||
throw (unsigned int)5;
|
||||
|
||||
printf("After throw (BAD)\n");
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <iostream>
|
||||
|
||||
struct TestStruct {
|
||||
float q;
|
||||
};
|
||||
|
||||
class TestClass {
|
||||
public:
|
||||
int x, y;
|
||||
virtual ~TestClass() {
|
||||
std::cout << "TestClass destructor, GOOD" << std::endl;
|
||||
};
|
||||
void yyy() {
|
||||
std::cout << "REALLY GOOD" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
class Something {
|
||||
public:
|
||||
char z;
|
||||
virtual ~Something() {
|
||||
std::cout << "Something destructor, GOOD" << std::endl;
|
||||
};
|
||||
virtual void zzz() {
|
||||
std::cout << "BAD" << std::endl;
|
||||
};
|
||||
};
|
||||
|
||||
class TestClass2 : public TestClass, public Something {
|
||||
public:
|
||||
int z;
|
||||
virtual ~TestClass2() {
|
||||
std::cout << "TestClass2 destructor, GOOD" << std::endl;
|
||||
};
|
||||
virtual void zzz() {
|
||||
std::cout << "GOOD" << std::endl;
|
||||
};
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
/*
|
||||
* For this program, all subtests successful will print:
|
||||
* - 12 'GOOD'
|
||||
* - 0 'BAD'
|
||||
*/
|
||||
|
||||
int x = 5;
|
||||
TestClass p;
|
||||
TestStruct s;
|
||||
|
||||
std::cout << typeid(x).name() << std::endl;
|
||||
if (strcmp(typeid(x).name(), "int") == 0) {
|
||||
std::cout << "typeid(x) is int, GOOD" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "typeid(x) is NOT int, BAD" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << typeid(p).name() << std::endl;
|
||||
if (strcmp(typeid(p).name(), "class TestClass") == 0) {
|
||||
std::cout << "typeid(p) is \"class TestClass\", GOOD" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "typeid(p) is NOT \"class TestClass\", BAD" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << typeid(s).name() << std::endl;
|
||||
if (strcmp(typeid(s).name(), "struct TestStruct") == 0) {
|
||||
std::cout << "typeid(s) is \"struct TestStruct\", GOOD" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "typeid(s) is NOT \"struct TestStruct\", BAD" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Reached virtual methods and dynamic_cast test. GOOD" << std::endl;
|
||||
|
||||
TestClass2* kz = new TestClass2;
|
||||
|
||||
Something* ks = static_cast<Something*>(kz);
|
||||
|
||||
ks->zzz();
|
||||
|
||||
TestClass* pk = dynamic_cast<TestClass*>(ks);
|
||||
|
||||
pk->yyy();
|
||||
|
||||
std::cout << "Reached virtual destructor test. GOOD" << std::endl;
|
||||
|
||||
delete pk;
|
||||
|
||||
std::cout << "Finished all tests. GOOD" << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
|
||||
void test1() {
|
||||
__try {
|
||||
printf("Inside __try block. (GOOD)\n");
|
||||
|
||||
RaiseException(
|
||||
0xE0000001,
|
||||
0,
|
||||
0,
|
||||
nullptr
|
||||
);
|
||||
|
||||
printf("After RaiseException. (BAD)\n");
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
printf("In __except block. (GOOD)\n");
|
||||
|
||||
unsigned long excepCode = GetExceptionCode();
|
||||
|
||||
printf("Exception code=0x%x\n", excepCode);
|
||||
|
||||
if (excepCode == 0xE0000001) {
|
||||
printf("Exception code IS same, GOOD\n");
|
||||
}
|
||||
else {
|
||||
printf("Exception code DOES NOT MATCH, BAD\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("After __except block. (GOOD)\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
/*
|
||||
* For this program, all subtests successful will print:
|
||||
* - 4 'GOOD'
|
||||
* - 0 'BAD'
|
||||
*/
|
||||
|
||||
test1();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -78,6 +78,8 @@ def my_sandbox(path, rootfs):
|
||||
|
||||
ql.os.set_syscall('vfork', __vfork)
|
||||
|
||||
os.unlink(fr'{ROOTFS}/proc/sys/kernel/core_pattern')
|
||||
|
||||
ql.run()
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[CODE]
|
||||
ram_size = 0xa00000
|
||||
load_address = 0x80800000
|
||||
entry_point = 0x80800000
|
||||
heap_address = 0xa0000000
|
||||
heap_size = 0x300000
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user