[DLL Injection] Wel op XP (32bit), niet op Windows 7 (64bit)

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Om een aantal functies binnen een 3rd party applicatie (32bit) aan te roepen ben ik genoodzaakt om native code in het process te injecteren. Ik heb hier geen ervaring mee en na een tijd zoeken en lezen op het internet is het mij gelukt om een DLL injector te maken. Om dit te testen heb ik een eenvoudige dll gemaakt die enkel een system beep uitvoert zodat ik weet of het inladen van de code gelukt is of niet.

Nu werk ik op mijn primaire machine met Windows 7 64bit. Het injecteren de code lijkt allemaal goed te gaan, ik krijg geen errors terug en er crasht ook niets alleen ik hoor geen system beep :'(

Ik heb daarom een virtuele machine geïnstalleerd met XP 32bit en daarop werkt het injecteren/laden van de code wel gewoon goed. De DLL zelf is 32bit, mijn injector is geschreven in C# en het process waarin ik de test dll injecteer is ook een native 32bit applicatie. UAC staat uit op mijn Windows 7 systeem.

Wat doe ik fout? Ik heb op het internet gezocht maar er lijken alleen verschillen te zijn wanneer de dll en de applicatie waarin het geïnjecteerd moet worden 64bit applicaties zijn, maar dat is niet het geval.

Injector:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
hProcess =
    WinApi.OpenProcess(
        WinApi.PROCESS_VM_READ | WinApi.PROCESS_VM_WRITE | WinApi.PROCESS_VM_OPERATION |
        WinApi.PROCESS_QUERY_INFORMATION | WinApi.PROCESS_CREATE_THREAD, false, (uint) targetProcess.Id);

if (hProcess == IntPtr.Zero || Marshal.GetLastWin32Error() != 0)
    throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to open process");

IntPtr llAddr = WinApi.GetProcAddress(WinApi.GetModuleHandle("kernel32.dll"), "LoadLibraryA");

if (llAddr == IntPtr.Zero || Marshal.GetLastWin32Error() != 0)
    throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to find address of LoadLibraryA function");

byte[] dllPathBytes = Encoding.ASCII.GetBytes(dllPath);

IntPtr dllAddr = WinApi.VirtualAllocEx(hProcess, IntPtr.Zero, (uint) dllPathBytes.Length + 1,
                                       WinApi.MEM_RESERVE | WinApi.MEM_COMMIT,
                                       WinApi.PAGE_EXECUTE_READWRITE);

if (dllAddr == IntPtr.Zero || Marshal.GetLastWin32Error() != 0)
    throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to allocate memory for the dll path");

int bytesWritten;
WinApi.WriteProcessMemory(hProcess, dllAddr, dllPathBytes, (uint) dllPathBytes.Length + 1, out bytesWritten);

if (dllPathBytes.Length + 1 != bytesWritten || Marshal.GetLastWin32Error() != 0)
    throw new Win32Exception(Marshal.GetLastWin32Error(),
                             String.Format(
                                 "Unable to write data to allocated memory ({0} of {1} bytes written)",
                                 bytesWritten, dllPathBytes.Length));

uint tId;
IntPtr ipThread = WinApi.CreateRemoteThread(hProcess, IntPtr.Zero, 0, llAddr, dllAddr, 0, out tId);

if (ipThread == IntPtr.Zero || Marshal.GetLastWin32Error() != 0)
    throw new Win32Exception("Unable to load dll"); 

Test DLL:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBox(NULL, "This is a message from your injected DLL!", "Dll Injection Test", MB_OK);
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

[ Voor 30% gewijzigd door IceM op 03-08-2009 10:53 ]

...


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Windows XP 64-bit en Windows Vista 64-bit kunnen niet beepen. So I asume dat Windows 7 64-bit dat ook niet kan.

http://msdn.microsoft.com.../ms679277%28VS.85%29.aspx

Doe eens wat anders met je DLL? Schrijf wat weg naar een bestand ofzo?

[ Voor 44% gewijzigd door Snake op 02-08-2009 16:19 ]

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Ik heb het ook geprobeerd met een DLL die een message box laat zien, dat werkt ook prima op XP maar ook niet op Windows 7. Toch bedankt voor de tip :).

...


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

En dit eens gebruiken in je C++ DLL? Misschien kan je daar een debugger op attachen? : http://msdn.microsoft.com....debugger.isattached.aspx

Maar ik weet wel niet hoe je vanuit een applicatie een venster lanceert wat je VRAAGT om de debugger te attachen.

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Snake schreef op zondag 02 augustus 2009 @ 16:28:
En dit eens gebruiken in je C++ DLL? Misschien kan je daar een debugger op attachen? : http://msdn.microsoft.com....debugger.isattached.aspx

Maar ik weet wel niet hoe je vanuit een applicatie een venster lanceert wat je VRAAGT om de debugger te attachen.
Aangezien de code in mijn dll helemaal niet uitgevoerd lijkt te worden (geen beep, geen messagebox), hoe moet ik dan vragen om een debugger? Als ik kan vragen om een debugger dan is mijn probleem feitelijk al opgelost...

...


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Aangezien je niet weet wat er gebeurt (Wordt DllMain uberhaupt aangeroepen?) kan je in de DllMain vragen voor de debugger.

Soms krijg je bij een crash van een app een venster met de vraag: wil je een debugger attachen. Als ik nu eens wist hoe je dat kon vragen vanuit je code zelf (helaas moet ik je dat antwoord schuldig blijven), dan wist je zeker dat hij kwam in die DllMain.

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Snake schreef op zondag 02 augustus 2009 @ 16:40:
Aangezien je niet weet wat er gebeurt (Wordt DllMain uberhaupt aangeroepen?) kan je in de DllMain vragen voor de debugger.

Soms krijg je bij een crash van een app een venster met de vraag: wil je een debugger attachen. Als ik nu eens wist hoe je dat kon vragen vanuit je code zelf (helaas moet ik je dat antwoord schuldig blijven), dan wist je zeker dat hij kwam in die DllMain.
Hij komt op windows 7 dus niet in die dllmain, de dll lijkt gewoon niet uitgevoerd te worden. Als test heb ik de messagebox nog even buiten de switch gezet, ook dit geeft geen resultaat op windows 7 (en 2 message boxen tijdens het injecteren en 1 tijdens het afsluiten van het programma waarin de dll in is geinjecteerd op XP).

...


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:34
Wat een zootje is die injector-code zeg. Je gebruikt afwisselend IntPtr.Zero en (IntPtr)NULL, je process handle heet "hwnd", en je reserveert geheugen (met executable permissie bovendien) op basis van dllPath.Length (die je dan maar weer naar een IntPtr cast terwijl het geen pointer is) en vervolgens schrijf je er een andere string naartoe, waarbij je bovendien het afsluitende nulkarakter vergeet. Waarschijnlijk gaat dit door omstandigheden allemaal toevallig net goed maar netjes is anders. Ik vraag me af of je wel doorhebt waar je mee bezig bent.

Verder misbruik je de pointer naar LoadLibraryA uit je lokale procesruimte (!) als thread procedure in je doelproces. Ik vermoed eigenlijk dat het hier ook op mis gaat: in 32-bits Windows zal kernel32.dll waarschijnlijk in alle processen op hetzelfde adres gemapt worden maar waarschijnlijk komt het adres van LoadLibrary in je 64-bits injector niet overeen met die in de 32-bits applicatie (die uberhaupt een compleet ongerelateerde set 32-bits libraries gebruikt).

Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Soultaker schreef op zondag 02 augustus 2009 @ 17:57:
Wat een zootje is die injector-code zeg. Je gebruikt afwisselend IntPtr.Zero en (IntPtr)NULL, je process handle heet "hwnd", en je reserveert geheugen (met executable permissie bovendien) op basis van dllPath.Length (die je dan maar weer naar een IntPtr cast terwijl het geen pointer is) en vervolgens schrijf je er een andere string naartoe, waarbij je bovendien het afsluitende nulkarakter vergeet. Waarschijnlijk gaat dit door omstandigheden allemaal toevallig net goed maar netjes is anders. Ik vraag me af of je wel doorhebt waar je mee bezig bent.

Verder misbruik je de pointer naar LoadLibraryA uit je lokale procesruimte (!) als thread procedure in je doelproces. Ik vermoed eigenlijk dat het hier ook op mis gaat: in 32-bits Windows zal kernel32.dll waarschijnlijk in alle processen op hetzelfde adres gemapt worden maar waarschijnlijk komt het adres van LoadLibrary in je 64-bits injector niet overeen met die in de 32-bits applicatie (die uberhaupt een compleet ongerelateerde set 32-bits libraries gebruikt).
Deze injector code is gebaseerd op een tutorial van internet, ik heb dit inderdaad nog nooit eerder gedaan zoals in de start post ook is aangegeven.

Buiten het feit dat deze implementatie niet netjes is werkt het ook met verschillende andere injectors die ik in binary vorm van het internet heb gehaald niet op Windows 7 en wel op XP, totdat ik deze heb uitgeprobeerd, hiermee werkt het wel.

Over het misbruiken van de pointer naar de LoadLibraryA functie: dit is inderdaad de gedachtengang achter de code, in elk process wordt (werd?) dit hetzelfde gemapped. Zie quote van artikel op codeproject:
The first problem is actually solved by itself. Both LoadLibrary and FreeLibray are functions residing in kernel32.dll. Because kernel32 is guaranteed to be present and at the same load address in every "normal" process (see Appendix A), the address of LoadLibrary/FreeLibray is the same in every process too. This ensures that a valid pointer is passed to the remote process.

[ Voor 14% gewijzigd door IceM op 02-08-2009 19:08 ]

...


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:34
IceM schreef op zondag 02 augustus 2009 @ 18:57:
Buiten het feit dat deze implementatie niet netjes is werkt het ook met verschillende andere injectors die ik in binary vorm van het internet heb gehaald niet op Windows 7 en wel op XP
Tja, in dit subforum gaat het over zelf programmeren, niet over tooltjes van internet halen die misschien wel of niet werken. Wie zegt dat al die andere injectors die ook niet werken niet op dezelfde tutorial gebaseerd zijn?
Over het misbruiken van de pointer naar de LoadLibraryA functie: dit is inderdaad de gedachtengang achter de code, in elk process wordt (werd?) dit hetzelfde gemapped.
Ik kan je wel haast garanderen dat die anders gemapt wordt in een 32-bits proces dan in een 64-bits proces. Bestaat er ook een Dependency Walker voor 64-bit Windows? Zo ja, dan kun je 't snel genoeg daarmee uitzoeken.

Het lijkt me dus dat je een manier moet bedenken om het adres van LoadLibrary in het remote proces uit te zoeken... tricky. Hardcoden is misschien wel het simpelste als je er toch van uit gaat dat die waarde constant is.

Acties:
  • 0 Henk 'm!

  • barber
  • Registratie: Oktober 2001
  • Niet online
Een andere manier om code te injecteren in een ander process zonder dat je afhankelijk bent van de locaties van WinAPI functies is het gebruiken van windows hooks. Zoek maar op SetWindowsHookEx.

Als je een hook zet op een ander process (of alle processen tegelijk:system-wide hook) moet je gebruik maken van een aparte dll die in het target process geladen wordt. Dus je hebt minimaal een dll en een executable nodig. De dll bevat jouw code die je wilt uitvoeren in het andere process. En met de executable zet je de hook op het andere process. Je kan bijvoorbeeld een hook zetten op windows messages.

Je kan beter wat voorbeelden hiervan op internet zoeken, want echt simpel is het ook niet. Verder kan je alleen een 32bits dll in een 32 bits process laden en een 64bits dll in een 64bit process, andere comibinaties dus niet.

En als je applicatie die de hook heeft gezet stopt, dan worden ook de hooks weer weggehaald. (Dit is met een trucje wel weer te voorkomen.)

Dit heb ik regelmatig gedaan in het windows 2000 tijdperk, maar sindsdien zijn virusscanner hier wat meer op bedacht en ook moet je administrator rechten (of debug rechten) hebben om dit uit te kunnen voeren.

Ook in het boek Advanced Windows van Jeffrey Richter gaat hoofdstuk 18 helemaal over dit onderwerp. 70 pagina's over het uitvoeren van code in een ander process.

Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Ik weet niet of ik misschien het idee heb gegeven dat de dll en/of de applicatie waarin het geïnjecteerd moet worden 64bit versies zijn, dit is dus niet zo, enkel het OS is 64 bit. Aangezien het op XP wel werkt maar op mijn Windows 7 installatie niet heb ik dit erbij vermeld.

En ja, dat dit over zelf programmeren gaat snap ik ook wel, maar als ik iets nog nooit heb gedaan dan zal ik toch ergens de informatie vandaan moeten halen en toch ergens mee moeten testen, niet? In alle documentatie die ik kan vinden wordt gemeld dat het address van kernel32 in elk process gelijk is, maar dat dit adres wel (kan) wijzigen na een reboot.

...


Acties:
  • 0 Henk 'm!

  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 18-08 21:31
IceM schreef op zondag 02 augustus 2009 @ 18:57:
Over het misbruiken van de pointer naar de LoadLibraryA functie: dit is inderdaad de gedachtengang achter de code, in elk process wordt (werd?) dit hetzelfde gemapped. Zie quote van artikel op codeproject:
The first problem is actually solved by itself. Both LoadLibrary and FreeLibray are functions residing in kernel32.dll. Because kernel32 is guaranteed to be present and at the same load address in every "normal" process (see Appendix A), the address of LoadLibrary/FreeLibray is the same in every process too. This ensures that a valid pointer is passed to the remote process.
[/quote]
Waar hij zegt "guaranteed" bedoelt hij "in oudere versies van Windows leek het er op dat het adres altijd hetzelfde was. Dat die plaats echt vast ligt is nooit door Microsoft gegarandeerd.

In nieuwere versies van Windows wordt address space layout randomization gebruikt waardoor kernel32.dll niet altijd op dezelfde plaats in het geheugen laadt, je zult dus een andere manier moeten bedenken om die thread te starten.

hmm, misschien gebeurt dit alleen tussen reboots... zit ik toch fout.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:34
@_js_: die randomization geldt, neem ik aan, alleen voor 64-bit processen en niet voor 32-bit processen zoals die van de TS (zou anders allerlei compatibiliteitsproblemen veroorzaken) dus dan kun je in principe nog steeds het adres van LoadLibrary hardcoden.

(En ja, dat is lelijk, maar doe een beter voorstel. ;))

[ Voor 10% gewijzigd door Soultaker op 03-08-2009 02:36 ]


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Ik heb de injector code wat netter geschreven en het gehele project expliciet als x86 code gecompileerd. Na elke api call controleer ik de win32 error code. CreateRemoteThread lijkt niet te werken. Ik krijg als error code 5 terug (ERROR_ACCESS_DENIED). Ik heb alle flags na gelopen en de constante int waarden nogmaals gecontroleerd en dit lijkt allemaal in orde. Het openen van het process met PROCESS_CREATE_THREAD geeft ook geen error code dus dit lijkt ook goed te gaan.

UAC is uitgeschakeld, ik krijg deze error ook wanneer ik de binary expliciet met "Run as administrator" uitvoer, maar dit heeft geen extra effect wanneer UAC al uit staat.

Ik heb daarom een kleine test gedaan en wanneer ik bijvoorbeeld notepad zelf opstart binnen mijn injector en dan de dll injecteer dan gaat dit wel goed, ik lijk dus geen rechten te hebben voor het uitvoeren van CreateRemoteThread terwijl het process wel geopend is met PROCESS_CREATE_THREAD.

Op internet zijn diverse posts te vinden met mensen die hetzelfde probleem op vista hebben, maar verder als "run as administrator" komen de antwoorden niet...

...


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

The handle must have the PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights. For more information, see Process Security and Access Rights.
Dus niet alleen PROCESS_CREATE_THREAD is nodig voor de CreateRemoteThread(), maar ik zie dat je die rights wel al hebt staan. Desalniettemin, probeer het eens met PROCESS_ALL_ACCESS.

[ Voor 46% gewijzigd door .oisyn op 03-08-2009 11:29 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:34
Krijg je niet gewoon ACCESS_DENIED omdat het adres van je thread functie op een niet-gemapt adres ligt? Ik kan me voorstellen dat het entry point in een executable page moet liggen. Je kunt dit verifiëren door in 32-bits (wat wél werkt) expres een niet-gemapt adres mee te geven en dan te kijken of je dezelfde foutmelding geeft (de MSDN pagina over CreateRemoteThread geeft er niet echt uitsluitsel over).
IceM schreef op maandag 03 augustus 2009 @ 10:52:
Ik heb de injector code wat netter geschreven en het gehele project expliciet als x86 code gecompileerd. Na elke api call controleer ik de win32 error code.
Goed begin. :) Ik vermoed dat het probleem toch uiteindelijk neerkomt op het adres van LoadLibrary uitzoeken, maar code opschonen en error checking toevoegen is altijd een goed idee als je code niet werkt zoals verwacht.

[ Voor 174% gewijzigd door Soultaker op 03-08-2009 11:30 ]


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Het adres van de thread functie is het adres naar loadlibrarya right? (zodat ik zeker weet dat we over hetzelfde praten). De pointer naar dit adres is (zonder te rebooten) altijd hetzelfde. Wanneer ik de pointer naar deze functie verander in een random waarde en mijn dll dan probeer te injecten in het process dat ik zelf opstart crashed deze applicatie (notepad). Wanneer ik het adres naar deze functie verander wanneer ik de dll wil injecteren in een proces dat ik niet zelf heb opgestart dan gebeurt er niets, maar krijg ik wederom ERROR_ACCESS_DENIED terug (de applicatie blijft verder dus gewoon draaien).

Het lijkt er dus op dat die access denied error expliciet geld voor CreateRemoteThread, en niet omdat de threadpointer niet geldig is en dat de applicatie daarom geen rechten heeft om dit uit te voeren.

[ Voor 3% gewijzigd door IceM op 03-08-2009 11:43 ]

...


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:34
IceM schreef op maandag 03 augustus 2009 @ 11:42:
Het adres van de thread functie is het adres naar loadlibrarya right? [..] Wanneer ik de pointer naar deze functie verander in een random waarde en mijn dll dan probeer te injecten in het process dat ik zelf opstart crashed deze applicatie
Dat bedoelde ik inderdaad, en blijkbaar was dat 't niet. :) Je kunt .oisyn's suggestie nog proberen...

Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 18-09 09:28
Met PROCESS_ALL_ACCESS werkt het helaas ook niet, zelfde melding ... :(

Edit: Hm, wat ben ik een eikel :X
Wanneer ik notepad via win+r opstart dan is het notepad.exe process 64bits, wanneer ik notepad vanuit mijn applicatie opstart dan is deze 32 bits. Het injecteren werkt gewoon prima in een 32bits process.... Sorry.

Bedankt voor de hulp allemaal... _/-\o_

[ Voor 67% gewijzigd door IceM op 03-08-2009 18:32 ]

...

Pagina: 1