IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Discussion :

DLL, prefered base address, rebasing et autres joyeuseries


Sujet :

Windows

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut DLL, prefered base address, rebasing et autres joyeuseries
    Bonjour à tous,

    Je viens de découvrir avec étonnement qu'il fallait définir autant que possible la "prefered base address" des DLLs que l'on va mapper de façon à ce que le loader n'ait pas à les "rebaser".
    Ceci afin d'éviter de devoir modifier toutes les adresses absolues existantes mais également pour garder la possibilité que le code de la DLL soit partagé entre plusieurs process.
    (source ICI et ICI)

    Sous réserve d'avoir mal compris, j'ai deux questions qui me viennent à l'esprit :
    1) Est-ce correct ce que j'ai résumé plus haut? Parce qu'avec le nombre actuel de DLLs, il devient assez complexe d'estimer un endroit "libre" en mémoire, sans compter la taille que font chaque dll une fois mappées en mémoire.
    2) S'il y a rebasing car l'adresse "préférée" n'est pas disponible lors du mapping, comment le loader peut-il modifier chaque adresse absolue qui se trouve dans le code? Ca me semble presque impossible.

    Merci d'avance,
    Nicolas

  2. #2
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Tiens, bonne question !

    Pour ma part, je pense avoir compris ça comme toi.

    1/ ça je sais pas...

    2/ Si, il n'y a pas de raison que ce soit impossible. Par exemple, on prefere l'adresse A, mais on est chargé à l'adresse B. Il y a donc un ecart de B-A octet entre les deux adresses.

    Ben au chargement du code de la DLL, il suffit d'ajouter B-A à toutes les adresses mémoires. Sur du code ASM ça me parait un poil complexe mais pas impossible du tout, et il me semble que les DLL intègrent des informations symboliques (nom des fonctions, signatures, types de retours...)

    Il suffirait donc, au moment de compiler la DLL, de remplacer l'adresse de base par un symbole. Toutes les adresses sont exprimées en offset par rapport à ce symbole (un peu comme pour du code Position-Independant-Code) et au moment de la charger en mémoire, emplacer ce symbole par l'adresse de chargement.

    N'empeche que tout ça attise ma curiosté, alors si quelqu'un a des infos...

  3. #3
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Ah, en lisant un peu mieux tes liens, il y a ceci :

    http://www.ddj.com/showArticle.jhtml...dj0012b&pgno=8

    Il preconise de choisir l'adresse de départ d'une DLL en fonction de la premiere lettre de son nom (dans une plage mémoire qu'il a d'abord soigneusement identifiée comme étant la plage mémoire de chargement des DLL).

    Ca ne me parait pas complement idiot, le but étant de répartir au mieux les DLL en mémoire.

    Mais bon, comme le dit l'auteur,
    "Base address conflicts are not the only cause of slow DLL load times, or even the most important one."
    Donc bon, je ne suis pas sûr que ça vaille le coup de s'embeter avec ça.

    Surtout que :
    - pour que la repartition en fonction du nom fonctionne bien, il faut que toutes les DLLs jouent le jeu
    - la probabilité d'avoir une DLL qui commence par "a" n'est surement pas la meme que de commencer par "w" il faudrait donc des tailles de plage pour chaque lettre adaptées à la probabilité
    - un algo bien foutu dans une DLL bien foutu remplacera toutes les bonnes adresses du monde (sauf les bonnes adresses de bistrots)

    Enfin, à mon avis !

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Salut buzzkaido,

    Un petit exemple où la "relocation" des adresses par le loader ne peut pas fonctionner : un contrôle CRC sur la partie de code de la DLL, par exemple pour une protection anti-crack ou anti-virale (on peut remettre en doute l'utilité d'une telle méthode mais soit, elle n'est pas impossible) : un simple changement des adresses absolues hardcodées dans la partie code ferait échouer ce test CRC, et je doute que le loader modifie la valeur de ce CRC .

    Autre situation :
    Imaginons que le loader de Windows analyse entièrement le code de chaque DLL qui doit être "relocatée". En terme de perfs, je n'ose même pas imaginer ce que ça peut donner : "revenez dans une heure les gars, je suis en train d'analyser le code de toutes vos DLLs". Mais soit "imaginons"
    Imaginons qu'un codeur en assembleur ait pondu un code comme ça :
    Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    mov eax, 0400000h
    lea eax, DWORD PTR [eax * 3]
    (Désolé pour la syntaxe, l'asm ça date un peu pour moi)
    Non pas qu'il veuille récupérer une adresse mais simplement multiplier eax par 3 sans utiliser l'instruction mul.
    Ca semble quand même être un loader vachement bien pensé s'il arrive à comprendre que cette instruction ne fait pas référence à une adresse mémoire mais est simplement un calcul.


    Et on pourrait encore trouver une quirielle d'autres exemples, peut-être mieux choisis qui tendent à prouver que le loader ne modifie certainement pas les adresses absolues dans le code.


    A mon sens, pour qu'une DLL (ou même un exécutable) soit "relocatable", il faut que le compilateur l'ait permis, et que tous les sauts, appel de fonctions, références de variables, soient calculés en fonction de la "base address" réelle. Mais ça me semble aussi un énorme travail...


    Citation Envoyé par buzzkaido
    Ah, en lisant un peu mieux tes liens, il y a ceci :

    http://www.ddj.com/showArticle.jhtml...dj0012b&pgno=8

    Il preconise de choisir l'adresse de départ d'une DLL en fonction de la premiere lettre de son nom (dans une plage mémoire qu'il a d'abord soigneusement identifiée comme étant la plage mémoire de chargement des DLL).

    Ca ne me parait pas complement idiot, le but étant de répartir au mieux les DLL en mémoire.

    Mais bon, comme le dit l'auteur,

    Donc bon, je ne suis pas sûr que ça vaille le coup de s'embeter avec ça.

    Surtout que :
    - pour que la repartition en fonction du nom fonctionne bien, il faut que toutes les DLLs jouent le jeu
    - la probabilité d'avoir une DLL qui commence par "a" n'est surement pas la meme que de commencer par "w" il faudrait donc des tailles de plage pour chaque lettre adaptées à la probabilité
    - un algo bien foutu dans une DLL bien foutu remplacera toutes les bonnes adresses du monde (sauf les bonnes adresses de bistrots)

    Enfin, à mon avis !
    Je suis d'accord, il n'est sans doute pas vraiment important d'en prendre compte lorsque l'on code. Mais j'aimerai quand même comprendre comment cette relocation est possible...

  5. #5
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Effectivement, bien vu pour les exemple en asm....

    A mon sens, pour qu'une DLL (ou même un exécutable) soit "relocatable", il faut que le compilateur l'ait permis, et que tous les sauts, appel de fonctions, références de variables, soient calculés en fonction de la "base address" réelle. Mais ça me semble aussi un énorme travail...
    Non, pas sûr du tout, il existe pour les instruction de saut (jmp, jea...) la possibilité de donner une adresse "relative" à la position courante.

    Donc pour le compilateur, ça ne complique un peu les choses, mais c'est pas impossible.

    Comme expliqué dans tes liens, un code en adresse relative est un peu plus lent, meme si sur les processeurs d'aujourd'hui, je ne pense pas que ça change grand chose...

    Par contre, l'article precise que les DLL windows ne fonctionnenent pas sur ce principe, mais avec une adresse de base. C'est pour ça qu'à mon avis il s'agit d'un symbole remplacé par sa valeur au moment du chargement.

    Par contre, pour le coup des anti-virus, je sais pas trop comment il se debrouillent...

    Tout ça n'étant que des suppositions, si quelqu'un a des infos, je suis preneur !

  6. #6
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Non, pas sûr du tout, il existe pour les instruction de saut (jmp, jea...) la possibilité de donner une adresse "relative" à la position courante.
    Si je me souviens bien, cela dépend de la taille du saut : un saut de plus de 127 bytes sera automatiquement un saut long, donc avec une adresse absolue.
    127 bytes, c'est tout de même assez court que pour n'utiliser que des sauts de ce type.

    Par contre, l'article precise que les DLL windows ne fonctionnenent pas sur ce principe, mais avec une adresse de base. C'est pour ça qu'à mon avis il s'agit d'un symbole remplacé par sa valeur au moment du chargement.
    Pour avoir désassemblé une DLL écrite en C++ hier, je peux te dire que l'adresse est bien hardcodée dans le code asm et quelle dépend de la "prefered address base".
    Prenons comme exemple une DLL avec une pab à 0x10000000, une section data qui commence à 3000. Tu verras dans le code, lorsque tu fais références à des variables, des adresses hardcodées comme 0x10003004, 0x1000300F, ...
    Et pour peu que tu changes à la main la pab dans le header du fichier, ça plante royalement dès que le code commence à utiliser des adresses (logique vu que les adresses sont hardcodées et que la zone mémoire sur laquelle elles pointent n'est pas initialisée ou ne contient pas les bonnes valeurs)...

    Enfin bref, il faut attendre de plus amples informations

  7. #7
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Si je me souviens bien, cela dépend de la taille du saut : un saut de plus de 127 bytes sera automatiquement un saut long, donc avec une adresse absolue.
    Oui, sur un 386...

    Depuis j'ai un peu laissé tombé l'assembleur, et je sais pas trop pour les nouveaux processeurs, mais ça m'étonnerait que ce soit le cas. Cette limitation était principalement dû au fait que les instructions devaient tenir sur 16 bit, donc 8 bit pour l'instruction de saut + 8 bits pour l'adresse, en signé car relatif.

    Depuis, on a des proc 64 bits...

    Par contre, le fait que l'adresse de base soit hardcodée m'étonnes... du coup, effectivement, la relocation c'est un truc bien balèze !

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Depuis j'ai un peu laissé tombé l'assembleur, et je sais pas trop pour les nouveaux processeurs, mais ça m'étonnerait que ce soit le cas.
    C'est toujours pareil pour les processeurs 32 bits il me semble, les sauts courts ne sont toujours codés que sur 16 bits.
    Enfin dans tous les cas, un simple désassemblage te montre qu'il y a énormément de sauts longs qui sont utilisés...

    Si tu veux, prend un "petit" programme comme OllyDBG, jette un oeil dans un exe ou une dll, tu vas comprendre tout de suite que la "relocation" est quasiment impossible vu le nombre de références absolues que l'on trouve...

  9. #9
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Encore quelques compléments d'information ICI.

    Apparemment pour qu'un module soit "relocatable" il faut que ça ait été défini par le compilateur et avoir créé une section qui sert exclusivement à cela.

    Là où j'ai des doutes c'est lorsqu'il dit que le loader modifie le code. Pour les raisons citées plus haut ça me semble infaisable.
    Je pencherais plutôt pour un mécanisme style table d'importation, où les adresses réelles sont remplacées par le loader.

    Je continue mes recherches

  10. #10
    Membre éclairé
    Avatar de buzzkaido
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2004
    Messages
    821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2004
    Messages : 821
    Points : 734
    Points
    734
    Par défaut
    Mais, de toute manière, une DLL embarque necessairement une table des adresses, non ?

    Ne serait-ce que pour que le programme qui charge cette DLL puisse appeler les bons morceaux de code ?

  11. #11
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Si par "table des adresses" tu entends table de "relocation", alors oui j'ai lu ça quelque part mais je ne le savais pas avant aujourd'hui.
    Pour les exécutables, ça dépend du compilateur toujours d'après ce que j'ai lu.
    (Source ICI)

    Pour ce qui est de la table d'importation (si je ne me trompe pas dans les termes que j'utilise), je ne pense pas quelle soit obligatoire. Je pense avoir déjà vu des exécutables qui n'en possédaient pas, mais ils se basaient sur le fait que ntdll et kernel32 étaient toujours mappées et à la même adresse pour utiliser LoadLibrary, ...

  12. #12
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    J'ai continué mes investigations.
    Pour ceux que ça intéresse, voici des informations supplémentaires que j'ai obtenues :

    Après avoir lu dans plusieurs articles que les adresses absolues étaient patchées en mémoire par le loader de Windows, j'ai effectué mes propres tests.
    J'ai chargé deux fois la même DLL en ayant pris soin de modifier son nom => le loader de Windows les a mappées à deux adresses différentes comme indiqué dans les articles.
    J'ai ensuite été regarder le code d'une même fonction dans chaque DLL : les adresses absolues avaient été modifiées en fonction du rebasing lorsque c'était nécessaire.

    Ayant lu que la section ".reloc" servait lors du rebasing, j'ai été voir ce dont elle était faite avec PE Browse PRO.
    On y voit une liste d'adresses et leur RVA, permettant ainsi au loader de patcher toutes ces adresses s'il y a rebasing.

    Maintenant je me pose toujours la question de savoir comment ça peut fonctionner si jamais le code est protégé avec un contrôle CRC, voire un code compressé, ...
    Peut-être le rebasing ne fonctionne-t-il pas dans ces cas de figure, ou la personne qui écrit le code/le compilateur doit s'arranger pour garder un endroit non protégé qui peut-être patché par le loader...

  13. #13
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour,

    Pour ce qui est de la table d'importation (si je ne me trompe pas dans les termes que j'utilise), je ne pense pas quelle soit obligatoire.
    L'IAT (Import Address Table) est obligatoire sous Windows XP et sup. Un PE doit obligatoirement importer au moins une fonction (et incidemment une DLL), sinon le loader ne charge pas le PE en question.

    Maintenant je me pose toujours la question de savoir comment ça peut fonctionner si jamais le code est protégé avec un contrôle CRC, voire un code compressé, ...
    Peut-être le rebasing ne fonctionne-t-il pas dans ces cas de figure, ou la personne qui écrit le code/le compilateur doit s'arranger pour garder un endroit non protégé qui peut-être patché par le loader...
    Si une partie du code est cryptée le loader ne pourra pas appliquer les relocs (ce qui est normal). Il faut donc mettre en jeu soit même sont propre moteur de relocation (ce qui est assez compliqué, mais il suffit au fond de mimer le comportement du loader lorsqu'il applique les relocs). Le problème restant qu'a un moment ou à un autre, pour appliquer les relocs sur le code crypté, celui ci devra être en clair... donc on perd tout l'intérêt du cryptage.

    Dan ce cas là le mieux est d'écrire du code relatif, c-a-d qui n' utilise pas d'adresses "hardcodées" (codées en dur).

    Si une partie du code est soumise à une vérification d'intégrité (CRC , checksum, Hash, etc.) et qu'il y a eu relocation alors la vérification d'intégrité échouera... D'où encore une fois l'utilité d'écrire du code qui puisse être "relogé" (indépendant du contexte dans lequel il se trouve, le contexte étant ici l'adresse de base du PE).

  14. #14
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Salut Neitsa,
    Merci pour tes compléments d'information

    L'IAT (Import Address Table) est obligatoire sous Windows XP et sup. Un PE doit obligatoirement importer au moins une fonction (et incidemment une DLL), sinon le loader ne charge pas le PE en question.
    Au temps pour moi.

    Dan ce cas là le mieux est d'écrire du code relatif, c-a-d qui n' utilise pas d'adresses "hardcodées" (codées en dur).
    Heu, ça semble tellement facile lorsque tu le dis :p
    Tu veux dire n'utiliser que des variables allouées dynamiquement? Il existe une option dans certains compilateurs ou il faut simplement n'utiliser que des variables locales et / ou créées par "malloc" (cad APIs natives sous-jacentes)?


    Et dire que j'ai déjà écrit des petites routines de vérification d'intégrité sans jamais me soucier que mon module puisse être "relogé"...
    Enfin d'un autre coté, je ne pense pas qu'un module d'exécutable soit souvent relogé. Il me semble avoir lu qu'il n'y a que dans le cas où on lui donne l'address base de ntdll ou kernell32 qui, elles, ne peuvent pas être relogées...


    Dans tous les cas, merci pour ces infos

  15. #15
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Citation Envoyé par NicolasJolet
    Heu, ça semble tellement facile lorsque tu le dis :p
    Euh... oui c'est plus facile à dire qu'à faire

    Tu veux dire n'utiliser que des variables allouées dynamiquement? Il existe une option dans certains compilateurs ou il faut simplement n'utiliser que des variables locales et / ou créées par "malloc" (cad APIs natives sous-jacentes)?
    Oui c'est ça, n'utiliser que des variables locales (qui se trouveront donc sur la pile) ou allouées à l'exécution. Le but étant d'éviter toute référence à une adresse (de variable, de pointeur ou autres) du programme dans toute la routine concernée.

    Après ça on peut vérifier qu'aucune adresse des relocations ne se réfère à la routine concernée (i.e qui ne doit pas être soumise à des relocations). Il y a plusieurs astuces pour cela sans même ouvrir le programme pour regarder le code.

    Enfin d'un autre coté, je ne pense pas qu'un module d'exécutable soit souvent relogé. Il me semble avoir lu qu'il n'y a que dans le cas où on lui donne l'address base de ntdll ou kernell32 qui, elles, ne peuvent pas être relogées...
    Les exécutables ne sont jamais relogés. Le chargeur "mappe" en premier l'exécutable dans l'espace d'adressage du processus (ensuite viennent ntdll.dll qui se charge de la fin du chargement, puis kernel32.dll chargée d'initialiser une partie du thread primaire). Comme l'exécutable est le premier dans l'espace du processus, il ne peut pas être relogé.
    Et même si l'on changeait son ImageBase pour la faire coïncider avec une DLL, c'est la DLL qui serait obliger de "bouger".

    On peut s'éviter ces problèmes lorsqu'il s'agit de protéger des binaires, en mettant la (les) routine d'intégrité dans l'exécutable (même si là elle est théoriquement plus visible). En la camouflant (cryptage quelconque) ou en mettant des systèmes de protections plus avancés (watchdogs, processus externe, etc.) afin de "perdre" l'attaquant.

    Mais au final, les routines de protections sont généralement mieux loties ailleurs que dans l'exécutable.

  16. #16
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Après ça on peut vérifier qu'aucune adresse des relocations ne se réfère à la routine concernée (i.e qui ne doit pas être soumise à des relocations). Il y a plusieurs astuces pour cela sans même ouvrir le programme pour regarder le code.
    Tu en dis trop et pas assez là
    Tu veux dire avec simplement un petit outil, ou plutôt avec des règles de "conception" lors de l'écriture du code?


    Mais au final, les routines de protections sont généralement mieux loties ailleurs que dans l'exécutable.
    Tu veux dire plutôt dans une DLL?

  17. #17
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Tu veux dire avec simplement un petit outil, ou plutôt avec des règles de "conception" lors de l'écriture du code?
    Pour ce qui est des règles, il suffit de se borner à ce dont on parlait dans les précédents messages (pas de variables globales, tout en local !).

    Pour être sûr que le compilo fait bien tout comme on le désire (pas de code pouvant être relogé), il faut ensuite vérifier son boulot.

    - Première solution : vérifier le code sous un désassembleur. Ca peut être long et on peut faire des erreurs par simple omission...

    - Deuxième solution :

    Demander à l'éditeur de lien (linker) de produire un fichier MAP qui renferme des informations très intéressantes, notamment sur les unités de compilations. En gros il s'agit d'un simple fichier texte qui nous informe sur le boulot entre le compilateur et l'éditeur de lien.
    On à le droit notamment aux adresses des fonctions, leurs tailles et leur unités de compilation (et divers autres renseignements).

    Tous les éditeurs de liens qui valent le coup génère à la demande ce fichier MAP. Pour GCC (via ld) il faut passer ces switchs au linker:

    où bla.map est le nom du fichier MAP généré.

    Pour les suites Microsoft (Visual C++, Compiler toolkit, etc. via link.exe) il faut passer:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    /MAP /MAPINFO:EXPORTS
    Imaginons une hypothétique DLL disposant d'une fonction exportée nommée "FooFunc" (fichier nommé Foobar.c).
    Je compile et lie en générant un fichier map (que je peux ouvrir avec n'importe quel éditeur de texte).

    Dans le fichier MAP, je cherche le nom de ma fonction, ici un fichier MAP généré par ld (GCC) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     .text          0x6d6811d0      0x480 obj\Release\Foobar.o
                    0x6d6811d0                FooFunc

    La fonction est dans la section de code .text, débute en 0x6d6811d0 et fait 0x480 octets en taille.

    (attention avec les fichiers MAP de ld, lorsque plusieurs fonctions sont dans une même unité de compilation[fichier objet], c'est la taille totale des fonctions qui est donnée. Les fichiers MAP de Microsoft sont plus parlants et donne les infos pour chaque fonctions).

    Malheureusement les fichiers MAP sont peu bavards sur les relocs... il faut disposer d'un outil externe ou coder son petit outil perso.

    En prenant PE Browse Pro, il suffit de regarder la section relocs et les RVA de la section .text et voir si une RVA "tape" dans notre fonction.(RVA = VA - ImageBase).

    Pour ma fonction FooFunc, sa RVA de départ est de :

    0x6d6811d0 - 0x6d680000 = 0x11d0

    RVA de fin :

    0x11d0 + 0x480 = 0x1650

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Structure for Base Relocations
    134 Fixups
    PageRVA:  00001000 (Section: .text)
    BlockSize:  256
     Offset   RVA      Type    Contents
         1: 00001043, HIGHLOW 0x6D684000 ; non (à l'extérieur de FooFunc)
         2: 0000104D, HIGHLOW 0x6D684010 ; non " "
    [cut]
        14: 0000119C, HIGHLOW 0x6D684000 ; non " "
        15: 0000125C, HIGHLOW 0x6D684020 ; oui relocation dans FooFunc !
        16: 00001289, HIGHLOW 0x6D684028 ; oui " "
    [cut]
        50: 0000163E, HIGHLOW 0x6D684028 ; oui
        51: 00001657, HIGHLOW 0x6D682000 ; non à l'extérieur de FooFunc
    Bien que PE Browse pro soit très pratique, programmer un petit outils dans ce genre est très simple. Si tu ne le diffuse pas (pas besoin de vérifications très poussées sur la conformité du PE), ça tient en 40 lignes de C à tout casser.

    Le format de la section .reloc est très simple et bien documenté dans la spécification PE/COFF, ça tient sur une page.

    En parsant le fichier MAP pour trouver ta fonction (son adresse, sa taille) et en passant cela à un parser de reloc, tu peux en quelque lignes savoir si ton code (plus précisément telle ou telle fonction) est "victime" de relocation...

    J'espère que mon explication n'est pas trop confuse...

    Mais au final, les routines de protections sont généralement mieux loties ailleurs que dans l'exécutable.

    Tu veux dire plutôt dans une DLL?
    En fait j'ai un peu trop généraliser sur ce coup là... . Tout dépend du contexte. Planquer une routine dans une DLL qui est toute seule avec son exécutable, ca ne change pas grand chose au final. Planquer 50 routines de vérification dans 10 DLLs, c'est mieux. Surtout si les DLLs communiquent entre elles et avec l'exécutable.

    C'est difficile de généraliser sur les protections logicielles, ça dépend de beaucoup de paramètres et puis, c'est un métier en soi...

  18. #18
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    J'espère que mon explication n'est pas trop confuse...
    Du tout, j'ai tout suivi

    Par contre, ce que dont je doute pour l'instant c'est de la finalité de cette démarche.
    L'utilité n'est bien ici que dans le cas du code d'une ou quelques fonctions particulières soumises à une "protection" (cryptage, compression, CRC, ...)?!
    Donc imaginons une routine qui vérifie qu'une clé est bien enregistrée. On souhaite crypter cette routine afin que quelqu'un ne puisse pas effectuer une analyse statique du code ou poser des BP sur des appels d'APIs particulières (quoique je doute que ca tienne vraiment la route).
    À ce moment, il faut vérifier que cette fonction ne contienne pas de relocation afin que l'on puisse protéger cette partie de code sans qu'elle plante en cas de rebasing. C'est bien ça ?!

    Pour résumer : on ne doit protéger que le strict minimum, sous peine de devoir se farcir une vérification manuelle de chaque fonction?!


    Quid des compresseurs et packers, ils vont devoir effectuer le travail de relocation eux-mêmes si jamais il y a rebasing?
    UPX pour prendre un exemple simple puisqu'il ne sert "qu'à" compresser.

    En fait j'ai un peu trop généraliser sur ce coup là... . Tout dépend du contexte. Planquer une routine dans une DLL qui est toute seule avec son exécutable, ca ne change pas grand chose au final. Planquer 50 routines de vérification dans 10 DLLs, c'est mieux. Surtout si les DLLs communiquent entre elles et avec l'exécutable.

    C'est difficile de généraliser sur les protections logicielles, ça dépend de beaucoup de paramètres et puis, c'est un métier en soi...
    Je suis tout à fait d'accord Je demandais juste une précision car si on ne mettait pas une routine de protection dans un exécutable, à part dans une DLL, je ne voyais pas bien où la mettre...

  19. #19
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour,

    Citation Envoyé par NicolasJolet
    L'utilité n'est bien ici que dans le cas du code d'une ou quelques fonctions particulières soumises à une "protection" (cryptage, compression, CRC, ...)?!
    Exactement.

    Donc imaginons une routine qui vérifie qu'une clé est bien enregistrée. On souhaite crypter cette routine afin que quelqu'un ne puisse pas effectuer une analyse statique du code ou poser des BP sur des appels d'APIs particulières (quoique je doute que ca tienne vraiment la route).
    À ce moment, il faut vérifier que cette fonction ne contienne pas de relocation afin que l'on puisse protéger cette partie de code sans qu'elle plante en cas de rebasing. C'est bien ça ?!
    C'est bien ça

    Pour résumer : on ne doit protéger que le strict minimum, sous peine de devoir se farcir une vérification manuelle de chaque fonction?!
    Voilà


    Quid des compresseurs et packers, ils vont devoir effectuer le travail de relocation eux-mêmes si jamais il y a rebasing?
    UPX pour prendre un exemple simple puisqu'il ne sert "qu'à" compresser.
    Oui c'est bien ça. Le compresseur / packer va prendre la section reloc avant de la compresser (soit il la laisse telle quelle (mais compressée), soit il la transforme dans son propre format) et lors de l'étape de décompression il fera le travail du chargeur de Windows, à savoir appliquer les relocations sur le code.

  20. #20
    Membre actif
    Profil pro
    Inscrit en
    Février 2006
    Messages
    413
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2006
    Messages : 413
    Points : 286
    Points
    286
    Par défaut
    Et bien, je pense avoir eu les réponses à toutes mes interrogations sur le sujet

    Je te remercie pour toutes ces explications et éclaircissements

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. La base addresse du Kernel32.dll avec un code inconnu !
    Par The Hidden Ghost dans le forum x86 32-bits / 64-bits
    Réponses: 5
    Dernier message: 13/07/2014, 00h15
  2. [SGBD] Copie d'une base mysql à une autre
    Par nicoaix dans le forum Outils
    Réponses: 1
    Dernier message: 23/06/2006, 17h57
  3. [VB6] création dll win32 et appel par un autre programme
    Par Tankian85 dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 10/03/2006, 08h21
  4. Réponses: 17
    Dernier message: 13/02/2006, 14h43
  5. Réponses: 1
    Dernier message: 17/12/2005, 00h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo