Shellcode Windows - PEB

Prérequis

Cet article a pour but d’initier à la création de shellcode sous Windows. Beaucoup de shellcodes sous Windows listent les modules pour résoudre des adresses de fonction. Dans cette première partie nous allons écrire en assembleur un programme (et non un shellcode) qui va lister ses propres modules. Voici donc les prérequis pour suivre cet article :

Process Environement Block

Le PEB (Process Environement Block) est une structure accessible en user-mode qui contient un tas d’information sur le processus courant. Cette structure est référencée dans le TEB (Thread Environement Block) accessible via le registre FS du thread courant. Le pointeur vers le PEB est située à 0x30 octets plus loin par rapport au début du TEB.

Commençons par écrire un bout de code qui va afficher son adresse.

; ---------------------------------
; Programme pour inspecter le PEB
; rappel :
;    - nasm -f win32 main.asm
;    - gcc -m32 main.obj
;    - .\a.exe
; ---------------------------------
global _main
extern _printf

section .code

_main:
    mov ebx,[fs:0x30] ; Récupère l'adresse du PEB
    ; affiche l'adresse du PEB
    push ebx
    push fmt_PEB
    call _printf
    add esp,8
    ret

section .data
fmt_PEB:
    db 'PEB : %08.8x',10,0        

Parcourir les modules

Le contenu de la structure est partiellement détaillée ici : https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa813706(v=vs.85).aspx

Les symboles de debug microsoft en disent beaucoup plus sur PEB, lançons Windbg et ouvrons un exécutable 32 bits.

Lancez les commandes suivantes dans l’ordre.

  • .sympath srv* : défini l’emplacement des symboles (srv* pour télécharger les symboles )
  • .reload : recharge les symboles
  • dt ntdll!_PEB -r2 : montre récursivement les informations sur la structure _PEB.

Le membre Ldr contient la liste des modules chargés avec le processus. Un module est un exécutable ou une dll et chaque processus est composé de un ou plusieurs modules. Complétons notre code en affichant l’adresse de la structure Ldr.

    mov ebx,[ebx+0x0C] ; Récupère le membre _PEB.Ldr
    
    ; afficher _PEB.Ldr
    push ebx
    push fmt_Ldr
    call _printf
    add  esp,8

InLoadOrderModuleList est un item d’une liste chaînée circulaire (Flink item suivante, Blink item précédent). Le membre Flink pointe vers le prochain item du même type qui est contenu dans une structure de type _LDR_DATA_TABLE_ENTRY.

Cette structure contient des informations sur le module notamment son nom et l’adresse où il a été chargé en mémoire. Le code suivant va nous permettre de parcourir la liste,

lea esi,[ebx + 0x0C] ; &Ldr.InLoaderOrderLinks premier item de la liste circulaire
    mov edi,esi          ; Sauvegarde de l'adresse du premier item
    
enum_module:
    lodsd                ; Charge le second item de la liste
    
    cmp eax,edi          ; Si == au premier item de la liste tour fini
    je exit

    mov esi,eax          ; esi pointe sur la structure _LDR_DATA_TABLE_ENTRY
    lea ebx,[esi + 0x24] ; Récupère l'adresse de FullDllName
    
    push ebx
    call _printUnicodeString@4
    add esp,4
    
    jmp enum_module
exit:
    ret

Comme la liste chaînée est circulaire l’adresse de départ est sauvegarder dans edi. L’instruction lodsd charge le DWORD pointé par esi. Pour savoir si l’on à parcouru toute la liste on compare l’adresse du premier item avec l’adresse courante. Ensuite on récupère l’adresse du membre FullDllName qui correspond au nom du module.

Ce membre est une structure de type _UNICODE_STRING

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

Voici donc une petite procédure pour réaliser l’affichage sans trop s’embêter en utilisant printf.

; ---------------------------------------------------------
; Fonction : _printUnicodeString@4 
; Description : Affiche une chaine unicode
; Entrée : adresse d'une structure de type UNICODE_STRING
; Sortie : la chaine de caractère
; Retour : aucun
; ---------------------------------------------------------
_printUnicodeString@4:
    push ebp
    mov ebp,esp
    mov eax,[ebp + 8] ; adresse de la structure UNICODE_STRING
    
    mov eax,[eax + 4] ; contenu l'attribut Buffer
    
    ; afficher la chaine unicode
    push eax          
    push fmt_unicode
    call _printf
    add esp,8
    
    leave
    ret

La procédure récupère le membre Buffer puis on réalise l’affichage avec la fonction printf et le format ‘%ws’ pour afficher une chaîne Unicode.

Voici le code complet du programme,

global  _main
extern  _printf

section .code

; ---------------------------------------------------------
; Fonction : _printUnicodeString@4 
; Description : Affiche une chaine unicode
; Entrée : adresse d'une structure de type UNICODE_STRING
; Sortie : la chaine de caractère
; Retour : aucun
; ---------------------------------------------------------
_printUnicodeString@4:
    push ebp
    mov ebp,esp
    mov eax,[ebp + 8] ; adresse de la structure UNICODE_STRING
    
    mov eax,[eax + 4] ; contenu l'attribut Buffer
    
    ; afficher la chaine unicode
    push eax          
    push fmt_unicode
    call _printf
    add esp,8
    
    leave
    ret
    
_main:
    mov ebx,[fs:0x30] ; Récupère l'adresse du PEB 
    
    ; afficher l'adresse du PEB
    push ebx
    push    fmt_PEB
    call    _printf
    add     esp, 8
    
    mov ebx,[ebx+0x0C] ; Récupère le contenu de _PEB.Ldr
    
    ; afficher _PEB.Ldr
    push ebx
    push fmt_Ldr
    call _printf
    add  esp,8
    
    
    lea esi,[ebx + 0x0C] ; &Ldr.InLoaderOrderLinks premier item de la liste circulaire
    mov edi,esi          ; Sauvegarde de l'adresse du premier item
    
enum_module:
    lodsd                ; Charge le second item de la liste
    
    cmp eax,edi          ; Si == au premier item de la liste tour fini
    je exit

    mov esi,eax          ; esi pointe sur la structure _LDR_DATA_TABLE_ENTRY
    lea ebx,[esi + 0x24] ; Récupère l'adresse de FullDllName
    
    push ebx
    call _printUnicodeString@4
    add esp,4
    
    jmp enum_module
    
    
exit:
    ret

section .data
fmt_PEB:
    db 'PEB : %08.8x', 10, 0
fmt_Ldr:
    db 'LDR : %08.8x', 10, 0
fmt_unicode:
    db ' %ws',10,0
fmt_hex:
    db '0x%08.8x',10,0

On compile et on exécute,

Réferences