Packer sans ta mère, Level II, Prérequis III : LoaderDATA

On va voir aujourd’hui un grand classique des packer et aussi des shellcodes; Comment trouver et parser la liste des DLL afin de trouver le début d’une DLL mappée en mémoire;

Désormais on sait que grace au PEB + 0xC on peut trouver l’emplacement de la structure Loader Data.

Screen Shot 2014-05-24 at 9.43.52

 

Loader Data nommé aussi PEB_LDR_DATA va contenir la liste des DLL et a quel offset mémoire trouver les DLL de notre process. Mais si on regarde à cet emplacement on tombe sur une structure fort déroutante (Moi je vous jure, j’ai mis du temps à comprendre et avec le livre de  Sir Russinovich sur les genoux !). Cette structure ne propose pas une seul liste de DLL chargée mais 3 listes. Ces listes utilisent un format de liste chainée.  Ce que chez M. Windows on nomme des LIST_ENTRY.

Il y a 3 listes disponible concernant les DLL en mémoire ;

  • La liste de DLL chargée par windows au démarrage de l’exécutable (InLoadOrderModuleList)
  • La liste de DLL actuellement en mémoire. (InMemoryOrderModuleList)
  • La liste de DLL initialisée (InInitialisationOrderModuleList)

Je vous l’accorde pour l’instant c’est pas trop parlant;  Autant les deux premier on comprend bien la différence, si un exécutable après initialisation charge avec la fonctions LoadLibrary une nouvelle DLL on ne la retrouvera pas dans la première liste. La 3eme liste en fait ne comprend pas notre exécutable lui même. C’est donc équivalent à la seconde option sans notre propre process, car oui le listing des DLL en mémoire contient notre propre process.

Bon ça c’était la partie facile. Voyons ces listes chainées. Une liste chainée utilise deux pointeurs FLINK (Forward Link) et BLINK (BackWard link). Et c’est à partir de là qu’il faut sortir un schéma. Voici un petit exemple de cette structure. Cet exemple contient 3 records.

list_entry

Personne est tombé dans les pommes ? J’ai un ancien collègue qui aurait probablement éructé un “C’est quoi c’te structure de Nazi”. Alors petite explication. Chaque Flink donne l’adresse du Flink suivant, idem pour le Blink qui donne lui l’adresse du Blink précédent. Les deux premiers records sont les header il n’ont pas de data, seul le prochain Flink vous permettra d’arriver à la structure qui nous intéresse.

Donc si on veut voir la structure 2, il faut lire le 1re Flink, puis le Second, et enfin sous le Flink on trouvera le record qui vous intéresse.Mais il reste une petite subtilitée… Dans notre cas, Il y a 3 listes, donc chaque entrée comporte en header 3 couples de flink/blink. Donc en fait pour une entrée la structure est :

Diagram2

Donc si on suis les flink de InLoadOrderModuleList notre structure sera 0X18 bytes plus loin. Si on suit les Flink de InInitialisationOrderModuleList, la structure n’est qu’a 0x8 bytes plus loin. Funny n’est-ce pas.

Ha ! Et en plus quand on cherche quelque chose, il faut retenir l’offset du premier Flink des headers sous peine de tourner en boucle Ad Vitam. Donc pour aider à la comprenette, prenons un exemple. Dans cet exemple 32 bits, le Loader data d’après le PEB est situé en 0x211EA0. Le format de loader data au final c’est :

  • 0x0 Dword Lenght (Bullshit, on s’en sert pas)
  • 0x4 Dword Unused
  • 0x8 Dword SSHandle (On s’en fout aussi)

Et on attaque les fameuses list entry.

  • 0x0C List_Entry (Donc 2 Dword, le Flink et le Blink) InLoadOrderModuleList
  • 0x14 List_Entry InMemoryOrderModuleList
  • 0x1C List_Entry InInitialisationOrderModuleList

blinkheader

Ok donc admettons que l’on souhaite voir la première entrée de  la liste InInitialisationOrderModuleList, le Flink de ce header nous informe d’allez voir le premier record en 0x241F58 (regardez en 0x1c). Je suis en train de suivre le flink de la 3eme liste donc j’ai ma structure si chèrement désirée en flink + 0x8. Et devinez  ce qu’on y trouve…

Victoire enfin une structure tant recherchée. En 241F58 j’ai bien le flink suivant, mais surtout ;

module

On trouve en 0X241f60 l’offset en mémoire de notre image PE loadée de ntdll.dll. Je sais que c’est ntdll.dll car en 0x241F78 j’ai l’offset d’une string unicode donnant de nom de la DLL.

cette structure, Qui démarre au flink de la 1ere listes se nomme LDR_MODULE. En deux mots pour les packers il y a deux trucs juteux

  • En 0x18 la base adresse, ici en 0x24160 qui nous dit que cette dll est loadée en 0x7C900000
  • En 0x30 un pointeur sur la chaine unicode du nom de la DLL (ici en 0x241f78 qui pointe vers 0x7C92040c)

Alors après il est rare que les packers testent le nom de la dll directement. Soit certains savent que kernel32.dll c’est toujours la seconde et que la 1ere c’est toujours ntdll.dll. Soit ils utilisent un genre de hash du pauvre pour retrouver le nom de la DLL.

Petit exemple de “Use the force luke” tiré d’un malware quelconque..

push    30h
pop     ecx
mov     esi, fs:[ecx]   ; PEB (FS:[0x30])
mov     esi, [esi+0Ch]  ; ESI = LoaderData
mov     esi, [esi+1Ch]  ; ESI = Flink InInitialisationOrderModuleList
mov     ebp, [esi+8]    ; EBP = Base addresse de ntdll
mov     ds:ntdllbase, ebp

Un autre générique et chiadé a moi :) (Oui je t’ai vu toi dans le fond ricaner, on peut faire mieux)

; Find DLL , à apeller avec le hash convoité
; Return la base addresse dans EAX

HASH_SFT equ 0x7
HASH_NTDLL.DLL equ 0xBC1A1445
HASH_KERNEL32.DLL equ 0xA9B35F35
HASH_USER32.DLL equ 0x2D37FEFB

  push ebp
  mov  ebp,esp
  mov  ebx, [fs:0x30]  	  ;  pointer sur PEB  fs:0x30
  mov  ebx, [ebx+0x0C]    ;  pointeur sur  PEB->Ldr
  mov  edx, ebx+0x14      ; addr du 1er flink... alias le point de sortie a sauver   
  mov  ebx, [ebx+0x14]    ; flink premier module de la liste InMemoryOrder 
  xor  ecx,ecx
  xor  eax,eax
  jmp .startlist
.nextmod:
  cmp	ebx,edx
  je .tfini
.startlist:
  mov  esi, [ebx+0x28]    ;  pointeur sur la liste (unicode)
.readchar:
  lodsw			  ; lis un Word (unicode), x00\Char
  test al,al              ; Fin de la string ?
  jz .stopreadchar 
  cmp al,0x60             ; Si minuscule convert to Majuscule, 
  jbe .stoschar
  sub al,0x20             ; pass en majuscule
.stoschar
  xor	cl,al             ; Hash du pauvre, rolxor
  rol ecx,HASH_SFT
  jmp .readchar
.stopreadchar:
  cmp	 ecx,[ebp+0x8]    ; Parametre 1, Hash DLL Name
  je .tfinifound
  mov ecx,0		  ; Reset the hash
  mov ebx, [ebx]          ; choppe le module suivant
  mov esi, [ebx+0x4]      ; module base address
  jmp	.nextmod
.tfinifound
  mov eax, [ebx+0x10]     ; module base address
  jmp	.tgohome
.tfini:
  mov	eax,0 		; pas trouvé l'offset return 0
.tgohome:
  mov esp,ebp
  pop ebp
  retn 4

et un autre petit exemple “Use the force en 64 Bits”. Tout est pareil, sauf la taille des adresse évidemment ce qui décale toute notre affaire.

push 60h           
pop rcx
mov rax,[gs:rcx]   ; Peb
mov rax,[rax+18h]  ; LoaderData
mov rsi,[rax+30h]  ; Ldr.InInitializationOrderModuleList
lodsq              ; skip ntdll.dll
mov rbx,[rax+10h]  ; kernel32.dll base

Et voila, la structure est bien chelou mais désormais vous êtes aptes à retrouver l’offset de n’importe quelle DLL mappée en mémoire et/ou détecter cette action dans le packer de votre choix.

a+

This entry was posted in Asm, Malware, Reverse, Windows and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

AlphaOmega Captcha Classica  –  Enter Security Code