bashcommand-linehexida

How to find the memory address offsets from IDA in binary file


I am trying to write a command line application which can modify a .so file using hex values at specific addresses.

I'm using IDA Demo and HxD Hex Editor to attain the addresses which need to be updated, however, each time I try to modify the file, no matter which language I use (bash scripting, php, python) every time I edit the file, it updates from the wrong address offsets which I have from IDA and HxD Hex Editor.

I have seen a few posts regarding this, but as of yet, none have been able to give a definitive answer as to how to attain the addresses which HxD and IDA have no issue finding :(

In python I have used the mmap function to attempt this using the below;

import mmap
import contextlib
import os

filesize = os.stat("filetomodify.so").st_size
print int(filesize)


with open("filetomodify.so", 'r+b') as f:
    with contextlib.closing(mmap.mmap(f.fileno(),  access=mmap.ACCESS_WRITE)) as m:
        m[0x173596] = "FF 20"
        m[0x18D88E] = "FF 20"
        m.close()

This uses the os library to determine the size of the file which is returned as 10025936, however, each time I add the length=filesize argument between f.fileno() and access=mmap.ACCESS_WRITE I always get a mmap.error: [Errno 22] Invalid argument Error Message, but when I omit the argument, I get a m: TypeError: Required argument 'length' (pos 2) not found

The bash script I have used is one which I found here, which modifies the file okay, but updates the wrong addresses;

#!/bin/bash

# param 1: file
# param 2: offset
# param 3: value
# param 4: Size of Bytes
function replaceByte() {
    printf "$(printf '\\x%02X' $3)" | dd of="$1" bs=$4 seek=$2 count=1 conv=notrunc &> /dev/null
}

# Usage:
replaceByte 'filetomod.so' "0x173596" "95 E5 0A 2F 66 1E 32 EE 4C B8 9A 6E BD EC 01" 15

Calling the function as described above updates the file and writes it ok, but when I do a find for the updated byte string (e.g, 95 E5 0A 2F 66 1E 32 EE 4C B8 9A 6E BD EC 01 -- Just a long unique string which you would not expect to find in a file) it has been inserted at the incorrect address, or it tells me that the offset is outside of the file size.

Lastly, the php code I used (which I wasn't expecting much success with anyway) was;

<?php
$fp = fopen('binary_file.bin', 'w+b');
fseek($fp, 173596, );   
fwrite($fp, '95 E5 0A 2F 66 1E 32 EE 4C B8 9A 6E BD EC 01');
fclose($fp);
?>

However, rather than editing the Hex values of the binary file, this added the string as a string to the binary file at HxD Address 0002A61C with the hex code as 39 35 20 45 35 20 30 41 20 32 46 20 36 36 20 31 45 20 33 32 20 45 45 20 34 43 20 42 38 20 39 41 20 36 45 20 42 44 20 45 43 20 30 31 which had the output of 95 E5 0A 2F 66 1E 32 EE 4C B8 9A 6E BD EC 01

Any help with this would be greatly appreciated. My only requirement for the answers would be that it can be run from command line, whether php, bash, python, java etc.

Thank you in advance.


Solution

  • After some trials in a virtual machine running Centos 6.5, I got it

    function replaceByte() {
        printf "$(printf '\\x%02X' $3)" |
        dd of="$1" ibs=1 count=1 obs=1 seek="$2" conv=notrunc &> /dev/null
    }
    

    The problem is the unit of the seek parameter: BLOCKS, not BYTES.

    When you change the default block size, both input block size and output block size are changed. Therefore the seek parameter refers to blocks whose size is the value of bs parameter.

    To solve this, you must set the input block size ibs as youd did but the output block size obs must be set to 1. Otherwise, as output is a disk file, the obs value is set to 512 bytes (the disk block size).

    You can try the following test to check the size of the output block is 512, not 1:

    1. Create a text file (file.txt) containing an entire blank line (for example, 30 white spaces)
    2. Check that the size of the file is near 30 bytes.
    3. Run echo -e '\x41' | dd if=file.txt ibs=1 count=1 seek=3 conv=notrunc (0x41 is the ASCII code for A)
    4. Check that the size of file.txt has changed: 1537 bytes (3 blocks of 512 bytes per block plus 1 -- 3*512+1)

    5. As file.txt is "corrupted", repeat the steps 1 and 2.

    6. Run echo -e '\x41' | dd if=file.txt ibs=1 count=1 obs=1 seek=3 conv=notrunc
    7. Check that the size of file.txt has not changed.
    8. Look the contents of the file and check that there is an 'A' replacing the 4th blank.

    Best regards.

    P.D: this funcion replaceByte() should work fine for just ONE byte. If you need to change several contiguous bytes in a single call, you must replace the printf, perhaps, with a loop... and the ibs=1 with the proper value.