How can we run a simple DLL file that does not have any export function directly (from within Windows and without using any other code), for example using rundLL?
Assume that the DLL code is in the simplest possible form:
#include <windows.h>
#pragma comment (lib, "user32.lib")
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
MessageBox(
NULL,
"HI!",
MB_OK
);
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
Now, if we want to run this DLL (for example, for testing) directly from within Windows (due to the lack of any export function), what should we do?
Related
I try to hook CopyFile2 function, for that I wrote this dll:
#include "..\..\..\minhook-1.3.3\include\MinHook.h"
WCHAR msgbuf[1024];
#define DbgPrint(format, ...) wsprintf(msgbuf, format, __VA_ARGS__); \
OutputDebugString(msgbuf);
#if defined _M_X64
#pragma comment(lib, "libMinHook.x64.lib")
#elif defined _M_IX86
#pragma comment(lib, "libMinHook.x86.lib")
#endif
typedef HRESULT(WINAPI *COPY_FILE_2)(
_In_ PCWSTR pwszExistingFileName,
_In_ PCWSTR pwszNewFileName,
_In_opt_ COPYFILE2_EXTENDED_PARAMETERS *pExtendedParameters
);
COPY_FILE_2 fpCopyFile2 = NULL;
HRESULT WINAPI DetourCopyFile2(
_In_ PCWSTR pwszExistingFileName,
_In_ PCWSTR pwszNewFileName,
_In_opt_ COPYFILE2_EXTENDED_PARAMETERS *pExtendedParameters
)
{
DbgPrint(L"=> DetourCopyFile2\n");
DbgPrint(L"DetourCopyFile2.pwszExistingFileName = %ws\n", pwszExistingFileName);
DbgPrint(L"DetourCopyFile2.pwszNewFileName = %ws\n", pwszNewFileName);
return fpCopyFile2(pwszExistingFileName, pwszNewFileName, pExtendedParameters);
}
void InstallHook()
{
DbgPrint(L"=> InstallHook\n");
// Initialize MinHook.
if (MH_Initialize() != MH_OK)
{
DbgPrint(L"failed MH_Initialize\n");
return;
}
if (MH_CreateHook(&CopyFile2, &DetourCopyFile2, (LPVOID*)&fpCopyFile2) != MH_OK)
{
DbgPrint(L"failed MH_CreateHook\n");
}
else
{
if (MH_EnableHook(&CopyFile2) != MH_OK)
{
DbgPrint(L"failed MH_EnableHook\n");
}
}
}
HINSTANCE hInstance = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DbgPrint(L"DLL_PROCESS_ATTACH");
hInstance = hinstDLL;
DisableThreadLibraryCalls(hInstance);
InstallHook();
break;
}
return TRUE;
}
When I inject this dll to test prograg its work, and DebugView shows the Messages:
int main()
{
OutputDebugString(L"=> main");
printf("inject now");
getchar();
CopyFile2(L"", L"", NULL);
system("pause");
return 0;
}
I run the CSharpConsole64.exe from Deviare2 project, and I saw that Explorer uses CopyFile2 function to copy files.
My question is why when I inject this DLL into the Windows Explorer I get Only the first messages that the hook was successful, but when I copy a file there are no messages from the detour function in the DebugView? And how can I solve the problem?
I am using Visual Studio 2017 Operating System Windows 10 64 bit.
I also tried Hook ReadFile function and I got messages in debugview but not every time I copied a file, something here is not clear to me what is different in Explorer, any help would be greatly appreciated.
I know it's been a long time since this question was asked. I am answering so that it may be helpful for others.
I have the same problem, tested it with Detours. With the same test program you used I see DebugView messages in hooked CopyFile2 function but explorer.exe does not show any messages in hooked function. Also same as you for explorer.exe I saw that hooking was successful from DetourAttach function in DllMain.
After analyzing call stack of CopyFile2 in explorer.exe using x64dbg, I found that CopyFile2 in kernel32.dll is never called. I realized that both kernel32.dll and kernelbase.dll have a CopyFile2 function with the same signature. I saw that actually CopyFile2 function in kernelbase.dll is called instead of the one in kernel32.dll.
I think your hooking is certainly correct but you are hooking CopyFile2 from kernel32.dll. Since explorer.exe uses CopyFile2 from kernelbase.dll instead of kernel32.dll the hooking function is never called. You need to hook CopyFile2 from kernelbase.dll.
I haven't used MinHook before but it should support hooking a function in a specified module. Detours library has DetourFindFunction to get address of a function in specified module. Or you can try using GetProcAddress.
I have made a short DLL which will display a MessageBox when it is loaded by a process. However, the message box is displayed two times. So, it looks like the DLL Export Function is called twice. Could you please see if there is something that needs to be modified in my code so that the MessageBox is displayed only once?
#include <windows.h>
#include <stdio.h>
__declspec(dllexport) void sampledllmain()
{
MessageBox(NULL, "sample text","sample title", 0);
}
BOOL APIENTRY DllMain(HANDLE hHandle, DWORD dwReason, LPVOID Reserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
sampledllmain();
break;
}
return 1;
}
I load the DLL using rundll32 as shown below:
rundll32 "path to DLL",sampledllmain
As described by the information page about rundll32.exe :
It loads the specified DLL via LoadLibrary().
It obtains the address of the function via GetProcAddress().
It calls the function, passing the command line tail which is the .
In your case :
When LoadLibrary() is invoked it calls DllMain, in your case DllMain calls sampledllmain.
Obtains the address of sampledllmain
calls sampledllmain ( second time ).
to fix your issue:
add a second function dummy (for example )
use rundll32 "path to DLL",dummy on the command line
PS: On an additional note you could also debug your dll there are guides that describe how to do it
An example with MessageBox call in dllMain
#include <windows.h>
__declspec(dllexport) void sample(void)
{
MessageBox(NULL, "sample text test", "sample title test", 0);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, "attach", "DllMain", 0);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
MessageBox(NULL, "detach", "DllMain", 0);
break;
}
return TRUE;
}
Run with rundll32.exe lib.dll,sample
Is it possible to initialise CRITICAL_SECTION statically, as in pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER?
In other words, is it possible in C to initialize a global CRITICAL_SECTION inside a library without having to mess with DllMain etc.?
Yes, simply initialize in DLL_PROCESS_ATTACH and delete in DLL_PROCESS_DETACH
CRITICAL_SECTION g_cs = {0};
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // 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.
InitializeCriticalSection(&g_cs);
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.
DeleteCriticalSection(&g_cs);
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
References:
InitializeCriticalSection
DeleteCriticalSection
Yes! But you have to make sure that it's only done once per process.
But this is typically easiest to achieve by using the DLL_PROCESS_ATTACH case of the DLLMain
switch( fdwReason ) statement.
Another possibility in earlier versions is to instruct the linker to set a pointer to your initialization function as a user-defined global initializer. There is some discussion of this here:
http://msdn.microsoft.com/en-us/library/bb918180.aspx
Here is an example:
CRITICAL_SECTION criticalSection;
static void __cdecl Initialize(void) {
InitializeCriticalSection(&criticalSection);
}
#pragma section(".CRT$XCU", read)
__declspec(allocate(".CRT$XCU"))
const void (__cdecl *pInitialize)(void) = Initialize;
The answer above from Raymond Chen solves the problem: "You can use InitOnceExecuteOnce to initialize the critical section on first use. That's what PTHREAD_MUTEX_INITIALIZER does under the covers."
note that this will work on Vista and above only --- #rkosegi
You can do it for older versions of Windows by writing your own InitOnceExecuteOnce function using InterlockedCompareExchange. --- #RaymondChen
My program uses SetWindowsHookEx to set a global hook function in my DLL. However I want the DLL to work with a file so I need it a file to be opened once. I can't use DllMain's DLL_PROCESS_ATTACH because it's called multiple times. What is the best solution to my problem?
Use a static flag to tell whether you've initialized already or not.
void DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
static BOOL initialized = FALSE;
switch(dwReason) {
case DLL_PROCESS_ATTACH:
if(!initialized) {
// Perform initialization here...ex: open your file.
initialized = TRUE;
}
break;
case DLL_PROCESS_DETACH:
if(initialized) {
// Perform cleanup here...ex: close your file.
initialized = FALSE;
}
break;
};
}
I am using the tutorial on this MSDN link to implement a way of transferring data from one process to another. Although I was advised in an earlier question to use the Pipe methods, due to certain constraints I have no choice but to use the CreateFileMapping method.
Now, i've succesfully managed to make two seperate window form projects within the same solution and by editing some properties both of the forms load at the same time.
Furthermore I have managed to implement the code given in the MSDN sample into the first (Producer) and second (Consumer) program without any compilation errors.
The problem I am having now is when I run the first program and try to create the handle to the mapped file, I am given an error saying it was unsuccesful and I do not understand why this is happening.
I have added both the Producer and Consumer code files to demonstrate what I am trying to do.
Producer:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
//File header definitions
#define IDM_FILE_ROLLDICE 1
#define IDM_FILE_QUIT 2
#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[]=TEXT("Message from first process!");
void AddMenus(HWND);
LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
////Standard windows stuff - omitted to save space.
//////////////////////
// WINDOWS FUNCTION //
//////////////////////
LRESULT CALLBACK WindowFunc(HWND hMainWindow, UINT message,
WPARAM wParam, LPARAM lParam)
{
WCHAR buffer[256];
LPCTSTR pBuf;
struct DiceData storage;
HANDLE hMapFile;
switch(message)
{
case WM_CREATE:
{
// Create Menus
AddMenus(hMainWindow);
}
break;
case WM_COMMAND:
// Intercept menu choices
switch(LOWORD(wParam))
{
case IDM_FILE_ROLLDICE:
{
//Roll dice and store results in variable
//storage = RollDice();
////Copy results to buffer
//swprintf(buffer,255,L"Dice 1: %d, Dice 2: %d",storage.dice1,storage.dice2);
////Show via message box
//MessageBox(hMainWindow,buffer,L"Dice Result",MB_OK);
hMapFile = CreateFileMapping(
(HANDLE)0xFFFFFFFF, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
szName); // name of mapping object
if (hMapFile == NULL)
{
MessageBox(hMainWindow,L"Could not create file mapping object",L"Error",NULL);
return 1;
}
pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
MessageBox(hMainWindow,L"Could not map view of file",L"Error",NULL);
CloseHandle(hMapFile);
return 1;
}
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
_getch();
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
}
break;
case IDM_FILE_QUIT:
SendMessage(hMainWindow, WM_CLOSE, 0, 0);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hMainWindow, message, wParam, lParam);
}
//
//Setup menus
//
Consumer:
#include <windows.h>
#include <stdio.h>
#include <conio.h>
//File header definitions
#define IDM_FILE_QUIT 1
#define IDM_FILE_POLL 2
#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
//Prototypes
void AddMenus(HWND);
LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
//More standard windows creation, again omitted.
//////////////////////
// WINDOWS FUNCTION //
//////////////////////
LRESULT CALLBACK WindowFunc(HWND hMainWindow, UINT message,
WPARAM wParam, LPARAM lParam)
{
HANDLE hMapFile;
LPCTSTR pBuf;
switch(message)
{
case WM_CREATE:
{
// Create Menus
AddMenus(hMainWindow);
break;
}
case WM_COMMAND:
{
// Intercept menu choices
switch(LOWORD(wParam))
{
case IDM_FILE_POLL:
{
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
szName); // name of mapping object
if (hMapFile == NULL)
{
MessageBox(hMainWindow,L"Could not open file mapping object",L"Error",NULL);
return 1;
}
pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
MessageBox(hMainWindow,L"Could not map view of file",L"Error",NULL);
CloseHandle(hMapFile);
return 1;
}
MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
break;
}
case IDM_FILE_QUIT:
SendMessage(hMainWindow, WM_CLOSE, 0, 0);
break;
}
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hMainWindow, message, wParam, lParam);
}
//
//Setup menus
//
It's by no means tidy and final but it's just a start, thanks for any help.
Edit: Error
Edit2: Output
Your code for the producer works for me. What version of Windows are you using? In newer versions (like Vista and 7) there are additional security restrictions placed on accessing shared memory. There is a note about this in the MSDN article you referenced above, saying that you must be an Administrator to create global shared memory objects in Windows Vista/7.
You should also make a call to GetLastError() to see which error code is actually returned from CreateFileMapping(), that may be helpful in determining the root cause of the problem.
Ensure the Global name is unique; this can be done with a tool named Process Explorer.
If not unique, this would typically fail with Error code 6 (The handle is invalid) upon calling CreateFileMappinng
Download Process Explorer from SysInternals
Run Process Explorer
Run your application up to a break point just prior to the CreateFileMapping() failure
Search for MyFileMappingObject using Find (Ctrl-F)
If anything comes up such as another FileMap, Mutex, etc.. consider a more unique name, and ensure your application is not the one creating it.
Note: Consider naming your FileMapping using a GUID (File -> Tools > Create GUID) within Visual Studio
Maybe we learned from the same material/examples in the past. I had the same problem after migrating from XP to Windows 7:
NULL Handle return value and GetLastError() = 5.
ERROR_ACCESS_DENIED
5 (0x5)
Access is denied.
System Error Codes (0-499): http://msdn.microsoft.com/en-us/library/ms681382.aspx
I used a lpName with backslashes like in the Microsoft example from http://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx that you posted above.
After changing the name of the file mapping object (lpName) from "Global\MyFileMappingObject" to "GlobalMyFileMappingObject", the CreateFileMapping function works again under Windows 7 with no other changes.
"The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character ('\'). Creating a file mapping object in the global namespace from a session other than session zero requires the SeCreateGlobalPrivilege privilege. For more information, see Kernel Object Namespaces".
This is not just a name-change! If you need access to the global namespace, then you have to go the SeCreateGlobalPrivilege way.
Under Windows 7
I found:
OpenFileMapping(FILE_MAP_ALL_ACCESS, ...);
Causes issues with:
CreateFileMapping(
(HANDLE)0xFFFFFFFF, // use paging file
Try:
OpenFileMappingA(SECTION_MAP_WRITE | SECTION_MAP_READ,...);