POC attempt to make cmplog work on x64 (#1713)
* POC attempt to make cmplog work on x64 windows POC seems working unix POC seems working :) * no register collisions * rsp-related ref support iced optional dep iced depends on cmplog warnings one more warning comments cleanup ci unbreak rebase windows unbreak rebase unix unbreak unix only fmt check clang formatting clang formatting again make clippy happy formatting double import windows unbreak hashmap is conditional leftover definition tutorial related formatter review fixes comments .asm fuzz targets for cmplog on Windows more tests rip-relative reference support without index register form proper ignore rip-related references and ignore 8 bit comparisons another try_into packing * harness modification reverted * dummy commit to restart CI * review comments --------- Co-authored-by: sbarsky <sbarsky@denuvo.com> Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
e615cb4aed
commit
72c862171e
2
.github/workflows/build_and_test.yml
vendored
2
.github/workflows/build_and_test.yml
vendored
@ -605,7 +605,7 @@ jobs:
|
|||||||
- name: Build fuzzers/frida_libpng
|
- name: Build fuzzers/frida_libpng
|
||||||
run: cd fuzzers/frida_libpng/ && cargo make test
|
run: cd fuzzers/frida_libpng/ && cargo make test
|
||||||
- name: Build fuzzers/frida_gdiplus
|
- name: Build fuzzers/frida_gdiplus
|
||||||
run: cd fuzzers/frida_gdiplus/ && cargo make test
|
run: cd fuzzers/frida_gdiplus/ && cargo make test && cargo make test_cmplog
|
||||||
- name: Build fuzzers/tinyinst_simple
|
- name: Build fuzzers/tinyinst_simple
|
||||||
run: cd fuzzers/tinyinst_simple/ && cargo make test
|
run: cd fuzzers/tinyinst_simple/ && cargo make test
|
||||||
|
|
||||||
|
2
fuzzers/frida_gdiplus/.gitignore
vendored
2
fuzzers/frida_gdiplus/.gitignore
vendored
@ -1,5 +1,7 @@
|
|||||||
corpus_discovered
|
corpus_discovered
|
||||||
|
output_t*
|
||||||
*.exp
|
*.exp
|
||||||
*.lib
|
*.lib
|
||||||
*.obj
|
*.obj
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
@ -32,3 +32,4 @@ libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] }
|
|||||||
libloading = "0.7"
|
libloading = "0.7"
|
||||||
mimalloc = { version = "*", default-features = false }
|
mimalloc = { version = "*", default-features = false }
|
||||||
color-backtrace = "0.5"
|
color-backtrace = "0.5"
|
||||||
|
iced-x86 = { version = "1.20.0", features = ["code_asm"] }
|
||||||
|
@ -24,6 +24,12 @@ script='''
|
|||||||
cl.exe /LD harness.cc /link /dll gdiplus.lib ole32.lib
|
cl.exe /LD harness.cc /link /dll gdiplus.lib ole32.lib
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
[tasks.harness_windows_cmplog_test]
|
||||||
|
script_runner="@shell"
|
||||||
|
script='''
|
||||||
|
ml64 cmplog_test.asm /subsystem:windows /link /dll /def:cmplog_test.def /entry:dll_main /out:cmplog.dll
|
||||||
|
'''
|
||||||
|
|
||||||
# Fuzzer
|
# Fuzzer
|
||||||
[tasks.fuzzer]
|
[tasks.fuzzer]
|
||||||
linux_alias = "unsupported"
|
linux_alias = "unsupported"
|
||||||
@ -56,6 +62,28 @@ linux_alias = "unsupported"
|
|||||||
mac_alias = "unsupported"
|
mac_alias = "unsupported"
|
||||||
windows_alias = "test_windows"
|
windows_alias = "test_windows"
|
||||||
|
|
||||||
|
[tasks.test_cmplog]
|
||||||
|
linux_alias = "unsupported"
|
||||||
|
mac_alias = "unsupported"
|
||||||
|
windows_alias = "test_windows_cmplog"
|
||||||
|
|
||||||
|
[tasks.test_windows_cmplog]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script='''
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
for %%i in (t1 t2 t3 t4 t5 t6 t7) do (
|
||||||
|
echo Testing %%i...
|
||||||
|
rmdir /s /q output_%%i
|
||||||
|
start "" "frida_gdiplus.exe" -H cmplog.dll -i corpus -o output_%%i --libs-to-instrument cmplog.dll -F %%i -C
|
||||||
|
ping -n 3 127.0.0.1>NUL && taskkill /im frida_gdiplus.exe /F
|
||||||
|
>nul 2>nul dir /a-d "output_%%i" && (echo Files exist) || (exit /b 1337)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo All tests done
|
||||||
|
'''
|
||||||
|
dependencies = [ "fuzzer", "harness_windows_cmplog_test" ]
|
||||||
|
|
||||||
[tasks.test_windows]
|
[tasks.test_windows]
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script='''
|
script='''
|
||||||
|
102
fuzzers/frida_gdiplus/cmplog_test.asm
Normal file
102
fuzzers/frida_gdiplus/cmplog_test.asm
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
public dll_main
|
||||||
|
public t1
|
||||||
|
public t2
|
||||||
|
public t3
|
||||||
|
public t4
|
||||||
|
public t5
|
||||||
|
public t6
|
||||||
|
public t7
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
crash:
|
||||||
|
mov rax, 0
|
||||||
|
mov rax, [rax]
|
||||||
|
ret
|
||||||
|
|
||||||
|
; dummy test which does not produce crashes or coverage
|
||||||
|
test_no_cov:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 64 bits mem/reg
|
||||||
|
t1:
|
||||||
|
cmp rdx, 8
|
||||||
|
jb @f
|
||||||
|
mov rax, 01234567812345678h
|
||||||
|
cmp qword ptr [rcx], rax ; demonstrate rax stack usage (see emit_comparison_handling function)
|
||||||
|
je crash
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 32 bits mem/reg
|
||||||
|
t2:
|
||||||
|
cmp rdx, 4
|
||||||
|
jb @f
|
||||||
|
mov r8d, 012345678h
|
||||||
|
mov rax, 100h ; test indes/scale usage
|
||||||
|
cmp dword ptr [rcx + rax*2 - 200h], r8d
|
||||||
|
je crash
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 16 bits mem/reg
|
||||||
|
t3:
|
||||||
|
cmp rdx, 2
|
||||||
|
jb @f
|
||||||
|
mov r8w, 01234h
|
||||||
|
cmp word ptr [rcx], r8w
|
||||||
|
je crash
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 64 bit reg/reg
|
||||||
|
t4:
|
||||||
|
cmp rdx, 8
|
||||||
|
jb @f
|
||||||
|
mov rax, 01234567812345678h
|
||||||
|
mov rcx, qword ptr [rcx]
|
||||||
|
cmp rax, rcx
|
||||||
|
je crash
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 32 bit reg/imm
|
||||||
|
t5:
|
||||||
|
cmp rdx, 4
|
||||||
|
jb @f
|
||||||
|
mov rcx, qword ptr [rcx]
|
||||||
|
cmp rcx, 012345678h
|
||||||
|
je crash
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 32 bit rsp-related reference
|
||||||
|
t6:
|
||||||
|
cmp rdx, 4
|
||||||
|
jb @f
|
||||||
|
sub rsp, 8
|
||||||
|
mov dword ptr [rsp], 012345678h
|
||||||
|
mov ecx, dword ptr [rcx]
|
||||||
|
cmp dword ptr [rsp], ecx
|
||||||
|
je crash
|
||||||
|
add rsp, 8
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test 32 bit rip-related reference
|
||||||
|
t7_rip_rel_ref:
|
||||||
|
dq 012345678h
|
||||||
|
t7:
|
||||||
|
cmp rdx, 4
|
||||||
|
jb @f
|
||||||
|
mov ecx, dword ptr [rcx]
|
||||||
|
cmp dword ptr [t7_rip_rel_ref], ecx
|
||||||
|
je crash
|
||||||
|
@@:
|
||||||
|
ret
|
||||||
|
|
||||||
|
dll_main:
|
||||||
|
mov eax, 1
|
||||||
|
ret
|
||||||
|
|
||||||
|
END
|
8
fuzzers/frida_gdiplus/cmplog_test.def
Normal file
8
fuzzers/frida_gdiplus/cmplog_test.def
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
EXPORTS
|
||||||
|
t1
|
||||||
|
t2
|
||||||
|
t3
|
||||||
|
t4
|
||||||
|
t5
|
||||||
|
t6
|
||||||
|
t7
|
@ -323,6 +323,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Cmplog observer is enabled");
|
||||||
// Create an observation channel using cmplog map
|
// Create an observation channel using cmplog map
|
||||||
let cmplog_observer = CmpLogObserver::new("cmplog", true);
|
let cmplog_observer = CmpLogObserver::new("cmplog", true);
|
||||||
|
|
||||||
|
@ -216,6 +216,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
|
|||||||
|
|
||||||
let coverage = CoverageRuntime::new();
|
let coverage = CoverageRuntime::new();
|
||||||
let cmplog = CmpLogRuntime::new();
|
let cmplog = CmpLogRuntime::new();
|
||||||
|
println!("cmplog runtime created");
|
||||||
|
|
||||||
let mut frida_helper =
|
let mut frida_helper =
|
||||||
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog));
|
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog));
|
||||||
|
@ -1065,6 +1065,7 @@ pub mod windows_asan_handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles exceptions on Windows
|
||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
pub mod windows_exception_handler {
|
pub mod windows_exception_handler {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
@ -452,6 +452,7 @@ where
|
|||||||
|
|
||||||
let cmps_len = {
|
let cmps_len = {
|
||||||
let meta = state.metadata_map().get::<CmpValuesMetadata>();
|
let meta = state.metadata_map().get::<CmpValuesMetadata>();
|
||||||
|
log::trace!("meta: {:x?}", meta);
|
||||||
if meta.is_none() {
|
if meta.is_none() {
|
||||||
return Ok(MutationResult::Skipped);
|
return Ok(MutationResult::Skipped);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ all-features = true
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serdeany_autoreg"]
|
default = ["serdeany_autoreg"]
|
||||||
cmplog = []
|
cmplog = ["iced-x86"]
|
||||||
serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"]
|
serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@ -33,6 +33,7 @@ yaxpeax-arm = "0.2.4"
|
|||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
yaxpeax-x86 = "1.2.2"
|
yaxpeax-x86 = "1.2.2"
|
||||||
|
iced-x86 = { version = "1.20.0", features = ["code_asm"], optional = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../libafl", default-features = false, version = "0.11.2", features = [
|
libafl = { path = "../libafl", default-features = false, version = "0.11.2", features = [
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
//! This allows the fuzzer to potentially solve the compares, if a compare value is directly
|
//! This allows the fuzzer to potentially solve the compares, if a compare value is directly
|
||||||
//! related to the input.
|
//! related to the input.
|
||||||
//! Read the [`RedQueen`](https://www.ndss-symposium.org/ndss-paper/redqueen-fuzzing-with-input-to-state-correspondence/) paper for the general concepts.
|
//! Read the [`RedQueen`](https://www.ndss-symposium.org/ndss-paper/redqueen-fuzzing-with-input-to-state-correspondence/) paper for the general concepts.
|
||||||
use std::ffi::c_void;
|
|
||||||
|
|
||||||
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
#[cfg(all(feature = "cmplog", target_arch = "x86_64"))]
|
||||||
#[cfg(target_arch = "aarch64")]
|
use std::collections::HashMap;
|
||||||
use frida_gum_sys::Insn;
|
|
||||||
|
use dynasmrt::dynasm;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
Error,
|
Error,
|
||||||
@ -24,11 +24,19 @@ extern "C" {
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use frida_gum::ModuleMap;
|
use frida_gum::ModuleMap;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use frida_gum::{instruction_writer::InstructionWriter, stalker::StalkerOutput};
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use frida_gum::{
|
use frida_gum::{
|
||||||
instruction_writer::{Aarch64Register, IndexMode, InstructionWriter},
|
instruction_writer::{Aarch64Register, IndexMode, InstructionWriter},
|
||||||
stalker::StalkerOutput,
|
stalker::StalkerOutput,
|
||||||
};
|
};
|
||||||
|
use frida_gum_sys::Insn;
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "x86_64"))]
|
||||||
|
use iced_x86::{
|
||||||
|
BlockEncoder, Code, DecoderOptions, Instruction, InstructionBlock, MemoryOperand, MemorySize,
|
||||||
|
OpKind, Register,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
use crate::utils::{disas_count, writer_register};
|
use crate::utils::{disas_count, writer_register};
|
||||||
@ -43,8 +51,15 @@ pub enum SpecialCmpLogCase {
|
|||||||
Tbnz,
|
Tbnz,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "x86_64"))]
|
||||||
|
/// Speciial `CmpLog` Cases for `aarch64`
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SpecialCmpLogCase {}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use yaxpeax_arm::armv8::a64::{InstDecoder, Opcode, Operand, ShiftStyle};
|
use yaxpeax_arm::armv8::a64::{InstDecoder, Opcode, Operand, ShiftStyle};
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use yaxpeax_x86::long_mode::InstDecoder;
|
||||||
|
|
||||||
/// The [`frida_gum_sys::GUM_RED_ZONE_SIZE`] casted to [`i32`]
|
/// The [`frida_gum_sys::GUM_RED_ZONE_SIZE`] casted to [`i32`]
|
||||||
///
|
///
|
||||||
@ -73,13 +88,35 @@ pub enum CmplogOperandType {
|
|||||||
// We don't need a memory type because you cannot directly compare with memory
|
// We don't need a memory type because you cannot directly compare with memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of an operand loggged during `CmpLog`
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "x86_64"))]
|
||||||
|
pub enum CmplogOperandType {
|
||||||
|
/// A Register
|
||||||
|
Reg(Register),
|
||||||
|
/// An immediate value
|
||||||
|
Imm(u64),
|
||||||
|
/// A memory operand
|
||||||
|
Mem(Register, Register, i64, u32, MemorySize), // base, index, disp, scale, mem_size
|
||||||
|
}
|
||||||
|
|
||||||
/// `Frida`-based binary-only innstrumentation that logs compares to the fuzzer
|
/// `Frida`-based binary-only innstrumentation that logs compares to the fuzzer
|
||||||
/// `LibAFL` can use this knowledge for powerful mutations.
|
/// `LibAFL` can use this knowledge for powerful mutations.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub struct CmpLogRuntime {
|
pub struct CmpLogRuntime {
|
||||||
ops_save_register_and_blr_to_populate: Option<Box<[u8]>>,
|
save_register_and_blr_to_populate: Option<Box<[u8]>>,
|
||||||
ops_handle_tbz_masking: Option<Box<[u8]>>,
|
handle_tbz_masking: Option<Box<[u8]>>,
|
||||||
ops_handle_tbnz_masking: Option<Box<[u8]>>,
|
handle_tbnz_masking: Option<Box<[u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Frida`-based binary-only innstrumentation that logs compares to the fuzzer
|
||||||
|
/// `LibAFL` can use this knowledge for powerful mutations.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub struct CmpLogRuntime {
|
||||||
|
save_registers: Option<Box<[u8]>>,
|
||||||
|
restore_registers: Option<Box<[u8]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FridaRuntime for CmpLogRuntime {
|
impl FridaRuntime for CmpLogRuntime {
|
||||||
@ -106,6 +143,7 @@ impl FridaRuntime for CmpLogRuntime {
|
|||||||
impl CmpLogRuntime {
|
impl CmpLogRuntime {
|
||||||
/// Create a new [`CmpLogRuntime`]
|
/// Create a new [`CmpLogRuntime`]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub fn new() -> CmpLogRuntime {
|
pub fn new() -> CmpLogRuntime {
|
||||||
Self {
|
Self {
|
||||||
ops_save_register_and_blr_to_populate: None,
|
ops_save_register_and_blr_to_populate: None,
|
||||||
@ -114,8 +152,19 @@ impl CmpLogRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`CmpLogRuntime`]
|
||||||
|
#[must_use]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub fn new() -> CmpLogRuntime {
|
||||||
|
Self {
|
||||||
|
save_registers: None,
|
||||||
|
restore_registers: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Call the external function that populates the `cmplog_map` with the relevant values
|
/// Call the external function that populates the `cmplog_map` with the relevant values
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
extern "C" fn populate_lists(&mut self, op1: u64, op2: u64, retaddr: u64) {
|
extern "C" fn populate_lists(&mut self, op1: u64, op2: u64, retaddr: u64) {
|
||||||
// log::trace!(
|
// log::trace!(
|
||||||
// "entered populate_lists with: {:#02x}, {:#02x}, {:#02x}",
|
// "entered populate_lists with: {:#02x}, {:#02x}, {:#02x}",
|
||||||
@ -130,8 +179,25 @@ impl CmpLogRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
extern "C" fn populate_lists(size: u8, op1: u64, op2: u64, retaddr: u64) {
|
||||||
|
// log::trace!(
|
||||||
|
// "entered populate_lists with: {:#02x}, {:#02x}, {:#02x}",
|
||||||
|
// op1, op2, retaddr
|
||||||
|
// );
|
||||||
|
let mut k = (retaddr >> 4) ^ (retaddr << 8);
|
||||||
|
|
||||||
|
k &= (CMPLOG_MAP_W as u64) - 1;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
__libafl_targets_cmplog_instructions(k, size, op1, op2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate the instrumentation blobs for the current arch.
|
/// Generate the instrumentation blobs for the current arch.
|
||||||
#[allow(clippy::similar_names)]
|
#[allow(clippy::similar_names)]
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
fn generate_instrumentation_blobs(&mut self) {
|
fn generate_instrumentation_blobs(&mut self) {
|
||||||
macro_rules! blr_to_populate {
|
macro_rules! blr_to_populate {
|
||||||
($ops:ident) => {dynasm!($ops
|
($ops:ident) => {dynasm!($ops
|
||||||
@ -243,9 +309,82 @@ impl CmpLogRuntime {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::similar_names)]
|
||||||
|
#[cfg(all(windows, target_arch = "x86_64"))]
|
||||||
|
fn generate_instrumentation_blobs(&mut self) {
|
||||||
|
macro_rules! save_registers {
|
||||||
|
($ops:ident) => {dynasm!($ops
|
||||||
|
; .arch x64
|
||||||
|
; push rcx
|
||||||
|
; push rdx
|
||||||
|
; push r8
|
||||||
|
; push r9
|
||||||
|
; push r10
|
||||||
|
; push r11
|
||||||
|
; push rax
|
||||||
|
);};
|
||||||
|
}
|
||||||
|
let mut save_registers = dynasmrt::VecAssembler::<dynasmrt::x64::X64Relocation>::new(0);
|
||||||
|
save_registers!(save_registers);
|
||||||
|
self.save_registers = Some(save_registers.finalize().unwrap().into_boxed_slice());
|
||||||
|
|
||||||
|
macro_rules! restore_registers {
|
||||||
|
($ops:ident) => {dynasm!($ops
|
||||||
|
; .arch x64
|
||||||
|
; pop rax
|
||||||
|
; pop r11
|
||||||
|
; pop r10
|
||||||
|
; pop r9
|
||||||
|
; pop r8
|
||||||
|
; pop rdx
|
||||||
|
; pop rcx
|
||||||
|
);};
|
||||||
|
}
|
||||||
|
let mut restore_registers = dynasmrt::VecAssembler::<dynasmrt::x64::X64Relocation>::new(0);
|
||||||
|
restore_registers!(restore_registers);
|
||||||
|
self.restore_registers = Some(restore_registers.finalize().unwrap().into_boxed_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::similar_names)]
|
||||||
|
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||||
|
fn generate_instrumentation_blobs(&mut self) {
|
||||||
|
macro_rules! save_registers {
|
||||||
|
($ops:ident) => {dynasm!($ops
|
||||||
|
; .arch x64
|
||||||
|
; push rax
|
||||||
|
; push rdi
|
||||||
|
; push rsi
|
||||||
|
; push rdx
|
||||||
|
; push rcx
|
||||||
|
; push r8
|
||||||
|
; push r9
|
||||||
|
);};
|
||||||
|
}
|
||||||
|
let mut save_registers = dynasmrt::VecAssembler::<dynasmrt::x64::X64Relocation>::new(0);
|
||||||
|
save_registers!(save_registers);
|
||||||
|
self.save_registers = Some(save_registers.finalize().unwrap().into_boxed_slice());
|
||||||
|
|
||||||
|
macro_rules! restore_registers {
|
||||||
|
($ops:ident) => {dynasm!($ops
|
||||||
|
; .arch x64
|
||||||
|
; pop r9
|
||||||
|
; pop r8
|
||||||
|
; pop rcx
|
||||||
|
; pop rdx
|
||||||
|
; pop rsi
|
||||||
|
; pop rdi
|
||||||
|
; pop rax
|
||||||
|
);};
|
||||||
|
}
|
||||||
|
let mut restore_registers = dynasmrt::VecAssembler::<dynasmrt::x64::X64Relocation>::new(0);
|
||||||
|
restore_registers!(restore_registers);
|
||||||
|
self.restore_registers = Some(restore_registers.finalize().unwrap().into_boxed_slice());
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the blob which saves the context, jumps to the populate function and restores the context
|
/// Get the blob which saves the context, jumps to the populate function and restores the context
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[cfg(target_arch = "aaarch64")]
|
||||||
pub fn ops_save_register_and_blr_to_populate(&self) -> &[u8] {
|
pub fn ops_save_register_and_blr_to_populate(&self) -> &[u8] {
|
||||||
self.ops_save_register_and_blr_to_populate.as_ref().unwrap()
|
self.ops_save_register_and_blr_to_populate.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
@ -253,6 +392,7 @@ impl CmpLogRuntime {
|
|||||||
/// Get the blob which handles the tbz opcode masking
|
/// Get the blob which handles the tbz opcode masking
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[cfg(target_arch = "aaarch64")]
|
||||||
pub fn ops_handle_tbz_masking(&self) -> &[u8] {
|
pub fn ops_handle_tbz_masking(&self) -> &[u8] {
|
||||||
self.ops_handle_tbz_masking.as_ref().unwrap()
|
self.ops_handle_tbz_masking.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
@ -260,11 +400,163 @@ impl CmpLogRuntime {
|
|||||||
/// Get the blob which handles the tbnz opcode masking
|
/// Get the blob which handles the tbnz opcode masking
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[cfg(target_arch = "aaarch64")]
|
||||||
pub fn ops_handle_tbnz_masking(&self) -> &[u8] {
|
pub fn ops_handle_tbnz_masking(&self) -> &[u8] {
|
||||||
self.ops_handle_tbnz_masking.as_ref().unwrap()
|
self.ops_handle_tbnz_masking.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit the instrumentation code which is responsible for operands value extraction and cmplog map population
|
/// Emit the instrumentation code which is responsible for operands value extraction and cmplog map population
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "x86_64"))]
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
#[inline]
|
||||||
|
pub fn emit_comparison_handling(
|
||||||
|
&self,
|
||||||
|
address: u64,
|
||||||
|
output: &StalkerOutput,
|
||||||
|
op1: &CmplogOperandType, //first operand of the comparsion
|
||||||
|
op2: &CmplogOperandType, //second operand of the comparsion
|
||||||
|
_shift: &Option<SpecialCmpLogCase>,
|
||||||
|
_special_case: &Option<SpecialCmpLogCase>,
|
||||||
|
) {
|
||||||
|
let writer = output.writer();
|
||||||
|
|
||||||
|
writer.put_bytes(&self.save_registers.clone().unwrap());
|
||||||
|
|
||||||
|
// let int3 = [0xcc];
|
||||||
|
// writer.put_bytes(&int3);
|
||||||
|
|
||||||
|
let mut insts = vec![];
|
||||||
|
// self ptr is not used so far
|
||||||
|
let mut size_op = 0;
|
||||||
|
|
||||||
|
let arg_reg_1;
|
||||||
|
let arg_reg_2;
|
||||||
|
let arg_reg_3;
|
||||||
|
let arg_reg_4;
|
||||||
|
let mut tmp_reg = HashMap::new();
|
||||||
|
tmp_reg.insert(8, Register::RAX);
|
||||||
|
tmp_reg.insert(4, Register::EAX);
|
||||||
|
tmp_reg.insert(2, Register::AX);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
arg_reg_1 = Register::CL;
|
||||||
|
arg_reg_2 = Register::RDX;
|
||||||
|
arg_reg_3 = Register::R8;
|
||||||
|
arg_reg_4 = Register::R9;
|
||||||
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
arg_reg_1 = Register::DL;
|
||||||
|
arg_reg_2 = Register::RSI;
|
||||||
|
arg_reg_3 = Register::RDX;
|
||||||
|
arg_reg_4 = Register::RCX;
|
||||||
|
}
|
||||||
|
let mut set_size = |s: usize| {
|
||||||
|
if size_op == 0 {
|
||||||
|
size_op = s;
|
||||||
|
} else {
|
||||||
|
assert_eq!(size_op, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// we put the operand value into rax and than push it on stack, so the
|
||||||
|
// only clobbered register is rax, and if actual operand value uses it,
|
||||||
|
// we simply restore it from stack
|
||||||
|
for (op_num, op) in [op1, op2].iter().enumerate() {
|
||||||
|
let op_num: i64 = op_num.try_into().unwrap();
|
||||||
|
match op {
|
||||||
|
CmplogOperandType::Reg(reg) => {
|
||||||
|
let info = reg.info();
|
||||||
|
set_size(info.size());
|
||||||
|
let reg_largest = reg.full_register();
|
||||||
|
if reg_largest == Register::RAX {
|
||||||
|
insts.push(
|
||||||
|
// we rely on the fact that latest saved register on stack is rax
|
||||||
|
Instruction::with1(
|
||||||
|
Code::Push_rm64,
|
||||||
|
MemoryOperand::with_base_displ(Register::RSP, op_num * 8),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
insts.push(Instruction::with1(Code::Push_rm64, reg_largest).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CmplogOperandType::Mem(reg_base, reg_index, disp, scale, mem_size) => {
|
||||||
|
let size;
|
||||||
|
let inst;
|
||||||
|
match *mem_size {
|
||||||
|
MemorySize::UInt64 | MemorySize::Int64 => {
|
||||||
|
size = 8;
|
||||||
|
inst = Code::Mov_r64_rm64;
|
||||||
|
}
|
||||||
|
MemorySize::UInt32 | MemorySize::Int32 => {
|
||||||
|
size = 4;
|
||||||
|
inst = Code::Mov_r32_rm32;
|
||||||
|
}
|
||||||
|
MemorySize::UInt16 | MemorySize::Int16 => {
|
||||||
|
size = 2;
|
||||||
|
inst = Code::Mov_r16_rm16;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Invalid memory size");
|
||||||
|
size = 4;
|
||||||
|
inst = Code::Push_rm32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_size(size);
|
||||||
|
let mut disp_adjusted = *disp;
|
||||||
|
let mut reg_base = *reg_base;
|
||||||
|
if reg_base == Register::RSP {
|
||||||
|
// 0x38 is an amount of bytes used by save_registers()
|
||||||
|
disp_adjusted = disp_adjusted + 0x38 + 8_i64 * op_num;
|
||||||
|
}
|
||||||
|
let tmp_reg_adjusted = *tmp_reg.get(&size).unwrap();
|
||||||
|
// in case of RIP, disp is an absolute address already calculated
|
||||||
|
// by iced, we can simply load it to rax (but in this case index register must
|
||||||
|
// be not rax)
|
||||||
|
if reg_base == Register::RIP {
|
||||||
|
insts.push(
|
||||||
|
Instruction::with2(Code::Mov_r64_imm64, Register::RAX, disp_adjusted)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
reg_base = Register::RAX;
|
||||||
|
disp_adjusted = 0;
|
||||||
|
}
|
||||||
|
insts.push(
|
||||||
|
Instruction::with2(
|
||||||
|
inst,
|
||||||
|
tmp_reg_adjusted,
|
||||||
|
MemoryOperand::with_base_index_scale_displ_size(
|
||||||
|
reg_base,
|
||||||
|
*reg_index,
|
||||||
|
*scale,
|
||||||
|
disp_adjusted,
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
insts.push(Instruction::with1(Code::Push_rm64, Register::RAX).unwrap());
|
||||||
|
}
|
||||||
|
CmplogOperandType::Imm(imm) => {
|
||||||
|
insts.push(Instruction::with1(Code::Pushq_imm32, *imm as i32).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insts.push(Instruction::with2(Code::Mov_r8_imm8, arg_reg_1, size_op as u64).unwrap());
|
||||||
|
insts.push(Instruction::with1(Code::Pop_r64, arg_reg_2).unwrap());
|
||||||
|
insts.push(Instruction::with1(Code::Pop_r64, arg_reg_3).unwrap());
|
||||||
|
insts.push(Instruction::with2(Code::Mov_r64_imm64, arg_reg_4, address).unwrap());
|
||||||
|
let block = InstructionBlock::new(&insts, 0);
|
||||||
|
let block = BlockEncoder::encode(64, block, DecoderOptions::NONE).unwrap();
|
||||||
|
writer.put_bytes(block.code_buffer.as_slice());
|
||||||
|
writer.put_call_address((CmpLogRuntime::populate_lists as usize).try_into().unwrap());
|
||||||
|
|
||||||
|
writer.put_bytes(&self.restore_registers.clone().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -274,8 +566,8 @@ impl CmpLogRuntime {
|
|||||||
output: &StalkerOutput,
|
output: &StalkerOutput,
|
||||||
op1: &CmplogOperandType, //first operand of the comparsion
|
op1: &CmplogOperandType, //first operand of the comparsion
|
||||||
op2: &CmplogOperandType, //second operand of the comparsion
|
op2: &CmplogOperandType, //second operand of the comparsion
|
||||||
_shift: Option<(ShiftStyle, u8)>,
|
_shift: &Option<(ShiftStyle, u8)>,
|
||||||
special_case: Option<SpecialCmpLogCase>,
|
special_case: &Option<SpecialCmpLogCase>,
|
||||||
) {
|
) {
|
||||||
let writer = output.writer();
|
let writer = output.writer();
|
||||||
|
|
||||||
@ -347,6 +639,114 @@ impl CmpLogRuntime {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "x86_64"))]
|
||||||
|
#[allow(clippy::similar_names)]
|
||||||
|
#[inline]
|
||||||
|
/// Check if the current instruction is cmplog relevant one(any opcode which sets the flags)
|
||||||
|
#[must_use]
|
||||||
|
pub fn cmplog_is_interesting_instruction(
|
||||||
|
_decoder: InstDecoder,
|
||||||
|
_address: u64,
|
||||||
|
instr: &Insn,
|
||||||
|
) -> Option<(
|
||||||
|
CmplogOperandType,
|
||||||
|
CmplogOperandType,
|
||||||
|
Option<SpecialCmpLogCase>,
|
||||||
|
Option<SpecialCmpLogCase>,
|
||||||
|
)> {
|
||||||
|
let bytes = instr.bytes();
|
||||||
|
let mut decoder =
|
||||||
|
iced_x86::Decoder::with_ip(64, bytes, instr.address(), iced_x86::DecoderOptions::NONE);
|
||||||
|
if !decoder.can_decode() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut instruction = iced_x86::Instruction::default();
|
||||||
|
decoder.decode_out(&mut instruction);
|
||||||
|
match instruction.mnemonic() {
|
||||||
|
iced_x86::Mnemonic::Cmp | iced_x86::Mnemonic::Sub => {} // continue
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.op_count() != 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't support rip related reference with index register yet
|
||||||
|
if instruction.memory_base() == Register::RIP
|
||||||
|
&& instruction.memory_index() != Register::None
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if instruction.memory_size() == MemorySize::UInt8
|
||||||
|
|| instruction.memory_size() == MemorySize::Int8
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let op1 = match instruction.op0_kind() {
|
||||||
|
OpKind::Register => CmplogOperandType::Reg(instruction.op0_register()),
|
||||||
|
OpKind::Immediate16
|
||||||
|
| OpKind::Immediate32
|
||||||
|
| OpKind::Immediate64
|
||||||
|
| OpKind::Immediate32to64 => CmplogOperandType::Imm(instruction.immediate(0)),
|
||||||
|
OpKind::Memory => {
|
||||||
|
// can't use try_into here, since we need to cast u64 to i64
|
||||||
|
// which is fine in this case
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
CmplogOperandType::Mem(
|
||||||
|
instruction.memory_base(),
|
||||||
|
instruction.memory_index(),
|
||||||
|
instruction.memory_displacement64() as i64,
|
||||||
|
instruction.memory_index_scale(),
|
||||||
|
instruction.memory_size(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let op2 = match instruction.op1_kind() {
|
||||||
|
OpKind::Register => CmplogOperandType::Reg(instruction.op1_register()),
|
||||||
|
OpKind::Immediate16
|
||||||
|
| OpKind::Immediate32
|
||||||
|
| OpKind::Immediate64
|
||||||
|
| OpKind::Immediate32to64 => CmplogOperandType::Imm(instruction.immediate(1)),
|
||||||
|
OpKind::Memory =>
|
||||||
|
{
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
CmplogOperandType::Mem(
|
||||||
|
instruction.memory_base(),
|
||||||
|
instruction.memory_index(),
|
||||||
|
instruction.memory_displacement64() as i64,
|
||||||
|
instruction.memory_index_scale(),
|
||||||
|
instruction.memory_size(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// debug print, shows all the cmp instrumented instructions
|
||||||
|
if log::log_enabled!(log::Level::Debug) {
|
||||||
|
use iced_x86::{Formatter, NasmFormatter};
|
||||||
|
let mut formatter = NasmFormatter::new();
|
||||||
|
let mut output = String::new();
|
||||||
|
formatter.format(&instruction, &mut output);
|
||||||
|
log::debug!(
|
||||||
|
"inst: {:x} {:?}, {:?} {:?}",
|
||||||
|
instruction.ip(),
|
||||||
|
output,
|
||||||
|
op1,
|
||||||
|
op2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((op1, op2, None, None))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
#[allow(clippy::similar_names)]
|
#[allow(clippy::similar_names)]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -30,7 +30,7 @@ use yaxpeax_x86::amd64::InstDecoder;
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::asan::asan_rt::AsanRuntime;
|
use crate::asan::asan_rt::AsanRuntime;
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(feature = "cmplog")]
|
||||||
use crate::cmplog_rt::CmpLogRuntime;
|
use crate::cmplog_rt::CmpLogRuntime;
|
||||||
use crate::{coverage_rt::CoverageRuntime, drcov_rt::DrCovRuntime};
|
use crate::{coverage_rt::CoverageRuntime, drcov_rt::DrCovRuntime};
|
||||||
|
|
||||||
@ -520,7 +520,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
#[cfg(all(
|
||||||
|
feature = "cmplog",
|
||||||
|
any(target_arch = "aarch64", target_arch = "x86_64")
|
||||||
|
))]
|
||||||
if let Some(rt) = runtimes.match_first_type_mut::<CmpLogRuntime>() {
|
if let Some(rt) = runtimes.match_first_type_mut::<CmpLogRuntime>() {
|
||||||
if let Some((op1, op2, shift, special_case)) =
|
if let Some((op1, op2, shift, special_case)) =
|
||||||
CmpLogRuntime::cmplog_is_interesting_instruction(decoder, address, instr)
|
CmpLogRuntime::cmplog_is_interesting_instruction(decoder, address, instr)
|
||||||
@ -529,11 +532,11 @@ where
|
|||||||
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
||||||
rt.emit_comparison_handling(
|
rt.emit_comparison_handling(
|
||||||
address,
|
address,
|
||||||
&output,
|
output,
|
||||||
&op1,
|
&op1,
|
||||||
&op2,
|
&op2,
|
||||||
shift,
|
&shift,
|
||||||
special_case,
|
&special_case,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,6 @@ pub mod drcov_rt;
|
|||||||
pub mod executor;
|
pub mod executor;
|
||||||
|
|
||||||
/// Utilities
|
/// Utilities
|
||||||
#[cfg(unix)]
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
// for parsing asan and cmplog cores
|
// for parsing asan and cmplog cores
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
use frida_gum::instruction_writer::Aarch64Register;
|
use frida_gum::instruction_writer::Aarch64Register;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use frida_gum::instruction_writer::X86Register;
|
use frida_gum::instruction_writer::X86Register;
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use frida_gum_sys;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@ -122,7 +120,7 @@ pub fn writer_register(reg: u16, sizecode: SizeCode, zr: bool) -> Aarch64Registe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Translate from `RegSpec` to `X86Register`
|
/// Translate from `RegSpec` to `X86Register`
|
||||||
#[cfg(all(target_arch = "x86_64", unix))]
|
#[cfg(target_arch = "x86_64")]
|
||||||
const X86_64_REGS: [(RegSpec, X86Register); 34] = [
|
const X86_64_REGS: [(RegSpec, X86Register); 34] = [
|
||||||
(RegSpec::eax(), X86Register::Eax),
|
(RegSpec::eax(), X86Register::Eax),
|
||||||
(RegSpec::ecx(), X86Register::Ecx),
|
(RegSpec::ecx(), X86Register::Ecx),
|
||||||
@ -162,7 +160,8 @@ const X86_64_REGS: [(RegSpec, X86Register); 34] = [
|
|||||||
|
|
||||||
/// The writer registers
|
/// The writer registers
|
||||||
/// frida registers: <https://docs.rs/frida-gum/0.4.0/frida_gum/instruction_writer/enum.X86Register.html>
|
/// frida registers: <https://docs.rs/frida-gum/0.4.0/frida_gum/instruction_writer/enum.X86Register.html>
|
||||||
#[cfg(all(target_arch = "x86_64", unix))]
|
/// capstone registers: <https://docs.rs/capstone-sys/0.14.0/capstone_sys/x86_reg/index.html>
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
@ -177,7 +176,7 @@ pub fn writer_register(reg: RegSpec) -> X86Register {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Translates a frida instruction to a disassembled instruction.
|
/// Translates a frida instruction to a disassembled instruction.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(target_arch = "x86_64", unix))]
|
||||||
pub(crate) fn frida_to_cs(decoder: InstDecoder, frida_insn: &frida_gum_sys::Insn) -> Instruction {
|
pub(crate) fn frida_to_cs(decoder: InstDecoder, frida_insn: &frida_gum_sys::Insn) -> Instruction {
|
||||||
decoder.decode_slice(frida_insn.bytes()).unwrap()
|
decoder.decode_slice(frida_insn.bytes()).unwrap()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user