Why do Powershell and the WinAPI recognize huge numbers as negative numbers when seeking in a file?

-2

I'm studying the NTFS filesystem, and I have a weird problem when trying to seek in \\.\PhysicalDrive0 with a huge number like 0xb2ec0000 (3001810944). The $MFT of my Windows partition is supposed to reside at that offset (which is the case).

When I was seeking from that number (even from the decimal form: 3001810944), SetFilePointer returned ERROR_NEGATIVE_SEEK, so I decided to learn how negative numbers work in hexadecimal.

From that topic, I understood why 0xb2ec0000 was considered as negative, because it starts with b. Powershell also recognizes it as negative:

PS A:\core> 0xb2ec0000
-1293156352

It can't only be a negative number, if we exclude the existence of negative numbers in hexadecimal, from some hexadecimal to decimal converters we realize that it's also equal to 3001810944.

To successfully seek at offset 0xb2ec0000, I decided to seek two times of 3001810944 / 2 bytes (1500905472), it worked fine, but it's a problem if some natural numbers are considered as negative if they aren't in the given context, it doesn't seem normal at all.

And concerning the division, if I decide to do 0xb2ec0000 / 2 it outputs:

PS A:\core> 0xb2ec0000 / 2
-646578176

but

PS A:\core> 3001810944 / 2 
1500905472

Here is the Rust code of the program:

You can reproduce the example by opening \\.\PhysicalDrive0 and seeking at offset 0xb2ec0000:

extern "system"{
    // [...]
    fn CreateFileA(a: *const u8, b: u32, c: u32, d: *mut c_void, e: u32, f: u32, g: *mut c_void) -> *mut c_void;
    fn SetFilePointer(a: *mut c_void, b: i64, c: *mut i32, d: u32) -> i32;  
}
// [...]

let boot = CreateFileA(
    "\\\\.\\PhysicalDrive0\0".as_ptr(),
    25,
    0x00000002 | 0x00000001,
    null_mut(),
    3,
    128,
    null_mut()
);

// [...] Calculating $MFT offset

let mft_offset: i64 = mft_logical_cluster * (bpb.wBytesPerSec * bpb.uchSecPerClust as u16) as i64 + (dsk_info.dwRelativeSector * 512) as i64;

println!("{:x}", mft_offset); // outputs 0xb2ec0000

if(SetFilePointer(boot,mft_offset,null_mut(),0) == -1){
    println!("Error: {}", GetLastError()); // outputs 131
}
winapi
rust
numbers
hex
asked on Stack Overflow Feb 17, 2020 by centipede_ • edited Feb 17, 2020 by Shepmaster

1 Answer

3

You have incorrectly defined SetFilePointer as taking an i64 when it is an i32.

If you take the time to thoroughly read the docs for SetFilePointer, you'll see:

lDistanceToMove

The low order 32-bits of a signed value that specifies the number of bytes to move the file pointer.

If lpDistanceToMoveHigh is not NULL, lpDistanceToMoveHigh and lDistanceToMove form a single 64-bit signed value that specifies the distance to move.

If lpDistanceToMoveHigh is NULL, lDistanceToMove is a 32-bit signed value. A positive value for lDistanceToMove moves the file pointer forward in the file, and a negative value moves the file pointer back.

lpDistanceToMoveHigh

A pointer to the high order 32-bits of the signed 64-bit distance to move.

If you do not need the high order 32-bits, this pointer must be set to NULL.

You will need to split the i64 into two parts and pass each part separately, the high bits as a pointer to a value.

You should not be attempting to write the FFI definitions yourself (because you'll get them wrong). Ideally, use the winapi crate, which has the correct definition for SetFilePointer.

Even better, just use regular Rust types like File and Seek.

answered on Stack Overflow Feb 17, 2020 by Shepmaster

User contributions licensed under CC BY-SA 3.0