Le loader d'Obsidium :

du junk ...

Le loader de Obsidium est junké à l'aide de multitude de petits saut au dessus de bytes aléatoires ce qui rend le code désassemblé par olly plus que moche mais ce junk est très simple et nous verrons comment l'éradiquer en quelques lignes avec un script IDC.

des appels aux APIs redirigés ...

Chaque appel à une API depuis le loader est redirigé vers une fonction qui se chargera de désassembler les premières instructions, de les copier dans un buffer et de vérifier qu'il n'y a pas d'int3 posées ce qui permet d'éviter les breakpoint à l'entrée des API, finalement cette protection facilite le travail, il suffit en effet de poser un HBP juste avant que la redirection ne rende la main a l'API pour breaker sur toutes les APIs qu'appelle le loader.

des Anti-XXX ...

Le loader utilise aussi une quantité d'exception (division par 0, instruction illégale, access violation) et en profite pour effacer les DRs (debug registers) ce qui a pour effet de supprimer nos HBP. Pour éviter cela j'ai utilisé mon plugin SEHPOINJ qui n'est vraiment pas gegene niveau rapidité mais qui fait bien son boulot ;) de plus le script que j'ai fait pour arriver à l'oep du programme n'utilise pas de HBP. Obsidium utilise plusieurs anti-debuggers classiques (plus exotique : il contient un anti-Syser) la plupart de ces anti-debuggers sont contré grâce a l'utilisation de HideDebugger, il est juste nécessaire de remplacer les premières instructions des APIs FindWindowA et Process32Next par la suite d'instruction "xor eax , eax | retn 8"

et même des threads !

Enfin 2 threads sont créés, la première déchiffre une portion de code et l'execution du loader ne continue que lorsqu'elle a finit sont travail la seconde est une protection anti-attach qui boucle en permanence sur la fonction DbgBreakPoint toute les 750ms avec une priorité élevée ce qui permet de détecter une tentative d'attachement au processus en cour. Lorsque olly cherche à s'attacher à un processus en cours il appelle DebugActiveProcess qui va elle même appeler DbgUiIssueRemoteBreakin après avoir attacher olly au processus, DbgUiIssueRemoteBreakin va simplement créer une thread dans le processus cible avec comme point d'entrée la fonction DbgUiRemoteBreakin qui appellera DbgBreakPoint qui ne contient d'une bête int3 (ouuuuf) seulement la thread anti-attach, du fait de sa priorité élevée, va appeler DbgBreakPoint avant que la thread ne soit crée par DbgUiIssueRemoteBreakin, olly pensant que l'int3 lui est destinée l'ignore et poursuit l'exécution, la thread anti-attach détecte alors qu'un petit malin essaie de s'attacher et kill le processus. Cette méthode est assez lourde (on fait quand même tourner une thread en boucle pour rien ...) et il aurait été plus léger de patcher la fonction DbgBreakPoint même si cette technique est moins efficace ... Bref, pour éviter de se taper du multi-thread qu'olly déteste au plus haut point, il suffit de poser un BP sur CreateRemoteThread (pour éviter de se faire avoir par les anti-bp_sur_API) d'attendre que le processus break dessus, pour la première thread : poser un 0xEBFE (saut qui saute sur lui même), laisser la thread faire son travail et se terminer, dès qu'elle a fini son boulot on restaure les anciens bytes à la place du "jmp $-2" on attend que le processus re-break sur notre BP, on pose un BP sur SleepEx (encore une fois pour éviter les anti-BP) dès que la thread crée break dessus, on la kill.

enfin l'oep \o/

Pour arriver à l'oep j'ai utilisé la bonne vieille méthode de la remonté de la pile qui certes n'est pas très élégante mais à le mérite d'être rapide et efficace, arrivé à l'emplacement probable de l'oep il suffit de poser un HBP dessus, de relancer le processus et de regarder à quelle addresse s'est produite la dernière exception le jmp oep n'est pas loin. Voilà qui est fini concernant la quête de l'oep ;) j'ai fait un petit script qui devrait (j'insiste sur le devrait, je ne l'ai finalement testé que sur une application :p) être générique et qui permet de trouver l'oep de façon automatique il est disponible dans l'archive. Passons maintenant a la reconstruction de l'exe

Reconstruction du binaire

Stolen bytes

Tout d'abord l'oep est grossièrement protégé, les premiers bytes ont étés supprimés et les instructions émulées avant le saut sur l'oep, les instructions étant de simples "push cst" je n'ai pas trop creusé et je les ai fixés à la main en me basant sur les dword présent sur la pile après le jmp oep.

reconstruction de l'IAT

Attaquons nous maintenant à la partie qui m'a le plus posé problème : la reconstruction de l'IAT. Obsidium redirige la quasi totalité des call [API] vers une fonction qui se situe dans une page mémoire allouée et qui est chargée de rechercher l'adresse de l'API puis de lui passer la main, la fonction en profite pour vérifier qu'il n'y a pas de BP posé après le call [API] (comme lorsqu'on effectue un step over). Généralement pour récupérer les adresses des APIs avec ce genre de technique on patche la fonction de redirection juste après qu'elle ai trouvé l'addresse de l'API et il suffit alors de la placer dans l'IAT. Le probleme avec obsidium c'est qu'il y a plusieurs "endroits" différents où sont calculé les adresses des APIs de plus le code de redirection est junké, il est donc compliqué de savoir où poser nos BPs ou nos hooks qui, dans le cas d'un hook, écrasent des portions de codes. J'ai pensé faire un tracer qui parcourrait le code jusqu'au moment où la fonction de redirection donne la main à l'API mais cette solution est lente est peut pratique à mettre en place lorsque l'on debug le programme en même temps que l'on fixe son IAT, la solution la plus pratique (enfin je trouve...) et que j'ai décidé de mettre en place a été de ripper la routine de redirection à l'aide de IDA et de la modifier de façon à ce qu'elle retourne l'adresse de l'API. Pour dumper cette routine, je me suis servit de LordPE, dumpé la page mémoire et ouvert le .dmp il suffit alors de localiser la routine à l'aide d'une signature, de créer une fonction et on peut commencer à étudier le code. Le code a été junké, probablement après compilation, avec un outil maison qui intercale des saut court entre les instructions au dessus de bytes aléatoires ce qui donne un code qui a cette tete sous olly et sous IDA : Ce junk n'est pas très robuste et j'ai fait mon premier script IDC pour le zigouillé, étant donné qu'il est assez court je me permet de le mettre dans mon post :

  1. #include <idc.idc>
  2.  
  3. //basé sur un IDC de waganono, merci waga ;)
  4.  
  5. #define START_ADDRESS 0x0
  6. #define END_ADDRESS 0x0
  7.  
  8. static KillJmp(){
  9. auto curseur,c2,delta,b,b2,deb;
  10. curseur = START_ADDRESS;
  11.  
  12. MakeData(START_ADDRESS,FF_BYTE,END_ADDRESS-START_ADDRESS,0) // on remplace la portion de code par des datas
  13.  
  14. while(curseur<END_ADDRESS){
  15. b = Byte(curseur);
  16. b2 = Byte(curseur+1);
  17. // si l'instruction est un saut inconditionnel et qui saute moin de 6 bytes plus loin ....
  18. if\((b == 0xEB) && (b2 < 6\)){
  19. c2 = curseur;
  20. deb = curseur;
  21. curseur = curseur+b2+2;
  22. // alors on le nop ainsi que les bytes au dessus des quel il sautait
  23. while(c2<curseur){
  24. PatchByte(c2, 0x90);
  25. MakeCode(c2);
  26. c2 = c2 + 1;
  27. }
  28. // on hide la zone remplie de nop
  29. HideArea(deb,curseur,"","","",-1);
  30. }
  31. //sinon on passe a l'instruction suivante
  32. else{
  33. MakeCode(curseur);
  34. curseur = curseur + ItemSize(curseur);
  35. }
  36.  
  37. }
  38. Message("\n");
  39. }
  40.  
  41.  
  42. static main(){
  43. KillJmp();
  44. }

une fois dé-junké la routine de redirection ressemble à ça (les blancs entre les instructions correpondent aux nops hiddés (ou "cachés" pour faire plaisir à __ed ;) )): code d'Obsidium déjunké plus sympathique n'est ce pas, même si la routine reste assez imposante ;)

Une fois le code déjunké, il suffit de ripper le code, de le modifier pour qu'il retourne les addresses des APIs au lieu de leur passer la main il faut aussi un peu le modifier pour avoir du code parfaitement fonctionnel et utilisable dans une dll injectée. Après avoir rippé la routine et lancé pour la première fois mon call fixer j'ai obtenu certains résultat aberrant, après avoir testé plusieurs choses, il est apparu que cela semble être du as une vérification du meme type que securom sur la rapidité de la reconstruction, un simple sleep et tout rentre dans l'ordre. Obsidium utilise des dword aléatoires ou des adresses d'API pour remplacer les 0 entres les imports de DLLs différentes je fixe ça en vérifiant que les adresses appartiennent bien a un module et si deux adresses qui se suivent appartiennent à 2 modules différents, je cherche la quelle des deux est référencée dans le code, c'est plutôt efficace et j'obtiens une IAT toute propre ;) ce qui a été le plus compliqué c'est de ripper et modifier le code d'Obsidium pour qu'il fonctionne correctement mais au final ca se fait bien si on est un peu hargneux.

déchiffrement des portions de code chiffrés

Obsidium permet au programmeur de chiffrer ses routines sensibles qui seront déchiffrées au moment de leur exécution, en gros la fonction qui doit être protégée est de la forme :

void mafonction(void)
{
	déchiffre(cst)
	[...]
	chiffre(cst)
}

les fonctions "chiffre" et "déchiffre" sont dans l'IAT du programme, au moment du "packing" du programme, obsidium chiffre la portion de code compris entre " déchiffre" et "chiffre" et lors du chargement de l'application va charger dans l'IAT des pointeurs sur ses fonction de chiffrement contenues dans une page mémoire allouée. Ce qui donne dans olly un code qui ressemble à ça :

PUSH 14 		<- constante (== taille du code chiffré)
CALL [9A00AC]	<- fonction de déchiffrement
PUSH ECX		<- début du code chiffré
POP SS                                  
RETN 16FB
PUSH EBX
[...]
CLI
ADD [EAX],EAX
PUSH 14		<- constante (== taille du code chiffré)
CALL [9A00B4]	<- fonction de chiffrement

Pour déchiffrer ces portions de code, j'ai choisis la simplicité, il suffit de repérer le retn 4 qui rend la main au programme, de le modifier en un "pop reg | pop reg | retn" pour pouvoir appeler la fonction de déchiffrement avec les arguments suivant : déchiffrement(addresse_call_déchiffre , taille_du_code), ensuite il suffit de scanner la section de code à la recherche des appels à la routine de déchiffrement, de récupérer la taille du code à déchiffrer grâce au "push cst" situé juste au dessus, d'appeler la fonction de déchiffrement modifiée et de patcher le code pour qu'il n'appelle plus la fonction de chiffrement ainsi que celle de déchiffrement.

Enfin il faut remplacer les fonctions du SDK de obsidium, pour ça il faut un peu étudier le code de l'application pour comprendre ce que sont censés retourner les fonctions ainsi que leurs arguments ce qui se fait relativement bien aussi ....

Pour terminer, je n'ai pas réussi à cracker complètement le programme que j'étudiais, obsidium permet de chiffrer des portions de codes non disponibles dans la version de démo à l'aide d'une clef contenue dans la licence, n'ayant pas de licence, impossible de déchiffrer le code ....

J'ai conscience de ne pas vraiment être clair, il doit manquer des explications et celle que j'ai fourni sont un peu confuse mais je n'ai jamais été très doué pour expliquer mon cheminement, j'espère malgré tout que vous avez pu comprendre le fonctionnement global de obsidium et que mes sources pourront vous aider et vous éclairer. J'ai enlevé dans mes sources toutes les constantes spécifique au programme que j'ai analysé (et non analisé ;) ) pour éviter d'être embêté ....

vous trouverez l'ensemble des outils que j'ai développé pour ce packer ICI, le call fixer est injecté à l'aide de mon plugin IinjOlly disponible ICI et le ollyscript a été testé avec ODBGScript dernière version disponible LA

Un gros bibi à tous les zigotos de IRC et FC ;)