Il y a 2 grandes familles de protection des imports, la redirection vers un code se chargeant de retrouver l'API correspondante à partir de l'adresse de retour de l'appel (Obsidium par exemple) et la redirection vers une page mémoire allouée dans laquelle le protector va placer les instructions de l'API polymorphisées / junkées (Asprotect -Asprotect utilise les 2 techniques en fait mais bon ..-) c'est cette deuxième famille qu'implémente StarForce.

Généralement lorsque l'on a affaire à ce type de protection, on cherche l'endroit où est polymorphisé le code de l'API et on a directement l'adresse de l'API ce qui se fait assez bien par exemple avec Asprotect le hic c'est que la partie du code de StarForce qui se charge de cette opération est particulièrement junké et longue il a donc fallut trouver une autre solution.

Pour retrouver l'API correspondant aux instructions polymorphisées il suffit simplement de dé-polymorphiser le code et de rechercher un code semblable dans les DLLs loadées reste "juste" à réussir à dé-polymorphiser :D

StarForce contrairement aux protections que j'ai déjà eu l'occasion d'étudier ne se contente pas de junker les X premières instructions ou s'arrêter au premier jmp / call, il va polymorphiser TOUT le code de l'API y compris le code situé dans une boucle, les différentes branches d'un saut etc. de plus il utilise 18 règles de poly différentes ce qui complique un peu la tache ...

Heureusement, j'avais commencé quelque temps auparavant à coder un désassembleur spécialisé dans l'étude de code (il ne retourne qu'une structure détaillant l'instruction et non une string) ce qui m'a été bien utile :p car une fois isolé les règles de poly il a suffit de coder une fonction pour éliminer chacune de ces règles.

Les règles de poly de StarForce sont juste ici :

LEA R32 , [R32 + X]
LEA R32 , [R32 + 0x100000000 - X]
=
NOP
**********************************
LEA ESP , [ESP - 4]
MOV [ESP] , X
=
PUSH X
**********************************
XCHG R32a , R32b
PUSH R32b
XCHG R32a , R32b
=
PUSH R32a
**********************************
XCHG R32a , R32b
XCHG R32b , R32a
=
NOP
**********************************
XCHG R32a , R32b
XCHG R32c , R32a
XCHG R32c , R32b
XCHG R32c , R32a
=
NOP
**********************************
PUSH R32a
MOV R32a , X
XCHG [ESP] , R32a
=
PUSH X
**********************************
PUSH X
POP Y
=
MOV Y , X(avec X ou Y qui ne sont pas du type [X] )
**********************************
CMC
CMC
=
NOP
**********************************
MOV / XCHG R32a , R32a
=
NOP
**********************************
LEA R32a , [R32a]
=
NOP
**********************************
NOP
=
NOP(tu m'en diras tant baboon !)
**********************************
CALL &+5
XCHG [ESP],EAX
LEA EAX,[EAX+F]
XCHG [ESP],EAX
PUSH X
RETN
=
CALL X
**********************************
XCHG [ESP] , R32a
LEA ESP , [ESP+4]
=
POP R32a
**********************************
MOV R32 , [ESP]
LEA ESP , [ESP+4]
=
POP R32a
**********************************
PUSH R32a
PUSH X
MOV R32a , [ESP]
XCHG [ESP+4] , R32a
LEA ESP , [ESP+4]
=
PUSH X
**********************************
PUSH X
INSa [instruction ne modifiant et n'utilisant pas esp]
LEA ESP , [ESP+4]
=
INSa
**********************************
XCHG Xa , Xb
MOV Xb , Xa
=
MOV Xa , Xb
**********************************
PUSH R32a
XCHG [ESP] , R32a
=
PUSH R32a
**********************************

Vous remarquerez la règle de poly "PUSH X | INSa | LEA ESP , [ESP+4]" <=> "INSa" avec INSa ne modifiant pas esp qui impose d'avoir un désassemblage avancé de l'instruction, ce genre de polymorphisme est amha le poly du futur ! les dépendances inter-instructions permettent de polymorphiser du code bien plus efficacement que lorsqu'on se contente de travailler instruction par instruction.

Comme je vous l'ai dit StarForce polymorphise TOUTES les instructions des APIs y compris les sauts j'ai donc choisis lorsque je désassemble le code polymorphisé de toujours suivre les sauts inconditionnel et de ne pas suivre les conditionnels

Ensuite pour la comparaison avec les APIs des DLLs loadées j'ai utilisé un de mes anciens code servant à parser l'export table, ce code va se charger, à l'initialisation du call fixer, de rechercher toutes les DLLs loadées, pour chaque DLL il va parcourir la table des imports, désassembler et dépolymorphiser les 20 premières instructions de l'API (les mov edi , edi étant considérés comme du junk par mon dé-polymorphiseur, il faut les supprimer des listings des APIs), à chaque instruction le pointeur vers cette instruction est vérifié (les DLLs exportant parfois des variables, il peut arriver que mon désassembleur en suivant un saut atterrisse à une adresse invalide) si l'instruction désassemblée est un retn ou n'est pas reconnue par mon désassembleur le désassemblage s'arrête enfin toutes ses informations sont stockées dans une liste chainée de structures.

voila le fonctionnement globale de mon call fixer :

  • desassemble les 20 premières instructions de toutes les APIs des DLLs présentes
  • pour chaque entrée de l'IAT :
    1. si l'adresse ne correspond pas à une API ou si elle n'est pas nulle :
    2.  desassemble et dépolymorphise les 20 premières instructions
    3.  compare ces instructions à toutes celles des APIs listées
    4.  si pas de correspondance trouvée ou si on trouve plus d'une correspondance on affiche un message d'erreur
    5.  retour à l'etape 1
  • tout est fixé alors on mange un bonbon

voili voilou :D

J’espère que ce petit article vous donnera envie de vous frotter un peu aux codes junkés / polymorphisé et que grâce à cela vous deviendrez riches et célèbres

Le code de mon call fixer ainsi que celui de mon désassembleur sont disponibles ICI, mon désassembleur n'est pas fini et n'est pas exempt de bugs enfin (une fois n'est pas coutume) ces codes sont distribués sous la licence Creative Commons suivante : Creative Commons License

Dernière chose : prière aux zozos de ne pas utiliser mes codes pour se faire mousser en rlzant des cracks de logiciels ….