To be simple, we have two similar functions:
void f1()
{
printf("%d", 123);
}
void f2()
{
printf("%d", 124);
}
Now we call f1 in main and it prints 123. When it is compiled, the disassembly of f1
may be like:
08048424 <f1>:
8048424: 55 push %ebp
8048425: 89 e5 mov %esp,%ebp
8048427: 83 ec 18 sub $0x18,%esp
804842a: b8 40 86 04 08 mov $0x8048640,%eax
804842f: c7 44 24 04 7b 00 00 movl $0x7b,0x4(%esp)
8048436: 00
8048437: 89 04 24 mov %eax,(%esp)
804843a: e8 05 ff ff ff call 8048344 <printf@plt>
804843f: c9 leave
8048440: c3 ret
The machine code of f2 is similar to the f1's.
Now I want to replace the f1 with the machine code of f2 at the runtime. I use memcpy(f1, f2, SIZE_OF_F2_MACHINE_CODE). Sure it comes the problem — a segment fault.
Now I want to know if there exists a solution to solve this problem. This is a common C program. As I know, we can use such code below to set page writable in Linux kernel:
int set_page_rw(long unsigned int addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
if(pte->pte & ~_PAGE_RW)
pte->pte |= _PAGE_RW
}
but it does not work at normal Linux C programs. Then what works?
Don't overwrite the procedure, overwrite the symbol reference in the symbol table instead. That does require dynamic linkage. Alternatively you can overwrite the call(s) to the function with a call to the other function, but things like NX
bits may come to stand in your way. Self-modifying code is generally frowned upon.
Why do you ask? If your wish is just to eventually be able to call some functions whose code was generated by the same process, you can proceed differently:
- Always use function pointers to call such dynamically generated functions; my suggestion is, for readability reasons, to
typedef
their signature before declaring the pointer, see this answer. Generate the function and get a pointer to it.
You could for example generate a C source file
generated.c
, fork a process, perhaps withsystem("gcc -fPIC -O -shared generated.c -o generated.so");
to compile it, thendlopen("./generated.so", RTLD_GLOBAL)
and get the pointer of the generated function withdlsym
. See dlopen(3) man page for details. FYI, MELT is doing that.You could also generate the machine code of the function in memory (probably obtained with mmap(2) using
PROT_EXEC
flag). Several JIT (just-in-time translation) libraries are available: GNU lightning (quick generation of slow running machine code), myjit, libjit, LLVM (slow generation of optimized machine code), LuaJIT...
If you really wish to overwrite some existing function code, you might do that, but it requires a big lot of care and is painful (e.g. because the new function code needs more space than the old one, and also because of relocation issues). Use the mmap(2) and/or mprotect(2) syscalls to get permission for such tricks. But be prepared for debugging nightmares. You may want to script your gdb
debugger with your python scripts.
For kernel modules the story is different. I heard that some network related kernel code (iptables
perhaps?) may use JIT techniques to generate machine code and run it.
I tried to find an answer to you, but failed. What I actually succeeded to do - is only to simplify the questionable code:
void f1( )
{
}
int main( )
{
*(char*) f1 = *(char*) f1;
return( 0 );
}
And yes, it fails on segmentation fault (in gcc) or on memory access violation (in MS VC).
EDIT:
Actually I succeeded to do what you want
(basing on the answer of Basile Starynkevitch). But only for x86, only in gcc, and only for your specific example. Below are several code examples.
First - the simplified example.
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
void f1( )
{
}
int main( )
{
int rc;
int pagesize;
char *p;
f1( );
pagesize = sysconf( _SC_PAGE_SIZE );
printf( "pagesize=%d (0x%08X).\n", pagesize, pagesize );
if( pagesize == -1 )
return( 2 );
p = (char*) f1;
printf( "p=0x%08X.\n", p );
p = (char*) ((size_t) p & ~(pagesize - 1));
printf( "p=0x%08X.\n", p );
rc = mprotect( p, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC );
printf( "rc=%d.\n", rc );
if( rc != 0 )
return( 2 );
printf( "'mprotect()' succeeded.\n" );
*(char*) f1 = *(char*) f1;
printf( "Write succeeded.\n" );
f1( );
printf( "Call succeeded.\n" );
return( 0 );
}
You compile this and launch once. It will fail, but you will know page size. Let's say, it is 4096. Then you compile this example like this:
gcc a1.c -falign-functions=4096
And it should work.
Output:
pagesize=4096 (0x00001000).
p=0x00402000.
p=0x00402000.
rc=0.
'mprotect()' succeeded.
Write succeeded.
Call succeeded.
Now the advanced example:
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
//extern void f1( void ) __attribute__(( aligned( 4096 ) ));
__asm__( ".text" );
__asm__( ".align 4096" );
void f1( void )
{
printf( "%d\n", 123 );
}
void f2( void )
{
printf( "%d\n", 124 );
}
int main( void )
{
int rc;
int pagesize;
char *p;
int i;
printf( "f1=0x%08X.\n", f1 );
printf( "f2=0x%08X.\n", f2 );
f1( );
f2( );
pagesize = sysconf( _SC_PAGE_SIZE );
printf( "pagesize=%d (0x%08X).\n", pagesize, pagesize );
if( pagesize == -1 )
return( 2 );
p = (char*) f1;
printf( "p=0x%08X.\n", p );
p = (char*) ((size_t) p & ~(pagesize - 1));
printf( "p=0x%08X.\n", p );
rc = mprotect( p, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC );
printf( "rc=%d.\n", rc );
if( rc != 0 )
return( 2 );
printf( "'mprotect()' succeeded.\n" );
for( i = 0; i < (size_t) f2 - (size_t) f1; i++ ) {
if( ((char*) f2)[ i ] == 124 ) {
printf( "i=%d.\n", i );
((char*) f1)[ i ] = ((char*) f2)[ i ];
}
}
//memcpy( f1, f2, (size_t) f2 - (size_t) f1 );
printf( "Write succeeded.\n" );
f1( );
f2( );
printf( "Call succeeded.\n" );
return( 0 );
}
You can't use "memcpy()" here (it is commented) because calls to "printf()" inside "f1()" and "f2()" are relative, not absolute. And I could not find how to make them absolute ("neither -fPIC", nor "-fno-PIC" worked in my case). If you don't have relative function calls in "f1()" and "f2()", I believe you can use "memcpy()" (but I did not try).
You should also use alignment of "f1()" to page size (unless you are sure you have enough code before "f1()" starts). If you have gcc 4.3 and higher, you can use attribute (it is commented because I have gcc v4.1.2). If not, you can use that ugly and not reliable "_asm_".
Output:
f1=0x00402000.
f2=0x0040201E.
123
124
pagesize=4096 (0x00001000).
p=0x00402000.
p=0x00402000.
rc=0.
'mprotect()' succeeded.
i=12.
Write succeeded.
124
124
Call succeeded.
And, of course, that horrible "if( ((char*) f2)[ i ] == 124 )". It serves to distinguish between what should be replaced (the printed number) and what should not (relative references). Clearly, this is very simplified algorithm. You will have to implement your own, suitable for your task.
来源:https://stackoverflow.com/questions/12947388/is-there-a-way-to-modify-the-code-of-a-function-in-a-linux-c-program-at-runtime