Programm im Speicher erzeugen und ausführen



  • Meine Anwendung empfängt Daten über eine Serielle Schnittstelle. Bei den Daten handelt es sich um ein ausführbares Programm (EXE).

    Dieses soll nun ohne auf die Festplatte geschrieben werden ausgeführt werden. Welcher Aufruf/Befehl ermöglicht dies?



  • Das geht nicht! Du must die Exe auf die Festplatte schreiben und dann ausführen.



  • Das Programme aus dem Speicher heraus aufgerufen werden können muss gehen!

    Beispiel: Auf der Arbeit hab ich so nen netten Fall, da ist die Exe verschlüsslet auf der Platte, ein Dongel entschlüsselt sie und das Ladeprogramm führt es aus. Speziell in diesem Fall währe es sehr sehr unschön wenn die entschlüsslte exe auf der Festplatte rumliegt...

    Aber wie geht das?



  • ^^geht bestimmt, aber ist wohl nicht einfach. vielleicht hilft das: http://windows-internal.net/MS.Press-Microsoft.Windows.Int/0735619174/ch06lev1sec2.html
    🙂



  • AntonWert schrieb:

    Das Programme aus dem Speicher heraus aufgerufen werden können muss gehen!

    Beispiel: Auf der Arbeit hab ich so nen netten Fall, da ist die Exe verschlüsslet auf der Platte, ein Dongel entschlüsselt sie und das Ladeprogramm führt es aus. Speziell in diesem Fall währe es sehr sehr unschön wenn die entschlüsslte exe auf der Festplatte rumliegt...

    Aber wie geht das?

    Ich meine, es gäbe da einen fiesen Hack, mit dem sowas geht. Aber der ist total abgefuckt und irgendwie nicht gut.

    Außerdem: Was bringt mir das? Wer meine entschlüsselte Executable im Tempfile-Chaos von Windows findet, kann auch einen Memory Dump davon machen. Also Unsinn.



  • Aber wie geht das?

    EXE's können nur von Laufwerken (welcher Art auch immer) gestartet werden - ich nehme an das es mittels eines virtuellen Laufwerkes bewerkstelligt wird(RAM drive).



  • hier macht das einer mit ner dll.
    vieleicht hilft dir das ja ein stück weiter.
    http://www.joachim-bauch.de/tutorials/load_dll_memory.html/en#windows-executables-the-pe-format



  • man könnte eine Art template-exe zum erstellen des Prozesses benutzen, und anschließend die exe(im Speicher) von Hand einbetten: Dlls laden, PEB fixen, PE-Header anpassen... 😉



  • man könnte ja auch ein paar dateifunktionen ala CreateFile irgentwie
    über inlinehooking oder sowas umbauen, so dass sie nicht auf die platte
    sondern den ram zugreifen. habe ich mal versucht, aber aufgegeben, weil
    ich die olle "kernel32.dll" nicht unprotecten konnte 😞

    könnte aber klappen (theroetisch)



  • Weiß nicht ob das so ohne Weiteres funktioniert, du könntest aber mal VirtualAlloc mit PAGE_EXECUTE_READWRITE als memory-protection versuchen.
    Also deine Daten, die du empfängst in den mit VirtualAlloc angeforderten Speicherbereich schreiben und dann ausführen. Funktioniert so natürlich nicht, da EXE-Dateien ja nicht nur ausführbaren Code enthalten. Vielleicht kannst du deine empfangenen Daten ja noch so abändern, dass du in den Speicherbereich gleich ausführbaren Code reinschreibst.

    Na gut, das ist wohl nicht die beste (oder kürzeste) Lösung, war auch nur so ein Gedanke...



  • AntonWert schrieb:

    Das Programme aus dem Speicher heraus aufgerufen werden können muss gehen!

    Blaaaaaaaaaaah

    Beispiel: Auf der Arbeit hab ich so nen netten Fall, da ist die Exe verschlüsslet auf der Platte, ein Dongel entschlüsselt sie und das Ladeprogramm führt es aus. Speziell in diesem Fall währe es sehr sehr unschön wenn die entschlüsslte exe auf der Festplatte rumliegt...

    Aber wie geht das?

    Das ist was anderes. In dem Fall ist ein Teil des Programms immernoch unverschlüsselt, nämlich die PE-Header, einige PE Datenstrukturen, sowie ein kleiner Programmschnippsel, der den Verschlüsselten Teil mit Hilfe des Dongles entschlüsselt etc.
    DAS geht. Ist aber a) was gänzlich anderes und b) ziemlich kompliziert.



  • Bier schrieb:

    Vielleicht kannst du deine empfangenen Daten ja noch so abändern, dass du in den Speicherbereich gleich ausführbaren Code reinschreibst.

    Das kann gehen. Allerdings muss dazu der Code Position-Independant sein. Bei x64 kein Problem, bei x86 eher schon. Oder man kann natürlich eigene Relocation-Tables mitschicken. Auf jeden Fall auch nicht ganz trivial.

    p.S.: und irgendeine Art wie der versendete Code an DLL-Importe rankommt muss man sich auch einfallen lassen. Also z.B. einen eigenen Import-Table mitschicken (und auflösen bevor man den Code ausführt). Oder die Adressen der Funktionen LoadLibrary + GetProcAddress mitgeben, und den Code alles dynamisch laden lassen (umständlich, aber möglich).



  • am besten wäre es ja einfach den windows-loader umzubauen. (wurde glaubich schon
    erwäht). scheint aber nicht zu klappen, als normaluser ohne debugrechte, etc.
    darf ich nicht im addressraum von kernel32 rumschreiben, VirtualProtect meldet
    immer access_deinded. (code: 998). eben getestet.

    bekommt jeder prozess eine kopie von kernel32 oder wird die dll einfach nur
    in den prozess eingeblendet?

    kann man nicht einfach die ganze funktion irgentwie auslesen und woanders
    ausführen? (nach relocation).

    da muss es doch was geben, wäre die eleganteste lösung



  • ^^man könnte 'createprocess' nachbauen bzw. abändern. für die exe wird ja ein 'section objekt' (auch als filemapping bekannt) angelegt. da könnte man vielleicht ansetzen und eine dateilose section (CreateFileMapping() ohne gültiges file-handle aufrufen) erzeugen, die man dann mit den daten der exe füttert. hört sich nach viel bastelei an, wär aber sicherlich interessant, sich 'ne CreateMemoryProcess()-funktion zu bauen.
    🙂



  • @AntonWert: Das geht ungefähr in drei-vier Schritten, Speicher allokieren (1), Deine Daten, die ja ausführbaren Code darstellen, rüberkopieren (2), den Speicherbereich als ausführbar "einstellen" und Funktionspointer darauf (3) setzen:

    {
        void (*my_func)(void) = NULL;
        void *pMem = NULL;
        void *pDeineDaten = NULL;
        DWORD ManyBytes = 4096;
        DWORD OldProtect = 0;
    
        ... /* pDeineDaten mit Daten füllen */
    
        pMem = VirtualAlloc(NULL, ManyBytes, MEM_COMMIT, PAGE_READWRITE);
        memcpy(pMem, pDeineDaten, ManyBytes);
        VirtualProtect(pMem, ManyBytes, PAGE_EXECUTE, &OldProtect);
        my_func = (void (*)(void))pMem;
        my_func();
    }
    

    Viel Glück beim Debuggen! 🙂



  • abc.w schrieb:

    @AntonWert: Das geht ungefähr in drei-vier Schritten, Speicher allokieren (1), Deine Daten, die ja ausführbaren Code darstellen, rüberkopieren (2), den Speicherbereich als ausführbar "einstellen" und Funktionspointer darauf (3) setzen:

    {
        void (*my_func)(void) = NULL;
        void *pMem = NULL;
        void *pDeineDaten = NULL;
        DWORD ManyBytes = 4096;
        DWORD OldProtect = 0;
    
        ... /* pDeineDaten mit Daten füllen */
    
        pMem = VirtualAlloc(NULL, ManyBytes, MEM_COMMIT, PAGE_READWRITE);
        memcpy(pMem, pDeineDaten, ManyBytes);
        VirtualProtect(pMem, ManyBytes, PAGE_EXECUTE, &OldProtect);
        my_func = (void (*)(void))pMem;
        my_func();
    }
    

    Viel Glück beim Debuggen! 🙂

    ^^ so kannste aber keine 'richtige exe' ausführen.
    🙂



  • das klappt so nicht. zum einen, weil die relocaton fehlt und zum anderen weil die
    echse einen header hat, den du mitausführen willst. 😉



  • Ach so, eine richtige exe soll es sein, habe ich überlesen. Dann... keine Ahnung... 🙂



  • vielleicht schaust mal im ReactOS source code wie CreateProcess() funktioniert, da die exe ja sowieso zuerst in den speicher geladen wird damit sie überhaupt ausführbar ist. alles andere sollte sich dann ergeben, oder FreeDOS wie die exe laden... etc..



  • mittlerweile habe ich es geschafft, kernel32 funktionen erfolgreich
    umzubiegen. allerdings scheint weder CreateProcess noch LoadLibrary
    auf dateifunktionen wie CreateFile zurückzugreifen. 😞

    wie machen die das? ich versuche grade die ntdll funktionen zu hooken.
    vielleicht klappt das ja.



  • du musst dazu nicht zwingend createprocess umschreiben,es sind nur folgende schritte nötig:

    - .exe einlesen (optional)
    - entsprechend speicher allocieren für dos/ntheader
    - header in allocierten speicher kopieren,sections richtig erstellen
    - STARTUPINFO/PROCESS_INFORMATION/CONTEXT richtig setzen
    - CreateProcess aufrufen

    poc code dazu:

    //--------------------------------------------------------
    // Dynamic Process Forking of Portable Executable
    // Author : Vrillon / Venus
    // Date   : 07/14/2008
    //--------------------------------------------------------
    /*********************************************************/
    /* With this header, you can create and run a process    */
    /* from memory and not from a file.                      */
    /*********************************************************/
    
    #ifdef WIN32
        #include <windows.h>
    #else
        #error Process Forking Requires a Windows Operating System
    #endif
    
    #include <stdio.h>
    
    /////////////////////////////////////////////////////////////
    // NtUnmapViewOfSection (ZwUnmapViewOfSection)
    // Used to unmap a section from a process.
    typedef long int (__stdcall* NtUnmapViewOfSectionF)(HANDLE,PVOID);
    NtUnmapViewOfSectionF NtUnmapViewOfSection = (NtUnmapViewOfSectionF)GetProcAddress(LoadLibrary("ntdll.dll"),"NtUnmapViewOfSection");
    
    /////////////////////////////////////////////////////////////
    // Fork Process
    // Dynamically create a process based on the parameter 'lpImage'. The parameter should have the entire
    // image of a portable executable file from address 0 to the end.
    bool ForkProcess(LPVOID lpImage)
    {
        // Variables for Process Forking
        long int                lWritten;
        long int                lHeaderSize;
        long int                lImageSize;
        long int                lSectionCount;
        long int                lSectionSize;
        long int                lFirstSection;
        long int                lPreviousProtection;
        long int                lJumpSize;
    
        bool                    bReturnValue;
    
        LPVOID                    lpImageMemory;
        LPVOID                    lpImageMemoryDummy;
    
        IMAGE_DOS_HEADER        dsDosHeader;
        IMAGE_NT_HEADERS        ntNtHeader;
        IMAGE_SECTION_HEADER    shSections[512 * 2];
    
        PROCESS_INFORMATION        piProcessInformation;
        STARTUPINFO                suStartUpInformation;
    
        CONTEXT                    cContext;
    
        // Variables for Local Process
        FILE*                    fFile;
        char*                    pProcessName;
    
        long int                lFileSize;
        long int                lLocalImageBase;
        long int                lLocalImageSize;
    
        LPVOID                    lpLocalFile;
    
        IMAGE_DOS_HEADER        dsLocalDosHeader;
        IMAGE_NT_HEADERS        ntLocalNtHeader;
    
        /////////////////////////////////////////////////////////////////
        // End Variable Definition
    
        bReturnValue = false;
    
        pProcessName = new char[MAX_PATH];
        ZeroMemory(pProcessName,MAX_PATH);
    
        // Get the file name for the dummy process
        if(GetModuleFileName(NULL,pProcessName,MAX_PATH) == 0)
        {
            delete [] pProcessName;
            return bReturnValue;
        }
    
        // Open the dummy process in binary mode
        fFile = fopen(pProcessName,"rb");
        if(!fFile)
        {
            delete [] pProcessName;
            return bReturnValue;
        }
    
        fseek(fFile,0,SEEK_END);
    
        // Get file size
        lFileSize = ftell(fFile);
    
        rewind(fFile);
    
        // Allocate memory for dummy file
        lpLocalFile = new LPVOID[lFileSize];
        ZeroMemory(lpLocalFile,lFileSize);
    
        // Read memory of file
        fread(lpLocalFile,lFileSize,1,fFile);
    
        // Close file
        fclose(fFile);
    
        // Grab the DOS Headers
        memcpy(&dsLocalDosHeader,lpLocalFile,sizeof(dsLocalDosHeader));
    
        if(dsLocalDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
        {
            delete [] pProcessName;
            delete [] lpLocalFile;
            return bReturnValue;
        }
    
        // Grab NT Headers
        memcpy(&ntLocalNtHeader,(LPVOID)((long int)lpLocalFile+dsLocalDosHeader.e_lfanew),sizeof(dsLocalDosHeader));
    
        if(ntLocalNtHeader.Signature != IMAGE_NT_SIGNATURE)
        {
            delete [] pProcessName;
            delete [] lpLocalFile;
            return bReturnValue;
        }
    
        // Get Size and Image Base
        lLocalImageBase = ntLocalNtHeader.OptionalHeader.ImageBase;
        lLocalImageSize = ntLocalNtHeader.OptionalHeader.SizeOfImage;
    
        // Deallocate
        delete [] lpLocalFile;
    
        // Grab DOS Header for Forking Process
        memcpy(&dsDosHeader,lpImage,sizeof(dsDosHeader));
    
        if(dsDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
        {
            delete [] pProcessName;
            return bReturnValue;
        }
    
        // Grab NT Header for Forking Process
        memcpy(&ntNtHeader,(LPVOID)((long int)lpImage+dsDosHeader.e_lfanew),sizeof(ntNtHeader));
    
        if(ntNtHeader.Signature != IMAGE_NT_SIGNATURE)
        {
            delete [] pProcessName;
            return bReturnValue;
        }
    
        // Get proper sizes
        lImageSize = ntNtHeader.OptionalHeader.SizeOfImage;
        lHeaderSize = ntNtHeader.OptionalHeader.SizeOfHeaders;
    
        // Allocate memory for image
        lpImageMemory = new LPVOID[lImageSize];
        ZeroMemory(lpImageMemory,lImageSize);
    
        lpImageMemoryDummy = lpImageMemory;
    
        lFirstSection = (long int)(((long int)lpImage+dsDosHeader.e_lfanew) + sizeof(IMAGE_NT_HEADERS));
    
        memcpy(shSections,(LPVOID)(lFirstSection),sizeof(IMAGE_SECTION_HEADER)*ntNtHeader.FileHeader.NumberOfSections);
        memcpy(lpImageMemoryDummy,lpImage,lHeaderSize);
    
        // Get Section Alignment
        if((ntNtHeader.OptionalHeader.SizeOfHeaders % ntNtHeader.OptionalHeader.SectionAlignment) == 0)
        {
            lJumpSize = ntNtHeader.OptionalHeader.SizeOfHeaders;
        }
        else
        {
            lJumpSize  = (ntNtHeader.OptionalHeader.SizeOfHeaders/ntNtHeader.OptionalHeader.SectionAlignment);
            lJumpSize += 1;
            lJumpSize *= (ntNtHeader.OptionalHeader.SectionAlignment);
        }
    
        lpImageMemoryDummy = (LPVOID)((long int)lpImageMemoryDummy + lJumpSize);
    
        // Copy Sections To Buffer
        for(lSectionCount = 0; lSectionCount < ntNtHeader.FileHeader.NumberOfSections; lSectionCount++)
        {
            lJumpSize = 0;
            lSectionSize = shSections[lSectionCount].SizeOfRawData;
    
            memcpy(lpImageMemoryDummy,(LPVOID)((long int)lpImage + shSections[lSectionCount].PointerToRawData),lSectionSize);
    
            if((shSections[lSectionCount].Misc.VirtualSize % ntNtHeader.OptionalHeader.SectionAlignment)==0)
            {
                lJumpSize = shSections[lSectionCount].Misc.VirtualSize;
            }
            else
            {
                lJumpSize  = (shSections[lSectionCount].Misc.VirtualSize/ntNtHeader.OptionalHeader.SectionAlignment);
                lJumpSize += 1;
                lJumpSize *= (ntNtHeader.OptionalHeader.SectionAlignment);
            }
    
            lpImageMemoryDummy = (LPVOID)((long int)lpImageMemoryDummy + lJumpSize);
        }
    
        ZeroMemory(&suStartUpInformation,sizeof(STARTUPINFO));
        ZeroMemory(&piProcessInformation,sizeof(PROCESS_INFORMATION));
        ZeroMemory(&cContext,sizeof(CONTEXT));
    
        suStartUpInformation.cb = sizeof(suStartUpInformation);
    
        // Create Process
        if(CreateProcess(NULL,pProcessName,NULL,NULL,false,CREATE_SUSPENDED,NULL,NULL,&suStartUpInformation,&piProcessInformation))
        {
            cContext.ContextFlags = CONTEXT_FULL;
            GetThreadContext(piProcessInformation.hThread,&cContext);
    
            // Check image base and image size
            if(lLocalImageBase == (long int)ntNtHeader.OptionalHeader.ImageBase && lImageSize <= lLocalImageSize)
            {
                VirtualProtectEx(piProcessInformation.hProcess,(LPVOID)((long int)ntNtHeader.OptionalHeader.ImageBase),lImageSize,PAGE_EXECUTE_READWRITE,(unsigned long*)&lPreviousProtection);
            }
            else
            {
                if(!NtUnmapViewOfSection(piProcessInformation.hProcess,(LPVOID)((DWORD)lLocalImageBase)))
                    VirtualAllocEx(piProcessInformation.hProcess,(LPVOID)((long int)ntNtHeader.OptionalHeader.ImageBase),lImageSize,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
            }
    
            // Write Image to Process
            if(WriteProcessMemory(piProcessInformation.hProcess,(LPVOID)((long int)ntNtHeader.OptionalHeader.ImageBase),lpImageMemory,lImageSize,(unsigned long*)&lWritten))
            {
                bReturnValue = true;
            }
    
            // Set Image Base
            if(WriteProcessMemory(piProcessInformation.hProcess,(LPVOID)((long int)cContext.Ebx + 8),&ntNtHeader.OptionalHeader.ImageBase,4,(unsigned long*)&lWritten))
            {
                if(bReturnValue == true)
                    bReturnValue = true;
            }
    
            if(bReturnValue == false)
            {
                delete [] pProcessName;
                delete [] lpImageMemory;
                return bReturnValue;
            }
    
            // Set the new entry point
            cContext.Eax = ntNtHeader.OptionalHeader.ImageBase + ntNtHeader.OptionalHeader.AddressOfEntryPoint;
    
            SetThreadContext(piProcessInformation.hThread,&cContext);
    
            if(lLocalImageBase == (long int)ntNtHeader.OptionalHeader.ImageBase && lImageSize <= lLocalImageSize)
                VirtualProtectEx(piProcessInformation.hProcess,(LPVOID)((long int)ntNtHeader.OptionalHeader.ImageBase),lImageSize,lPreviousProtection,0);
    
            // Resume the process
            ResumeThread(piProcessInformation.hThread);
        }
    
        delete [] pProcessName;
        delete [] lpImageMemory;
    
        return bReturnValue;    
    }
    
    /////////////////////////////////////////////////////////////
    // Fork Process From Resource
    // Dynamically create a process from a resource file.
    bool ForkProcessFromResource(int iResource,char* pResourceSection)
    {
        HGLOBAL        hResData;
        HRSRC        hResInfo;
    
        LPVOID        lpRes;
        LPVOID        lpMemory;
        long int    lSize;
    
        HMODULE        hModule;
    
        bool bReturn;
    
        hModule = GetModuleHandle(0);
        bReturn = false;
    
        if(!hModule)
            return bReturn;
    
        hResInfo = FindResource(hModule, MAKEINTRESOURCE(iResource), pResourceSection);
        if(!hResInfo)
        {
            return bReturn;
        }
    
        hResData = LoadResource(hModule, hResInfo);
        if(!hResData)
        {
            return bReturn;
        }
    
        lpRes = LockResource(hResData);
        if(!lpRes)
        {
            FreeResource(hResData);
            return bReturn;
        }
    
        lSize = SizeofResource(hModule, hResInfo);
    
        lpMemory = new LPVOID[lSize];
        ZeroMemory(lpMemory,lSize);
    
        memcpy (lpMemory, lpRes, lSize);
    
        bReturn = ForkProcess(lpMemory);
    
        FreeResource(hResData);
        delete [] lpMemory;
    
        return bReturn;
    }
    

Log in to reply