我转储我的RAM(它的一部分 - 仅代码段),以便找到放置C函数的位置。我没有map文件,我不知道boot / init例程究竟是做什么的。
我将程序加载到RAM中,然后如果我转储RAM,很难找到确切的功能在哪里。我想在C源代码中使用不同的模式,以便在内存转储中识别它们。
我尝试使用包含函数名称的不同第一个变量启动每个函数,例如:
char this_function_name[]="main";
但它不起作用,因为此字符串将放在数据段中。
我有简单的16位RISC CPU和实验专有编译器(没有GCC或任何众所周知的)。系统具有16Mb的RAM,与其他应用程序共享(引导加载程序,下载程序)。几乎不可能找到一个独特的N NOP或smth序列。像0xABCD。我想在RAM中找到所有函数,所以我需要在RAM-dump中可见的函数的唯一标识符。
代码段的最佳模式是什么?
答案 0 :(得分:7)
如果是我,我会使用符号表,例如“nm a.out | grep main”。获取您想要的任何功能的真实地址。
如果你真的没有符号表,那就自己动手吧。
struct tab {
void *addr;
char name[100]; // For ease of searching, use an array.
} symtab[] = {
{ (void*)main, "main" },
{ (void*)otherfunc, "otherfunc" },
};
搜索名称,地址将紧接在其之前。转到地址。 ; - )
答案 1 :(得分:3)
如果编译器具有内联asm,则可以使用它来创建模式。编写一些NOP指令,您可以通过内存转储中的操作码轻松识别这些指令:
MOV r0,r0
MOV r0,r0
MOV r0,r0
MOV r0,r0
答案 2 :(得分:1)
数字常量放在代码段中,在函数说明中编码。所以你可以尝试使用魔术数字,如0xDEADBEEF等等。
即。这是使用Visual C ++的简单C函数的反汇编视图:
void foo(void)
{
00411380 push ebp
00411381 mov ebp,esp
00411383 sub esp,0CCh
00411389 push ebx
0041138A push esi
0041138B push edi
0041138C lea edi,[ebp-0CCh]
00411392 mov ecx,33h
00411397 mov eax,0CCCCCCCCh
0041139C rep stos dword ptr es:[edi]
unsigned id = 0xDEADBEEF;
0041139E mov dword ptr [id],0DEADBEEFh
你可以看到0xDEADBEEF将它变成函数的源代码。请注意,您在可执行文件中实际看到的内容取决于CPU的字节顺序(tx.Richard)。
这是一个x86示例。但RISC CPU(MIPS等)具有将指令移动到寄存器中的指令 - 这些指令也可以具有特殊的可识别值(尽管MIPS只有16位,IIRC)。
Psihodelia - 抓住你的意图变得越来越难。它只是您想要找到的单一功能吗?那么你不能一个接一个地放置5个NOP并寻找它们吗?你控制编译器/汇编器/链接器/加载器吗?您可以使用哪些工具?
答案 3 :(得分:1)
如你所知,这:
char this_function_name[]="main";
...最终会将堆栈中的指针设置为包含该字符串的数据段。但是,这个:
char this_function_name[]= { 'm', 'a', 'i', 'n' };
...可能会将所有这些字节放在您的堆栈中,这样您就能够识别代码中的字符串(我只是在我的平台上尝试过)。
希望这有帮助
答案 4 :(得分:1)
如何找到一个完全不同的方法来解决您的真正问题,即找到一个特定的代码块:使用差异。
使用所包含的函数编译一次代码,并将其注释掉一次。生成两者的RAM转储。然后,区分两个转储以查看更改的内容 - 这将是新的代码块。 (您可能必须对转储进行某种处理以删除内存地址以获得干净的差异,但在任何一种情况下,指令的顺序应该相同。)
答案 5 :(得分:1)
为什么不让每个函数转储自己的地址。像这样:
void* fnaddr( char* fname, void* addr )
{
printf( "%s\t0x%p\n", fname, addr ) ;
return addr ;
}
void test( void )
{
static void* fnaddr_dummy = fnaddr( __FUNCTION__, test ) ;
}
int main (int argc, const char * argv[])
{
static void* fnaddr_dummy = fnaddr( __FUNCTION__, main ) ;
test() ;
test() ;
}
通过使fnaddr_dummy为静态,每个函数执行一次转储。显然,您需要调整fnaddr()以支持您在系统上的任何输出或日志记录方式。不幸的是,如果系统执行延迟初始化,您将只获得实际调用的函数的地址(这可能足够好)。
答案 6 :(得分:0)
你可以通过调用相同的虚函数来启动每个函数,如:
void identifyFunction(unsigned int identifier) { }
您的每个函数都会使用不同的参数(1,2,3,...)调用identifyFunction函数。这不会给你一个神奇的mapfile,但是当你检查代码转储时,你应该能够快速找到identifyFunction的位置,因为会有很多跳转到该地址。接下来扫描那些跳转并在跳转之前检查以查看传递的参数。然后你可以制作自己的mapfile。通过一些脚本,这应该是相当自动的。