diff --git a/01/project-hbj-attacker/src/helper/elf.rs b/01/project-hbj-attacker/src/helper/elf.rs index 7a23355..b463761 100644 --- a/01/project-hbj-attacker/src/helper/elf.rs +++ b/01/project-hbj-attacker/src/helper/elf.rs @@ -5,6 +5,7 @@ use std::fs::File; use std::ops::Deref; use ouroboros::self_referencing; + fn open_mem_map(path: &str) -> Result> { let file = File::open(path)?; unsafe { Ok(Mmap::map(&file)?) } @@ -108,4 +109,9 @@ impl ExecuteLinkFile { Ok(str.to_owned()) } -} + + pub fn get_e_type(&self) -> u16 + { + self.borrow_elf().header.e_type + } +} \ No newline at end of file diff --git a/01/project-hbj-attacker/src/helper/injectors.rs b/01/project-hbj-attacker/src/helper/injectors.rs index a033775..fccb146 100644 --- a/01/project-hbj-attacker/src/helper/injectors.rs +++ b/01/project-hbj-attacker/src/helper/injectors.rs @@ -98,45 +98,11 @@ pub fn inject2(proc: &Process, seg_rw: (u64, u64)) -> Result<(), Box Result> // thread inject { - let regs = ptrace::getregs(proc.get_pid())?; // Alloc rwx memory - let injected_inst = assemble(regs.rip as u64, |asm| { - asm.mov(rax, 9u64)?; // Syscall 9 (mmap) + let page_addr + = proc.alloc_pages(1, (libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as u64)? as usize; - asm.mov(rdi, 0u64)?; // Addr - asm.mov(rsi, 4096u64)?; // Length, we alloc a page (4K) - asm.mov( - rdx, - (libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as u64, - )?; // Set protect to rwx - asm.mov(r10, (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64)?; // Private and anonymous - asm.mov(r8, 01i64)?; // Fd (-1 because we want anonymous) - asm.mov(r9, 0u64)?; // Offset - - asm.syscall()?; // Syscall interrupt - asm.int3()?; // (Important!!!) Use int3 interrupt to retrieve control flow - Ok(()) - })?; - - proc.write_memory_ptrace(regs.rip as usize, &injected_inst)?; - println!( - "{GREEN}[trace]{RESET} write instructions to {:#016x}", - regs.rip - ); - - // Continue target - ptrace::cont(proc.get_pid(), None)?; - println!("{GREEN}[trace]{RESET} continue from {:#016x}", regs.rip); - proc.wait(); - - println!( - "{GREEN}[trace]{RESET} int3 at {:#016x}", - ptrace::getregs(proc.get_pid())?.rip - ); - - let regs_after_map = ptrace::getregs(proc.get_pid())?; - let page_addr = regs_after_map.rax as usize; println!( "{GREEN}[trace]{RESET} allocated page is at {:#016x}", page_addr @@ -180,8 +146,7 @@ pub fn inject3(proc: &Process, seg_rw: (u64, u64)) -> Result Result, std::io::Error> { let mut processes = HashMap::::new(); @@ -73,7 +73,7 @@ impl Process let copy_len = usize::min(word_size - head_offset, data.len()); bytes[head_offset..head_offset + copy_len].copy_from_slice(&data[..copy_len]); - let new_word = libc::c_long::from_ne_bytes(bytes); + let new_word = libc::c_long::from_le_bytes(bytes); ptrace::write(pid, aligned_addr as *mut libc::c_void, new_word)?; Ok(copy_len) @@ -87,7 +87,7 @@ impl Process { let mut arr = [0u8; size_of::()]; arr.copy_from_slice(data); - let val = libc::c_long::from_ne_bytes(arr); + let val = libc::c_long::from_le_bytes(arr); ptrace::write(pid, addr as *mut libc::c_void, val)?; Ok(size_of::()) } @@ -102,7 +102,7 @@ impl Process let orig_word = ptrace::read(pid, addr as *mut libc::c_void)?; let mut bytes = orig_word.to_ne_bytes(); bytes[..data.len()].copy_from_slice(data); - let new_word = libc::c_long::from_ne_bytes(bytes); + let new_word = libc::c_long::from_le_bytes(bytes); ptrace::write(pid, addr as *mut libc::c_void, new_word)?; Ok(data.len()) @@ -239,15 +239,30 @@ impl Process Ok(written) } - pub fn find_remote_proc(&self, module: &str, symbol: &str) -> Option + pub fn find_remote_proc( + &self, + module: &str, // Full path of module, like '/usr/lib/libc.so.6' + symbol: &str // Symbol name, like 'printf' + ) -> Option { - let target_maps = fs::read_to_string(format!("/proc/{}/maps", self.pid)).ok()?; - let base = MemoryMap::new(&target_maps.lines().collect::>()).module_base_address(module)?; - let elf = ExecuteLinkFile::prase(module).ok()?; let sym = elf.prase_dyn_sym(symbol).ok()?; - Some(sym.st_value + base) + let target_maps = fs::read_to_string(format!("/proc/{}/maps", self.pid)).ok()?; + let base = MemoryMap::new(&target_maps.lines().collect::>()).module_base_address(module)?; + + let is_undefined = sym.st_shndx == section_header::SHN_UNDEF as usize; + + if !is_undefined && sym.st_value != 0 { + return if elf.get_e_type() == header::ET_DYN { + Some(base + sym.st_value) + } else { + // ET_EXEC or others: assume st_value is absolute + Some(sym.st_value) + } + } + + None } pub fn find_got_pointer_plt(&self, symbol: &str) -> Option @@ -258,4 +273,63 @@ impl Process let r_sym = elf.get_rela_sym(symbol).ok()?; Some(r_sym.r_offset + self.map.module_base_address(&exe)?) } + + pub fn execute_once_inplace( + &self, + payload_builder: F + ) + -> Result> + where F: Fn(u64) -> Option> + { + const GREEN: &str = "\x1b[32m"; + const RESET: &str = "\x1b[0m"; + + // Save context + let regs = ptrace::getregs(self.pid)?; + let payload = payload_builder(regs.rip).context("payload build failed")?; + + let buffer = self.read_memory_vm(regs.rip as usize, payload.len() + 1)?; + let instruction = [&payload as &[u8], &[0xccu8]].concat(); + + + self.write_memory_ptrace(regs.rip as usize, &instruction)?; + println!("{GREEN}[trace]{RESET} write instructions to {:#016x}", regs.rip); + + // Continue target + ptrace::cont(self.pid, None)?; + println!("{GREEN}[trace]{RESET} continue from {:#016x}", regs.rip); + self.wait(); + + let r = ptrace::getregs(self.pid)?; + println!("{GREEN}[trace]{RESET} int3 at {:#016x}", r.rip); + + self.write_memory_ptrace(regs.rip as usize, &buffer)?; + ptrace::setregs(self.pid, regs)?; + Ok(r) + } + + pub fn alloc_pages(&self, count: u64, permissions: u64) + -> Result> + { + // Alloc r-x private memory + let r = self.execute_once_inplace(|addr| { + let r = assemble(addr, |asm| { + asm.mov(rax, 9u64)?; // Syscall 9 (mmap) + + asm.mov(rdi, 0u64)?; // Addr + asm.mov(rsi, 0x1000u64 * count)?; // Length, we alloc a page (4K) + asm.mov(rdx, permissions, )?; + asm.mov(r10, (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64)?; // Private and anonymous + asm.mov(r8, -1i64)?; // Fd (-1 because we want anonymous) + asm.mov(r9, 0u64)?; // Offset + + asm.syscall()?; // Syscall interrupt + Ok(()) + }).ok()?; + + Some(r) + })?; + + Ok(r.rax as u64) + } } \ No newline at end of file diff --git a/01/project-hbj-hook/src/asm.rs b/01/project-hbj-hook/src/asm.rs index 5b18ecb..be5f766 100644 --- a/01/project-hbj-hook/src/asm.rs +++ b/01/project-hbj-hook/src/asm.rs @@ -1,7 +1,7 @@ // asm.rs -use iced_x86::{Instruction, code_asm::*}; +use iced_x86::{code_asm::*}; pub fn assemble(addr: u64, op: F) -> Result, Box> where diff --git a/01/project-hbj-hook/src/disassembly.rs b/01/project-hbj-hook/src/disassembly.rs deleted file mode 100644 index bcefbb7..0000000 --- a/01/project-hbj-hook/src/disassembly.rs +++ /dev/null @@ -1,16 +0,0 @@ - -/* - let cs = Capstone::new() - .x86() - .mode(arch::x86::ArchMode::Mode64) - .syntax(arch::x86::ArchSyntax::Att) - .detail(true) - .build()?; - - let code = vec![0x55, 0x48, 0x8b, 0x05, 0xb8, 0x13, 0x00, 0x00]; - let insns = cs.disasm_all(&code, 0x1000)?; - - for insn in insns.iter() { - println!("{}", insn); - } - */ \ No newline at end of file diff --git a/01/project-hbj-hook/src/elf.rs b/01/project-hbj-hook/src/elf.rs index 5f42557..fae2639 100644 --- a/01/project-hbj-hook/src/elf.rs +++ b/01/project-hbj-hook/src/elf.rs @@ -48,6 +48,7 @@ impl ExecuteLinkFile { Ok(loads) } + #[allow(unused)] pub fn get_dynamic(&self) -> Result> { let dynamic = self.borrow_elf() .program_headers @@ -94,6 +95,7 @@ impl ExecuteLinkFile { Ok(dyn_sym.clone()) } + #[allow(unused)] pub fn prase_dyn_sym(&self, name: &str) -> Result> { let dyn_sym = self.borrow_elf() .dynsyms.iter() @@ -112,6 +114,7 @@ impl ExecuteLinkFile { Ok(str.to_owned()) } + #[allow(unused)] pub fn get_e_type(&self) -> u16 { self.borrow_elf().header.e_type diff --git a/01/project-hbj-hook/src/hooks.rs b/01/project-hbj-hook/src/hooks.rs new file mode 100644 index 0000000..8b9c310 --- /dev/null +++ b/01/project-hbj-hook/src/hooks.rs @@ -0,0 +1,163 @@ + +// hooks.rs + +use std::error::Error; + +use crate::map::MemoryMap; +use crate::processes::Process; +use anyhow::Context; + +use crate::asm::assemble; + +const GREEN: &str = "\x1b[32m"; +const YELLOW: &str = "\x1b[33m"; +const RESET: &str = "\x1b[0m"; + +#[allow(unused)] +pub fn plt_hook( + proc: &Process, + map: &MemoryMap, + symbol: &str, + payload: F, +) -> Result<(), Box> +where + F: Fn(&Process, u64, u64) -> Result, Box>, +{ + let bias = map.module_base_address(&proc.get_exe()?).unwrap_or(0); + let got_item_ptr = proc + .find_got_pointer_plt(symbol) + .context("Unable to find symbol")?; + + println!("{GREEN}[memory map]{RESET} Bias is {:#016x}", bias); + + let got_item_byte: [u8; 8] = proc + .read_memory_vm(got_item_ptr as usize, 8)? + .try_into() + .map_err(|_| "Failed to convert Vec to array")?; + let got_item = u64::from_le_bytes(got_item_byte); + println!("{GREEN}[memory map]{RESET} got_item = {:#016x}", got_item); + + let page_addr = proc.alloc_pages(1, (libc::PROT_READ | libc::PROT_EXEC) as u64)?; + println!( + "{GREEN}[plt hook]{RESET} allocated page is at {:#016x}", + page_addr + ); + + let payload = payload(&proc, page_addr, got_item)?; + if payload.len() > 0x1000 { + return Err(Box::::from("payload exceeds 0x1000 bytes")); + } + + proc.write_memory_ptrace(page_addr as usize, &payload)?; + println!( + "{GREEN}[plt hook]{RESET} wrote {} bytes of instructions/data to {:#016x}", + payload.len(), + page_addr + ); + proc.write_memory_ptrace(got_item_ptr as usize, &page_addr.to_le_bytes())?; + println!( + "{GREEN}[plt hook]{RESET} rewrote got item at {:#016x} to {:#016x}, old value is {:#016x}", + got_item_ptr, page_addr, got_item + ); + + Ok(()) +} + +pub fn inline_hook( + proc: &Process, + module: &str, + symbol: &str, + payload: F, // With stdcall, the payload has to clean the stack. But that's uncommon. +) -> Result<(), Box> +where + F: Fn(&Process, u64, u64) -> Result, Box>, +{ + let remote_addr = proc.find_remote_proc(module, symbol).context("Unable to find proc")?; + + println!("{GREEN}[memory map]{RESET} remote proc addr = {:#016x}", remote_addr); + + let page_addr = proc.alloc_pages(2, (libc::PROT_READ | libc::PROT_EXEC) as u64)?; + println!( + "{GREEN}[inline hook]{RESET} allocated page is at {:#016x}", + page_addr + ); + + let payload_addr = page_addr + 32; + let gateway_addr = page_addr; + + let payload = payload(&proc, payload_addr, gateway_addr)?; + if payload.len() > 0x2000 - 32 { return Err(Box::::from("payload exceeds limit bytes")); } + proc.write_memory_ptrace(payload_addr as usize, &payload)?; + println!("{GREEN}[inline hook]{RESET} wrote payload to {:#016x}", payload_addr); + + let jmp_inst = assemble(remote_addr, |asm| { + asm.jmp(payload_addr)?; + Ok(()) + })?; + + let (gateway_sz, gap_sz) = proc.disassemble(remote_addr, 128, |inst| { + let mut sum_inst_size = 0u32; + + for i in inst.iter() { + println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string()); + if (sum_inst_size as usize) < jmp_inst.len() { + sum_inst_size += i.bytes().len() as u32; + } else { + break; + } + } + + if (sum_inst_size as usize) < jmp_inst.len() { + return Err(Box::::from( + "Unable to find a suitable instruction boundary.", + )); + } + + let boundary = remote_addr + sum_inst_size as u64; + + // Moving instructions to other locations for execution is !NOT! always safe, + // but for library functions, this usually works, + // because the first few instructions are usually endbr64, push ebp or something like that. + let mut gateway = proc.read_memory_vm(remote_addr as usize, sum_inst_size as usize)?; + + println!("{GREEN}[inline hook]{RESET} instruction boundary located at {:#016x}", boundary); + + gateway.append(&mut assemble(gateway_addr + gateway.len() as u64, |asm|{ + asm.jmp(boundary)?; + Ok(()) + })?); + + proc.write_memory_ptrace(gateway_addr as usize, &gateway)?; + println!("{GREEN}[inline hook]{RESET} {} bytes of gateway code write to {:#016x}",gateway.len(), gateway_addr); + Ok((gateway.len(), sum_inst_size)) + })?; + + proc.disassemble(gateway_addr, gateway_sz as u64, |inst| { + for i in inst.iter() { + println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string()); + } + Ok(()) + })?; + + proc.write_memory_ptrace(remote_addr as usize, &vec![0x90u8; gap_sz as usize])?; + proc.write_memory_ptrace(remote_addr as usize, &jmp_inst)?; + + println!("{GREEN}[inline hook]{RESET} wrote springboard to {:#016x}", remote_addr); + + proc.disassemble(remote_addr, gap_sz as u64, |inst| { + for i in inst.iter() { + println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string()); + } + Ok(()) + })?; + + proc.disassemble(remote_addr + gap_sz as u64, 32, |inst| { + for i in inst.iter() { + println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string()); + break; + } + Ok(()) + })?; + + Ok(()) +} diff --git a/01/project-hbj-hook/src/main.rs b/01/project-hbj-hook/src/main.rs index 5c3b3b5..d033645 100644 --- a/01/project-hbj-hook/src/main.rs +++ b/01/project-hbj-hook/src/main.rs @@ -1,76 +1,24 @@ + +// main.rs + use std::error::Error; -use std::ffi::CString; use anyhow::Context; use iced_x86::code_asm::*; use crate::map::MemoryMap; use crate::processes::{get_pid_by_name, Process}; use nix::unistd::Pid; - use libc::user_regs_struct; use nix::sys::ptrace; - use crate::asm::assemble; const GREEN: &str = "\x1b[32m"; const RESET: &str = "\x1b[0m"; -mod disassembly; mod elf; mod map; mod processes; mod asm; - -pub fn plt_hook(proc: &Process, map: &MemoryMap, symbol: &str, payload: F) - -> Result<(), Box> -where - F: Fn(&Process, u64, u64) -> Vec -{ - let bias = map.module_base_address(&proc.get_exe()?).unwrap_or(0); - let got_item_ptr = proc.find_got_pointer_plt(symbol).context("Unable to find symbol")?; - - println!("{GREEN}[memory map]{RESET} Bias is {:#016x}", bias); - - let got_item_byte: [u8; 8] = proc.read_memory_vm(got_item_ptr as usize, 8)? - .try_into() - .map_err(|_| "Failed to convert Vec to array")?; - let got_item = u64::from_le_bytes(got_item_byte); - println!( - "{GREEN}[memory map]{RESET} got_item = {:#016x}", got_item - ); - - // Alloc r-x private memory - let regs = ptrace::getregs(proc.get_pid())?; - let r = proc.execute_once_inplace(&assemble(regs.rip as u64, |asm| { - asm.mov(rax, 9u64)?; // Syscall 9 (mmap) - - asm.mov(rdi, 0u64)?; // Addr - asm.mov(rsi, 0x1000u64)?; // Length, we alloc a page (4K) - asm.mov( - rdx, - (libc::PROT_READ | libc::PROT_EXEC) as u64, - )?; // Set protect to rwx - asm.mov(r10, (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64)?; // Private and anonymous - asm.mov(r8, 01i64)?; // Fd (-1 because we want anonymous) - asm.mov(r9, 0u64)?; // Offset - - asm.syscall()?; // Syscall interrupt - Ok(()) - })?)?; - - let page_addr = r.rax as u64; - println!("{GREEN}[trace]{RESET} allocated page is at {:#016x}", page_addr); - - let payload = payload(&proc, page_addr, got_item); - if payload.len() > 0x1000 { return Err(Box::::from("payload exceeds 0x1000 bytes")); } - - proc.write_memory_ptrace(page_addr as usize, &payload)?; - println!("{GREEN}[trace]{RESET} wrote {} bytes of instructions/data to {:#016x}", payload.len(), page_addr); - proc.write_memory_ptrace(got_item_ptr as usize, &page_addr.to_le_bytes())?; - println!("{GREEN}[trace]{RESET} rewrote got item at {:#016x} to {:#016x}, old value is {:#016x}", - got_item_ptr, page_addr, got_item); - - Ok(()) -} +mod hooks; fn main() -> Result<(), Box> { // Find our target program @@ -107,8 +55,9 @@ fn main() -> Result<(), Box> { // Do inject here - plt_hook(&process, &map, "write", |proc, addr, old| { + hooks::inline_hook(&process, "/usr/lib/libc.so.6", "write", |_proc, addr, old| { let inst = assemble(addr, |asm| { + asm.endbr64()?; asm.push(rdi)?; asm.push(rsi)?; asm.push(rdx)?; @@ -136,7 +85,7 @@ fn main() -> Result<(), Box> { payload.splice(0x800..(0x800 + 7), Vec::from("[hook] ".as_bytes())); payload.splice(0x810..(0x810 + 2), Vec::from("\r\n".as_bytes())); - return payload; + Ok(payload) })?; // End inject logics diff --git a/01/project-hbj-hook/src/map.rs b/01/project-hbj-hook/src/map.rs index e120905..cfc4bca 100644 --- a/01/project-hbj-hook/src/map.rs +++ b/01/project-hbj-hook/src/map.rs @@ -70,6 +70,7 @@ impl MemoryMap { Self { regions } } + #[allow(unused)] pub fn first_rw_segment(&self, module: &str) -> Option<(u64, u64)> { self.regions .iter() @@ -77,6 +78,7 @@ impl MemoryMap { .map(|r| (r.start_addr, r.end_addr)) } + #[allow(unused)] pub fn first_exec_segment(&self, module: &str) -> Option<(u64, u64)> { self.regions .iter() @@ -84,7 +86,7 @@ impl MemoryMap { .map(|r| (r.start_addr, r.end_addr)) } - + #[allow(unused)] pub fn module_base_address( &self, module: &str // Full path of module, like '/usr/lib/libc.so.6' @@ -106,6 +108,7 @@ impl MemoryMap { Some(map_item.start_addr - first_load.p_vaddr) } + #[allow(unused)] pub fn collect_module(&self, module: &str) -> Vec { let r = self.regions.iter() diff --git a/01/project-hbj-hook/src/processes.rs b/01/project-hbj-hook/src/processes.rs index 6c44b32..8594ea9 100644 --- a/01/project-hbj-hook/src/processes.rs +++ b/01/project-hbj-hook/src/processes.rs @@ -4,17 +4,18 @@ use std::collections::HashMap; use std::fs; +use anyhow::Context; use nix::sys::uio::{process_vm_readv, RemoteIoVec, process_vm_writev}; use nix::unistd::Pid; use std::error::Error; -use std::ffi::CString; + use std::io::{IoSliceMut, IoSlice}; +use capstone::{arch, Capstone, Instructions}; +use capstone::arch::{BuildsCapstone, BuildsCapstoneSyntax}; use goblin::elf64::{header, section_header}; use iced_x86::code_asm::{r10, r8, r9, rax, rdi, rdx, rsi}; -use iced_x86::Instruction; use nix::sys::ptrace; - -use libc::{dlsym, user_regs_struct, RTLD_NEXT}; +use libc::{user_regs_struct}; use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; use crate::asm::assemble; use crate::elf::ExecuteLinkFile; @@ -149,6 +150,7 @@ impl Process Some(f) } + #[allow(unused)] pub fn get_pid(&self) -> Pid { self.pid.clone() } pub fn get_exe(&self) -> Result> @@ -188,6 +190,7 @@ impl Process } } + #[allow(unused)] pub fn write_memory_vm(&self, mut start_addr: usize, mut data: &[u8]) -> Result> { let mut total_written = 0usize; @@ -245,6 +248,7 @@ impl Process Ok(written) } + #[allow(unused)] pub fn find_remote_proc( &self, module: &str, // Full path of module, like '/usr/lib/libc.so.6' @@ -280,12 +284,19 @@ impl Process Some(r_sym.r_offset + self.map.module_base_address(&exe)?) } - pub fn execute_once_inplace(&self, payload: &[u8]) -> Result> + pub fn execute_once_inplace( + &self, + payload_builder: F + ) + -> Result> + where F: Fn(u64) -> Option> { // Save context let regs = ptrace::getregs(self.pid)?; + let payload = payload_builder(regs.rip).context("payload build failed")?; + let buffer = self.read_memory_vm(regs.rip as usize, payload.len() + 1)?; - let instruction = [payload, &[0xccu8]].concat(); + let instruction = [&payload as &[u8], &[0xccu8]].concat(); self.write_memory_ptrace(regs.rip as usize, &instruction)?; @@ -303,4 +314,49 @@ impl Process ptrace::setregs(self.pid, regs)?; Ok(r) } -} \ No newline at end of file + + pub fn alloc_pages(&self, count: u64, permissions: u64) + -> Result> + { + // Alloc r-x private memory + let r = self.execute_once_inplace(|addr| { + let r = assemble(addr, |asm| { + asm.mov(rax, 9u64)?; // Syscall 9 (mmap) + + asm.mov(rdi, 0u64)?; // Addr + asm.mov(rsi, 0x1000u64 * count)?; // Length, we alloc a page (4K) + asm.mov(rdx, permissions, )?; + asm.mov(r10, (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64)?; // Private and anonymous + asm.mov(r8, -1i64)?; // Fd (-1 because we want anonymous) + asm.mov(r9, 0u64)?; // Offset + + asm.syscall()?; // Syscall interrupt + Ok(()) + }).ok()?; + + Some(r) + })?; + + Ok(r.rax as u64) + } + + pub fn disassemble(&self, addr: u64, size: u64, callback: F) + -> Result> + where F: Fn(&Instructions) -> Result> + { + let instruction = self.read_memory_vm(addr as usize, size as usize)?; + + let cs = Capstone::new() + .x86() + .mode(arch::x86::ArchMode::Mode64) + .syntax(arch::x86::ArchSyntax::Att) + .detail(true) + .build()?; + + let inst = cs.disasm_all(&instruction, addr)?; + + let r = callback(&inst)?; + + Ok(r) + } +} diff --git a/01/project-hbj/target b/01/project-hbj/target index f8e719b..f7598cf 100755 Binary files a/01/project-hbj/target and b/01/project-hbj/target differ