Native Code Execution (NCE)

It's finally here, we're executing code completely natively. This is something that would completely break the project if it wasn't possible.
This commit is contained in:
◱ PixelyIon 2019-08-17 04:34:37 +05:30
parent 182729b247
commit 92c1491a84
15 changed files with 228 additions and 1367 deletions

View File

@ -10,7 +10,6 @@ add_subdirectory("libraries/fmt")
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp) set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
include_directories(libraries/unicorn/include)
include_directories(${source_DIR}) include_directories(${source_DIR})
add_library(lightswitch SHARED add_library(lightswitch SHARED
@ -24,5 +23,5 @@ add_library(lightswitch SHARED
${source_DIR}/switch/common.cpp ${source_DIR}/switch/common.cpp
${source_DIR}/switch/loader/nro.cpp ${source_DIR}/switch/loader/nro.cpp
) )
target_link_libraries(lightswitch ${CMAKE_SOURCE_DIR}/libraries/unicorn/libunicorn.a fmt tinyxml2) target_link_libraries(lightswitch fmt tinyxml2)
target_compile_options(lightswitch PRIVATE -Wno-c++17-extensions) target_compile_options(lightswitch PRIVATE -Wno-c++17-extensions)

View File

@ -1,307 +0,0 @@
/* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015-2017 */
/* This file is released under LGPL2.
See COPYING.LGPL2 in root directory for more details
*/
#ifndef UNICORN_ARM64_H
#define UNICORN_ARM64_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _MSC_VER
#pragma warning(disable:4201)
#endif
//> ARM64 registers
typedef enum uc_arm64_reg {
UC_ARM64_REG_INVALID = 0,
UC_ARM64_REG_X29,
UC_ARM64_REG_X30,
UC_ARM64_REG_NZCV,
UC_ARM64_REG_SP,
UC_ARM64_REG_WSP,
UC_ARM64_REG_WZR,
UC_ARM64_REG_XZR,
UC_ARM64_REG_B0,
UC_ARM64_REG_B1,
UC_ARM64_REG_B2,
UC_ARM64_REG_B3,
UC_ARM64_REG_B4,
UC_ARM64_REG_B5,
UC_ARM64_REG_B6,
UC_ARM64_REG_B7,
UC_ARM64_REG_B8,
UC_ARM64_REG_B9,
UC_ARM64_REG_B10,
UC_ARM64_REG_B11,
UC_ARM64_REG_B12,
UC_ARM64_REG_B13,
UC_ARM64_REG_B14,
UC_ARM64_REG_B15,
UC_ARM64_REG_B16,
UC_ARM64_REG_B17,
UC_ARM64_REG_B18,
UC_ARM64_REG_B19,
UC_ARM64_REG_B20,
UC_ARM64_REG_B21,
UC_ARM64_REG_B22,
UC_ARM64_REG_B23,
UC_ARM64_REG_B24,
UC_ARM64_REG_B25,
UC_ARM64_REG_B26,
UC_ARM64_REG_B27,
UC_ARM64_REG_B28,
UC_ARM64_REG_B29,
UC_ARM64_REG_B30,
UC_ARM64_REG_B31,
UC_ARM64_REG_D0,
UC_ARM64_REG_D1,
UC_ARM64_REG_D2,
UC_ARM64_REG_D3,
UC_ARM64_REG_D4,
UC_ARM64_REG_D5,
UC_ARM64_REG_D6,
UC_ARM64_REG_D7,
UC_ARM64_REG_D8,
UC_ARM64_REG_D9,
UC_ARM64_REG_D10,
UC_ARM64_REG_D11,
UC_ARM64_REG_D12,
UC_ARM64_REG_D13,
UC_ARM64_REG_D14,
UC_ARM64_REG_D15,
UC_ARM64_REG_D16,
UC_ARM64_REG_D17,
UC_ARM64_REG_D18,
UC_ARM64_REG_D19,
UC_ARM64_REG_D20,
UC_ARM64_REG_D21,
UC_ARM64_REG_D22,
UC_ARM64_REG_D23,
UC_ARM64_REG_D24,
UC_ARM64_REG_D25,
UC_ARM64_REG_D26,
UC_ARM64_REG_D27,
UC_ARM64_REG_D28,
UC_ARM64_REG_D29,
UC_ARM64_REG_D30,
UC_ARM64_REG_D31,
UC_ARM64_REG_H0,
UC_ARM64_REG_H1,
UC_ARM64_REG_H2,
UC_ARM64_REG_H3,
UC_ARM64_REG_H4,
UC_ARM64_REG_H5,
UC_ARM64_REG_H6,
UC_ARM64_REG_H7,
UC_ARM64_REG_H8,
UC_ARM64_REG_H9,
UC_ARM64_REG_H10,
UC_ARM64_REG_H11,
UC_ARM64_REG_H12,
UC_ARM64_REG_H13,
UC_ARM64_REG_H14,
UC_ARM64_REG_H15,
UC_ARM64_REG_H16,
UC_ARM64_REG_H17,
UC_ARM64_REG_H18,
UC_ARM64_REG_H19,
UC_ARM64_REG_H20,
UC_ARM64_REG_H21,
UC_ARM64_REG_H22,
UC_ARM64_REG_H23,
UC_ARM64_REG_H24,
UC_ARM64_REG_H25,
UC_ARM64_REG_H26,
UC_ARM64_REG_H27,
UC_ARM64_REG_H28,
UC_ARM64_REG_H29,
UC_ARM64_REG_H30,
UC_ARM64_REG_H31,
UC_ARM64_REG_Q0,
UC_ARM64_REG_Q1,
UC_ARM64_REG_Q2,
UC_ARM64_REG_Q3,
UC_ARM64_REG_Q4,
UC_ARM64_REG_Q5,
UC_ARM64_REG_Q6,
UC_ARM64_REG_Q7,
UC_ARM64_REG_Q8,
UC_ARM64_REG_Q9,
UC_ARM64_REG_Q10,
UC_ARM64_REG_Q11,
UC_ARM64_REG_Q12,
UC_ARM64_REG_Q13,
UC_ARM64_REG_Q14,
UC_ARM64_REG_Q15,
UC_ARM64_REG_Q16,
UC_ARM64_REG_Q17,
UC_ARM64_REG_Q18,
UC_ARM64_REG_Q19,
UC_ARM64_REG_Q20,
UC_ARM64_REG_Q21,
UC_ARM64_REG_Q22,
UC_ARM64_REG_Q23,
UC_ARM64_REG_Q24,
UC_ARM64_REG_Q25,
UC_ARM64_REG_Q26,
UC_ARM64_REG_Q27,
UC_ARM64_REG_Q28,
UC_ARM64_REG_Q29,
UC_ARM64_REG_Q30,
UC_ARM64_REG_Q31,
UC_ARM64_REG_S0,
UC_ARM64_REG_S1,
UC_ARM64_REG_S2,
UC_ARM64_REG_S3,
UC_ARM64_REG_S4,
UC_ARM64_REG_S5,
UC_ARM64_REG_S6,
UC_ARM64_REG_S7,
UC_ARM64_REG_S8,
UC_ARM64_REG_S9,
UC_ARM64_REG_S10,
UC_ARM64_REG_S11,
UC_ARM64_REG_S12,
UC_ARM64_REG_S13,
UC_ARM64_REG_S14,
UC_ARM64_REG_S15,
UC_ARM64_REG_S16,
UC_ARM64_REG_S17,
UC_ARM64_REG_S18,
UC_ARM64_REG_S19,
UC_ARM64_REG_S20,
UC_ARM64_REG_S21,
UC_ARM64_REG_S22,
UC_ARM64_REG_S23,
UC_ARM64_REG_S24,
UC_ARM64_REG_S25,
UC_ARM64_REG_S26,
UC_ARM64_REG_S27,
UC_ARM64_REG_S28,
UC_ARM64_REG_S29,
UC_ARM64_REG_S30,
UC_ARM64_REG_S31,
UC_ARM64_REG_W0,
UC_ARM64_REG_W1,
UC_ARM64_REG_W2,
UC_ARM64_REG_W3,
UC_ARM64_REG_W4,
UC_ARM64_REG_W5,
UC_ARM64_REG_W6,
UC_ARM64_REG_W7,
UC_ARM64_REG_W8,
UC_ARM64_REG_W9,
UC_ARM64_REG_W10,
UC_ARM64_REG_W11,
UC_ARM64_REG_W12,
UC_ARM64_REG_W13,
UC_ARM64_REG_W14,
UC_ARM64_REG_W15,
UC_ARM64_REG_W16,
UC_ARM64_REG_W17,
UC_ARM64_REG_W18,
UC_ARM64_REG_W19,
UC_ARM64_REG_W20,
UC_ARM64_REG_W21,
UC_ARM64_REG_W22,
UC_ARM64_REG_W23,
UC_ARM64_REG_W24,
UC_ARM64_REG_W25,
UC_ARM64_REG_W26,
UC_ARM64_REG_W27,
UC_ARM64_REG_W28,
UC_ARM64_REG_W29,
UC_ARM64_REG_W30,
UC_ARM64_REG_X0,
UC_ARM64_REG_X1,
UC_ARM64_REG_X2,
UC_ARM64_REG_X3,
UC_ARM64_REG_X4,
UC_ARM64_REG_X5,
UC_ARM64_REG_X6,
UC_ARM64_REG_X7,
UC_ARM64_REG_X8,
UC_ARM64_REG_X9,
UC_ARM64_REG_X10,
UC_ARM64_REG_X11,
UC_ARM64_REG_X12,
UC_ARM64_REG_X13,
UC_ARM64_REG_X14,
UC_ARM64_REG_X15,
UC_ARM64_REG_X16,
UC_ARM64_REG_X17,
UC_ARM64_REG_X18,
UC_ARM64_REG_X19,
UC_ARM64_REG_X20,
UC_ARM64_REG_X21,
UC_ARM64_REG_X22,
UC_ARM64_REG_X23,
UC_ARM64_REG_X24,
UC_ARM64_REG_X25,
UC_ARM64_REG_X26,
UC_ARM64_REG_X27,
UC_ARM64_REG_X28,
UC_ARM64_REG_V0,
UC_ARM64_REG_V1,
UC_ARM64_REG_V2,
UC_ARM64_REG_V3,
UC_ARM64_REG_V4,
UC_ARM64_REG_V5,
UC_ARM64_REG_V6,
UC_ARM64_REG_V7,
UC_ARM64_REG_V8,
UC_ARM64_REG_V9,
UC_ARM64_REG_V10,
UC_ARM64_REG_V11,
UC_ARM64_REG_V12,
UC_ARM64_REG_V13,
UC_ARM64_REG_V14,
UC_ARM64_REG_V15,
UC_ARM64_REG_V16,
UC_ARM64_REG_V17,
UC_ARM64_REG_V18,
UC_ARM64_REG_V19,
UC_ARM64_REG_V20,
UC_ARM64_REG_V21,
UC_ARM64_REG_V22,
UC_ARM64_REG_V23,
UC_ARM64_REG_V24,
UC_ARM64_REG_V25,
UC_ARM64_REG_V26,
UC_ARM64_REG_V27,
UC_ARM64_REG_V28,
UC_ARM64_REG_V29,
UC_ARM64_REG_V30,
UC_ARM64_REG_V31,
//> pseudo registers
UC_ARM64_REG_PC, // program counter register
UC_ARM64_REG_CPACR_EL1,
//> thread registers
UC_ARM64_REG_TPIDR_EL0,
UC_ARM64_REG_TPIDRRO_EL0,
UC_ARM64_REG_TPIDR_EL1,
UC_ARM64_REG_ENDING, // <-- mark the end of the list of registers
//> alias registers
UC_ARM64_REG_IP0 = UC_ARM64_REG_X16,
UC_ARM64_REG_IP1 = UC_ARM64_REG_X17,
UC_ARM64_REG_FP = UC_ARM64_REG_X29,
UC_ARM64_REG_LR = UC_ARM64_REG_X30,
} uc_arm64_reg;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,219 +0,0 @@
/* This file is released under LGPL2.
See COPYING.LGPL2 in root directory for more details
*/
/*
This file is to support header files that are missing in MSVC and
other non-standard compilers.
*/
#ifndef UNICORN_PLATFORM_H
#define UNICORN_PLATFORM_H
/*
These are the various MSVC versions as given by _MSC_VER:
MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003)
MSVC++ 7.0 _MSC_VER == 1300
MSVC++ 6.0 _MSC_VER == 1200
MSVC++ 5.0 _MSC_VER == 1100
*/
#define MSC_VER_VS2003 1310
#define MSC_VER_VS2005 1400
#define MSC_VER_VS2008 1500
#define MSC_VER_VS2010 1600
#define MSC_VER_VS2012 1700
#define MSC_VER_VS2013 1800
#define MSC_VER_VS2015 1900
// handle stdbool.h compatibility
#if !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64))
// MSVC
// stdbool.h
#if (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE)
// this system does not have stdbool.h
#ifndef __cplusplus
typedef unsigned char bool;
#define false 0
#define true 1
#endif // __cplusplus
#else
// VisualStudio 2013+ -> C99 is supported
#include <stdbool.h>
#endif // (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE)
#else
// not MSVC -> C99 is supported
#include <stdbool.h>
#endif // !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64))
#if (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || defined(_KERNEL_MODE)
// this system does not have stdint.h
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
#ifndef _INTPTR_T_DEFINED
#define _INTPTR_T_DEFINED
#ifdef _WIN64
typedef long long intptr_t;
#else /* _WIN64 */
typedef _W64 int intptr_t;
#endif /* _WIN64 */
#endif /* _INTPTR_T_DEFINED */
#ifndef _UINTPTR_T_DEFINED
#define _UINTPTR_T_DEFINED
#ifdef _WIN64
typedef unsigned long long uintptr_t;
#else /* _WIN64 */
typedef _W64 unsigned int uintptr_t;
#endif /* _WIN64 */
#endif /* _UINTPTR_T_DEFINED */
#define INT8_MIN (-127i8 - 1)
#define INT16_MIN (-32767i16 - 1)
#define INT32_MIN (-2147483647i32 - 1)
#define INT64_MIN (-9223372036854775807i64 - 1)
#define INT8_MAX 127i8
#define INT16_MAX 32767i16
#define INT32_MAX 2147483647i32
#define INT64_MAX 9223372036854775807i64
#define UINT8_MAX 0xffui8
#define UINT16_MAX 0xffffui16
#define UINT32_MAX 0xffffffffui32
#define UINT64_MAX 0xffffffffffffffffui64
#else // this system has stdint.h
#include <stdint.h>
#endif // (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || defined(_KERNEL_MODE)
// handle inttypes.h compatibility
#if (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2013)) || defined(_KERNEL_MODE)
// this system does not have inttypes.h
#define __PRI_8_LENGTH_MODIFIER__ "hh"
#define __PRI_64_LENGTH_MODIFIER__ "ll"
#define PRId8 __PRI_8_LENGTH_MODIFIER__ "d"
#define PRIi8 __PRI_8_LENGTH_MODIFIER__ "i"
#define PRIo8 __PRI_8_LENGTH_MODIFIER__ "o"
#define PRIu8 __PRI_8_LENGTH_MODIFIER__ "u"
#define PRIx8 __PRI_8_LENGTH_MODIFIER__ "x"
#define PRIX8 __PRI_8_LENGTH_MODIFIER__ "X"
#define PRId16 "hd"
#define PRIi16 "hi"
#define PRIo16 "ho"
#define PRIu16 "hu"
#define PRIx16 "hx"
#define PRIX16 "hX"
#if defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012)
#define PRId32 "ld"
#define PRIi32 "li"
#define PRIo32 "lo"
#define PRIu32 "lu"
#define PRIx32 "lx"
#define PRIX32 "lX"
#else // OSX
#define PRId32 "d"
#define PRIi32 "i"
#define PRIo32 "o"
#define PRIu32 "u"
#define PRIx32 "x"
#define PRIX32 "X"
#endif // defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012)
#if defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012)
// redefine functions from inttypes.h used in cstool
#define strtoull _strtoui64
#endif
#define PRId64 __PRI_64_LENGTH_MODIFIER__ "d"
#define PRIi64 __PRI_64_LENGTH_MODIFIER__ "i"
#define PRIo64 __PRI_64_LENGTH_MODIFIER__ "o"
#define PRIu64 __PRI_64_LENGTH_MODIFIER__ "u"
#define PRIx64 __PRI_64_LENGTH_MODIFIER__ "x"
#define PRIX64 __PRI_64_LENGTH_MODIFIER__ "X"
#else
// this system has inttypes.h by default
#include <inttypes.h>
#endif // #if defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE)
// sys/time.h compatibility
#if defined(_MSC_VER)
#include <sys/timeb.h>
#include <winsock2.h>
#include <windows.h>
static int gettimeofday(struct timeval* t, void* timezone)
{
struct _timeb timebuffer;
_ftime( &timebuffer );
t->tv_sec = (long)timebuffer.time;
t->tv_usec = 1000*timebuffer.millitm;
return 0;
}
#else
#include <sys/time.h>
#endif
// unistd.h compatibility
#if defined(_MSC_VER)
static int usleep(uint32_t usec)
{
HANDLE timer;
LARGE_INTEGER due;
timer = CreateWaitableTimer(NULL, TRUE, NULL);
if (!timer)
return -1;
due.QuadPart = (-((int64_t) usec)) * 10LL;
if (!SetWaitableTimer(timer, &due, 0, NULL, NULL, 0)) {
CloseHandle(timer);
return -1;
}
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
return 0;
}
#else
#include <unistd.h>
#endif
// misc support
#if defined(_MSC_VER)
#ifdef _WIN64
typedef signed __int64 ssize_t;
#else
typedef _W64 signed int ssize_t;
#endif
#define va_copy(d,s) ((d) = (s))
#define strcasecmp _stricmp
#if (_MSC_VER < MSC_VER_VS2015)
#define snprintf _snprintf
#endif
#if (_MSC_VER <= MSC_VER_VS2013)
#define strtoll _strtoi64
#endif
#endif
#endif // UNICORN_PLATFORM_H

View File

@ -1,725 +0,0 @@
/* Unicorn Emulator Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015-2017 */
/* This file is released under LGPL2.
See COPYING.LGPL2 in root directory for more details
*/
#ifndef UNICORN_ENGINE_H
#define UNICORN_ENGINE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "platform.h"
#include <stdarg.h>
#if defined(UNICORN_HAS_OSXKERNEL)
#include <libkern/libkern.h>
#else
#include <stdlib.h>
#include <stdio.h>
#endif
struct uc_struct;
typedef struct uc_struct uc_engine;
typedef size_t uc_hook;
#include "arm64.h"
#ifdef __GNUC__
#define DEFAULT_VISIBILITY __attribute__((visibility("default")))
#else
#define DEFAULT_VISIBILITY
#endif
#ifdef _MSC_VER
#pragma warning(disable:4201)
#pragma warning(disable:4100)
#ifdef UNICORN_SHARED
#define UNICORN_EXPORT __declspec(dllexport)
#else // defined(UNICORN_STATIC)
#define UNICORN_EXPORT
#endif
#else
#ifdef __GNUC__
#define UNICORN_EXPORT __attribute__((visibility("default")))
#else
#define UNICORN_EXPORT
#endif
#endif
#ifdef __GNUC__
#define UNICORN_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define UNICORN_DEPRECATED __declspec(deprecated)
#else
#pragma message("WARNING: You need to implement UNICORN_DEPRECATED for this compiler")
#define UNICORN_DEPRECATED
#endif
// Unicorn API version
#define UC_API_MAJOR 1
#define UC_API_MINOR 0
// Unicorn package version
#define UC_VERSION_MAJOR UC_API_MAJOR
#define UC_VERSION_MINOR UC_API_MINOR
#define UC_VERSION_EXTRA 2
/*
Macro to create combined version which can be compared to
result of uc_version() API.
*/
#define UC_MAKE_VERSION(major, minor) ((major << 8) + minor)
// Scales to calculate timeout on microsecond unit
// 1 second = 1000,000 microseconds
#define UC_SECOND_SCALE 1000000
// 1 milisecond = 1000 nanoseconds
#define UC_MILISECOND_SCALE 1000
// Architecture type
typedef enum uc_arch {
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
UC_ARCH_ARM64, // ARM-64, also called AArch64
UC_ARCH_MIPS, // Mips architecture
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
UC_ARCH_SPARC, // Sparc architecture
UC_ARCH_M68K, // M68K architecture
UC_ARCH_MAX,
} uc_arch;
// Mode type
typedef enum uc_mode {
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
// arm / arm64
UC_MODE_ARM = 0, // ARM mode
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
// mips
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
// x86 / x64
UC_MODE_16 = 1 << 1, // 16-bit mode
UC_MODE_32 = 1 << 2, // 32-bit mode
UC_MODE_64 = 1 << 3, // 64-bit mode
// ppc
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
// sparc
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
// m68k
} uc_mode;
// All type of errors encountered by Unicorn API.
// These are values returned by uc_errno()
typedef enum uc_err {
UC_ERR_OK = 0, // No error: everything was fine
UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate()
UC_ERR_ARCH, // Unsupported architecture: uc_open()
UC_ERR_HANDLE, // Invalid handle
UC_ERR_MODE, // Invalid/unsupported mode: uc_open()
UC_ERR_VERSION, // Unsupported version (bindings)
UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API)
UC_ERR_READ_UNALIGNED, // Unaligned read
UC_ERR_WRITE_UNALIGNED, // Unaligned write
UC_ERR_FETCH_UNALIGNED, // Unaligned fetch
UC_ERR_HOOK_EXIST, // hook for this event already existed
UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start()
UC_ERR_EXCEPTION // Unhandled CPU exception
} uc_err;
/*
Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK)
@address: address where the code is being executed
@size: size of machine instruction(s) being executed, or 0 when size is unknown
@user_data: user data passed to tracing APIs.
*/
typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, void *user_data);
/*
Callback function for tracing interrupts (for uc_hook_intr())
@intno: interrupt number
@user_data: user data passed to tracing APIs.
*/
typedef void (*uc_cb_hookintr_t)(uc_engine *uc, uint32_t intno, void *user_data);
/*
Callback function for tracing IN instruction of X86
@port: port number
@size: data size (1/2/4) to be read from this port
@user_data: user data passed to tracing APIs.
*/
typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size, void *user_data);
/*
Callback function for OUT instruction of X86
@port: port number
@size: data size (1/2/4) to be written to this port
@value: data value to be written to this port
*/
typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data);
// All type of memory accesses for UC_HOOK_MEM_*
typedef enum uc_mem_type {
UC_MEM_READ = 16, // Memory is read from
UC_MEM_WRITE, // Memory is written to
UC_MEM_FETCH, // Memory is fetched
UC_MEM_READ_UNMAPPED, // Unmapped memory is read from
UC_MEM_WRITE_UNMAPPED, // Unmapped memory is written to
UC_MEM_FETCH_UNMAPPED, // Unmapped memory is fetched
UC_MEM_WRITE_PROT, // Write to write protected, but mapped, memory
UC_MEM_READ_PROT, // Read from read protected, but mapped, memory
UC_MEM_FETCH_PROT, // Fetch from non-executable, but mapped, memory
UC_MEM_READ_AFTER, // Memory is read from (successful access)
} uc_mem_type;
// All type of hooks for uc_hook_add() API.
typedef enum uc_hook_type {
// Hook all interrupt/syscall events
UC_HOOK_INTR = 1 << 0,
// Hook a particular instruction - only a very small subset of instructions supported here
UC_HOOK_INSN = 1 << 1,
// Hook a range of code
UC_HOOK_CODE = 1 << 2,
// Hook basic blocks
UC_HOOK_BLOCK = 1 << 3,
// Hook for memory read on unmapped memory
UC_HOOK_MEM_READ_UNMAPPED = 1 << 4,
// Hook for invalid memory write events
UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5,
// Hook for invalid memory fetch for execution events
UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6,
// Hook for memory read on read-protected memory
UC_HOOK_MEM_READ_PROT = 1 << 7,
// Hook for memory write on write-protected memory
UC_HOOK_MEM_WRITE_PROT = 1 << 8,
// Hook for memory fetch on non-executable memory
UC_HOOK_MEM_FETCH_PROT = 1 << 9,
// Hook memory read events.
UC_HOOK_MEM_READ = 1 << 10,
// Hook memory write events.
UC_HOOK_MEM_WRITE = 1 << 11,
// Hook memory fetch for execution events
UC_HOOK_MEM_FETCH = 1 << 12,
// Hook memory read events, but only successful access.
// The callback will be triggered after successful read.
UC_HOOK_MEM_READ_AFTER = 1 << 13,
} uc_hook_type;
// Hook type for all events of unmapped memory access
#define UC_HOOK_MEM_UNMAPPED (UC_HOOK_MEM_READ_UNMAPPED + UC_HOOK_MEM_WRITE_UNMAPPED + UC_HOOK_MEM_FETCH_UNMAPPED)
// Hook type for all events of illegal protected memory access
#define UC_HOOK_MEM_PROT (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT)
// Hook type for all events of illegal read memory access
#define UC_HOOK_MEM_READ_INVALID (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_UNMAPPED)
// Hook type for all events of illegal write memory access
#define UC_HOOK_MEM_WRITE_INVALID (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_UNMAPPED)
// Hook type for all events of illegal fetch memory access
#define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED)
// Hook type for all events of illegal memory access
#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT)
// Hook type for all events of valid memory access
// NOTE: UC_HOOK_MEM_READ is triggered before UC_HOOK_MEM_READ_PROT and UC_HOOK_MEM_READ_UNMAPPED, so
// this hook may technically trigger on some invalid reads.
#define UC_HOOK_MEM_VALID (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH)
/*
Callback function for hooking memory (READ, WRITE & FETCH)
@type: this memory is being READ, or WRITE
@address: address where the code is being executed
@size: size of data being read or written
@value: value of data being written to memory, or irrelevant if type = READ.
@user_data: user data passed to tracing APIs
*/
typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,
uint64_t address, int size, int64_t value, void *user_data);
/*
Callback function for handling invalid memory access events (UNMAPPED and
PROT events)
@type: this memory is being READ, or WRITE
@address: address where the code is being executed
@size: size of data being read or written
@value: value of data being written to memory, or irrelevant if type = READ.
@user_data: user data passed to tracing APIs
@return: return true to continue, or false to stop program (due to invalid memory).
NOTE: returning true to continue execution will only work if if the accessed
memory is made accessible with the correct permissions during the hook.
In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback,
the memory should be uc_mem_map()-ed with the correct permissions, and the
instruction will then read or write to the address as it was supposed to.
In the event of a UC_MEM_FETCH_UNMAPPED callback, the memory can be mapped
in as executable, in which case execution will resume from the fetched address.
The instruction pointer may be written to in order to change where execution resumes,
but the fetch must succeed if execution is to resume.
*/
typedef bool (*uc_cb_eventmem_t)(uc_engine *uc, uc_mem_type type,
uint64_t address, int size, int64_t value, void *user_data);
/*
Memory region mapped by uc_mem_map() and uc_mem_map_ptr()
Retrieve the list of memory regions with uc_mem_regions()
*/
typedef struct uc_mem_region {
uint64_t begin; // begin address of the region (inclusive)
uint64_t end; // end address of the region (inclusive)
uint32_t perms; // memory permissions of the region
} uc_mem_region;
// All type of queries for uc_query() API.
typedef enum uc_query_type {
// Dynamically query current hardware mode.
UC_QUERY_MODE = 1,
UC_QUERY_PAGE_SIZE,
UC_QUERY_ARCH,
} uc_query_type;
// Opaque storage for CPU context, used with uc_context_*()
struct uc_context;
typedef struct uc_context uc_context;
/*
Return combined API version & major and minor version numbers.
@major: major number of API version
@minor: minor number of API version
@return hexical number as (major << 8 | minor), which encodes both
major & minor versions.
NOTE: This returned value can be compared with version number made
with macro UC_MAKE_VERSION
For example, second API version would return 1 in @major, and 1 in @minor
The return value would be 0x0101
NOTE: if you only care about returned value, but not major and minor values,
set both @major & @minor arguments to NULL.
*/
UNICORN_EXPORT
unsigned int uc_version(unsigned int *major, unsigned int *minor);
/*
Determine if the given architecture is supported by this library.
@arch: architecture type (UC_ARCH_*)
@return True if this library supports the given arch.
*/
UNICORN_EXPORT
bool uc_arch_supported(uc_arch arch);
/*
Create new instance of unicorn engine.
@arch: architecture type (UC_ARCH_*)
@mode: hardware mode. This is combined of UC_MODE_*
@uc: pointer to uc_engine, which will be updated at return time
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);
/*
Close a Unicorn engine instance.
NOTE: this must be called only when there is no longer any
usage of @uc. This API releases some of @uc's cached memory, thus
any use of the Unicorn API with @uc after it has been closed may
crash your application. After this, @uc is invalid, and is no
longer usable.
@uc: pointer to a handle returned by uc_open()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_close(uc_engine *uc);
/*
Query internal status of engine.
@uc: handle returned by uc_open()
@type: query type. See uc_query_type
@result: save the internal status queried
@return: error code of uc_err enum type (UC_ERR_*, see above)
*/
UNICORN_EXPORT
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result);
/*
Report the last error number when some API function fail.
Like glibc's errno, uc_errno might not retain its old value once accessed.
@uc: handle returned by uc_open()
@return: error code of uc_err enum type (UC_ERR_*, see above)
*/
UNICORN_EXPORT
uc_err uc_errno(uc_engine *uc);
/*
Return a string describing given error code.
@code: error code (see UC_ERR_* above)
@return: returns a pointer to a string that describes the error code
passed in the argument @code
*/
UNICORN_EXPORT
const char *uc_strerror(uc_err code);
/*
Write to register.
@uc: handle returned by uc_open()
@regid: register ID that is to be modified.
@value: pointer to the value that will set to register @regid
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value);
/*
Read register value.
@uc: handle returned by uc_open()
@regid: register ID that is to be retrieved.
@value: pointer to a variable storing the register value.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_reg_read(uc_engine *uc, int regid, void *value);
/*
Write multiple register values.
@uc: handle returned by uc_open()
@rges: array of register IDs to store
@value: pointer to array of register values
@count: length of both *regs and *vals
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals, int count);
/*
Read multiple register values.
@uc: handle returned by uc_open()
@rges: array of register IDs to retrieve
@value: pointer to array of values to hold registers
@count: length of both *regs and *vals
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count);
/*
Write to a range of bytes in memory.
@uc: handle returned by uc_open()
@address: starting memory address of bytes to set.
@bytes: pointer to a variable containing data to be written to memory.
@size: size of memory to write to.
NOTE: @bytes must be big enough to contain @size bytes.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t size);
/*
Read a range of bytes in memory.
@uc: handle returned by uc_open()
@address: starting memory address of bytes to get.
@bytes: pointer to a variable containing data copied from memory.
@size: size of memory to read.
NOTE: @bytes must be big enough to contain @size bytes.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size);
/*
Emulate machine code in a specific duration of time.
@uc: handle returned by uc_open()
@begin: address where emulation starts
@until: address where emulation stops (i.e when this address is hit)
@timeout: duration to emulate the code (in microseconds). When this value is 0,
we will emulate the code in infinite time, until the code is finished.
@count: the number of instructions to be emulated. When this value is 0,
we will emulate all the code available, until the code is finished.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count);
/*
Stop emulation (which was started by uc_emu_start() API.
This is typically called from callback functions registered via tracing APIs.
@uc: handle returned by uc_open()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_emu_stop(uc_engine *uc);
/*
Register callback for a hook event.
The callback will be run when the hook event is hit.
@uc: handle returned by uc_open()
@hh: hook handle returned from this registration. To be used in uc_hook_del() API
@type: hook type
@callback: callback to be run when instruction is hit
@user_data: user-defined data. This will be passed to callback function in its
last argument @user_data
@begin: start address of the area where the callback is effect (inclusive)
@end: end address of the area where the callback is effect (inclusive)
NOTE 1: the callback is called only if related address is in range [@begin, @end]
NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered
@...: variable arguments (depending on @type)
NOTE: if @type = UC_HOOK_INSN, this is the instruction ID (ex: UC_X86_INS_OUT)
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
void *user_data, uint64_t begin, uint64_t end, ...);
/*
Unregister (remove) a hook callback.
This API removes the hook callback registered by uc_hook_add().
NOTE: this should be called only when you no longer want to trace.
After this, @hh is invalid, and nolonger usable.
@uc: handle returned by uc_open()
@hh: handle returned by uc_hook_add()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_hook_del(uc_engine *uc, uc_hook hh);
typedef enum uc_prot {
UC_PROT_NONE = 0,
UC_PROT_READ = 1,
UC_PROT_WRITE = 2,
UC_PROT_EXEC = 4,
UC_PROT_ALL = 7,
} uc_prot;
/*
Map memory in for emulation.
This API adds a memory region that can be used by emulation.
@uc: handle returned by uc_open()
@address: starting address of the new memory region to be mapped in.
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
@size: size of the new memory region to be mapped in.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
@perms: Permissions for the newly mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
or this will return with UC_ERR_ARG error.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
/*
Map existing host memory in for emulation.
This API adds a memory region that can be used by emulation.
@uc: handle returned by uc_open()
@address: starting address of the new memory region to be mapped in.
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
@size: size of the new memory region to be mapped in.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
@perms: Permissions for the newly mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
or this will return with UC_ERR_ARG error.
@ptr: pointer to host memory backing the newly mapped memory. This host memory is
expected to be an equal or larger size than provided, and be mapped with at
least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is undefined.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr);
/*
Unmap a region of emulation memory.
This API deletes a memory mapping from the emulation memory space.
@uc: handle returned by uc_open()
@address: starting address of the memory region to be unmapped.
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
@size: size of the memory region to be modified.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size);
/*
Set memory permissions for emulation memory.
This API changes permissions on an existing memory region.
@uc: handle returned by uc_open()
@address: starting address of the memory region to be modified.
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
@size: size of the memory region to be modified.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
@perms: New permissions for the mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
or this will return with UC_ERR_ARG error.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
/*
Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr()
This API allocates memory for @regions, and user must free this memory later
by free() to avoid leaking memory.
NOTE: memory regions may be splitted by uc_mem_unmap()
@uc: handle returned by uc_open()
@regions: pointer to an array of uc_mem_region struct. This is allocated by
Unicorn, and must be freed by user later with uc_free()
@count: pointer to number of struct uc_mem_region contained in @regions
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
/*
Allocate a region that can be used with uc_context_{save,restore} to perform
quick save/rollback of the CPU context, which includes registers and some
internal metadata. Contexts may not be shared across engine instances with
differing arches or modes.
@uc: handle returned by uc_open()
@context: pointer to a uc_engine*. This will be updated with the pointer to
the new context on successful return of this function.
Later, this allocated memory must be freed with uc_free().
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_context_alloc(uc_engine *uc, uc_context **context);
/*
Free the memory allocated by uc_context_alloc & uc_mem_regions.
@mem: memory allocated by uc_context_alloc (returned in *context), or
by uc_mem_regions (returned in *regions)
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_free(void *mem);
/*
Save a copy of the internal CPU context.
This API should be used to efficiently make or update a saved copy of the
internal CPU state.
@uc: handle returned by uc_open()
@context: handle returned by uc_context_alloc()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_context_save(uc_engine *uc, uc_context *context);
/*
Restore the current CPU context from a saved copy.
This API should be used to roll the CPU context back to a previous
state saved by uc_context_save().
@uc: handle returned by uc_open()
@buffer: handle returned by uc_context_alloc that has been used with uc_context_save
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_context_restore(uc_engine *uc, uc_context *context);
#ifdef __cplusplus
}
#endif
#endif

Binary file not shown.

View File

@ -10,11 +10,63 @@ namespace lightSwitch {
namespace constant { namespace constant {
constexpr uint64_t base_addr = 0x80000000; constexpr uint64_t base_addr = 0x80000000;
constexpr uint64_t stack_addr = 0x3000000; constexpr uint64_t stack_addr = 0x3000000;
constexpr size_t stack_size = 0x1000000; constexpr size_t stack_size = 280; //0x1000000
constexpr uint64_t tls_addr = 0x2000000; constexpr uint64_t tls_addr = 0x2000000;
constexpr size_t tls_size = 0x1000; constexpr size_t tls_size = 0x1000;
constexpr uint32_t nro_magic = 0x304F524E; // NRO0 in reverse constexpr uint32_t nro_magic = 0x304F524E; // NRO0 in reverse
constexpr uint_t svc_unimpl = 0x177202; // "Unimplemented behaviour" constexpr uint_t svc_unimpl = 0x177202; // "Unimplemented behaviour"
constexpr uint32_t base_handle_index = 0xd001; constexpr uint32_t base_handle_index = 0xD001;
constexpr uint16_t svc_last = 0x7F;
constexpr uint8_t num_regs = 31;
constexpr uint32_t tpidrro_el0 = 0x5E83; // ID of tpidrro_el0 in MRS
};
namespace instr {
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction
// For some reason if value is set to uint16_t it isn't read correctly ?
struct brk {
brk(uint16_t val) {
start = 0x0; // First 5 bits of an BRK instruction are 0
value = val;
end = 0x6A1; // Last 11 bits of an BRK instruction stored as uint16_t
}
bool verify() {
return (start == 0x0 && end == 0x6A1);
}
uint8_t start:5;
uint32_t value:16;
uint16_t end:11;
};
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call
struct svc {
bool verify() {
return (start == 0x1 && end == 0x6A0);
}
uint8_t start:5;
uint32_t value:16;
uint16_t end:11;
};
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register
struct mrs {
bool verify() {
return (end == 0xD53);
}
uint8_t Xt:5;
uint32_t Sreg:15;
uint16_t end:12;
};
};
enum xreg {
x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30
};
enum wreg {
w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25, w26, w27, w28, w29, w30
}; };
} }

View File

@ -15,7 +15,7 @@ namespace lightSwitch {
{"NRO", 1} {"NRO", 1}
}; };
public: public:
device(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : cpu(new hw::Cpu()), memory(new hw::Memory(cpu->GetEngine())), state{cpu, memory, settings, logger}, os({cpu, memory, settings, logger}) {}; device(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : cpu(new hw::Cpu()), memory(new hw::Memory()), state{cpu, memory, settings, logger}, os({cpu, memory, settings, logger}) {};
void run(std::string rom_file) { void run(std::string rom_file) {
try { try {
@ -27,7 +27,7 @@ namespace lightSwitch {
default: default:
break; break;
} }
cpu->Execute(constant::base_addr); cpu->Execute(hw::Memory::text, memory, os.SvcHandler, &state);
} catch (std::out_of_range &e) { } catch (std::out_of_range &e) {
throw exception("The ROM extension wasn't recognized."); throw exception("The ROM extension wasn't recognized.");
} }

View File

@ -1,46 +1,125 @@
#include "cpu.h" #include "cpu.h"
#include "../constant.h" #include "../constant.h"
namespace lightSwitch::hw { namespace lightSwitch::hw {
Cpu::Cpu() {
err = uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc);
if (err)
throw std::runtime_error("An error occurred while running 'uc_open': " + std::string(uc_strerror(err)));
}
Cpu::~Cpu() { Cpu::~Cpu() {
uc_close(uc); if (child) kill(child, SIGKILL);
} }
void Cpu::Execute(uint64_t address) { long *Cpu::ReadMemory(uint64_t address) { // Return a single word (32-bit)
// Set Registers status = ptrace(PTRACE_PEEKDATA, child, address, NULL);
SetRegister(UC_ARM64_REG_SP, constant::stack_addr + 0x100000); // Stack Pointer (For some reason programs move the stack pointer backwards so 0x100000 is added) if (status == -1) throw std::runtime_error("Cannot read memory");
SetRegister(UC_ARM64_REG_TPIDRRO_EL0, constant::tls_addr); // User Read-Only Thread ID Register return &status;
err = uc_emu_start(uc, address, std::numeric_limits<uint64_t>::max(), 0, 0);
if (err)
throw std::runtime_error("An error occurred while running 'uc_emu_start': " + std::string(uc_strerror(err)));
} }
void Cpu::StopExecution() { void Cpu::WriteMemory(uint64_t address) { // Write a single word (32-bit)
uc_emu_stop(uc); status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
if (status == -1) throw std::runtime_error("Cannot write memory");
} }
uint64_t Cpu::GetRegister(uint32_t reg_id) { void Cpu::ReadRegisters() { // Read all registers into 'regs'
uint64_t registerValue; iov = {&regs, sizeof(regs)};
err = uc_reg_read(uc, reg_id, &registerValue); status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
if (err) if (status == -1) throw std::runtime_error("Cannot read registers");
throw std::runtime_error("An error occurred while running 'uc_reg_read': " + std::string(uc_strerror(err)));
return registerValue;
} }
void Cpu::SetRegister(uint32_t regid, uint64_t value) { void Cpu::WriteRegisters() { // Write all registers from 'regs'
err = uc_reg_write(uc, regid, &value); iov = {&regs, sizeof(regs)};
if (err) status = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov);
throw std::runtime_error("An error occurred while running 'uc_reg_write': " + std::string(uc_strerror(err))); if (status == -1) throw std::runtime_error("Cannot write registers");
} }
uc_engine *Cpu::GetEngine() { void Cpu::ResumeProcess() { // Resumes a process stopped due to a signal
return uc; status = ptrace(PTRACE_CONT, child, NULL, NULL);
if (status == -1) throw std::runtime_error("Cannot resume process");
}
void bin(unsigned n) {
unsigned i;
std::string s;
for (i = 1 << 31; i > 0; i = i / 2)
(n & i) ? s += "1" : s += "0";
s = s.substr(16);
syslog(LOG_WARNING, "%s", s.c_str());
}
void Cpu::WriteBreakpoint(uint64_t &address_, uint64_t &size) {
auto address = (uint32_t *) address_;
for (uint64_t iter = 0; iter < size; iter++) {
auto instr_svc = reinterpret_cast<instr::svc *>(address + iter);
auto instr_mrs = reinterpret_cast<instr::mrs *>(address + iter);
if (instr_svc->verify()) {
// syslog(LOG_WARNING, "Found SVC call: 0x%X, At location 0x%X", instr_svc->value, ((uint64_t)address)+iter);
instr::brk brk((uint16_t) instr_svc->value);
address[iter] = *(uint32_t *) (&brk);
} else if (instr_mrs->verify() && instr_mrs->Sreg == constant::tpidrro_el0) {
// syslog(LOG_WARNING, "Found MRS call: 0x%X, At location 0x%X", instr_mrs->Xt, ((uint64_t)address)+iter);
instr::brk brk((uint16_t) (constant::svc_last + 1 + instr_mrs->Xt));
address[iter] = *(uint32_t *) (&brk);
}
}
}
void Cpu::Execute(Memory::Region region, std::shared_ptr<Memory> memory, std::function<void(uint16_t, void *)> svc_handler, void *device) {
tls = memory->region_map.at(hw::Memory::tls).address;
hw::Memory::RegionData rom = memory->region_map.at(hw::Memory::text);
WriteBreakpoint(rom.address, rom.size);
child = ExecuteChild(rom.address);
int stat = 0;
while (waitpid(child, &stat, 0)) {
if (WIFSTOPPED(stat)) {
ReadRegisters();
//syslog(LOG_INFO, "PC is at 0x%X", regs.pc);
if (!regs.pc || regs.pc == 0xBADC0DE) break;
// We store the instruction value as the immediate value. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0.
auto instr = reinterpret_cast<instr::brk *>(ReadMemory(regs.pc));
if (instr->verify()) {
if (instr->value <= constant::svc_last) {
svc_handler((uint16_t) instr->value, device);
syslog(LOG_ERR, "SVC has been called 0x%X", instr->value);
if (stop) break;
} else if (instr->value > constant::svc_last && instr->value <= constant::svc_last + constant::num_regs) {
// Catch MRS that reads the value of TPIDRRO_EL0 (TLS)
// https://switchbrew.org/wiki/Thread_Local_Storage
SetRegister(xreg(instr->value - (constant::svc_last + 1)), tls);
syslog(LOG_ERR, "MRS has been called 0x%X", instr->value - (constant::svc_last + 1));
} else syslog(LOG_ERR, "Received unhandled BRK 0x%X", instr->value);
}
regs.pc += 4; // Increment program counter by a single instruction (32 bits)
WriteRegisters();
} else if (WIFEXITED(stat))
break;
ResumeProcess();
}
kill(child, SIGABRT);
child = 0;
stop = false;
}
pid_t Cpu::ExecuteChild(uint64_t address) {
pid_t pid = fork();
if (!pid) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
asm volatile("BR %0"::"r"(address));
}
return pid;
}
void Cpu::StopExecution() { stop = true; }
uint64_t Cpu::GetRegister(xreg reg_id) {
return regs.regs[reg_id];
}
void Cpu::SetRegister(xreg reg_id, uint64_t value) {
regs.regs[reg_id] = value;
}
uint64_t Cpu::GetRegister(wreg reg_id) {
return ((uint32_t *) regs.regs)[wreg_lut[reg_id]];
}
void Cpu::SetRegister(wreg reg_id, uint32_t value) {
((uint32_t *) regs.regs)[wreg_lut[reg_id]] = value;
} }
} }

View File

@ -1,29 +1,54 @@
#pragma once #pragma once
#include <syslog.h> #include <syslog.h>
#include <unicorn/unicorn.h> #include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/uio.h>
#include <linux/elf.h>
#include <switch/constant.h>
#include "memory.h"
namespace lightSwitch::hw { namespace lightSwitch::hw {
class Cpu { class Cpu {
private: private:
uc_engine *uc; bool stop = false;
uc_err err; long status = 0;
pid_t child;
iovec iov;
user_pt_regs regs;
uint64_t tls;
static pid_t ExecuteChild(uint64_t address);
void ReadRegisters();
void WriteRegisters();
long *ReadMemory(uint64_t address);
void WriteMemory(uint64_t address);
void ResumeProcess();
void WriteBreakpoint(uint64_t &address, uint64_t &size);
uint8_t wreg_lut[31] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61};
public: public:
Cpu();
~Cpu(); ~Cpu();
void SetHook(void *HookInterrupt); void Execute(Memory::Region region, std::shared_ptr<Memory> memory, std::function<void(uint16_t, void *)> svc_handler, void *device);
void Execute(uint64_t address);
void StopExecution(); void StopExecution();
uint64_t GetRegister(uint32_t regid); uint64_t GetRegister(xreg reg_id);
void SetRegister(uint32_t regid, uint64_t value); void SetRegister(xreg reg_id, uint64_t value);
uc_engine *GetEngine(); uint64_t GetRegister(wreg reg_id);
void SetRegister(wreg reg_id, uint32_t value);
}; };
} }

View File

@ -6,21 +6,18 @@
namespace lightSwitch::hw { namespace lightSwitch::hw {
// TODO: Boundary checks // TODO: Boundary checks
Memory::Memory(uc_engine *uc_) : uc(uc_) { Memory::Memory() {
// Map stack memory // Map stack memory
Memory::Map(constant::stack_addr, constant::stack_size, stack); // Memory::Map(constant::stack_addr, constant::stack_size, stack);
// Map TLS memory // Map TLS memory
Memory::Map(constant::tls_addr, constant::tls_size, tls); Memory::Map(constant::tls_addr, constant::tls_size, tls);
} }
void Memory::Map(uint64_t address, size_t size, Region region) { void Memory::Map(uint64_t address, size_t size, Region region) {
region_map.insert(std::pair<Region, RegionData>(region, {address, size})); region_map.insert(std::pair<Region, RegionData>(region, {address, size}));
void *ptr = mmap((void *) address, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); void *ptr = mmap((void *) address, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON | MAP_FIXED, 0, 0);
if (!ptr) if (!ptr)
throw exception("An occurred while mapping region"); throw exception("An occurred while mapping region");
uc_err err = uc_mem_map_ptr(uc, address, size, UC_PROT_ALL, (void *) address);
if (err)
throw exception("Unicorn failed to map region: " + std::string(uc_strerror(err)));
} }
void Memory::Write(void *data, uint64_t offset, size_t size) { void Memory::Write(void *data, uint64_t offset, size_t size) {

View File

@ -1,13 +1,10 @@
#pragma once #pragma once
#include <string> #include <string>
#include <unicorn/unicorn.h>
#include <map> #include <map>
namespace lightSwitch::hw { namespace lightSwitch::hw {
class Memory { class Memory {
private:
uc_engine *uc;
public: public:
enum Region { enum Region {
stack, tls, text, rodata, data, bss stack, tls, text, rodata, data, bss
@ -18,7 +15,7 @@ namespace lightSwitch::hw {
}; };
std::map<Region, RegionData> region_map; std::map<Region, RegionData> region_map;
Memory(uc_engine *uc_); Memory();
void Map(uint64_t address, size_t size, Region region); void Map(uint64_t address, size_t size, Region region);

View File

@ -22,7 +22,7 @@ namespace lightSwitch::os::ipc {
bool handle_desc : 1; bool handle_desc : 1;
} *req_info; } *req_info;
IpcRequest(uint8_t *tlsPtr, device_state& state); IpcRequest(uint8_t *tlsPtr, device_state &state);
template<typename T> template<typename T>
T GetValue(); T GetValue();

View File

@ -1,49 +1,18 @@
#include <unicorn/arm64.h>
#include "os.h" #include "os.h"
namespace lightSwitch::os { namespace lightSwitch::os {
OS::OS(device_state state_) : state(std::move(state_)) { OS::OS(device_state state_) : state(std::move(state_)) {}
uc_err err = uc_hook_add(state.cpu->GetEngine(), &hook, UC_HOOK_INTR,
(void *) HookInterrupt, &state, 1, 0);
if (err)
throw std::runtime_error("An error occurred while running 'uc_hook_add': " +
std::string(uc_strerror(err)));
}
OS::~OS() { void OS::SvcHandler(uint16_t svc, void *vstate) {
uc_hook_del(state.cpu->GetEngine(), hook); device_state state = *((device_state *) vstate);
}
void OS::HookInterrupt(uc_engine *uc, uint32_t int_no, void *user_data) {
device_state state = *((device_state *) user_data);
try {
if (int_no == 2) {
uint32_t instr{};
uc_err err = uc_mem_read(uc, state.cpu->GetRegister(UC_ARM64_REG_PC) - 4, &instr, 4);
if (err)
throw exception("An error occurred while running 'uc_mem_read': " + std::string(uc_strerror(err)));
uint32_t svcId = instr >> 5U & 0xFF;
SvcHandler(svcId, state);
} else {
state.logger->write(Logger::ERROR, "An unhandled interrupt has occurred: {}", int_no);
state.cpu->StopExecution();
}
} catch (exception &e) {
state.logger->write(Logger::WARN, "An exception occurred during an interrupt: {}", e.what());
} catch (...) {
state.logger->write(Logger::WARN, "An unknown exception has occurred.");
}
}
void OS::SvcHandler(uint32_t svc, device_state &state) {
if (svc::svcTable[svc]) if (svc::svcTable[svc])
(*svc::svcTable[svc])(state); (*svc::svcTable[svc])(state);
else else
state.logger->write(Logger::WARN, "Unimplemented SVC 0x{0:x}", svc); state.logger->write(Logger::WARN, "Unimplemented SVC 0x{0:x}", svc);
} }
void OS::SvcHandler(uint32_t svc) { void OS::HandleSvc(uint16_t svc) {
SvcHandler(svc, state); SvcHandler(svc, &state);
} }
} }

View File

@ -11,16 +11,11 @@ namespace lightSwitch::os {
class OS { class OS {
private: private:
device_state state; device_state state;
uc_hook hook{};
public: public:
OS(device_state state_); OS(device_state state_);
~OS(); static void SvcHandler(uint16_t svc, void *vstate);
static void HookInterrupt(uc_engine *uc, uint32_t int_no, void *user_data); void HandleSvc(uint16_t svc);
static void SvcHandler(uint32_t svc, device_state &state);
void SvcHandler(uint32_t svc);
}; };
} }

View File

@ -2,56 +2,55 @@
#include <string> #include <string>
#include <syslog.h> #include <syslog.h>
#include <utility> #include <utility>
#include <unicorn/arm64.h>
#include "svc.h" #include "svc.h"
namespace lightSwitch::os::svc { namespace lightSwitch::os::svc {
void ConnectToNamedPort(device_state &state) { void ConnectToNamedPort(device_state &state) {
char port[constant::port_size]{0}; char port[constant::port_size]{0};
state.mem->Read(port, state.cpu->GetRegister(UC_ARM64_REG_X1), constant::port_size); state.mem->Read(port, state.cpu->GetRegister(xreg::x1), constant::port_size);
if (std::strcmp(port, "sm:") == 0) if (std::strcmp(port, "sm:") == 0)
state.cpu->SetRegister(UC_ARM64_REG_W1, constant::sm_handle); state.cpu->SetRegister(wreg::w1, constant::sm_handle);
else { else {
state.logger->write(Logger::ERROR, "svcConnectToNamedPort tried connecting to invalid port \"{0}\"", port); state.logger->write(Logger::ERROR, "svcConnectToNamedPort tried connecting to invalid port \"{0}\"", port);
state.cpu->StopExecution(); state.cpu->StopExecution();
} }
state.cpu->SetRegister(UC_ARM64_REG_W0, 0); state.cpu->SetRegister(wreg::w0, 0);
} }
void SendSyncRequest(device_state &state) { void SendSyncRequest(device_state &state) {
state.logger->write(Logger::DEBUG, "svcSendSyncRequest called for handle 0x{0:x}.", state.cpu->GetRegister(UC_ARM64_REG_X0)); state.logger->write(Logger::DEBUG, "svcSendSyncRequest called for handle 0x{0:x}.", state.cpu->GetRegister(xreg::x0));
uint8_t tls[constant::tls_ipc_size]; uint8_t tls[constant::tls_ipc_size];
state.mem->Read(&tls, constant::tls_addr, constant::tls_ipc_size); state.mem->Read(&tls, constant::tls_addr, constant::tls_ipc_size);
ipc::IpcRequest request(tls, state); ipc::IpcRequest request(tls, state);
state.cpu->SetRegister(UC_ARM64_REG_W0, 0); state.cpu->SetRegister(wreg::w0, 0);
} }
void OutputDebugString(device_state &state) { void OutputDebugString(device_state &state) {
std::string debug(state.cpu->GetRegister(UC_ARM64_REG_X1), '\0'); std::string debug(state.cpu->GetRegister(xreg::x1), '\0');
state.mem->Read((void *) debug.data(), state.cpu->GetRegister(UC_ARM64_REG_X0), state.cpu->GetRegister(UC_ARM64_REG_X1)); state.mem->Read((void *) debug.data(), state.cpu->GetRegister(xreg::x0), state.cpu->GetRegister(xreg::x1));
state.logger->write(Logger::INFO, "ROM Output: {0}", debug.c_str()); state.logger->write(Logger::INFO, "ROM Output: {0}", debug.c_str());
state.cpu->SetRegister(UC_ARM64_REG_W0, 0); state.cpu->SetRegister(wreg::w0, 0);
} }
void GetInfo(device_state &state) { void GetInfo(device_state &state) {
switch (state.cpu->GetRegister(UC_ARM64_REG_X1)) { switch (state.cpu->GetRegister(xreg::x1)) {
case constant::infoState::AllowedCpuIdBitmask: case constant::infoState::AllowedCpuIdBitmask:
case constant::infoState::AllowedThreadPriorityMask: case constant::infoState::AllowedThreadPriorityMask:
case constant::infoState::IsCurrentProcessBeingDebugged: case constant::infoState::IsCurrentProcessBeingDebugged:
state.cpu->SetRegister(UC_ARM64_REG_X1, 0); state.cpu->SetRegister(xreg::x1, 0);
break; break;
case constant::infoState::AddressSpaceBaseAddr: case constant::infoState::AddressSpaceBaseAddr:
state.cpu->SetRegister(UC_ARM64_REG_X1, constant::base_addr); state.cpu->SetRegister(xreg::x1, constant::base_addr);
break; break;
case constant::infoState::TitleId: case constant::infoState::TitleId:
state.cpu->SetRegister(UC_ARM64_REG_X1, 0); // TODO: Complete this state.cpu->SetRegister(xreg::x1, 0); // TODO: Complete this
break; break;
default: default:
state.logger->write(Logger::WARN, "Unimplemented GetInfo call. ID1: {0}, ID2: {1}", state.cpu->GetRegister(UC_ARM64_REG_X1), state.cpu->GetRegister(UC_ARM64_REG_X3)); state.logger->write(Logger::WARN, "Unimplemented GetInfo call. ID1: {0}, ID2: {1}", state.cpu->GetRegister(xreg::x1), state.cpu->GetRegister(xreg::x3));
state.cpu->SetRegister(UC_ARM64_REG_X1, constant::svc_unimpl); state.cpu->SetRegister(xreg::x1, constant::svc_unimpl);
return; return;
} }
state.cpu->SetRegister(UC_ARM64_REG_W0, 0); state.cpu->SetRegister(wreg::w0, 0);
} }
void ExitProcess(device_state &state) { void ExitProcess(device_state &state) {