2010-01-15 4 views
4

Je jette ma RAM (un morceau de celui-ci - segment de code seulement) afin de trouver où est la fonction C étant placé. Je n'ai pas de fichier map et je ne sais pas exactement ce que font les routines boot/init.C - Comment créer un motif dans un segment de code pour le reconnaître en mémoire?

Je charge mon programme en RAM, puis si je vider la RAM, il est très difficile de trouver exactement quelle est la fonction. Je voudrais utiliser différents modèles construits dans la source C, pour les reconnaître dans le vidage de la mémoire.

J'ai tryed pour commencer toutes les fonctions avec différentes première variable contenant le nom de la fonction, comme:

char this_function_name[]="main"; 

mais cela ne fonctionne pas, parce que cette chaîne sera placée dans le segment de données.

J'ai un simple processeur RISC 16 bits et un compilateur expérimental propriétaire (pas de GCC ou aucun connu). Le système a 16 Mo de RAM, partagé avec d'autres applications (bootloader, downloader). Il est presque impossible de trouver une séquence unique de N NOPs ou smth. comme 0xABCD. Je voudrais trouver toutes les fonctions dans la RAM, j'ai donc besoin d'identificateurs uniques de fonctions visibles dans RAM-dump.

Quel serait le meilleur modèle pour le segment de code?

+0

Quel compilateur/OS? –

+0

@Richard: plateforme embarquée, CPU expérimentale et compilateur – psihodelia

+1

Ugh. Obtenez un fichier de carte comme si votre vie en dépendait. –

Répondre

7

Si c'était moi, j'utiliserais la table des symboles, par ex. "nm a.out | grep main". Obtenez la véritable adresse de toute fonction que vous voulez.

Si vous n'avez vraiment aucune table de symboles, créez la vôtre.

struct tab { 
    void *addr; 
    char name[100]; // For ease of searching, use an array. 
} symtab[] = { 
    { (void*)main, "main" }, 
    { (void*)otherfunc, "otherfunc" }, 
}; 

Recherchez le nom et l'adresse le précédera immédiatement. Aller à l'adresse ;-)

+0

Non, je n'ai pas nm ou tout autre outils utiles, car il est très rare compilateur et CPU. – psihodelia

+0

Belle idée, cette table de symbole "de votre choix". ++ à vous –

+0

Cela ne fonctionne pas, car je ne peux vider que le segment de code. Un fichier binaire est également dans un format propriétaire, je ne peux pas le lire. – psihodelia

1

Les constantes numériques sont placées dans le segment de code, codé dans les instructions de la fonction. Vous pouvez donc essayer d'utiliser des nombres magiques comme 0xDEADBEEF et ainsi de suite.

I.e. voici la vue du démontage d'une fonction simple C avec Visual 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 

Vous pouvez voir le 0xDEADBEEF en faire la source de la fonction. Notez que ce que vous voyez réellement dans l'exécutable dépend de l'endianness du processeur (tx Richard).

Ceci est un exemple x86. Mais les processeurs RISC (MIPS, etc) ont des instructions qui se déplacent immédiatement dans les registres - ces immédiates peuvent aussi avoir des valeurs reconnaissables spéciales (bien que seulement 16 bits pour MIPS, IIRC).


Psihodelia - il devient de plus en plus difficile à attraper votre intention. Est-ce juste une seule fonction que vous voulez trouver? Alors ne pouvez-vous pas placer 5 NOP l'un après l'autre et les chercher? Contrôlez-vous le compilateur/assembleur/éditeur de liens/chargeur? Quels outils sont à votre disposition?

+0

Non, ça ne marche pas dans mon cas. Je n'ai pas de CPU x86, mais un simple CPU RISC. – psihodelia

+0

Assurez-vous de chercher EFBEADDE sur un x86. ;-) Aussi, n'allumez pas trop d'optimisations. –

+0

@psihodelia: même les processeurs RISC ont des instructions qui placent les immédiates dans les registres. –

3

Si votre compilateur a inm, vous pouvez l'utiliser pour créer un motif. Écrivez quelques NOP que vous pouvez facilement reconnaître par opcodes dans une décharge de mémoire:

MOV r0,r0 
MOV r0,r0 
MOV r0,r0 
MOV r0,r0 
+1

Par mesure de sécurité, placez une instruction de saut inconditionnelle au début du bloc d'assemblage pour ignorer le tout. De cette façon, vous pouvez mettre ce que vous voulez à l'intérieur (en général, j'utilise les opcodes qui, une fois déversés, deviennent des valeurs ASCII qui épellent quelque chose) sans se soucier de modifier l'exécution du programme. Oh, et chaque fois que vous faites quelque chose comme ça, assurez-vous de désactiver toutes les optimisations du compilateur (bien que certains compilateurs les désactivent automatiquement pour les fonctions avec l'assemblage en ligne). – bta

+0

Merci pour le tour avec le saut inconditionnel! A propos de l'assemblage en ligne: Je pense qu'il est utilisé pour les optimisations manuelles et ne devrait jamais être optimisé par les compilateurs. –

1

Comme vous l'avez dit, ceci:

char this_function_name[]="main"; 

...finira par définir un pointeur dans votre pile sur un segment de données contenant la chaîne. Cependant, ceci:

char this_function_name[]= { 'm', 'a', 'i', 'n' }; 

... sera probablement mis tous ces octets dans votre pile de sorte que vous serez en mesure de reconnaître la chaîne dans votre code (je viens d'essayer sur ma plate-forme).

Hope this helps

+0

Non, franchement, il va dans le segment de données dans mon cas. – psihodelia

+0

@psihodelia Wow ... J'ai essayé dans deux plates-formes différentes à la fois en utilisant GCC et cela a fonctionné dans les deux cas. Cependant, je me suis assuré que je n'avais aucune optimisation activée. Je ne suis pas sûr que GCC optimise une telle construction. Construisez-vous sans aucune optimisation? – figurassa

1

Que diriez-vous d'une approche complètement différente de votre vrai problème, qui est de trouver un bloc particulier de code: utilisation diff.

Compilez le code une fois avec la fonction en question incluse, et une fois avec elle en commentaire. Produire des dumps RAM des deux. Ensuite, comparez les deux vidages pour voir ce qui a changé - et ce sera le nouveau bloc de code. (Vous devrez peut-être faire un peu de traitement des dumps pour supprimer les adresses mémoire afin d'obtenir un diff propre, mais l'ordre des instructions devrait être le même dans les deux cas.)

1

vider sa propre adresse. Quelque chose comme ceci:

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() ; 
} 

En faisant fnaddr_dummy statique, la décharge est effectué une fois par fonction. Évidemment, vous devrez adapter fnaddr() pour prendre en charge tous les moyens de sortie ou de journalisation que vous avez sur votre système. Malheureusement, si le système effectue une initialisation paresseuse, vous obtiendrez uniquement les adresses des fonctions réellement appelées (ce qui peut être suffisant).

0

Vous pouvez commencer chaque fonction avec un appel à la même fonction factice comme:

identifyFunction void (identifiant unsigned int) { }

Chacune de vos fonctions appelleraient la identifyFunction fonction avec un paramètre différent (1, 2, 3, ...). Cela ne vous donnera pas un fichier mapfile magique, mais lorsque vous inspecterez le vidage de code, vous devriez être capable de trouver rapidement l'identité de identifyFunction car il y aura beaucoup de sauts à cette adresse. Prochain scan pour ceux qui sautent et vérifie avant le saut pour voir quel paramètre est passé. Ensuite, vous pouvez créer votre propre mapfile. Avec certains scripts, cela devrait être assez automatique.

Questions connexes