Beispielprogramm zu Codeinjection

Hallo allerseits,
erstmal vorweg, ich hab die Frage auch schon in einem anderen Forum gestellt, dort konnte mir aber nicht zur Lösung weitergeholfen werden.
C/C++ Forum :: Beispielprogramm zu Codeinjection

Ich versuch ein Programm zur Codeinjection zu schreiben und hab dazu ein Programm gefunden, dass in Assembler vorliegt und wollte das in C++ übersetzen und ausprobieren.

Hier ist erstmal der code was ich bisher hab:
Code:
#include <iostream>
#include <windows.h>

using namespace std;

void injectedThread();

int main()
{
    //get imagebase address
    HMODULE mHandle = GetModuleHandle(0);

    /*HMODULE mHandle;
    if(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, , mHandle) == 0)
    {
        cout << "GetModuleHandleEx() hat ein Problem" << endl;
        return 0;
    }*/

    //PE headers stuff
    DWORD *mHandlePtr = reinterpret_cast<DWORD*>(&mHandle);
    mHandlePtr += 60;   // 3Ch = 60d ... 3Ch = DOS_PREOFFSET

    DWORD mHandleEx = reinterpret_cast<DWORD>(mHandle) + *mHandlePtr;
    mHandleEx += 4;
    mHandleEx += 20;    //14h = 20d

    mHandlePtr = &mHandleEx;
    //DWORD dwSize = *(mHandlePtr + 56);
    DWORD dwSize = 1884160;



    //Internet Explorer fenster finden:
    HWND hIExplorer = FindWindow("IEFrame", 0);
    //HWND hIExplorer = FindWindow(0, "OllyDbg");
    if(hIExplorer == 0)
        cout << "Internet Explorer nicht gestartet." << endl;
    else
        cout << "Internet Explorer gefunden" << endl;

    DWORD dwPID;
    GetWindowThreadProcessId(hIExplorer, &dwPID);
    HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, false, dwPID);
    if(process == 0)
        cout << "Process konnte nicht geoeffnet werden" << endl;
    else
        cout << "Process geoeffnet" << endl;

    LPVOID baseAddrOfAllcRgn = VirtualAllocEx(process, mHandle, dwSize, MEM_COMMIT || MEM_RESERVE,PAGE_EXECUTE_READWRITE);
    //LPVOID baseAddrOfAllcRgn = VirtualAllocEx(process, NULL, dwSize, MEM_COMMIT || MEM_RESERVE,PAGE_EXECUTE_READWRITE);
    if(baseAddrOfAllcRgn == NULL)
    {
        cout << "VirtualAllocEx hat ein Problem: " << GetLastError() << endl;
        return 0;
    }
    else
        cout << "VirtualAllocEx hat speicher reserviert" << endl;

    bool bVFE = VirtualFreeEx(process, mHandle, 0, MEM_RELEASE);
    if( bVFE == 0)
    {
        cout << "VirtualFreeEx hat ein Problem: " << GetLastError() << endl;
        return 0;
    }

    else
        cout << "VirtualFreeEx hat speicher freigegeben" << endl;

    SIZE_T nBytesWritten;
    WriteProcessMemory(process, baseAddrOfAllcRgn, mHandle, dwSize, &nBytesWritten);

    LPDWORD lpNULL;
    CreateRemoteThread(process, 0, 0, (LPTHREAD_START_ROUTINE)injectedThread, mHandle, 0, lpNULL);

    ExitProcess(0);
    return 0;
}

void injectedThread()
{
    LoadLibrary("user32.dll");
    MessageBox(0, "IExplorer", "Hello from iExplorer", 0);

    ExitThread(0);
}
Das Programm lässt sich compilieren und ausführen, aber sobald es VirtualFreeEx() und VirtualAllocEx() erreicht, bekomm ich den Fehlercode 87, welcher besagt, dass die Parameter incorrect sind. Nun, ich hab mit diesen Funktionen keine Erfahrung und finde den Fehler nicht. Könnt ihr mir da weiterhelfen?
 
Also, vom Kompiler bekomm ich keine Warnungen. Ein Fehler beim VirtualAllocEx war die Verknüpfung von
Code:
MEM_COMMIT | MEM_RESERVE
wo ich zuerst logisches OR hatte. Nun bekomm ich den Fehlercode 487
MSDN hat gesagt.:
ERROR_INVALID_ADDRESS
487 (0x1E7)
Als Testprogramm hab ich jetzt OllyDbg statt den InternetExplorer genommen, da dieser nicht protected ist und ich deswegen besser den PE Header einsehen kann.

So, GetModuleHandle liefert mir ja 0x400000 zurück, was ich ja auch dem VirtualAllocEx übergebe. Über das Memory Map(von Olly) hab ich erfahren, das OllyDbg an dieser Stelle bei Access nur read stehen hat. Deswegen hab ich mich nun für die addresse 0x4B4000 entschieden. Ab hier beginnt das Daten segment, in welches ich auch schreiben kann(leider aber nicht ausführen).
Nunja, führe ich nun VirtualAllocEx mit dieser Addresse aus, bekomm ich von GetLastError den Fehlercode 6 (INVALID_HANDLE).

So langsam weiß ich nicht mehr weiter. Ich probier schon alles mögliche aus, komme aber einfach nicht voran
 
Du versucht Speicher an Adresse zu reservieren, die schon belegt ist. Kann und wird nicht gut gehen ;)

Entweder mit VirtualProtectEx den Speicher auf beschreibbar setzen (wobei man mit VirtualQuery noch am besten die Größe ermitteln sollte) und den Code hineinschreiben oder vorher mit ZwUnmapViewOfSection "entladen":
ZwUnmapViewOfSection routine (Windows Driver Kit)
oder eben nicht belegten Speicher wählen.
 
Möglichkeit1: raten ;)
VirtualQueryEx liefert einen MEM_FREE
Möglichkeit2:
den belegten Speicher durchgehen.
korrekt sollte es so funktionieren:
GetSystemInfo liefert eine SYSTEM_INFO
SYSTEM_INFO structure
unter anderem
Code:
  LPVOID    lpMinimumApplicationAddress;
  LPVOID    lpMaximumApplicationAddress;
von min zum max kann man also schon mal durchlaufen.
also sowas wie:
Code:
act = min;
while act < max:
  VirtualQueryEx(bla, act, info)
  if info.State == MEM_FREE:
    break;
  else:
    act = act + info.RegionSize
also im wesentlichen kann man die Information nutzen,
die VirtualQueryEx zurückgibt - da ist die RegionSize Angabe dabei (sowie beim Raten einer beliebigen Adresse die "Basis").
Man erfährt also, wo der nächste Speicherblock anfängt.

Edit: hier ist mal ein Programm samt (MASM) Quelltext, macht im Prinzip nichts anderes.
Allerdings ist da auch GUI Kram dabei, was den Quelltext etwas größer erscheinen lässt.
Die relevanten Zeilen sind so ab 500 bis 636
http://www.hackerboard.de/code-kitc...tex-schlaegt-immer-fehl-wieso.html#post226461
1852d1258293285-virtualprotectex-schlaegt-immer-fehl-wieso-screenshot.png
 
Erstmal vielen Dank für die Antworten. Ich hab jetzt mit der Methode von CDW beschreibaren Speicher gefunden, allerdings bekomm ich von GetLastError immer noch den Fehlercode 487, dass ich an der Addresse nicht schreiben kann.


Hier ist jetzt erstmal der Code, den ich nach OpenProcess angefügt hab. Über VirtualQueryEx such ich nach freien Speicher, und mit VirtualProtectEx möchte ich den Speicherbereich beschreibbar und ausführbar machen.
Code:
    //nach beschreibbarn speicher suchen
    _SYSTEM_INFO info;
    GetSystemInfo(&info);
    cout << info.lpMinimumApplicationAddress << endl;
    cout << info.lpMaximumApplicationAddress << endl;

    _MEMORY_BASIC_INFORMATION memInfo;
    LPVOID actualAddr = info.lpMinimumApplicationAddress;

    while(actualAddr < info.lpMaximumApplicationAddress)
    {
        VirtualQueryEx(process, (LPCVOID)actualAddr, &memInfo, sizeof(memInfo));
        if(memInfo.State == MEM_FREE)
            break;
        else
        {
            SIZE_T act = (SIZE_T)actualAddr;
            act += memInfo.RegionSize;
            actualAddr = (LPVOID)act;
        }
            //*actualAddr += memInfo.RegionSize;
    }

    cout << "Freien Speicher an der Speicherstelle " << actualAddr << " mit der size " << memInfo.RegionSize << " gefunden" << endl;
    DWORD previousProtection;
    if(VirtualProtectEx(process, actualAddr, memInfo.RegionSize, PAGE_EXECUTE_READWRITE, &previousProtection) == 0)
        cout << "VirtualProtect hat ein Problem: " << GetLastError() << endl;
VirtualProtectEx lifert mir aber wie gesagt den Fehlercode 487. Das versteh ich jetzt nicht ganz, ich dachte dieser Code wäre unbelegt und somit beschreibbar???
 
VirtualProtectEx lifert mir aber wie gesagt den Fehlercode 487. Das versteh ich jetzt nicht ganz, ich dachte dieser Code wäre unbelegt und somit beschreibbar???
Vielleicht habe ich mich etwas missverständlich ausgedrückt:
diese Methode geht den kompletten Speicher durch und gibt quasi den Zustand der jeweiligen Bereiche aus. MEM_FREE => ist frei. Also nicht alloziert. Wäre auch blöd, da dann jedes 32-bit Programm per Default 2-3 GB Speicher belegen würde ;)

Um irgendwas mit dem MEM_FREE Speicherbereich anzustellen, muss man zuerst VirtualAllocEx aufrufen (dem man auch gleich die die richtigen Parameter übergeben kann) - hier wären eventuell fehlende Rechte die mögliche Fehlerquelle. Das macht durchaus Sinn, da erst bei diesem Aufruf das System auch tatsächich versucht, den Speicherbereich "verfügbar" zu machen (d.h entweder den physikalischen RAMbereich dafür zu reservieren oder Swap).
Im übrigen kann und sollte man bei VirtualAllocEx nur die gewünschte Größe reservieren (auch wenn der Block deutlich größer wird) - dann wird der Rest zum freien Block ;).

VirtualProtect braucht man nur, wenn man Zugriffseigenschaften des "vorhandenen" Speichers nutzen möchte.
 
Ich hatte VirtualProtectEx eigentlich aufgerufen, da ich dachte, die gefundenen Bytes sind zwar MEM_FREE, aber das recht da rein zu schreiben hab ich deswegen nicht automatisch...

Du sagst ja, VirtualQueryEx geht denn kompletten Speicher durch, das wäre ja der RAM. GetSystemInfo() liefert mir ja lpMaximumApplicationAddress. Dieser Wert liegt bei mir bei 0x7ffeffff. Die Angabe ist doch in Bytes, oder? Naja, den Wert jetzt in Dezimal umgewandelt und zweimal durch 1024 geteilt erhalte ich 2047, ~ 2GB. Ich hab aber 8GB Arbeitsspeicher, was ist mit dem Rest, oder kann ein Win32 Programm nur maximal ~2GB belegen? Würde ja sinn machen, oder warum erwartet VirtualQueryEx einen HANDLE zum Prozess als erstes Argument?

Nun zum VirtualAllocEx:
Ich hab das VirtualProtectEx auskommentiert und VirtualAllocEx die gefundene Addresse mit dem nicht belegten Speicher übergeben. Als size hab ich erstmal nur 1000 genommen.
Code:
VirtualAllocEx(process, actualAddress, 1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
Eigentlich müsste diese Funktion mir doch jetzt den freien Speicher im fremden Prozess reservieren und mit nullen füllen, damit ich ihn benutzen kann.

Sobald mein Programm nun diese Funktion erreicht gehts nicht mehr weiter. Wie es scheint wird die Funktion nicht verlassen. Die CPU auslastung beträgt immer so rund 12-13%. Der nachfolgende Code wird nicht ausgeführt. Was geht da vor sich?

[Edit]
Der kursive Teil kann überlesen werden, da stimmte irgendwas nicht, hab den Computer nochmal neu gestartet und nun lässt sich das Programm auch wieder ausführen und VirtualAllocEx liefert mir wieder den Fehlercode 487. Eigentlich müsste ich den bereich doch jetzt allokieren können. Die Size von 1000 ist jedenfalls deutlich kleiner als die Regionsize aus der SystemInfo struktur. Warum funktioniert das nicht?
 
Zuletzt bearbeitet:
Um sicher zu gehen, dass es nicht an Rechtemangel liegt (bei "ALL_ACCESS" müsste man sich eigentlich "SeDebugPriviledge" Token verschaffen: http://msdn.microsoft.com/en-us/library/windows/desktop/aa446619(v=vs.85).aspx), teste mal den Code statt mit OpenProcess mit "CreateProcess(blub)".
Lass Dir mal die ganzen Zwischenwerte ausgeben (vor allem, den gefundenen Speicherbereich und die Rückgabewerte von VirtualQueryEx bei der Suche) oder steppe es mal im Debugger durch.
 
Ich krieg den Process mit CreateProcess nicht gestartet. Ich hab schon sehr viel ausprobiert, aber ich bekomm immer nur den Fehler 87, das ein parameter falsch ist.
Code:
    STARTUPINFO         startupInfo;
    PROCESS_INFORMATION processInfo;

    char szExe[] = "D:\\OllyDbg 2.0\\ollydbg.exe";
    if(CreateProcess((LPCTSTR)szExe, 0, 0, 0, false, 0, 0, 0, &startupInfo, &processInfo) != 0)
    {

Ich kann den Fehler einfach nicht finden.
 
ZeroMemory / memset(blub,0) für startup/processinfo.
Zumindest startup ist ein "input" Parameter und ist sonst als Stackvariable mit 99% Sicherheit mit irgendwelchen Werte "vorinitialisiert", die nicht hingehören.
Creating Processes
 
Ah, wunderbar ;)
Okay, wenn ich den Process via CreateProcess erstelle, ist VirtualAllocEx in der Lage Speicher zu reservieren und auch VirtualFreeEx liefert keine Fehlermeldungen (wird VirtualFreeEx denn eigentlich noch gebraucht?). Damit habe ich jetzt wohl bei OpenProcess ein Rechteproblem, oder?

Wenn ich CreateProcess benutze funktioniert das Programm bis zum WriteProcessMemory anstandslos, wo ich dann mal wieder den Fehler 487 bekomme :(
Hier nochmal zur kurzübersicht nur die Funktionen und ihre Argumente:
Code:
LPVOID baseAddrOfAllcRgn = VirtualAllocEx(process, actualAddr, 1000, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
VirtualFreeEx(process, actualAddr, 0, MEM_RELEASE);
SIZE_T nBytesWritten;
WriteProcessMemory(process, baseAddrOfAllcRgn, actualAddr, 1000, &nBytesWritten)
So, inwiefern brauch ich eigentlich WriteProcessMemory? Klar, mein Code muss ja an die unbelegte Speicherstelle geschrieben werden, aber macht das nicht schon CreateRemoteThread? Im moment übergebe ich ja actualAddress, was doch überhaupt keinen Sinn macht, oder?
 
Damit habe ich jetzt wohl bei OpenProcess ein Rechteproblem, oder?
Vermutlich. Wie gesagt, Du solltest mal ALLE Rückgabewerte prüfen. In Deinem Code sehen ich nämlich kein "SeDebugPrivilege" Anforderung.
CreateProcess gibt dem "Vater" nämlich automatisch die "ALL_ACCESS"
Rechte. Bei OpenProcess wird es komplizierter:
Explizit besagt die Doku zum CreateRemoteThread:
A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ
...
To open a handle to another process and obtain full access rights, you must enable the SeDebugPrivilege privilege
Versuche erstmal, statt "ALL_ACCESS" nur die "nötigen" Flags zu setzen.

Zugegeben, so ganz durchgestiegen bei den Rechtezuordnungen bin ich auch nicht (die Doku ist imho nicht ganz eindeutig) meine aber, dass CreateRemoteThread bei "Fremdprocessen" den "SeDebugPrivilege" erfordert (diese habe ich zumindest in solchen Fällen dem Prozess immer erteilt)

Beispielcode:
Enabling and Disabling Privileges in C++


Wenn ich CreateProcess benutze funktioniert das Programm bis zum WriteProcessMemory anstandslos, wo ich dann mal wieder den Fehler 487 bekomme :(
Hier nochmal zur kurzübersicht nur die Funktionen und ihre Argumente:
Code:
LPVOID baseAddrOfAllcRgn = VirtualAllocEx(process, actualAddr, 1000, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
VirtualFreeEx(process, actualAddr, 0, MEM_RELEASE);
SIZE_T nBytesWritten;
WriteProcessMemory(process, baseAddrOfAllcRgn, actualAddr, 1000, &nBytesWritten)
Wenn die Reihenfolge der APIs stimmt:
VirtualAlloc == malloc
VirtualFree == free
d.h zuerst wird Speicher alloziert, dann freigegeben und dann versucht, einen Inhalt reinzuschreiben. Im "normalen" C fällt es nicht sofort auf und man nennt das "dangling pointers" ;)

So, inwiefern brauch ich eigentlich WriteProcessMemory? Klar, mein Code muss ja an die unbelegte Speicherstelle geschrieben werden, aber macht das nicht schon CreateRemoteThread? Im moment übergebe ich ja actualAddress, was doch überhaupt keinen Sinn macht, oder?
CreateRemoteThread macht nix anderes, als einen Thread zu erstellen. Der einzige Unterschied zu CreateThread ist Prozesshandle als Zusatzparameter. Es sagt dem System lediglich "starte einen neuen Thread". lpStartAddress gibt an, wo die erste Anweisung dafür liegt.
Wenn an der Adresse nichts ist, wird CreateRemoteThread auch nichts hinüberkopieren (woher auch ;) ).

Nochmal zusammengefasst:
1. eine freie Speicheradresse mit der VirtualQueryEx suchen
2. Speicher mit richtigen Flags per VirtualAllocEx reservieren
3. Inhalt mit WriteProcessMemory schreiben
4. Ausführen mit CreateRemoteThread
5. Optional: wenn die Ausführung fertig ist(WaitForSingleObject), Speicher mit VirtualFreeEx freigeben
 
Bevor ich jetzt wieder zum OpenProcess() wechsel und somit mich dann erstmal mit der Rechtebeschaffung beschäftige, würde ich das jetzt ganz gerne mal mit CreateProcess ausprobieren. Immerhin hab ich jetzt ja schon speicher allokiert und das recht dorthin zu schreiben.

Mit WriteProcessMemory kann ich erfolgreich an die Addresse schreiben. Nun möchte ich den Code von der injectedThread Funktion an die besagte Addresse speichern.
Code:
WriteProcessMemory(process, baseAddrOfAllcRgn, [B](LPCVOID)injectedThread[/B], dwSize, &nBytesWritten)
Diese Funktion lässt sich erfolgreich ausführen und nBytesWritten liefert mir den Wert: 131062, genau soviel Bytes, wie ich sie über VirtualQueryEx über RegionSize bekommen habe.
Dieser Wert ist in meinen Augen jetzt extrem hoch für so eine kleine Funktion wie die injectedThread eine ist.

Meine vermutung ist jetzt, dass nicht nur die injectedThread Funktion (wenn ich sie überhaupt korrekt übergeben habe) an diese Addresse geschrieben wird, sondern auch noch die Bytes, die danach kommen.
Das heißt, dass ich die größe der Funktion ermitteln müsste. Kann ich die Size einer Funktion rausbekommen? Ich mein ungefähr sowas wie
Code:
dwSize = sizeof(injectedThread);
Weil ja WriteProcessMemory() erfolgrich ausgeführt wird, wird auch CreateReomteThread erfolgreich ausgeführt. ich bekomme den Handle zum thread ausgegeben aber OllyDbg stürtzt ab.
 
Kristallkugeln sind heutzutage rar und teuer - deswegen bitte mal ab unzu aktuellen Code anhängen (und nicht nur einen kleinen Ausschnitt davon) ;)

Meine vermutung ist jetzt, dass nicht nur die injectedThread Funktion (wenn ich sie überhaupt korrekt übergeben habe) an diese Addresse geschrieben wird, sondern auch noch die Bytes, die danach kommen.
Debuggen des Programms würde hier helfen ;)

Das heißt, dass ich die größe der Funktion ermitteln müsste. Kann ich die Size einer Funktion rausbekommen? Ich mein ungefähr sowas wie
Code:
dwSize = sizeof(injectedThread);
Afaik: Nein.
Dass Codeinjection öfters in Assembly durchgeführt wird, hängt nicht unbedingt damit zusammen, dass die Autoren ihre Elitärness zeigen wollen oder zu schwach für "richtiges" C++ sind ;).
Eine Methode, die mehr oder weniger gut läuft, ist im Wiki beschrieben:
DLL-Injection (hier wird letzendlich auch Code eingeschleust, der die DLL nachlädt).

Also eine Funktion nach der zu injectenden platzieren und dann die Differenz zwischen den beiden ausrechnen. Nur dass es keine Garantie gibt, dass der Compiler nicht eigenmächtig die Reihenfolge der Funktionen ändert.

Oder man platziert Labels (wie bei Goto) und den zu injectenden Code dazwischen. Im GCC kann man mit &&label die Adresse ermitteln (ist allerdings kein C/C++ Standardfeature). Sollte noch mehr solcher "compilerspezifischen" Methoden geben.

Zudem wird Dein injecteter Code:
Code:
void injectedThread()
{
    LoadLibrary("user32.dll");
    MessageBox(0, "IExplorer", "Hello from iExplorer", 0);

    ExitThread(0);
so nicht laufen.
Warum? Hier mal der endgültige Maschinencode,
den GCC generiert (MS VC++ sieht aber auch ähnlich aus)
Code:
CPU Disasm
Address   Hex dump          Command                                              Comments
004016CC  |.  83EC 18       SUB ESP, 18
004016CF  |.  C70424 774140 MOV DWORD PTR SS:[LOCAL.6], 00404177   ; /FileName => "user32.dll"
004016D6  |.  E8 FD090000   CALL 004020D8                          ; \KERNEL32.LoadLibraryA
004016DB  |.  83EC 04       SUB ESP, 4
004016DE  |.  C74424 0C 000 MOV DWORD PTR SS:[LOCAL.3], 0          ; /Type => MB_OK|MB_DEFBUTTON1|MB_APPLMODAL
004016E6  |.  C74424 08 824 MOV DWORD PTR SS:[LOCAL.4], 00404182   ; |Caption => "Hello from iExplorer"
004016EE  |.  C74424 04 974 MOV DWORD PTR SS:[LOCAL.5], 00404197   ; |Text => "IExplorer"
004016F6  |.  C70424 000000 MOV DWORD PTR SS:[LOCAL.6], 0          ; |hOwner => NULL
004016FD  |.  E8 7E090000   CALL 00402080                          ; \USER32.MessageBoxA
00401702  |.  83EC 10       SUB ESP, 10
00401705  |.  C70424 000000 MOV DWORD PTR SS:[LOCAL.6], 0          ; /Arg1 => 0
0040170C  |.  E8 CF090000   CALL 004020E0                          ; \ntdll.RtlExitUserThread
Wenn man den Block "as is" in einen anderen Prozess kopiert, was sollen die CALLs dann aufrufen? Und wo sollen die Stringkonstanten herkommen, die liegen schließlich in einer anderen Section und nicht im Speicherabschnitt der Funktion selbst?

Deswegen wird auch eher der erste Ansatz, der hier beschrieben ist:
Three Ways to Inject Your Code into Another Process - CodeProject
gewählt - DLL Namen hineinschreiben und CreateRemoteThread auf "LoadLibraryA" aufrufen. Die DLL kann dann in ganz normalem C/C++ geschrieben sein.
 
Wenn man den Block "as is" in einen anderen Prozess kopiert, was sollen die CALLs dann aufrufen? Und wo sollen die Stringkonstanten herkommen, die liegen schließlich in einer anderen Section und nicht im Speicherabschnitt der Funktion selbst?
Verdammt, daran hab ich überhaupt nicht gedacht. Aber jetzt, wo du es erwähnst ist es natürlich einleuchtend.

Ich hab meine injectThread Funktion nun etwas abgewandelt, sodass das Problem mit den hardgecodeten Addressen größtenteils behoben sein sollte:
Code:
typedef HMODULE (WINAPI *LOADLIBRARY)(LPCTSTR);
typedef int (WINAPI *MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, UINT);
typedef void (WINAPI *EXITPROCESS)(UINT);

void injectedThread()
{
    char *cPtr = new char[11];
    cPtr[0] = 'u';
    cPtr[1] = 's';
    cPtr[2] = 'e';
    cPtr[3] = 'r';
    cPtr[4] = '3';
    cPtr[5] = '2';
    cPtr[6] = '.';
    cPtr[7] = 'd';
    cPtr[8] = 'l';
    cPtr[9] = 'l';
    cPtr[10] = '\0';

    LOADLIBRARY fnLoadLibraryA;
    fnLoadLibraryA(cPtr);

    MESSAGEBOX fpMessageBox;
    fpMessageBox(0, cPtr, cPtr, 0);

    //ExitThread(0);
    EXITPROCESS fpExitProcess;
    fpExitProcess(123); //123d = 7Bh, lässt sich in Assembler leichter erkennen als 0
}
Über den Debugger(wieder mal Olly) hab ich nun erfahren, dass die injectThread Funktion an die freie Speicherstelle geschrieben wird. Das ist ja schonmal wunderbar..:D

Wenn ich danach aber CreateRemoteThread aufrufe, stützt mein Programm ab. Ich hab es auch schon mit einem Debugger überprüft, es liegt definitiv an der CreateRemoteThread Funktion. Das Programm kommt aus dieser Funktion nicht mehr raus.

Die DLL-Injection ist ja im Grunde genommen nur eine Erweiterung. Vielleicht werde ich mich damit auch mal auseinandersetzen, aber jetzt noch nicht da ich mit DLL's noch nicht gearbeitet hab.

Hier ist jetzt der komplette code, den ich bisher hab:
Code:
#include <iostream>
#include <windows.h>

using namespace std;

typedef HMODULE (WINAPI *LOADLIBRARY)(LPCTSTR);
typedef int (WINAPI *MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, UINT);
typedef void (WINAPI *EXITPROCESS)(UINT);

void injectedThread();

int main()
{
    HANDLE process;

    STARTUPINFO         startupInfo;
    PROCESS_INFORMATION processInfo;

    ZeroMemory(&startupInfo, sizeof(startupInfo));
    ZeroMemory(&processInfo, sizeof(processInfo));

    char szExe[] = "D:\\OllyDbg 2.0\\ollydbg.exe";
    if(CreateProcess((LPCTSTR)szExe, 0, 0, 0, false, CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, 0, 0, &startupInfo, &processInfo) != 0)
    {
        cout << "CreateProcess war erfolgreich" << endl;
    }
    else
    {
        cout << "CreateProcess war nicht erfolgreich: " << GetLastError() << endl;
        return 0;
    }

    process = processInfo.hProcess;
    if(process == 0)
        cout << "Process konnte nicht geoeffnet werden" << endl;
    else
        cout << "Process geoeffnet" << endl;

    //nach beschreibbarn speicher suchen
    _SYSTEM_INFO info;
    GetSystemInfo(&info);
    cout << "MinimumApplicationAddress: " << info.lpMinimumApplicationAddress << endl;
    cout << "MaximumApplicationAddress: " << info.lpMaximumApplicationAddress << endl;

    _MEMORY_BASIC_INFORMATION memInfo;
    LPVOID actualAddr = info.lpMinimumApplicationAddress;

    while(actualAddr < info.lpMaximumApplicationAddress)
    {
        VirtualQueryEx(process, (LPCVOID)actualAddr, &memInfo, sizeof(memInfo));
        if(memInfo.State == MEM_FREE)
            break;
        else
        {
            SIZE_T act = (SIZE_T)actualAddr;
            act += memInfo.RegionSize;
            actualAddr = (LPVOID)act;
        }
    }

    cout << "Freien Speicher an der Speicherstelle " << actualAddr << " mit der size " << memInfo.RegionSize << " gefunden" << endl;
    DWORD dwSize = memInfo.RegionSize -10;

    LPVOID baseAddrOfAllcRgn = VirtualAllocEx(process, actualAddr, dwSize, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
    if(baseAddrOfAllcRgn == NULL)
    {
        cout << "VirtualAllocEx hat ein Problem: " << GetLastError() << endl;
        return 0;
    }
    else
        cout << "VirtualAllocEx hat speicher reserviert" << endl;


    SIZE_T nBytesWritten;
    if(WriteProcessMemory(process, baseAddrOfAllcRgn, (LPCVOID)injectedThread, dwSize, &nBytesWritten) == 0)
    {
        cout << "WriteProcessMemory hat ein Problem: " << GetLastError() << endl;
        return 0;
    }
    else
    {
        cout << "WriteProcessMemory: " << nBytesWritten << " nBytesWritten" << endl;
    }

    //return 0;   //Befindet sich vorläufig hier, weil im moment CreateRemoteThread das Programm sonst zum absturz bringen würde

    LPDWORD lpNULL;
    HANDLE hThread = CreateRemoteThread(process, 0, 0, (LPTHREAD_START_ROUTINE)injectedThread, actualAddr, 0, lpNULL);
    if(hThread == NULL)
    {
        cout << "CreateRemoteThread hat ein Problem: " << GetLastError() << endl;
    }
    else
    {
        cout << "hTread = " << hThread << endl;
    }

    return 0;
}

void injectedThread()
{
    char *cPtr = new char[11];
    cPtr[0] = 'u';
    cPtr[1] = 's';
    cPtr[2] = 'e';
    cPtr[3] = 'r';
    cPtr[4] = '3';
    cPtr[5] = '2';
    cPtr[6] = '.';
    cPtr[7] = 'd';
    cPtr[8] = 'l';
    cPtr[9] = 'l';
    cPtr[10] = '\0';

    LOADLIBRARY fnLoadLibraryA;
    fnLoadLibraryA(cPtr);

    MESSAGEBOX fpMessageBox;
    fpMessageBox(0, cPtr, cPtr, 0);

    //ExitThread(0);
    EXITPROCESS fpExitProcess;
    fpExitProcess(123); //123d = 7Bh, lässt sich in Assembler leichter erkennen als 0
}
 
Ich hab meine injectThread Funktion nun etwas abgewandelt, sodass das Problem mit den hardgecodeten Addressen größtenteils behoben sein sollte:
Bevor berechtigterweise fragen zu dem größtenteils kommen, es gibt noch einen verbleibenden hardgecodeten call. Das ding ist, das der sich noch im Bereich befindet, der von WriteProcessMemory mit meinem Code überschrieben wird. Ich weiß jetzt nicht in wiefern das ein Problem darstellt. Die Routine, zu der der call zeigt beinhaltet auch noch drei weitere kleinere calls, allesamt zu mit überschriebenem Code. Ich würde meine Injectete Routine ja mal gerne in Olly durchaufen lassen, aber leider weiß ich nicht wie das geht.
 
Ich würde das etwas anders machen. Bei CreateRemoteThread kannst du einen Parameter angeben. Also erstellst du erst ein struct (zb PARAMETER), wo du alle wichtigen Adressen von Funktionen und die benötigten Daten reinschreibst, dieses schreibst du dann mit den Code, der ausgeführt werden soll, in den Zielprozess und rufst CreateRemoteThread mit der Adresse des Codes und des structs auf:

Code:
typedef struct
{
    MESSAGEBOX MessageBox;
    char cText[10];
    char cCaption[10];
} PARAMETER, *pPARAMETER;

void doInj()
{
    p = malloc(sizeof(PARAMETER));
    p.MessageBox = adr;
    strcpy(p.cText, "foo");
    strycpy(p.cCaption, "foo");
    ...
}

void injThread(pPARAMETER p)
{
   p->MessageBox(0,p->cText, p->cCaption, 0);
}
Ungefähr so ist es das sauberste.
 
Ich hab meine injectThread Funktion nun etwas abgewandelt, sodass das Problem mit den hardgecodeten Addressen größtenteils behoben sein sollte
Nein, ist es nicht. Der Sinn der "Verrenkungen" mit fpLoadLibrary/fpGetAddress Datentypen im Wiki ist, dass man daraus eine Struct basteln kann, die man vor dem Kopieren mit den Adressen initialisiert ;).

Bei Dir sind die Typen zwar definiert, aber es steht nix drin. Der Compiler mekert ja auch nicht umsonst
warning C4700: Die nicht initialisierte
lokale Variable "fnLoadLibraryA" wurde verwendet.
Zudem sollte man kein NEW verwenden, da es intern (sowohl GCC wie auch VC++) auch ein Aufruf an den Heapverwaltung ist.
Ergibt dann sowas für den injectedThread:
Code:
CPU Disasm
Address   Hex dump          Command                                  Comments
013813F0  /.  55            PUSH EBP
013813F1  |.  8BEC          MOV EBP, ESP
013813F3  |.  83EC 14       SUB ESP, 14
013813F6  |.  6A 0B         PUSH 0B
013813F8  |.  E8 E1540000   CALL 013868DE    <--- aufruf von NEW char 11
013813FD  |.  83C4 04       ADD ESP, 4
01381400  |.  8945 EC       MOV DWORD PTR SS:[LOCAL.5], EAX
01381403  |.  8B45 EC       MOV EAX, DWORD PTR SS:[LOCAL.5]
01381406  |.  8945 F8       MOV DWORD PTR SS:[LOCAL.2], EAX
01381409  |.  8B4D F8       MOV ECX, DWORD PTR SS:[LOCAL.2]
0138140C  |.  C601 75       MOV BYTE PTR DS:[ECX], 75
0138140F  |.  8B55 F8       MOV EDX, DWORD PTR SS:[LOCAL.2]
01381412  |.  C642 01 73    MOV BYTE PTR DS:[EDX+1], 73
....
01381451  |.  C642 0A 00    MOV BYTE PTR DS:[EDX+0A], 0
01381455  |.  8B45 F8       MOV EAX, DWORD PTR SS:[LOCAL.2]
01381458  |.  50            PUSH EAX
01381459  |.  FF55 FC       CALL DWORD PTR SS:[LOCAL.1]  <--- Aufruf von irgendwas mit dem String "user32.dll".
Die DLL-Injection ist ja im Grunde genommen nur eine Erweiterung.
Vielleicht werde ich mich damit auch mal auseinandersetzen, aber jetzt noch nicht da ich mit DLL's noch nicht gearbeitet hab.
Nun, der Code zeigt aber generelle Möglichkkeit, eine Codeinjection durchzuführen (da hier letzendlich einige Anweisungen ausgeführt werden - im Gegensatz zu der üblichen Methode mittels "CreateRemotThread(blub, LoadLibraryA, DLL_Name)" direkt die LoadLibrary API im anderen Prozess aufzurufen.

Im Moment habe ich eher das Gefühl, dass Dir hier eher an allgemeinem Verständniss mangelt, daher würde ich erstmal folgendes empfehlen:
PE Tutorial von ARTeam
ARTeam Website: Downloads / Tutorials / Portable Executable File Format Compendium v11
Insbesondere solltest Du Dir die Imports anschauen:
Understanding the Import Address Table
Iczelion's PE Tutorial 6: Import Table

Als Kurzfassung:
es geht darum, wie ein Programm letzendlich die WinAPIs/"Fremdfunktionen" aufrufen kann.

Dazu bastelt nämllich der Compiler/Linker erstmal eine Import Tabelle.
Hier werden DLL Namen und Funkktionsnamen/IDs (aka "ordinal values") eingetragen. Diese Tabelle ist ein Teil des PE-Formats und wird vom PE-Loader ausgefüllt. Der Compiler selbst biegt damit alle "LoadLibrary" Aufrufe im Code erstmal auf diese Tabelle um (genauer: es gibt i.d.R 2 Strategien: einmal CALL XYZ, wobei XYZ eine JMP Tabelle ist:
Code:
00401537  |.  E8 740B0000   CALL <JMP.&KERNEL32.VirtualAllocEx>      ; \KERNEL32.VirtualAllocEx
<-- das ist "Debugger Sugar", d.h der Debugger löst es automatisch auf. In wirklichkeit steht da:
Code:
CALL 004020B0
und an 004020B0:
Code:
004020B0   $- FF25 8C714000 JMP DWORD PTR DS:[<&KERNEL32.VirtualAllo
ohne "debugger sugar"
JMP DWORD PTR DS:[40718C]  <--- das ist IAT Eintrag
oder eben
Code:
01381258  |.  FF15 08903901 CALL DWORD PTR DS:[<&KERNEL32.VirtualAll ; \KERNEL32.VirtualAllocEx
aka
CALL DWORD PTR DS:[1399008] <--- call zu IAT Tabelle
Der Inj-Code soll aber in einer komplett anderen Umgebung laufen und kann nicht auf IAT&Co zugreifen.

Für Kernel32.dll geht man erstmal davon aus, dass die Basisadressen bei 2 laufenden Prozessen gleich sind. D.h man findet die tatsächliche Adresse der API heraus (GetProcAddress) und schreibt diese in den InjectionCode. Im InjectionCode selbst kann man mittels 2 Funktionen (LoadLibarary + GetProcAddress) alle anderen "nachholen".

Wenn dabei nicht soo viel compilerspezifisches Zeug aufkommen soll, so nimmt man den Weg über Structs (wie im Wiki):
struct füllen, in den Prozess kopieren, injection Code kopieren, CreateRemoteThread aufrufen (kann dabei 1 Parameter übernehmen - das ist dann die Adresse der Structs). Der Code kann dann die nötigen Aufrufe über die struct betätigen:
Code:
INJECTSTRUCT * is = (INJECTSTRUCT*)addr;       
    hDll = is->LoadLibrary(is->path);
    funktion = (fpFunktion)is->GetProcAddress(hDll, is->func);
    funktion();

2 Tipps zum Debuggen:

1. kann man vorläufig den eigenen Prozess nutzen (GetCurrentProcess gibt ein Handle auf den eigenen Prozess zurück) - sobald man über WriteProcessMemory
den Inj-Code geschrieben kann, Breakpoint darauf setzen und beobachten.

2. wenn alles im eigenen Prozess funktioniert:
Am Anfang des Inj-Codes while(1); setzen.
Dann kann man CreateRemoteThread aufrufen und gemütlich mit OllyDbg
an den anderen Prozess attachen, per F12 pausieren und while Loop
deaktivieren/überschreiben, so dass man in Ruhe sich ansehen kann,
was tatsächlich passiert.


Edit: ok, ich war etwas zu langsam :/
Das, was Du meinst ist ein Call zu "new".
 
Zurück
Oben