Suppose we have two programs. The program test is in binary form (ELF file), and the second program is a shred object file, say f.so. I want to hook some instruction in the program test and move execution to particular instructions located in f.so file.? I do not want to make a function call. For example, I want to hook an instruction (binary instruction) in the test program and remove its 4 byte (ARM 32-bit arch) and write a new branch instruction that points to an instruction located in f.so shared object program.!! is that possible using LD_PRELOAD?
Edit 1
Based on the helpful information provided (thanks in advance), I performed some experiments and I will explain that in some details (Sorry to provide pics not text) ...
Before instrumenting the target binary file, i.e, test, as this task maybe not easy particularly when we want to overwrite some bytes with new branch instruction. I performed some experiments to hook the control flow of the test program and to see if it is possible to move the execution to some place in f.so file. To this end and for simplicity, I used gdb as it is easy to modify the program counter register to points to other place.
In the following, I performed my test on binary files compiled for x86 architecture not for ARM, however, same task can be ported to ARM binaries.
The following image shows the core dump (disassembly) of (on left) shared object file f.so and (on right side) the target program, test.
My first question is why is there a difference in the memory addresses where the program test and f.so were loaded?
Then, I moved to GDB environment to patch a particular instruction in test binary and move the control (execution) to an arbitrary location in f.so. I used the following command and tested the code to ensure it run correctly by LD_PRELOAD in GDB environment
(gdb) set environment LD_PRELOAD=./f.so
(gdb) file ./test
Reading symbols from ./test...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/null/Desktop/Edit_elf/test
hello
19
[Inferior 1 (process 5827) exited normally]
(gdb)
Ok, now I set a breakpoint at main function in test binary to check weather our f.so is really loaded. After breakpoint I disassembled the function func1 in f.so ... it looks like below:
As we can see, the f.so is loaded in some place in memory (I really don't know) ... these addresses is same as presented in image 1 with a prefix =b7fd2 (for example in image 1 the address of first instruction in f.so is 520, and the address of the same instruction in image 2 is b7fd2520). I don't know precisely the reason for that but I think it is due to virtual memory mapping and related things.
I listed the value of registers for our test program (it is stopped at predefined breakpoint) and changed the value of program counter (eip in x86) to move the execution after breakpoint to some place in f.so.
Then I let the test program to continue its execution. Now it is expected that the CPU will move the execution to the place where eip points to, i.e., 0xb7fd2526.
Wow ... as we can see from the above image, we are able to move the execution from test to some place in shared library loaded by LD_PRELOAD. However...
Why there is a difference in address map of test and f.so? In my final goal, I want to patch the test binary and remove some bytes and overwrite new bytes (machine code) to inject a branch machine instruction to point to shared object file. Therefore, I need to calculate the target address of branch instruction correctly to embedded it with written bytes (machine code) and the target address AFAIK is calculated by considering the address of current instruction with some constant value. So I feel it is hard to create a machine code for a branch instruction to move the control or execution from the memory space (like 804843b) to (b7fd2526) ...
is that possible using LD_PRELOAD?
Sure: the LD_PRELOAD
ed library will have its initializers run at load time.
One of these initializers can binary-patch instructions inside the main executable (by doing mprotect(..., PROT_WRITE)
+ overwrite instruction bytes + mprotect(..., PROT_EXEC)
).
If the main binary is non-PIE
, you could hard-code the address to patch directly into the library.
If it is a PIE
binary, you'll have to find where in memory it was loaded and adjust the offset accordingly.