mirror of
https://github.com/frida/frida-core.git
synced 2026-11-13 04:31:07 -10:00
Compare commits
21
Commits
No files matched your search
+44
-10
@@ -64,7 +64,7 @@ jobs:
|
||||
if: matrix.id == 'linux-x86'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-multilib lib32stdc++-13-dev
|
||||
sudo apt-get install g++-multilib
|
||||
- name: Install setuptools for distutils shim
|
||||
if: ${{ startsWith(matrix.id, 'windows-') }}
|
||||
run: pip install setuptools
|
||||
@@ -137,12 +137,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { id: windows-x86_64-mingw, opts: '--host=x86_64-w64-mingw32 --without-prebuilds=sdk:host', pkg: g++-mingw-w64-x86-64 }
|
||||
- { id: windows-x86-mingw, opts: '--host=i686-w64-mingw32 --without-prebuilds=sdk:host', pkg: g++-mingw-w64-i686 }
|
||||
- { id: linux-mips, opts: '--host=mips-linux-gnu', pkg: g++-mips-linux-gnu }
|
||||
- { id: linux-mipsel, opts: '--host=mipsel-linux-gnu', pkg: g++-mipsel-linux-gnu }
|
||||
- { id: linux-mips64, opts: '--host=mips64-linux-gnuabi64', pkg: g++-mips64-linux-gnuabi64 }
|
||||
- { id: linux-mips64el, opts: '--host=mips64el-linux-gnuabi64', pkg: g++-mips64el-linux-gnuabi64 }
|
||||
- { id: windows-x86_64-mingw, opts: '--host=x86_64-w64-mingw32 --without-prebuilds=sdk:host', pkg: g++-mingw-w64-x86-64 }
|
||||
- { id: windows-x86-mingw, opts: '--host=i686-w64-mingw32 --without-prebuilds=sdk:host', pkg: g++-mingw-w64-i686 }
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -163,23 +159,61 @@ jobs:
|
||||
run: |
|
||||
./configure \
|
||||
${{ matrix.opts }} \
|
||||
${{ !contains(matrix.id, 'mips') && '--enable-compiler-backend' || '' }} \
|
||||
--enable-compiler-backend \
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
- name: Upload devkit
|
||||
if: ${{ !startsWith(matrix.id, 'linux-') }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: core-devkit-${{ matrix.id }}
|
||||
path: build/src/devkit/
|
||||
|
||||
debian-cross:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { id: linux-mips, host: mips-linux-gnu }
|
||||
- { id: linux-mipsel, host: mipsel-linux-gnu }
|
||||
- { id: linux-mips64, host: mips64-linux-gnuabi64 }
|
||||
- { id: linux-mips64el, host: mips64el-linux-gnuabi64 }
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:stable
|
||||
steps:
|
||||
- name: Install toolchain
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
g++ \
|
||||
g++-${{ matrix.host }} \
|
||||
git \
|
||||
make \
|
||||
python3 \
|
||||
xz-utils
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
- name: Build
|
||||
run: |
|
||||
./configure --host=${{ matrix.host }} ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
|
||||
manylinux:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x86_64, x86_64-musl, armhf, arm64, arm64-musl, mips, mipsel, mips64, mips64el]
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/frida/x-tools-linux-${{ matrix.arch }}:latest
|
||||
container:
|
||||
image: ghcr.io/frida/x-tools-linux-${{ matrix.arch }}:latest
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
@@ -285,7 +285,7 @@ namespace Frida.Barebone {
|
||||
// runs: the host looks once and passes on what it found.
|
||||
config_builder.open (new VariantType ("a(st)"));
|
||||
foreach (var e in kernel_notes.entries)
|
||||
config_builder.add ("(st)", e.key, e.value);
|
||||
config_builder.add ("(st)", e.key, (uint64) e.value);
|
||||
if (xnu_layout != null) {
|
||||
config_builder.add ("(st)", "process.number", xnu_layout.number_offset);
|
||||
config_builder.add ("(st)", "process.name", xnu_layout.name_offset);
|
||||
|
||||
@@ -53,6 +53,7 @@ SECTIONS {
|
||||
_VPICD_Force_Default_Behavior = .; . += 4;
|
||||
_VPICD_Phys_EOI = .; . += 4;
|
||||
_VPICD_Physically_Unmask = .; . += 4;
|
||||
_VWIN32_ThreadDataSlot = .; . += 4;
|
||||
__VWIN32_QueueUserApc = .; . += 4;
|
||||
__VWIN32_Get_Thread_Context = .; . += 4;
|
||||
__VWIN32_Set_Thread_Context = .; . += 4;
|
||||
|
||||
@@ -27,6 +27,10 @@ use crate::bindings::{
|
||||
#[cfg(feature = "linux-injected")]
|
||||
use crate::gum_modules::enumerate_symbols_in_range;
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
use crate::bindings::{
|
||||
GumPeModule, g_object_unref, gum_pe_module_get_version, gum_pe_module_new_from_memory,
|
||||
};
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
use crate::gum_windows::{enumerate_exports_in_range, enumerate_imports_in_module};
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
use crate::bindings::{GumFoundImportFunc, GumImportDetails, GumImportType_GUM_IMPORT_FUNCTION};
|
||||
@@ -114,6 +118,8 @@ pub struct GumNativeModule {
|
||||
version: *mut gchar,
|
||||
path: *mut gchar,
|
||||
range: GumMemoryRange,
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
pe: *mut GumPeModule,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -203,6 +209,10 @@ unsafe extern "C" fn gum_native_module_finalize(object: *mut GObject) {
|
||||
let module = object as *mut GumNativeModule;
|
||||
|
||||
g_free((*module).path as gpointer);
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
if !(*module).pe.is_null() {
|
||||
g_object_unref((*module).pe as gpointer);
|
||||
}
|
||||
|
||||
(*GUM_NATIVE_MODULE_PARENT_CLASS).finalize.unwrap()(object);
|
||||
}
|
||||
@@ -228,6 +238,18 @@ pub(crate) fn gum_native_module_new(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
pub(crate) fn gum_native_module_new_with_pe(path: &str, range: &GumMemoryRange) -> *mut GumModule {
|
||||
let module = gum_native_module_new(path, "", range);
|
||||
|
||||
unsafe {
|
||||
(*(module as *mut GumNativeModule)).pe =
|
||||
gum_pe_module_new_from_memory(range.base_address, ptr::null_mut());
|
||||
}
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
fn version_copied(version: &str) -> *mut gchar {
|
||||
if version.is_empty() {
|
||||
return ptr::null_mut();
|
||||
@@ -248,6 +270,10 @@ extern "C" fn gum_native_module_get_name(module: *mut GumModule) -> *const gchar
|
||||
extern "C" fn gum_native_module_get_version(module: *mut GumModule) -> *const gchar {
|
||||
unsafe {
|
||||
let native_module = module as *mut GumNativeModule;
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
if !(*native_module).pe.is_null() {
|
||||
return gum_pe_module_get_version((*native_module).pe);
|
||||
}
|
||||
(*native_module).version as *const gchar
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ use crate::{
|
||||
GIOErrorEnum_G_IO_ERROR_BROKEN_PIPE, GIOErrorEnum_G_IO_ERROR_FAILED,
|
||||
GIOErrorEnum_G_IO_ERROR_INVALID_ARGUMENT, GIOErrorEnum_G_IO_ERROR_NOT_FOUND,
|
||||
GIOErrorEnum_G_IO_ERROR_PERMISSION_DENIED, GumBareboneStreamOps, GumMemoryRange,
|
||||
GumModuleRegistry, GumPageProtection, GumRwxSupport, g_free, g_io_error_quark, g_malloc,
|
||||
g_object_unref, g_set_error_literal, g_utf8_to_utf16, g_utf16_to_utf8, gboolean, gchar,
|
||||
GumModule, GumModuleRegistry, GumPageProtection, GumRwxSupport, g_free, g_io_error_quark,
|
||||
g_malloc, g_object_unref, g_set_error_literal, g_utf8_to_utf16, g_utf16_to_utf8, gboolean,
|
||||
gchar,
|
||||
gint, gpointer, gsize, gssize, guint, gum_barebone_register_module, gum_mprotect,
|
||||
GumFoundRangeFunc, GumFoundThreadFunc, GumModifyThreadFlags, GumModifyThreadFunc,
|
||||
GumRangeDetails, GumThreadDetails, GumThreadFlags, GumThreadId, GumThreadRegistry,
|
||||
@@ -433,7 +434,7 @@ pub(crate) fn module_arrived(base: u64, path: &str) {
|
||||
let registry = crate::bindings::gum_module_registry_obtain();
|
||||
crate::bindings::gum_module_registry_lock(registry);
|
||||
|
||||
let native = gum::gum_native_module_new(path, "", &range);
|
||||
let native = gum::gum_native_module_new_with_pe(path, &range);
|
||||
gum_barebone_register_module(registry, native);
|
||||
g_object_unref(native as gpointer);
|
||||
|
||||
@@ -462,7 +463,7 @@ pub(crate) fn module_arrived(base: u64) {
|
||||
let registry = crate::bindings::gum_module_registry_obtain();
|
||||
crate::bindings::gum_module_registry_lock(registry);
|
||||
|
||||
let native = gum::gum_native_module_new(&module.path, "", &range);
|
||||
let native = gum::gum_native_module_new_with_pe(&module.path, &range);
|
||||
gum_barebone_register_module(registry, native);
|
||||
g_object_unref(native as gpointer);
|
||||
|
||||
@@ -506,37 +507,35 @@ fn register_the_process_modules(registry: *mut GumModuleRegistry) {
|
||||
for module in kernel::enumerate_modules() {
|
||||
known_mut().insert(module.base);
|
||||
|
||||
register_module(registry, &module.path, "", module.base, module.size);
|
||||
let range = range_of(module.base, module.size);
|
||||
register(registry, gum::gum_native_module_new_with_pe(&module.path, &range));
|
||||
}
|
||||
}
|
||||
|
||||
fn register_the_kernel_modules(registry: *mut GumModuleRegistry) {
|
||||
#[cfg(feature = "winnt")]
|
||||
for module in crate::winnt::enumerate_kernel_modules() {
|
||||
register_module(registry, &module.path, "", module.base, module.size);
|
||||
let range = range_of(module.base, module.size);
|
||||
register(registry, gum::gum_native_module_new(&module.path, "", &range));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "winnt"))]
|
||||
for module_info in unsafe { &*core::ptr::addr_of!(crate::MODULE_INFO) }.iter() {
|
||||
let path = format!("{}{}", kernel::MODULE_DIRECTORY, module_info.name);
|
||||
register_module(
|
||||
registry,
|
||||
&path,
|
||||
&module_info.version,
|
||||
module_info.offset as u64,
|
||||
module_info.size as u64,
|
||||
);
|
||||
let range = range_of(module_info.offset as u64, module_info.size as u64);
|
||||
register(registry, gum::gum_native_module_new(&path, &module_info.version, &range));
|
||||
}
|
||||
}
|
||||
|
||||
fn register_module(registry: *mut GumModuleRegistry, path: &str, version: &str, base: u64, size: u64) {
|
||||
let range = GumMemoryRange {
|
||||
fn range_of(base: u64, size: u64) -> GumMemoryRange {
|
||||
GumMemoryRange {
|
||||
base_address: base,
|
||||
size: size as gsize,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn register(registry: *mut GumModuleRegistry, module: *mut GumModule) {
|
||||
unsafe {
|
||||
let module = gum::gum_native_module_new(path, version, &range);
|
||||
gum_barebone_register_module(registry, module);
|
||||
g_object_unref(module as gpointer);
|
||||
}
|
||||
|
||||
@@ -717,7 +717,7 @@ unsafe fn targets() -> &'static mut BTreeMap<u32, Target> {
|
||||
static mut TARGETS: BTreeMap<u32, Target> = BTreeMap::new();
|
||||
|
||||
fn process_for_pid(pid: u32) -> u32 {
|
||||
let slot = unsafe { (THREAD_BLOCK_SLOT as *const u32).read() };
|
||||
let slot = thread_data_slot();
|
||||
let vm = unsafe { get_sys_vm_handle() };
|
||||
let first = unsafe { get_initial_thread_handle(vm) };
|
||||
|
||||
@@ -1664,7 +1664,6 @@ const WAKE_STUB_OFFSET: u32 = 0x1c0;
|
||||
const TRAMPOLINE_OFFSET: u32 = 0x180;
|
||||
|
||||
const INJECTION_STACK_SIZE: usize = 0x4000;
|
||||
const THREAD_BLOCK_SLOT: u32 = 0xc002_11cc;
|
||||
const PDB_MEMORY_CONTEXT: usize = 0x1c;
|
||||
const PR_SHARED: u32 = 0x8006_0000;
|
||||
const PD_FIXEDZERO: u32 = 0x3;
|
||||
@@ -1832,7 +1831,7 @@ pub fn enumerate_threads_of(pid: u32, found: &mut dyn FnMut(ThreadInfo), with_re
|
||||
}
|
||||
|
||||
fn enumerate_thread_handles_of(pid: u32, found: &mut dyn FnMut(u32, u32)) {
|
||||
let slot = unsafe { (THREAD_BLOCK_SLOT as *const u32).read() };
|
||||
let slot = thread_data_slot();
|
||||
let vm = unsafe { get_sys_vm_handle() };
|
||||
let first = unsafe { get_initial_thread_handle(vm) };
|
||||
|
||||
@@ -1965,7 +1964,7 @@ pub struct ProcessInfo {
|
||||
// process list is the deduplicated set of those, and the image path is where the command
|
||||
// line starts.
|
||||
pub fn enumerate_processes(found: &mut dyn FnMut(ProcessInfo)) {
|
||||
let slot = unsafe { (THREAD_BLOCK_SLOT as *const u32).read() };
|
||||
let slot = thread_data_slot();
|
||||
let vm = unsafe { get_sys_vm_handle() };
|
||||
let first = unsafe { get_initial_thread_handle(vm) };
|
||||
|
||||
@@ -1996,6 +1995,10 @@ pub fn enumerate_processes(found: &mut dyn FnMut(ProcessInfo)) {
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_data_slot() -> u32 {
|
||||
unsafe { (core::ptr::addr_of!(_VWIN32_ThreadDataSlot).read() as *const u32).read() }
|
||||
}
|
||||
|
||||
fn process_id(pdb: u32) -> u32 {
|
||||
let slot = unsafe { core::ptr::addr_of!(_KERNEL32_ProcessIdObfuscator).read() };
|
||||
if slot == 0 {
|
||||
@@ -2405,7 +2408,7 @@ extern "C" fn frida_win9x_on_control(message: u32, thread: u32) {
|
||||
}
|
||||
|
||||
fn tell_the_copy_of_a_departure(thread: u32) {
|
||||
let slot = unsafe { (THREAD_BLOCK_SLOT as *const u32).read() };
|
||||
let slot = thread_data_slot();
|
||||
if unsafe { (thread as *const u32).add(0x2c / 4).read() } != WIN32_THREAD {
|
||||
return;
|
||||
}
|
||||
@@ -3329,6 +3332,7 @@ unsafe extern "C" {
|
||||
static __PageReserve: unsafe extern "C" fn(u32, u32, u32) -> u32;
|
||||
static __PageCommit: unsafe extern "C" fn(u32, u32, u32, u32, u32) -> u32;
|
||||
static __PageCommitPhys: unsafe extern "C" fn(u32, u32, u32, u32) -> u32;
|
||||
static _VWIN32_ThreadDataSlot: u32;
|
||||
static __VWIN32_QueueUserApc: unsafe extern "C" fn(u32, u32, u32) -> u32;
|
||||
static _Hook_VMM_Fault: unsafe extern "C" fn();
|
||||
static _VMM_Add_DDB: unsafe extern "C" fn();
|
||||
|
||||
@@ -128,30 +128,7 @@ pub fn run_when_ready(action: fn()) {
|
||||
}
|
||||
|
||||
pub fn spawn_thread(entry: ThreadEntry, parameter: *mut c_void) -> isize {
|
||||
let start = Box::into_raw(Box::new(ThreadStart { entry, parameter }));
|
||||
|
||||
let mut handle: *mut c_void = core::ptr::null_mut();
|
||||
let status = unsafe {
|
||||
(_PsCreateSystemThread)(
|
||||
&mut handle,
|
||||
THREAD_ALL_ACCESS,
|
||||
core::ptr::null_mut(),
|
||||
core::ptr::null_mut(),
|
||||
core::ptr::null_mut(),
|
||||
thread_start,
|
||||
start as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status < 0 {
|
||||
drop(unsafe { Box::from_raw(start) });
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(_ZwClose)(handle);
|
||||
}
|
||||
|
||||
0
|
||||
(primitives().spawn_thread)(entry, parameter)
|
||||
}
|
||||
|
||||
// The kernel gives a system thread a stack of a few pages, and you cannot increase it. The
|
||||
@@ -2163,19 +2140,11 @@ fn active_process_head(system: usize) -> usize {
|
||||
}
|
||||
|
||||
fn sweep_for_active_process_head(system: usize) -> usize {
|
||||
let base = KERNEL_BASE.load(Ordering::Relaxed);
|
||||
if base == 0 {
|
||||
return 0;
|
||||
}
|
||||
let size = unsafe {
|
||||
(*core::ptr::addr_of!(crate::MODULE_INFO))
|
||||
.iter()
|
||||
.find(|m| m.offset as usize == base)
|
||||
.map(|m| m.size as usize)
|
||||
};
|
||||
let Some(size) = size else {
|
||||
let Some(kernel) = enumerate_kernel_modules().into_iter().next() else {
|
||||
return 0;
|
||||
};
|
||||
let base = kernel.base as usize;
|
||||
let size = kernel.size as usize;
|
||||
|
||||
for offset in (0..PROCESS_SCAN_SIZE).step_by(core::mem::size_of::<usize>()) {
|
||||
let node = system + offset;
|
||||
@@ -3407,6 +3376,7 @@ pub struct Primitives {
|
||||
pub free: fn(*mut u8, usize),
|
||||
pub alloc_code: fn(usize) -> *mut u8,
|
||||
pub free_code: fn(*mut u8, usize),
|
||||
pub spawn_thread: fn(ThreadEntry, *mut c_void) -> isize,
|
||||
pub protect: fn(u64, usize, u32) -> bool,
|
||||
pub region_at: fn(usize) -> Option<MemoryRegion>,
|
||||
pub enumerate_ranges: fn(&mut dyn FnMut(u64, u64, u32)),
|
||||
@@ -3462,6 +3432,7 @@ static KERNEL: Primitives = Primitives {
|
||||
free: kernel::free,
|
||||
alloc_code: kernel::alloc_code,
|
||||
free_code: kernel::free_code,
|
||||
spawn_thread: kernel::spawn_thread,
|
||||
protect: kernel::protect,
|
||||
region_at: kernel::region_at,
|
||||
enumerate_ranges: kernel::enumerate_ranges,
|
||||
@@ -3507,6 +3478,33 @@ mod kernel {
|
||||
free(ptr, size);
|
||||
}
|
||||
|
||||
pub fn spawn_thread(entry: ThreadEntry, parameter: *mut c_void) -> isize {
|
||||
let start = Box::into_raw(Box::new(ThreadStart { entry, parameter }));
|
||||
|
||||
let mut handle: *mut c_void = core::ptr::null_mut();
|
||||
let status = unsafe {
|
||||
(_PsCreateSystemThread)(
|
||||
&mut handle,
|
||||
THREAD_ALL_ACCESS,
|
||||
core::ptr::null_mut(),
|
||||
core::ptr::null_mut(),
|
||||
core::ptr::null_mut(),
|
||||
thread_start,
|
||||
start as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status < 0 {
|
||||
drop(unsafe { Box::from_raw(start) });
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(_ZwClose)(handle);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub fn protect(address: u64, size: usize, gum_prot: u32) -> bool {
|
||||
crate::winnt_paging::protect(address, size, gum_prot)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// nothing of the kernel's, thus it has primitives of its own, which frida_winnt_user_main selects
|
||||
// before it does anything else.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
@@ -9,7 +10,7 @@ use core::ffi::c_void;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::gum_windows::{FileApi, TextApi};
|
||||
use crate::kernel::MemoryRegion;
|
||||
use crate::kernel::{MemoryRegion, ThreadEntry};
|
||||
use crate::winnt::{
|
||||
BLOCK_PROCESS_ID, BLOCK_THREAD_ID, CURRENT_PROCESS, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE,
|
||||
COPY_LEFT, OBSERVED_PID, OBSERVED_THREAD, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, Primitives,
|
||||
@@ -124,6 +125,7 @@ pub static USER: Primitives = Primitives {
|
||||
free,
|
||||
alloc_code,
|
||||
free_code,
|
||||
spawn_thread,
|
||||
protect,
|
||||
region_at,
|
||||
enumerate_ranges,
|
||||
@@ -169,6 +171,7 @@ fn resolve_user_api() {
|
||||
create_event: core::mem::transmute(export(ntdll, b"NtCreateEvent")),
|
||||
close: core::mem::transmute(export(ntdll, b"NtClose")),
|
||||
exit_thread: core::mem::transmute(export(ntdll, b"RtlExitUserThread")),
|
||||
create_thread: core::mem::transmute(export(ntdll, b"RtlCreateUserThread")),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
flush_code: core::mem::transmute(export(ntdll, b"NtFlushInstructionCache")),
|
||||
create_heap: core::mem::transmute(export(ntdll, b"RtlCreateHeap")),
|
||||
@@ -868,6 +871,8 @@ struct UserApi {
|
||||
create_event: windows_fn!(*mut *mut c_void, u32, *mut c_void, u32, u8 => i32),
|
||||
close: windows_fn!(*mut c_void => i32),
|
||||
exit_thread: windows_fn!(u32 => !),
|
||||
create_thread: windows_fn!(*mut c_void, *mut c_void, u8, u32, usize, usize, *const c_void, *mut c_void,
|
||||
*mut *mut c_void, *mut c_void => i32),
|
||||
add_fault_handler: windows_fn!(u32, *const c_void => *mut c_void),
|
||||
remove_fault_handler: windows_fn!(*mut c_void => u32),
|
||||
create_heap: windows_fn!(u32, *mut c_void, usize, usize, *mut c_void, *mut c_void => usize),
|
||||
@@ -951,7 +956,7 @@ struct ThreadSlotApi {
|
||||
static mut THREAD_SLOT_API: Option<ThreadSlotApi> = None;
|
||||
|
||||
fn hear_about_faults() {
|
||||
unsafe { FAULT_HANDLER = (user_api().add_fault_handler)(1, on_fault as *const c_void) };
|
||||
unsafe { FAULT_HANDLER = (user_api().add_fault_handler)(1, frida_winnt_user_on_fault as *const c_void) };
|
||||
}
|
||||
|
||||
fn stop_hearing_about_faults() {
|
||||
@@ -1229,6 +1234,84 @@ fn free_code(ptr: *mut u8, _size: usize) {
|
||||
unsafe { (user_api().free_memory)(CURRENT_PROCESS, &mut address, &mut region, MEM_RELEASE) };
|
||||
}
|
||||
|
||||
fn spawn_thread(entry: ThreadEntry, parameter: *mut c_void) -> isize {
|
||||
let start = Box::into_raw(Box::new(UserThreadStart { entry, parameter }));
|
||||
|
||||
let mut handle: *mut c_void = core::ptr::null_mut();
|
||||
let status = unsafe {
|
||||
(user_api().create_thread)(CURRENT_PROCESS, core::ptr::null_mut(), 0, 0, 0, 0,
|
||||
frida_winnt_user_thread_start as *const c_void, start as *mut c_void, &mut handle,
|
||||
core::ptr::null_mut())
|
||||
};
|
||||
if status < 0 {
|
||||
drop(unsafe { Box::from_raw(start) });
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsafe { (user_api().close)(handle) };
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe extern "stdcall" fn user_thread_start(context: *mut c_void) -> u32 {
|
||||
unsafe { run_user_thread(context) }
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe extern "win64" fn user_thread_start(context: *mut c_void) -> u32 {
|
||||
unsafe { run_user_thread(context) }
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe extern "C" fn user_thread_start(context: *mut c_void) -> u32 {
|
||||
unsafe { run_user_thread(context) }
|
||||
}
|
||||
|
||||
unsafe fn run_user_thread(context: *mut c_void) -> u32 {
|
||||
let start = unsafe { Box::from_raw(context as *mut UserThreadStart) };
|
||||
unsafe { (start.entry)(start.parameter, 0) };
|
||||
0
|
||||
}
|
||||
|
||||
struct UserThreadStart {
|
||||
entry: ThreadEntry,
|
||||
parameter: *mut c_void,
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn frida_winnt_user_on_fault();
|
||||
fn frida_winnt_user_thread_start();
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
core::arch::global_asm!(
|
||||
".balign 16",
|
||||
".global frida_winnt_user_on_fault",
|
||||
"frida_winnt_user_on_fault:",
|
||||
"jmp {on_fault}",
|
||||
".balign 16",
|
||||
".global frida_winnt_user_thread_start",
|
||||
"frida_winnt_user_thread_start:",
|
||||
"jmp {thread_start}",
|
||||
on_fault = sym on_fault,
|
||||
thread_start = sym user_thread_start,
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
core::arch::global_asm!(
|
||||
".balign 16",
|
||||
".global frida_winnt_user_on_fault",
|
||||
"frida_winnt_user_on_fault:",
|
||||
"b {on_fault}",
|
||||
".balign 16",
|
||||
".global frida_winnt_user_thread_start",
|
||||
"frida_winnt_user_thread_start:",
|
||||
"b {thread_start}",
|
||||
on_fault = sym on_fault,
|
||||
thread_start = sym user_thread_start,
|
||||
);
|
||||
|
||||
fn protect(address: u64, size: usize, gum_prot: u32) -> bool {
|
||||
let mut base = address as *mut u8;
|
||||
let mut region = size;
|
||||
|
||||
@@ -299,26 +299,47 @@ namespace Frida.Barebone {
|
||||
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
uint64 descriptor = yield read_level3_descriptor (va, cancellable);
|
||||
uint64 page_mask = (1ULL << inpage_bits_for_granule (p.granule)) - 1;
|
||||
return (descriptor & INT48_MASK & ~page_mask) | (va & page_mask);
|
||||
|
||||
bool was_running = yield begin_physical_addressing (cancellable);
|
||||
uint64 pa = 0;
|
||||
GLib.Error? failure = null;
|
||||
try {
|
||||
pa = yield resolve_physical_address (va, p, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
failure = e;
|
||||
}
|
||||
yield end_physical_addressing (was_running, cancellable);
|
||||
throw_if_failed (failure);
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
public override async Bytes read_virtual (uint64 va, size_t size, Cancellable? cancellable)
|
||||
throws Efni.Error, IOError {
|
||||
if (physical_memory == null)
|
||||
return yield debugger.read_byte_array (va, size, cancellable);
|
||||
if (physical_memory != null)
|
||||
return yield read_virtual_through_page_tables (va, size, cancellable);
|
||||
|
||||
try {
|
||||
return yield debugger.read_byte_array (va, size, cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
if (!("parallels" in debugger.features))
|
||||
throw e;
|
||||
return yield read_virtual_through_page_tables (va, size, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
private async Bytes read_virtual_through_page_tables (uint64 va, size_t size, Cancellable? cancellable)
|
||||
throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
var taken = new ByteArray ();
|
||||
size_t offset = 0;
|
||||
while (offset < size) {
|
||||
uint64 cur_va = va + offset;
|
||||
uint64 pa = yield translate_address (cur_va, cancellable);
|
||||
if (!physical_memory.contains (pa))
|
||||
if (physical_memory != null && !physical_memory.contains (pa))
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is a device, not memory", cur_va);
|
||||
size_t chunk = size_t.min ((size_t) (p.granule - (cur_va & (p.granule - 1))), size - offset);
|
||||
taken.append (physical_memory.read (pa, chunk));
|
||||
taken.append (yield read_physical (pa, chunk, cancellable));
|
||||
offset += chunk;
|
||||
}
|
||||
|
||||
@@ -560,14 +581,14 @@ namespace Frida.Barebone {
|
||||
uint64 raw = d_buf.read_uint64 (0);
|
||||
Descriptor desc = Descriptor.parse (raw, level, p.granule);
|
||||
|
||||
if (desc.kind == BLOCK || level == 3) {
|
||||
if (desc.kind == INVALID)
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to resolve physical address for 0x%" + uint64.FORMAT_MODIFIER + "x", va);
|
||||
if (desc.kind == BLOCK) {
|
||||
uint64 span = (level == 3)
|
||||
? (uint64) 1 << inpage_bits_for_granule (p.granule)
|
||||
: (uint64) 1 << num_block_bits_at_level (level, p.granule);
|
||||
return (raw & INT48_MASK & ~(span - 1)) | (va & (span - 1));
|
||||
}
|
||||
if (desc.kind != TABLE)
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to resolve physical address for 0x%" + uint64.FORMAT_MODIFIER + "x", va);
|
||||
|
||||
table_pa = desc.target_address;
|
||||
level++;
|
||||
@@ -1406,11 +1427,12 @@ namespace Frida.Barebone {
|
||||
for (uint i = 0; i != t1sz; i++)
|
||||
parameters.upper_bits |= 1ULL << (63 - i);
|
||||
|
||||
// TTBR1 carries more than the table's address: an ASID above it, and CnP in
|
||||
// the bit below, which the table itself is far too aligned to have set.
|
||||
parameters.tt1 = regs.ttbr1 & INT48_MASK & ~((uint64) parameters.granule - 1);
|
||||
parameters.t1sz = t1sz;
|
||||
|
||||
uint64 first_table_size = (uint64) compute_max_entries (parameters.first_level, parameters)
|
||||
* Descriptor.SIZE;
|
||||
parameters.tt1 = regs.ttbr1 & INT48_MASK & ~(first_table_size - 1);
|
||||
|
||||
parameters.sprr_enabled = (regs.sprr_config & 1) != 0;
|
||||
parameters.sprr_perm = regs.sprr_perm;
|
||||
|
||||
@@ -1446,10 +1468,9 @@ namespace Frida.Barebone {
|
||||
regs.sprr_perm = val;
|
||||
}
|
||||
} else if ("parallels" in client.features) {
|
||||
// The stub serves no system register, and the monitor command that would
|
||||
// print them aborts the VM whenever another processor is somewhere it
|
||||
// cannot be read from, so leave the caller to manage without them.
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Parallels serves no system register");
|
||||
var parallels = (ParallelsStubClient) client;
|
||||
regs.tcr = yield parallels.read_system_register (thread, TCR_EL1, cancellable);
|
||||
regs.ttbr1 = yield parallels.read_system_register (thread, TTBR1_EL1, cancellable);
|
||||
} else {
|
||||
regs.tcr = yield thread.read_register ("tcr_el1", cancellable);
|
||||
regs.ttbr1 = yield thread.read_register ("ttbr1_el1", cancellable);
|
||||
|
||||
@@ -190,11 +190,9 @@ namespace Frida {
|
||||
|
||||
private async BareboneHostSession establish (BareboneConfig config, Debugger debugger, owned ConnectingFunc connecting,
|
||||
Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
// The arm64 MMU system registers (TTBR1_EL1/TCR_EL1) are exposed by some stubs and not
|
||||
// others (the Android emulator's does not); detect it from the advertised register set,
|
||||
// which selects host page-table walking versus the kernel's set_memory_* helpers.
|
||||
bool mmu_registers_available = debugger.arch != TargetArch.ARM64
|
||||
|| (debugger.has_register ("ttbr1_el1") && debugger.has_register ("tcr_el1"));
|
||||
|| (debugger.has_register ("ttbr1_el1") && debugger.has_register ("tcr_el1"))
|
||||
|| "parallels" in debugger.features;
|
||||
|
||||
Barebone.Machine machine;
|
||||
switch (debugger.arch) {
|
||||
|
||||
@@ -443,6 +443,7 @@ namespace Frida.Barebone {
|
||||
private Machine machine;
|
||||
private uint64 yield_point;
|
||||
private SymbolInfo? bug_check;
|
||||
private SymbolInfo? get_virtual_for_physical;
|
||||
|
||||
public WinNtKernelFlavor (Machine machine, Gee.Map<string, SymbolInfo> symbols) throws Efni.Error {
|
||||
this.machine = machine;
|
||||
@@ -455,9 +456,14 @@ namespace Frida.Barebone {
|
||||
yield_point = wait_for_single_object.offset;
|
||||
|
||||
bug_check = symbols["KeBugCheckEx"];
|
||||
get_virtual_for_physical = symbols["MmGetVirtualForPhysical"];
|
||||
}
|
||||
|
||||
public async void prepare (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var parallels = machine.debugger as ParallelsStubClient;
|
||||
if (parallels != null)
|
||||
parallels.physical_address_resolver = new WinNtPhysicalAddressResolver (machine, get_virtual_for_physical.offset);
|
||||
|
||||
var arm64 = machine as Arm64Machine;
|
||||
if (bug_check != null && arm64 != null)
|
||||
arm64.call_landing_zone = bug_check.offset;
|
||||
@@ -492,4 +498,29 @@ namespace Frida.Barebone {
|
||||
return (debugger.arch == TargetArch.ARM64) ? 4 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class WinNtPhysicalAddressResolver : Object, PhysicalAddressResolver {
|
||||
private Machine machine;
|
||||
private uint64 get_virtual_for_physical;
|
||||
private Gee.Map<uint64?, uint64?> page_aliases = new Gee.HashMap<uint64?, uint64?> (Numeric.uint64_hash,
|
||||
Numeric.uint64_equal);
|
||||
|
||||
private const uint64 PAGE_MASK = 0xfff;
|
||||
|
||||
public WinNtPhysicalAddressResolver (Machine machine, uint64 get_virtual_for_physical) {
|
||||
this.machine = machine;
|
||||
this.get_virtual_for_physical = get_virtual_for_physical;
|
||||
}
|
||||
|
||||
public async uint64 virtual_address_of (uint64 physical_address, Cancellable? cancellable)
|
||||
throws Efni.Error, IOError {
|
||||
uint64 page = physical_address & ~PAGE_MASK;
|
||||
uint64? alias = page_aliases[page];
|
||||
if (alias == null) {
|
||||
alias = yield machine.invoke (get_virtual_for_physical, { page }, cancellable);
|
||||
page_aliases[page] = alias;
|
||||
}
|
||||
return alias + (physical_address & PAGE_MASK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,16 @@ using Efni;
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class ParallelsStubClient : GDB.Client {
|
||||
public PhysicalAddressResolver? physical_address_resolver {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool physical_addressing = false;
|
||||
|
||||
private const uint HALT_TIMEOUT_MSEC = 1000;
|
||||
private const uint64 MONITOR_READ_ALIGNMENT = 16;
|
||||
private const uint64 MAX_MONITOR_READ_SIZE = 0x100;
|
||||
|
||||
private ParallelsStubClient (IOStream stream) {
|
||||
Object (stream: stream);
|
||||
@@ -22,6 +31,38 @@ namespace Frida.Barebone {
|
||||
return client;
|
||||
}
|
||||
|
||||
public async uint64 read_system_register (DebuggerThread stopped_thread, SystemRegister reg,
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint processor = processor_index_of (stopped_thread);
|
||||
string line = yield run_monitor_command ("readmsr %x %x".printf (processor, reg), cancellable);
|
||||
string[] tokens = line.split (" ");
|
||||
return uint64.parse (tokens[2], 16);
|
||||
}
|
||||
|
||||
public override async void set_physical_memory_mode (bool enabled, Cancellable? cancellable = null)
|
||||
throws Efni.Error, IOError {
|
||||
physical_addressing = enabled;
|
||||
}
|
||||
|
||||
public override async Bytes read_byte_array (uint64 address, size_t size, Cancellable? cancellable = null)
|
||||
throws Efni.Error, IOError {
|
||||
if (physical_addressing)
|
||||
return yield read_physical (address, size, cancellable);
|
||||
|
||||
return yield base.read_byte_array (address, size, cancellable);
|
||||
}
|
||||
|
||||
public override async void write_byte_array (uint64 address, Bytes bytes, Cancellable? cancellable = null)
|
||||
throws Efni.Error, IOError {
|
||||
if (!physical_addressing) {
|
||||
yield base.write_byte_array (address, bytes, cancellable);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 alias = yield physical_address_resolver.virtual_address_of (address, cancellable);
|
||||
yield base.write_byte_array (alias, bytes, cancellable);
|
||||
}
|
||||
|
||||
protected override async void detect_vendor_features (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
supported_features.add ("parallels");
|
||||
supported_features.add ("protected-code");
|
||||
@@ -34,10 +75,51 @@ namespace Frida.Barebone {
|
||||
arch = arch_from_cpu_type (GDB.Protocol.parse_uint (host.get_string ("cputype"), 10));
|
||||
pointer_size = GDB.Protocol.parse_uint (host.get_string ("ptrsize"), 10);
|
||||
byte_order = (host.get_string ("endian") == "little") ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
|
||||
suppress_solo = true;
|
||||
|
||||
yield halt (cancellable, HALT_TIMEOUT_MSEC);
|
||||
}
|
||||
|
||||
private async Bytes read_physical (uint64 address, size_t size, Cancellable? cancellable)
|
||||
throws Efni.Error, IOError {
|
||||
uint64 start = address & ~(MONITOR_READ_ALIGNMENT - 1);
|
||||
uint64 end = (address + size + MONITOR_READ_ALIGNMENT - 1) & ~(MONITOR_READ_ALIGNMENT - 1);
|
||||
|
||||
var window = new ByteArray.sized ((uint) (end - start));
|
||||
for (uint64 cursor = start; cursor != end;) {
|
||||
uint64 chunk_size = uint64.min (end - cursor, MAX_MONITOR_READ_SIZE);
|
||||
window.append (yield dump_physical (cursor, (size_t) chunk_size, cancellable));
|
||||
cursor += chunk_size;
|
||||
}
|
||||
|
||||
size_t offset = (size_t) (address - start);
|
||||
return new Bytes (window.data[offset:offset + size]);
|
||||
}
|
||||
|
||||
private async uint8[] dump_physical (uint64 address, size_t size, Cancellable? cancellable)
|
||||
throws Efni.Error, IOError {
|
||||
string hex = yield run_monitor_command (("dm %" + uint64.FORMAT_MODIFIER + "x %zx").printf (address, size),
|
||||
cancellable);
|
||||
if (hex.length != size * 2) {
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Unable to read from physical address 0x%" + uint64.FORMAT_MODIFIER +
|
||||
"x", address);
|
||||
}
|
||||
|
||||
var data = new uint8[size];
|
||||
for (size_t i = 0; i != size; i++)
|
||||
data[i] = (uint8) uint64.parse (hex.substring ((long) (i * 2), 2), 16);
|
||||
return data;
|
||||
}
|
||||
|
||||
private async string run_monitor_command (string command, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
string output = yield run_remote_command (command, cancellable);
|
||||
return output.split ("\n")[0];
|
||||
}
|
||||
|
||||
private static uint processor_index_of (DebuggerThread thread) {
|
||||
return (uint) uint64.parse (thread.id, 16) - 1;
|
||||
}
|
||||
|
||||
private static TargetArch arch_from_cpu_type (uint cpu_type) {
|
||||
switch (cpu_type) {
|
||||
case 0x00000007: return IA32;
|
||||
@@ -47,5 +129,15 @@ namespace Frida.Barebone {
|
||||
default: return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SystemRegister {
|
||||
TTBR1_EL1 = 0xc101,
|
||||
TCR_EL1 = 0xc102,
|
||||
}
|
||||
}
|
||||
|
||||
public interface PhysicalAddressResolver : Object {
|
||||
public abstract async uint64 virtual_address_of (uint64 physical_address, Cancellable? cancellable)
|
||||
throws Efni.Error, IOError;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Frida.Barebone {
|
||||
|
||||
Buffer headers;
|
||||
try {
|
||||
headers = debugger.make_buffer (yield debugger.read_byte_array (image, HEADERS_SIZE, cancellable));
|
||||
headers = debugger.make_buffer (yield machine.read_virtual (image, HEADERS_SIZE, cancellable));
|
||||
} catch (Efni.Error e) {
|
||||
return exports;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace Frida.Barebone {
|
||||
return exports;
|
||||
|
||||
Buffer directory = debugger.make_buffer (
|
||||
yield read_present_pages (debugger, image + directory_rva, directory_size, cancellable));
|
||||
yield read_present_pages (machine, image + directory_rva, directory_size, cancellable));
|
||||
var window = new DirectoryWindow (directory, directory_rva, directory_size);
|
||||
|
||||
uint32 count = directory.read_uint32 (NAME_COUNT_OFFSET);
|
||||
@@ -91,7 +91,7 @@ namespace Frida.Barebone {
|
||||
|
||||
Buffer headers;
|
||||
try {
|
||||
headers = debugger.make_buffer (yield debugger.read_byte_array (image, HEADERS_SIZE, cancellable));
|
||||
headers = debugger.make_buffer (yield machine.read_virtual (image, HEADERS_SIZE, cancellable));
|
||||
} catch (Efni.Error e) {
|
||||
return 0;
|
||||
}
|
||||
@@ -107,10 +107,10 @@ namespace Frida.Barebone {
|
||||
// Part of an image can be out of memory. Read it in one go when it is fully present, and
|
||||
// otherwise one page at a time, letting the absent pages read as zero, because no name or
|
||||
// table entry has that value.
|
||||
private static async Bytes read_present_pages (Debugger debugger, uint64 address, size_t size,
|
||||
private static async Bytes read_present_pages (Machine machine, uint64 address, size_t size,
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
try {
|
||||
return yield debugger.read_byte_array (address, size, cancellable);
|
||||
return yield machine.read_virtual (address, size, cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Frida.Barebone {
|
||||
PAGE_SIZE - (size_t) (cursor & (PAGE_SIZE - 1)));
|
||||
|
||||
try {
|
||||
unowned uint8[] page = (yield debugger.read_byte_array (cursor, chunk, cancellable))
|
||||
unowned uint8[] page = (yield machine.read_virtual (cursor, chunk, cancellable))
|
||||
.get_data ();
|
||||
Memory.copy ((uint8 *) result + offset, page, chunk);
|
||||
present++;
|
||||
|
||||
@@ -65,6 +65,8 @@ namespace Frida.Barebone {
|
||||
});
|
||||
}
|
||||
|
||||
yield add_vwin32_symbols (machine, symbols, cancellable);
|
||||
|
||||
return new Win9xLayout (modules, symbols);
|
||||
}
|
||||
|
||||
@@ -301,6 +303,43 @@ namespace Frida.Barebone {
|
||||
symbols.add (make_symbol (MODULE_TABLE, table));
|
||||
}
|
||||
|
||||
private static async void add_vwin32_symbols (Machine machine, Gee.List<SymbolInfo> symbols,
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 thread_data_slot = yield find_thread_data_slot (machine, symbols, cancellable);
|
||||
if (thread_data_slot != 0)
|
||||
symbols.add (make_symbol (THREAD_DATA_SLOT, thread_data_slot));
|
||||
}
|
||||
|
||||
private static async uint64 find_thread_data_slot (Machine machine, Gee.List<SymbolInfo> symbols,
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 getter = address_of_symbol (symbols, "VWIN32_GetContextHandle");
|
||||
if (getter == 0)
|
||||
return 0;
|
||||
|
||||
Debugger debugger = machine.debugger;
|
||||
size_t load_offset = TEST_FOR_WIN32_THREAD.length + 1;
|
||||
Buffer code = debugger.make_buffer (yield debugger.read_byte_array (getter,
|
||||
load_offset + LOAD_EBX_ABSOLUTE.length + 4, cancellable));
|
||||
for (size_t i = 0; i != TEST_FOR_WIN32_THREAD.length; i++) {
|
||||
if (code.read_uint8 (i) != TEST_FOR_WIN32_THREAD[i])
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i != LOAD_EBX_ABSOLUTE.length; i++) {
|
||||
if (code.read_uint8 (load_offset + i) != LOAD_EBX_ABSOLUTE[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return code.read_uint32 (load_offset + LOAD_EBX_ABSOLUTE.length);
|
||||
}
|
||||
|
||||
private static uint64 address_of_symbol (Gee.List<SymbolInfo> symbols, string name) {
|
||||
foreach (SymbolInfo symbol in symbols) {
|
||||
if (symbol.name == name)
|
||||
return symbol.offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static SymbolInfo make_symbol (string name, uint64 address) {
|
||||
return new SymbolInfo () {
|
||||
name = name,
|
||||
@@ -500,6 +539,10 @@ namespace Frida.Barebone {
|
||||
public const string KERNEL32_BASE = "KERNEL32_Base";
|
||||
public const string PROCESS_ID_OBFUSCATOR = "KERNEL32_ProcessIdObfuscator";
|
||||
public const string MODULE_TABLE = "KERNEL32_ModuleTable";
|
||||
public const string THREAD_DATA_SLOT = "VWIN32_ThreadDataSlot";
|
||||
|
||||
private const uint8[] TEST_FOR_WIN32_THREAD = { 0x83, 0x7f, 0x2c, 0x2a, 0x75 };
|
||||
private const uint8[] LOAD_EBX_ABSOLUTE = { 0x8b, 0x1d };
|
||||
|
||||
// movsx ecx, WORD PTR [eax+0x10] ; mov eax, ds:<table>
|
||||
private const uint8[] INDEX_MODULE_TABLE = { 0x0f, 0xbf, 0x48, 0x10, 0xa1 };
|
||||
|
||||
+11
-11
@@ -229,7 +229,7 @@ namespace Frida.Barebone {
|
||||
|
||||
Debugger debugger = machine.debugger;
|
||||
uint64 seed_page = seed - (seed % PCR_SCAN_SIZE);
|
||||
Buffer page = debugger.make_buffer (yield debugger.read_byte_array (seed_page, PCR_SCAN_SIZE, cancellable));
|
||||
Buffer page = debugger.make_buffer (yield machine.read_virtual (seed_page, PCR_SCAN_SIZE, cancellable));
|
||||
|
||||
for (size_t offset = 0; offset != PCR_SCAN_SIZE; offset += shape.pointer_size) {
|
||||
uint64 candidate = read_pointer (page, offset, shape);
|
||||
@@ -254,13 +254,13 @@ namespace Frida.Barebone {
|
||||
if (pcr_base == 0)
|
||||
return 0;
|
||||
|
||||
Buffer pcr = debugger.make_buffer (yield debugger.read_byte_array (pcr_base,
|
||||
Buffer pcr = debugger.make_buffer (yield machine.read_virtual (pcr_base,
|
||||
shape.version_block + shape.pointer_size, cancellable));
|
||||
uint64 version_block = read_pointer (pcr, shape.version_block, shape);
|
||||
if (!is_kernel_address (version_block, shape))
|
||||
return 0;
|
||||
|
||||
Buffer v = debugger.make_buffer (yield debugger.read_byte_array (version_block, VERSION_BLOCK_SIZE, cancellable));
|
||||
Buffer v = debugger.make_buffer (yield machine.read_virtual (version_block, VERSION_BLOCK_SIZE, cancellable));
|
||||
if (v.read_uint16 (MACHINE_TYPE_OFFSET) != shape.machine_type)
|
||||
return 0;
|
||||
|
||||
@@ -322,7 +322,7 @@ namespace Frida.Barebone {
|
||||
Debugger debugger = machine.debugger;
|
||||
Buffer head;
|
||||
try {
|
||||
head = debugger.make_buffer (yield debugger.read_byte_array (candidate + shape.self,
|
||||
head = debugger.make_buffer (yield machine.read_virtual (candidate + shape.self,
|
||||
shape.pointer_size, cancellable));
|
||||
} catch (Efni.Error e) {
|
||||
return false;
|
||||
@@ -337,7 +337,7 @@ namespace Frida.Barebone {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 list = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (
|
||||
uint64 list = read_pointer (debugger.make_buffer (yield machine.read_virtual (
|
||||
version_block + LOADED_MODULE_LIST_OFFSET, shape.pointer_size, cancellable)), 0, shape);
|
||||
if (!is_kernel_address (list, shape))
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the loaded module list");
|
||||
@@ -350,17 +350,17 @@ namespace Frida.Barebone {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 data_list = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (
|
||||
uint64 data_list = read_pointer (debugger.make_buffer (yield machine.read_virtual (
|
||||
version_block + DEBUGGER_DATA_LIST_OFFSET, shape.pointer_size, cancellable)), 0, shape);
|
||||
if (!is_kernel_address (data_list, shape))
|
||||
return 0;
|
||||
|
||||
uint64 block = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (data_list, shape.pointer_size,
|
||||
uint64 block = read_pointer (debugger.make_buffer (yield machine.read_virtual (data_list, shape.pointer_size,
|
||||
cancellable)), 0, shape);
|
||||
if (!is_kernel_address (block, shape))
|
||||
return 0;
|
||||
|
||||
uint64 head = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (
|
||||
uint64 head = read_pointer (debugger.make_buffer (yield machine.read_virtual (
|
||||
block + PROCESS_LIST_HEAD_OFFSET, shape.pointer_size, cancellable)), 0, shape);
|
||||
if (!is_kernel_address (head, shape))
|
||||
return 0;
|
||||
@@ -372,12 +372,12 @@ namespace Frida.Barebone {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 entry = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (head, shape.pointer_size,
|
||||
uint64 entry = read_pointer (debugger.make_buffer (yield machine.read_virtual (head, shape.pointer_size,
|
||||
cancellable)), 0, shape);
|
||||
if (entry == head || !is_kernel_address (entry, shape))
|
||||
return null;
|
||||
|
||||
Buffer e = debugger.make_buffer (yield debugger.read_byte_array (entry, shape.table_entry_size, cancellable));
|
||||
Buffer e = debugger.make_buffer (yield machine.read_virtual (entry, shape.table_entry_size, cancellable));
|
||||
|
||||
uint64 base_address = read_pointer (e, shape.dll_base, shape);
|
||||
if (!is_kernel_address (base_address, shape))
|
||||
@@ -427,7 +427,7 @@ namespace Frida.Barebone {
|
||||
if (length == 0 || length > MAX_NAME_SIZE || !is_kernel_address (buffer, shape))
|
||||
return "";
|
||||
|
||||
Bytes raw = yield machine.debugger.read_byte_array (buffer, length, cancellable);
|
||||
Bytes raw = yield machine.read_virtual (buffer, length, cancellable);
|
||||
try {
|
||||
return convert ((string) raw.get_data (), length, "UTF-8", "UTF-16LE");
|
||||
} catch (ConvertError e) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/efni.git
|
||||
revision = 3bcd99ff81b7b648ff4d784fb3154e55e7950c20
|
||||
revision = 4fdc5f4c0d7ec4086362961150a80a92bc6e4b72
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = c8f22bb1db7904adcc4ad046c0948ee9597a95fa
|
||||
revision = 17.26.3
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/idun.git
|
||||
revision = 6040e17b1ef871ff7ae5b18fffbe5f0396a0810c
|
||||
revision = 29184dff69074d5150c9e5c04227a5912de1fad7
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
+24
-13
@@ -4123,7 +4123,7 @@ FAIL: %s
|
||||
try {
|
||||
var device = yield manager.add_barebone_device (config);
|
||||
|
||||
uint pid = yield device.spawn ("C:\\WINDOWS\\SOL.EXE", null, null);
|
||||
uint pid = yield device.spawn ("C:\\WINDOWS\\WINVER.EXE", null, null);
|
||||
assert_true (pid != 0);
|
||||
|
||||
var settle = new TimeoutSource.seconds (3);
|
||||
@@ -4132,7 +4132,7 @@ FAIL: %s
|
||||
yield;
|
||||
settle.destroy ();
|
||||
|
||||
assert_true ((yield find_program (device, "sol.exe")) == pid);
|
||||
assert_true ((yield find_program (device, "winver.exe")) == pid);
|
||||
|
||||
yield device.resume (pid, null);
|
||||
|
||||
@@ -4142,7 +4142,7 @@ FAIL: %s
|
||||
yield;
|
||||
running.destroy ();
|
||||
|
||||
assert_true ((yield find_program (device, "sol.exe")) == pid);
|
||||
assert_true ((yield find_program (device, "winver.exe")) == pid);
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
assert_not_reached ();
|
||||
@@ -4172,7 +4172,7 @@ FAIL: %s
|
||||
var starter = yield session.create_script ("""
|
||||
const winExec = new NativeFunction(Process.getModuleByName('KERNEL32.DLL')
|
||||
.getExportByName('WinExec'), 'uint32', ['pointer', 'uint32']);
|
||||
send(['started', winExec(Memory.allocUtf8String('C:\\WINDOWS\\SOL.EXE'), 1)]);
|
||||
send(['started', winExec(Memory.allocUtf8String('C:\\WINDOWS\\WINVER.EXE'), 1)]);
|
||||
""", null, null);
|
||||
|
||||
var announced = new Gee.ArrayList<string> ();
|
||||
@@ -4192,17 +4192,17 @@ FAIL: %s
|
||||
var options = new ProcessQueryOptions ();
|
||||
options.scope = METADATA;
|
||||
|
||||
Process? game = null;
|
||||
Process? program = null;
|
||||
var list = yield device.enumerate_processes (options, null);
|
||||
for (int i = 0; i != list.size (); i++) {
|
||||
var path = list.get (i).parameters["path"];
|
||||
if (path != null && path.get_string ().down ().has_suffix ("sol.exe"))
|
||||
game = list.get (i);
|
||||
if (path != null && path.get_string ().down ().has_suffix ("winver.exe"))
|
||||
program = list.get (i);
|
||||
}
|
||||
assert_nonnull (game);
|
||||
assert_nonnull (program);
|
||||
|
||||
assert_true (game.name.down ().contains ("solitaire"));
|
||||
assert_true (game.parameters["path"].get_string ().down ().has_suffix ("sol.exe"));
|
||||
assert_true (program.name.down ().contains ("version"));
|
||||
assert_true (program.parameters["path"].get_string ().down ().has_suffix ("winver.exe"));
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
assert_not_reached ();
|
||||
@@ -6233,11 +6233,15 @@ FAIL: %s
|
||||
['pointer', 'uint', 'pointer', 'pointer', 'uint', 'pointer']);
|
||||
const terminateThread = fn('TerminateThread', 'int', ['pointer', 'uint']);
|
||||
const closeHandle = fn('CloseHandle', 'int', ['pointer']);
|
||||
const createEvent = fn('CreateEventA', 'pointer', ['pointer', 'int', 'int', 'pointer']);
|
||||
const waitForSingleObject = fn('WaitForSingleObject', 'uint', ['pointer', 'uint']);
|
||||
|
||||
const running = createEvent(NULL, 1, 0, NULL);
|
||||
const flag = Memory.alloc(4);
|
||||
const body = Memory.alloc(Process.pageSize);
|
||||
Memory.protect(body, Process.pageSize, 'rwx');
|
||||
|
||||
const setEvent = lib.getExportByName('SetEvent');
|
||||
const sleep = lib.getExportByName('Sleep');
|
||||
const little = value => [value & 0xff, (value >>> 8) & 0xff,
|
||||
(value >>> 16) & 0xff, (value >>> 24) & 0xff];
|
||||
@@ -6247,12 +6251,17 @@ FAIL: %s
|
||||
bytes.push(value.shr(i * 8).and(0xff).toUInt32());
|
||||
return bytes;
|
||||
};
|
||||
Memory.patchCode(body, 64, code => {
|
||||
Memory.patchCode(body, 96, code => {
|
||||
code.writeByteArray((Process.pointerSize === 4)
|
||||
? [0x6a, 0x64, 0xe8, ...little(sleep.sub(body.add(7)).toInt32()),
|
||||
? [0x68, ...little(running.toUInt32()),
|
||||
0xe8, ...little(setEvent.sub(body.add(10)).toInt32()),
|
||||
0x6a, 0x64, 0xe8, ...little(sleep.sub(body.add(17)).toInt32()),
|
||||
0xa1, ...little(flag.toUInt32()), 0x85, 0xc0, 0x74, 0xf0,
|
||||
0x33, 0xc0, 0xc2, 0x04, 0x00]
|
||||
: [0x48, 0x83, 0xec, 0x28, 0xb9, 0x64, 0x00, 0x00, 0x00,
|
||||
: [0x48, 0x83, 0xec, 0x28,
|
||||
0x48, 0xb9, ...wide(running), 0x48, 0xb8, ...wide(setEvent), 0xff, 0xd0,
|
||||
0x48, 0x83, 0xc4, 0x28,
|
||||
0x48, 0x83, 0xec, 0x28, 0xb9, 0x64, 0x00, 0x00, 0x00,
|
||||
0x48, 0xb8, ...wide(sleep), 0xff, 0xd0,
|
||||
0x48, 0x83, 0xc4, 0x28,
|
||||
0x48, 0xb8, ...wide(flag), 0x8b, 0x00, 0x85, 0xc0, 0x74, 0xd7,
|
||||
@@ -6265,6 +6274,7 @@ FAIL: %s
|
||||
|
||||
let blocks = 0;
|
||||
recv('follow', () => {
|
||||
waitForSingleObject(running, 0xffffffff);
|
||||
// A service of this system is reached by a call through a gate, which the
|
||||
// Stalker cannot follow. Thus leave the library that holds them alone.
|
||||
Stalker.exclude(lib);
|
||||
@@ -6286,6 +6296,7 @@ FAIL: %s
|
||||
flag.writeU32(1);
|
||||
terminateThread(handle, 0);
|
||||
closeHandle(handle);
|
||||
closeHandle(running);
|
||||
send('stopped');
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user