Toon posts:

[C++]Visual Studio - Geen/corrupte DLLMain export

Pagina: 1
Acties:

Vraag


  • sh4d0wman
  • Registratie: April 2002
  • Laatst online: 11:28

sh4d0wman

Long live the King!

Topicstarter
Probleem: Ik probeer een simpele DLL te maken maar krijg DLLMain niet exported e.g. niet terug te vinden met "Dll Export Viewer" en de foutmelding met rundll32.exe dat DllMain niet gevonden kan worden.

Code:
Ik ben begonnen met de standaard code van Visual Studio 2017:
(nieuw project -> Visual C++, Win Desktop, DLL)

Met deze code wordt DllMain niet geexporteerd in zowel de x86 als x64 builds wanneer ik naar release compile.

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
dllmain.cpp

BOOL WINAPI DllMain(  
         HINSTANCEhinstDLL,  // handle to DLL module
         DWORD fdwReason,     // reason for calling function
         LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH:
        // Initialize once for each new process.
        // Return FALSE to fail DLL load.
        WinExec("calc", 1);            
        break;

    case DLL_THREAD_ATTACH:        
        // Do thread-specific initialization.
        break;        
   
    case DLL_THREAD_DETACH:
        // Do thread-specific cleanup.            
        break;
   
    case DLL_PROCESS_DETACH:        
        // Perform any necessary cleanup.
        break;    
    }
        return TRUE;
}


Verander ik het naar:
A.
C++:
1
BOOL APIENTRY DllMain of BOOL  __stdcall DllMain

dan is er nog steeds geen DllMain export terug te vinden in zowel de x86 als x64 DLL.

Neem ik de volgende code op:
B.
C++:
1
2
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
EXTERN_DLL_EXPORT BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)


Dan is in een x64 build DllMain terug te vinden en werkt de DLL (start calc.exe of MsgBox).
Getest door de DLL in een applicatie te injecteren en te laden met "rundll32.exe test.dll DllMain"

Echter in een x86 build is DllMain mangled en geeft _DLLMain@12 als export in "Dll Export Viewer".
De applicatie wil de DLL niet laden en rundll32 geeft de foutmelding "Missing entry: DLLMain" maar start wel calc.exe of de MsgBox.

Waarom werkt DllMain niet "by default" en hoe los ik dit op?
Het zal wel iets simpels zijn maar ik zit er al een week op vast. :-(

Being a hacker does not say what side you are on. Being a hacker means you know how things actually work and can manipulate the way things actually work for good or for harm.
Come to the dark side. We've got cookies.

Beste antwoord (via sh4d0wman op 12-10-2018 12:54)


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:28

.oisyn

Moderator Devschuur® / Cryptocurrencies

Demotivational Speaker

Maar dat komt dus omdat je een functie probeert aan te roepen die niet bedoelt is voor rundll. Het probleem zit 'm in de __stdcall name mangling. In 32 bits geeft dat een leading underscore en een @ postfix met het aantal bytes van de parameters: _DllMain@12. In 64 bits wordt de name helemaal niet gemangled: DllMain

En daar zit dus het probleem. Want rundll zoekt een __stdcall functie die voldoet aan zijn signature: 3 pointers en een int, in totaal 16 bytes, dus _DllMain@16. Jouw DllMain's signature klopt niet met wat rundll verwacht, en daarom is ie verkeerd gemangled.

(De technische reden voor het verschil is dat __stdcall in 64 bits eigenljik niet meer bestaat en gewoon wordt geignored. Bij __stdcall is de callee verantwoordelijk voor het opruimen van de parameters die door de caller op de stack zijn gepushed, dmv een RET n instructie. In 64 bits zijn er geen verschillende calling conventions; de enige convention is dat de eerste parameters via registers gaan, de rest op de stack wordt gepushed, en caller is verantwoordelijk voor opruiming.

Het probleem bij __stdcall is dat als de caller een andere hoeveelheid parameters passt dan de callee verwacht, je stack corruption krijgt. Vandaar dat het aantal bytes van de parameters in de name mangling staan.)

[Voor 46% gewijzigd door .oisyn op 12-10-2018 12:46]

If I had a dollar for every time I didn't know what was going on, I'd be like: "Why am I always getting all this money?!"

Alle reacties


  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

DllMain is normaliter geen export. Je maakt aparte functies die je export en die roep je aan (ofwel met rundll32 of vanuit een ander programma). DllMain wordt aangeroepen zodra de DLL wordt geladen of geunload; hierin kan je globale initialisatie doen, resources vrijgeven, etc. mocht je dat nodig hebben.

https://docs.microsoft.co...dows/desktop/dlls/dllmain
MSDN: Exporting from a DLL Using __declspec(dllexport)

  • sh4d0wman
  • Registratie: April 2002
  • Laatst online: 11:28

sh4d0wman

Long live the King!

Topicstarter
@Radiant: dat snap ik als we een volwaardige dll willen bouwen. Echter is het in dit geval genoeg dat DllMain een stukje code draait, ik wil geen export beschikbaar maken.

Inmiddels heb ik onderstaande code:

Op x86 kan een process deze laden en uitvoeren. Echter rundll32 kan nog steeds geen DllMain vinden.
Op x64 kan een process deze laden en uitvoeren. Ook kan rundll32 DllMain vinden.
Dus opzich kan ik nu verder maar lijkt er met VisualStudio by design iets niet lekker te gaan met een x86 build.

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
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <windows.h>

int dll_hijack();

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        dll_hijack();
        return 0;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

int dll_hijack()
{
    MessageBox(0, L"DLL Hijacking!", L"DLL Message", MB_OK);
    return 0;
}

Being a hacker does not say what side you are on. Being a hacker means you know how things actually work and can manipulate the way things actually work for good or for harm.
Come to the dark side. We've got cookies.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:28

.oisyn

Moderator Devschuur® / Cryptocurrencies

Demotivational Speaker

Je haalt wat dingen door elkaar. Wat rundll specificeert als "entry point" is niet je DLL entry point. Het enige dat rundll doet is je dll laden met LoadLibrary(), en hij gaat dan op zoek naar de opgegeven "entry point" met GetProcAddress(). Daarvoor moet de opgegeven functie natuurlijk wel ook geexport worden. Bovendien moet hij voldoen aan de volgende signature:
C++:
1
void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);


Je DLL entry point is een stuk code dat wordt aangeroepen zodra je DLL wordt geladen in een proces. Die hoef je niet te exporteren.

If I had a dollar for every time I didn't know what was going on, I'd be like: "Why am I always getting all this money?!"


  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

Misschien is het handig om even te beschrijven wat je uiteindelijk wil bereiken. Als je een DLL wil die alleen iets uitvoert als LoadLibrary() wordt aangeroepen, dan kan je af met een DllMain. Dan heb je echter een vrij nutteloze DLL.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:28

.oisyn

Moderator Devschuur® / Cryptocurrencies

Demotivational Speaker

Radiant schreef op vrijdag 12 oktober 2018 @ 11:50:
Misschien is het handig om even te beschrijven wat je uiteindelijk wil bereiken. Als je een DLL wil die alleen iets uitvoert als LoadLibrary() wordt aangeroepen, dan kan je af met een DllMain. Dan heb je echter een vrij nutteloze DLL.
Aan zijn code te zien probeert hij gewoon custom code te injecteren in een andere executable (dmv "dll hijacking"). Dan heb je genoeg aan het gewoon het inladen van je DLL in het andere proces. Zijn code testen met rundll is dan niet zo handig, tenzij hij gewoon voor de vorm een dummy functie opneemt en die exporteert.

[Voor 11% gewijzigd door .oisyn op 12-10-2018 12:22]

If I had a dollar for every time I didn't know what was going on, I'd be like: "Why am I always getting all this money?!"


  • sh4d0wman
  • Registratie: April 2002
  • Laatst online: 11:28

sh4d0wman

Long live the King!

Topicstarter
@.oisyn
Ja, ik snap inmiddels dat DllMain niet geexporteerd hoeft te worden echter verklaart dit niet het afwijkende gedrag wat ik nu zie.

Waarom kan rundll32 DllMain wel vinden als ik voor x64 compileer maar niet voor x86? (dus zonder een export functie van DllMain te maken)

Beiden injecteren nu wel zonder probleem in het process.

@Radiant

Ik wil inderdaad simpelweg code injecteren in een bepaald process. Tot op heden was het genoeg om een simple calc.exe of messagebox via Metasploit DLL te gebruiken om DLL hijacking aan te tonen.

Echter nu wil ik DLL hijacking gebruiken om op een bepaalde locatie iets aan te passen door misbruik te maken van het vertrouwde process waar ik in injecteer. Hiervoor moet ik dus wat custom commando's laten uitvoeren hence the need om zelf te compilen vanaf scratch.

Het enige wat de DLL moet doen is wat code uitvoeren in DllMain. Dus vanuit programmeer gebied gezien een nutteloze DLL ja. :P

Being a hacker does not say what side you are on. Being a hacker means you know how things actually work and can manipulate the way things actually work for good or for harm.
Come to the dark side. We've got cookies.


  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

De tweede stap die rundll32 doet is niet zo interessant voor je (het opzoeken van de function/ordinal van de export die je wil uitvoeren) dus je kan beter gewoon een C-programmatje maken dat alleen LoadLibrary() aanroept om te testen.

De reden waarom rundll32 de x86 versie niet pakt zal wellicht zijn omdat je een 64-bit-OS hebt en de 32-bit-versie (WOW) van rundll32 moet hebben (naam is verwarrend ja, ze heten allemaal rundll32). Die staat in C:\Windows\SysWOW64.

  • sh4d0wman
  • Registratie: April 2002
  • Laatst online: 11:28

sh4d0wman

Long live the King!

Topicstarter
@Radiant bedankt ik zal er vanavond nog eens naar kijken. Ik compileer op 64-bit en zet dan de x86 DLL over naar een 32-bit virtual machine. Volgens mij heb ik rundll32 op beide omgevingen getest maar heb inmiddels begrepen dat het weinig zinvol is om als test te gebruiken tenzij ik een export in de DLL heb zitten.

Being a hacker does not say what side you are on. Being a hacker means you know how things actually work and can manipulate the way things actually work for good or for harm.
Come to the dark side. We've got cookies.


Acties:
  • Beste antwoord
  • +1Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:28

.oisyn

Moderator Devschuur® / Cryptocurrencies

Demotivational Speaker

Maar dat komt dus omdat je een functie probeert aan te roepen die niet bedoelt is voor rundll. Het probleem zit 'm in de __stdcall name mangling. In 32 bits geeft dat een leading underscore en een @ postfix met het aantal bytes van de parameters: _DllMain@12. In 64 bits wordt de name helemaal niet gemangled: DllMain

En daar zit dus het probleem. Want rundll zoekt een __stdcall functie die voldoet aan zijn signature: 3 pointers en een int, in totaal 16 bytes, dus _DllMain@16. Jouw DllMain's signature klopt niet met wat rundll verwacht, en daarom is ie verkeerd gemangled.

(De technische reden voor het verschil is dat __stdcall in 64 bits eigenljik niet meer bestaat en gewoon wordt geignored. Bij __stdcall is de callee verantwoordelijk voor het opruimen van de parameters die door de caller op de stack zijn gepushed, dmv een RET n instructie. In 64 bits zijn er geen verschillende calling conventions; de enige convention is dat de eerste parameters via registers gaan, de rest op de stack wordt gepushed, en caller is verantwoordelijk voor opruiming.

Het probleem bij __stdcall is dat als de caller een andere hoeveelheid parameters passt dan de callee verwacht, je stack corruption krijgt. Vandaar dat het aantal bytes van de parameters in de name mangling staan.)

[Voor 46% gewijzigd door .oisyn op 12-10-2018 12:46]

If I had a dollar for every time I didn't know what was going on, I'd be like: "Why am I always getting all this money?!"


  • sh4d0wman
  • Registratie: April 2002
  • Laatst online: 11:28

sh4d0wman

Long live the King!

Topicstarter
@.oisyn
Aha, dus als ik het nu goed begrijp kun je met rundll wel een export aanroepen (waarbij je als je niet oppast ook name mangling kan krijgen) maar niet DllMain.

Ik was er al wel achter dat er name mangling optrad en had er voor exports ook over gelezen. (toen ik DllMain exporteerde zag ik al dat deze in x64 normaal is en in x86 __DLLMain@12 ofzo werd)

Echter het verschil in loaden via een process vs rundll was het grote onbekende voor mij.
Dus daar kan ik het beste een simpele loader voor schrijven of domweg de DLL bij het target proces droppen.

edit: je edit verduidelijkt het maar toen had ik al gepost :+

[Voor 5% gewijzigd door sh4d0wman op 12-10-2018 12:54]

Being a hacker does not say what side you are on. Being a hacker means you know how things actually work and can manipulate the way things actually work for good or for harm.
Come to the dark side. We've got cookies.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 11:28

.oisyn

Moderator Devschuur® / Cryptocurrencies

Demotivational Speaker

sh4d0wman schreef op vrijdag 12 oktober 2018 @ 12:53:
@.oisyn
Aha, dus als ik het nu goed begrijp kun je met rundll wel een export aanroepen (waarbij je als je niet oppast ook name mangling kan krijgen) maar niet DllMain.
Hij mag best DllMain heten, als hij maar 16 bytes aan parameters heeft. En dat conflicteert natuurlijk met het standaard entrypoint dat de crt verwacht voor je dll ;)

If I had a dollar for every time I didn't know what was going on, I'd be like: "Why am I always getting all this money?!"

Pagina: 1


Tweakers maakt gebruik van cookies

Tweakers plaatst functionele en analytische cookies voor het functioneren van de website en het verbeteren van de website-ervaring. Deze cookies zijn noodzakelijk. Om op Tweakers relevantere advertenties te tonen en om ingesloten content van derden te tonen (bijvoorbeeld video's), vragen we je toestemming. Via ingesloten content kunnen derde partijen diensten leveren en verbeteren, bezoekersstatistieken bijhouden, gepersonaliseerde content tonen, gerichte advertenties tonen en gebruikersprofielen opbouwen. Hiervoor worden apparaatgegevens, IP-adres, geolocatie en surfgedrag vastgelegd.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Toestemming beheren

Hieronder kun je per doeleinde of partij toestemming geven of intrekken. Meer informatie vind je in ons cookiebeleid.

Functioneel en analytisch

Deze cookies zijn noodzakelijk voor het functioneren van de website en het verbeteren van de website-ervaring. Klik op het informatie-icoon voor meer informatie. Meer details

janee

    Relevantere advertenties

    Dit beperkt het aantal keer dat dezelfde advertentie getoond wordt (frequency capping) en maakt het mogelijk om binnen Tweakers contextuele advertenties te tonen op basis van pagina's die je hebt bezocht. Meer details

    Tweakers genereert een willekeurige unieke code als identifier. Deze data wordt niet gedeeld met adverteerders of andere derde partijen en je kunt niet buiten Tweakers gevolgd worden. Indien je bent ingelogd, wordt deze identifier gekoppeld aan je account. Indien je niet bent ingelogd, wordt deze identifier gekoppeld aan je sessie die maximaal 4 maanden actief blijft. Je kunt deze toestemming te allen tijde intrekken.

    Ingesloten content van derden

    Deze cookies kunnen door derde partijen geplaatst worden via ingesloten content. Klik op het informatie-icoon voor meer informatie over de verwerkingsdoeleinden. Meer details

    janee