我试图找到你的答案,但失败了。我实际成功做的只是简化有问题的代码:
void f1( )
{
}
int main( )
{
*(char*) f1 = *(char*) f1;
return( 0 );
}
是的,它因分段错误(在 gcc 中)或内存访问冲突(在 MS VC 中)而失败。
编辑:
其实我成功地做了你想做的
(基于 Basile Starynkevitch 的回答)。但仅适用于 x86,仅适用于 gcc,并且仅适用于您的具体示例。下面是几个代码示例。
首先 - 简化的例子。
#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 );
}
你编译它并启动一次。它会失败,但你会知道页面大小。假设是4096。然后你像这样编译这个例子:
gcc a1.c -falign-functions=4096
它应该工作。
输出:
pagesize=4096 (0x00001000).
p=0x00402000.
p=0x00402000.
rc=0.
'mprotect()' succeeded.
Write succeeded.
Call succeeded.
现在是高级示例:
#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 );
}
您不能在此处使用“ memcpy() ”(已注释),因为在“ f1() ”和“ f2() ”中对“ printf() ”的调用是相对的,而不是绝对的。而且我找不到如何使它们成为绝对的(“ -fPIC ”和“ -fno-PIC ”在我的情况下都不起作用)。如果您在“ f1() ”和“ f2() ”中没有相关的函数调用,我相信您可以使用“ memcpy() ”(但我没有尝试)。
您还应该使用“ f1() ”与页面大小的对齐方式(除非您确定在“ f1() ”开始之前有足够的代码)。如果你有 gcc 4.3 及更高版本,你可以使用属性(因为我有 gcc v4.1.2,所以有注释)。如果没有,您可以使用那个丑陋且不可靠的“ _asm_ ”。
输出:
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.
而且,当然,那个可怕的“ if( ((char*) f2)[ i ] == 124 ) ”。它用于区分应该替换的内容(打印的数字)和不应该替换的内容(相对引用)。显然,这是一个非常简化的算法。您将必须实现自己的,适合您的任务。