Sur le principe, je suis d'accord avec toi: j'aimerais bien utiliser autre chose qu'IDA, ne serait-ce que pour pouvoir l'améliorer et aussi pour avoir un logiciel libre mais seulement voilà, j'ai passé beaucoup beaucoup de temps avec IDA et aucun autre logiciel que je connais n'est au niveau. J'ai pas testé radare depuis un moment mais à l'époque c'était vraiment pas ça.
Le problème avec ce genre de logiciel c'est que le main d'oeuvre nécessaire pour atteindre un tel niveau est vraiment importante. Un autre problème pour moi c'est que tous ces logiciels libre ont une mauvaise approche du problème. En gros il te permettent de désassembler, de trouver les "basic block" et voilà. Quant aux graphes d'appels, je n'ai jamais utilisé ça, c'est illisible dès que la fonction n'est pas triviale. Or il me semble que faire ça c'est le niveau 0 du reverse engineering. C'est tout le reste qui est important:
l'analyse de la mémoire: savoir quel objet est référencé par qui, sans ça tu ne peux rien faire
typage: pouvoir définir des structures, pouvoir "décorer" le code de façon à ce que (exemple en ARM):
ldrrO,[r0,#40]
devienne
ldrr0,[r0,#masuperstructure_t.champ_magique]
Il faut avouer que c'est plus lisible. Et j'ajouterai que je ne parle pas de le faire à la main, c'est très important que le logiciel puisse comprendre ce genre de décoration (voir plus bas).
Idéalement il faudrait même aller plus loin: une fois qu'on a indiqué que, par exemple, à un endroits du code, R0 contient un pointeur vers une structure de type masuperstructure_t, il devrait pouvoir automatiquement décorer les autres accès.
l'analyse du code: c'est bien beau de désassembler mais en fait ça sert à rien si on a pas d'information sémantiques. Par exemple c'est utile de savoir qu'en ARM l'instruction
ldrr0,[r0,#40]
fait un accès à la mémoire à l'adresse R0+40. Et ce n'est pas du tout spécifique à l'ARM, on a ce genre de construction dans tous les assembleurs, plus ou moins simplement. A plus long terme, en y réfléchissant bien, on voit que c'est la seule façon d'essayer de faire de la décompilation. Mais même sans viser aussi loin, un logiciel qui serait capable de prendre le code assembleur, le mouliner et dans 95% des cas le transformer en un code équivalent dans un pseudo-assembleur ou un langage de très bas niveau (style l'assembleur de LLVM) serait vraiment idéal, car après tu peux écrire plein d'algo très génériques d'analyse de code. Évidemment arriver à faire ça est quand même super dur.
l'analyse des appels: pouvoir décorer une fonction (avec un prototype en C comme dans IDA ou n'importe quelel autre méthode) de façon à ce que le logiciel soit conscient de la convention d'appel et puisse, lors d'un appel, décorer ou indiquer quels sont les argument. Exemple: on a une fonction "f" et on indique que son type est "void f(int a, int b, int c, int d, int e)", plus tard on voit ce code:
Là on voit que le fait de savoir quel argument correspond à quoi est utile (en mettant des noms plus lisible que a, b, c, ... bien sûr). Et ça marche aussi pour l'analyse de la fonction elle même:
;voidf(inta,intb,intc,intd,inte)f:ldrr4,r0;aldrr5,[sp,#4] ; e
Sauf qu'évidemment dans le cas général ça se complique rapidement: il faut savoir analyser la pile d'appel, ce qui est indécidable dans le cas général mais il existe plein d'algo plus ou moins coûteux/puissant dans la littérature et c'est ça la valeur ajouté du logiciel.
l'analyse de la pile: j'en ai parlé au dessus mais ce n'est pas spécifique à un appel de fonction: que le logiciel puisse suivre l'évolution de la pile et comprendre que:
ldrr0,[sp,#10]
c'est pas juste un accès tout bête à la mémoire, c'est un accès une variable locale, et comme le logiciel suit l'évolution de la pile il peut même exactement laquelle, lui donner un nom, ...
être intéractif: je crois que c'est LE point le plus dur mais c'est aussi le I dans IDA :) Le fait de pouvoir regarder le code, décider que paf telle variable que le logiciel a nommé "a" s’appelle en fait "mastructure" et a pour type "masuperstructure_t" et ensuite laisser le logiciel digérer l'info et en tirer les conséquences qui s'impose. Alors évidemment je vais me faire taper par tous les gens qui trouve que les GUI clicou-clicou c'est le Mal(C), c'est Windows-Like(C) ou je sais pas quoi, mais (1) on peut le faire en console avec ncurses (c'est horrible mais pourquoi pas), (2) le logiciel n'a pas accès un Oracle pour nommer et typer les variables, il faut bien bosser à un moment.
Bref en conclusion je dirais que la raison principale c'est que quand tu as vraiment besoin de faire de la rétroingénierie, la dernière chose dont tu as envie c'est de perdre des heures à faire ce qu'un logiciel peut faire mieux que toi. C'est déjà assez difficile comme ça.
Peut-être qu'une partie du problème vient aussi du fait que les gens qui font vraiment de la rétroingénierie (et donc savent ce qu'ils veulent) n'ont pas le temps de contribuer à un tel logiciel open source. En tout cas c'est mon cas.
Si d'autres ont une opinion différente ou peuvent me prouver que je suis médisant et que certains logiciels open source font ça alors je serai heureux d'avoir tord :)
# Pourquoi les gens ne jurent que par IDA
Posté par pamaury . En réponse au journal Disséquer du binaire sous linux. Évalué à 10.
Sur le principe, je suis d'accord avec toi: j'aimerais bien utiliser autre chose qu'IDA, ne serait-ce que pour pouvoir l'améliorer et aussi pour avoir un logiciel libre mais seulement voilà, j'ai passé beaucoup beaucoup de temps avec IDA et aucun autre logiciel que je connais n'est au niveau. J'ai pas testé radare depuis un moment mais à l'époque c'était vraiment pas ça.
Le problème avec ce genre de logiciel c'est que le main d'oeuvre nécessaire pour atteindre un tel niveau est vraiment importante. Un autre problème pour moi c'est que tous ces logiciels libre ont une mauvaise approche du problème. En gros il te permettent de désassembler, de trouver les "basic block" et voilà. Quant aux graphes d'appels, je n'ai jamais utilisé ça, c'est illisible dès que la fonction n'est pas triviale. Or il me semble que faire ça c'est le niveau 0 du reverse engineering. C'est tout le reste qui est important:
typage: pouvoir définir des structures, pouvoir "décorer" le code de façon à ce que (exemple en ARM):
devienne
Il faut avouer que c'est plus lisible. Et j'ajouterai que je ne parle pas de le faire à la main, c'est très important que le logiciel puisse comprendre ce genre de décoration (voir plus bas).
Idéalement il faudrait même aller plus loin: une fois qu'on a indiqué que, par exemple, à un endroits du code, R0 contient un pointeur vers une structure de type masuperstructure_t, il devrait pouvoir automatiquement décorer les autres accès.
l'analyse du code: c'est bien beau de désassembler mais en fait ça sert à rien si on a pas d'information sémantiques. Par exemple c'est utile de savoir qu'en ARM l'instruction
fait un accès à la mémoire à l'adresse R0+40. Et ce n'est pas du tout spécifique à l'ARM, on a ce genre de construction dans tous les assembleurs, plus ou moins simplement. A plus long terme, en y réfléchissant bien, on voit que c'est la seule façon d'essayer de faire de la décompilation. Mais même sans viser aussi loin, un logiciel qui serait capable de prendre le code assembleur, le mouliner et dans 95% des cas le transformer en un code équivalent dans un pseudo-assembleur ou un langage de très bas niveau (style l'assembleur de LLVM) serait vraiment idéal, car après tu peux écrire plein d'algo très génériques d'analyse de code. Évidemment arriver à faire ça est quand même super dur.
l'analyse des appels: pouvoir décorer une fonction (avec un prototype en C comme dans IDA ou n'importe quelel autre méthode) de façon à ce que le logiciel soit conscient de la convention d'appel et puisse, lors d'un appel, décorer ou indiquer quels sont les argument. Exemple: on a une fonction "f" et on indique que son type est "void f(int a, int b, int c, int d, int e)", plus tard on voit ce code:
Là on voit que le fait de savoir quel argument correspond à quoi est utile (en mettant des noms plus lisible que a, b, c, ... bien sûr). Et ça marche aussi pour l'analyse de la fonction elle même:
Sauf qu'évidemment dans le cas général ça se complique rapidement: il faut savoir analyser la pile d'appel, ce qui est indécidable dans le cas général mais il existe plein d'algo plus ou moins coûteux/puissant dans la littérature et c'est ça la valeur ajouté du logiciel.
l'analyse de la pile: j'en ai parlé au dessus mais ce n'est pas spécifique à un appel de fonction: que le logiciel puisse suivre l'évolution de la pile et comprendre que:
c'est pas juste un accès tout bête à la mémoire, c'est un accès une variable locale, et comme le logiciel suit l'évolution de la pile il peut même exactement laquelle, lui donner un nom, ...
être intéractif: je crois que c'est LE point le plus dur mais c'est aussi le I dans IDA :) Le fait de pouvoir regarder le code, décider que paf telle variable que le logiciel a nommé "a" s’appelle en fait "mastructure" et a pour type "masuperstructure_t" et ensuite laisser le logiciel digérer l'info et en tirer les conséquences qui s'impose. Alors évidemment je vais me faire taper par tous les gens qui trouve que les GUI clicou-clicou c'est le Mal(C), c'est Windows-Like(C) ou je sais pas quoi, mais (1) on peut le faire en console avec ncurses (c'est horrible mais pourquoi pas), (2) le logiciel n'a pas accès un Oracle pour nommer et typer les variables, il faut bien bosser à un moment.
Bref en conclusion je dirais que la raison principale c'est que quand tu as vraiment besoin de faire de la rétroingénierie, la dernière chose dont tu as envie c'est de perdre des heures à faire ce qu'un logiciel peut faire mieux que toi. C'est déjà assez difficile comme ça.
Peut-être qu'une partie du problème vient aussi du fait que les gens qui font vraiment de la rétroingénierie (et donc savent ce qu'ils veulent) n'ont pas le temps de contribuer à un tel logiciel open source. En tout cas c'est mon cas.
Si d'autres ont une opinion différente ou peuvent me prouver que je suis médisant et que certains logiciels open source font ça alors je serai heureux d'avoir tord :)