Compare commits

..
Author SHA1 Message Date
Ole André Vadla Ravnås fdb9ea94d5 subprojects: Prepare for release 2026-11-13 12:29:43 +01:00
Ole André Vadla Ravnås fae46a858c subprojects: Bump outdated 2026-11-13 12:27:56 +01:00
Ole André Vadla Ravnås dd458211a6 tests: Follow the stalked thread once it runs
A thread followed before it starts is still in KERNEL32's starter,
which the test leaves to run natively, and the starter calls into the
thread's code rather than returning to it. Thus the Stalker never got
the thread back.
2026-11-13 12:15:58 +01:00
Ole André Vadla Ravnås d3b5b448ba tests: Run a program every Windows 95 has
Solitaire is an optional component, thus a minimal install lacks it.
2026-11-13 11:50:44 +01:00
Ole André Vadla Ravnås bb9df6664f barebone: Find VWIN32's thread slot on any build
The slot's address was that of Windows 95 OSR2, thus the original
release walked its threads through a stray pointer and hung. Read it
out of VWIN32_GetContextHandle() instead, as is done for KERNEL32.
2026-11-13 11:46:16 +01:00
Ole André Vadla Ravnås 3829029a86 ci: Run manylinux containers without seccomp
Docker's default profile on the Ubuntu 26.04 runners rejects
socketpair() through socketcall(2), which the x86 toolchain's glibc 2.5
relies on.
2026-11-13 11:22:58 +01:00
Ole André Vadla Ravnås 4e0b33576b ci: Build MinGW targets on the Ubuntu runner
Debian's MinGW headers break the GLib build. Also install Node.js for
the Debian builds, as the agent needs it.
2026-11-13 11:22:58 +01:00
Ole André Vadla Ravnås 6ed557f274 ci: Build cross targets in Debian container
Ubuntu 26.04 no longer ships the MIPS cross toolchains, and its default
GCC no longer matches lib32stdc++-13-dev.
2026-11-13 10:43:43 +01:00
Ole André Vadla Ravnås 8a0e06583d subprojects: Bump outdated 2026-11-13 10:15:26 +01:00
Ole André Vadla Ravnås 44bb483845 barebone: Spawn threads in NT process agents
The process agent warms GTask at startup, which starts a thread, and it
only knew how to start one in the kernel.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 2e11ad0eea barebone: Align the NT fault handler for CFG
Control Flow Guard accepts only the aligned address in each sixteen
bytes that the agent vouches for, thus a process with CFG ends itself
when it calls a handler that lies between.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 2ac5aa444a barebone: Find the NT process list from modules
The sweep took the kernel's range from a base the host never passes for
NT, thus found nothing where the host had not named the list head, as
on arm64.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 827a2fbc17 barebone: Pass kernel notes by value
The boxed value went through varargs as a pointer, so the agent was
given a host heap address in place of the loaded module list.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 43757c55d0 barebone: Continue every Parallels processor
The stub single-steps a processor told to continue alone, and never
reports the stop.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 5c59703c8a barebone: Read pages the Parallels stub refuses
The stub fails to read a mapped page whose access flag is clear, which
Windows leaves that way on pages it has not touched in a while, such as
the kernel's export directory. Read those through the page tables.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås e07c86fb03 barebone: Walk page tables through Parallels
Read the MMU registers and physical memory through the stub's monitor,
and write descriptors through the kernel's own mapping of each table.
Ask only the processor that stopped about its registers, as the monitor
aborts the VM when asked about another one.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 28b2452701 barebone: Read NT layout through the machine
The machine knows ways into guest memory that the stub lacks.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås feea45897c barebone: Translate arm64 addresses by full walk
The walk stopped at level 3, thus misread blocks and returned an
address for an unmapped page.
2026-11-13 09:58:29 +01:00
Ole André Vadla Ravnås 643d13e3a3 barebone: Mask TTBR1 by its first table's size
Windows leaves T1SZ at 17, which makes the first table 2 KiB, so its
address need not be aligned to a granule.
2026-11-13 09:38:39 +01:00
Ole André Vadla Ravnås d8ca97e7a4 barebone: Read versions of Windows process modules
Parse them with Gum's PeModule, like Gum's Windows backend does.
2026-11-12 21:33:19 +01:00
Ole André Vadla Ravnås 5ebeeacc80 subprojects: Bump outdated 2026-11-12 21:30:51 +01:00
19 changed files with 465 additions and 124 deletions

No files matched your search

+44 -10
View File
@@ -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
+1 -1
View File
@@ -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);
+1
View File
@@ -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;
+26
View File
@@ -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
}
}
+16 -17
View File
@@ -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);
}
+9 -5
View File
@@ -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();
+33 -35
View File
@@ -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)
}
+85 -2
View File
@@ -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;
+38 -17
View File
@@ -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);
+2 -4
View File
@@ -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) {
+31
View File
@@ -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);
}
}
}
+92
View File
@@ -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;
}
}
+6 -6
View File
@@ -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++;
+43
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/efni.git
revision = 3bcd99ff81b7b648ff4d784fb3154e55e7950c20
revision = 4fdc5f4c0d7ec4086362961150a80a92bc6e4b72
depth = 1
[provide]
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = c8f22bb1db7904adcc4ad046c0948ee9597a95fa
revision = 17.26.3
depth = 1
[provide]
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/idun.git
revision = 6040e17b1ef871ff7ae5b18fffbe5f0396a0810c
revision = 29184dff69074d5150c9e5c04227a5912de1fad7
depth = 1
[provide]
+24 -13
View File
@@ -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');
});