C - 如何在代码段中创建模式以在内存转储中识别它?

时间:2010-01-15 12:00:54

标签: c assembly embedded

我转储我的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中可见的函数的唯一标识符。

代码段的最佳模式是什么?

7 个答案:

答案 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。通过一些脚本,这应该是相当自动的。