Almost everything can be theoretically detected or prevented by going down to ring0. But drivers for game protection are often considered offensive.
Printable View
I would think that the guard page could be defeated by querying the protection on a page with VirtualQuery before actually accessing it. If it's flagged as a guard page, VirtualProtect can simply unset that flag (and then be used to reset it when done, of course). It doesn't appear that VirtualQuery actually triggers the STATUS_GUARD_PAGE_VIOLATION (which seems logical), but only attempting to access the memory. Of course, API hooking could be used to detect this type of detection, so I suppose this will always be about what side puts in more effort to defeat the other.
Indeed, since API hooking can be avoided by adding your own reversed or copied from ReactOS functions which totally replace the usermode part of the required functions.
But then again, the PE header page of the injected DLL can simply be wiped empty since it is of no use after the DLL has been loaded. Also the DLL must get rid of the MEM_IMAGE type of that page too, otherwise it would still look suspicious - 0x1000 byte section with MEM_IMAGE type followed by a larger section that after a small heuristics check looks like machine code would certainly look suspicious to more advanced detectors.
In ring3, there will probably be a limit how far you can go with this protection, but in ring0 there are practically no limits whatsoever.
Well, you can't test any posted theories with the above CloakDll code since it has issues. I've started putting together some code to analyze this cloaking utility. Unfortunately, CloakDll always causes the targeted app to crash when it is executed. Attaching OllyDbg to the running process prior to executing Cloakdll to determine the cause of the crash just doesn't provide any really helpful info.
I've resorted to using the author's other code, ManualMap for testing purposes sinces it's more reliable. You'll have to find the code on the net.
But anyway, here is the test code that I'm using so far, if you wish to confirm my statements:
Here's my injected dll to hijack the MessageBox call using MS's Detours library:
My injection routine:Code:#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
#pragma comment(lib, "user32.lib")
#include <stdio.h>
#include <windows.h>
#include "detours.h"
INT (WINAPI * trueMessageBox)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) = MessageBox;
INT WINAPI hookedMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
LPCSTR lpNewText = "You've been hijacked";
trueMessageBox(hWnd, lpNewText, lpCaption, uType);
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
LONG error;
(void)hinst;
(void)reserved;
if (dwReason == DLL_PROCESS_ATTACH)
{
MessageBox(NULL, "Started", "Test DLL Started", MB_OK);
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)trueMessageBox, hookedMessageBox);
error = DetourTransactionCommit();
if (error == NO_ERROR)
MessageBox(NULL, "Finished", "Test DLL Finished", MB_OK);
else
MessageBox(NULL, "Error", "Test DLL error", MB_OK);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)trueMessageBox, hookedMessageBox);
error = DetourTransactionCommit();
MessageBox(NULL, "Removed", "Test DLL Removed", MB_OK);
}
return TRUE;
}
I've writtin a very basic GUI "Hello world" app with a button to display a MessageBox for testing purposes and I've verified that the Detours code and my injecting process work properly. Thus, I'll be testing VirtualQuery/VirtualProtect and other theories to determine whether or not they work.Code:#pragma comment(lib, "advapi32.lib")
#include <stdio.h>
#include <windows.h>
#include <Tlhelp32.h>
int main(int argc, char **argv)
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
BOOL bProcess = Process32First(hTool32, &pe32);
if(bProcess == TRUE)
{
while((Process32Next(hTool32, &pe32)) == TRUE)
{
if(strcmp(pe32.szExeFile, argv[1]) == 0)
{
char DirPath[MAX_PATH] = {0};
char FullPath[MAX_PATH] = {0};
GetCurrentDirectory(MAX_PATH, DirPath);
sprintf(FullPath, "%s\\test.dll", DirPath);
HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE, FALSE, pe32.th32ProcessID);
LPVOID LoadLibraryAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"),
"LoadLibraryA");
LPVOID LLParam = (LPVOID)VirtualAllocEx(hProcess, NULL, strlen(FullPath),
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, LLParam, FullPath, strlen(FullPath), NULL);
CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryAddr,
LLParam, NULL, NULL);
CloseHandle(hProcess);
}
}
CloseHandle(hTool32);
}
return 0;
}
Finally, ListDlls utilty is a handy tool to have when you're investigating code such as CloakDll.
That's probably due to compiling with the "Enable Function Level Linking" flag. With that enabled, function pointers are indexes into a jump table so all that gets copied is part of the the table, which in the target process probably point to no mans land.Quote:
Unfortunately, CloakDll always causes the targeted app to crash when it is executed.
Also, freeing the memory your executing in the target process is going to lead to a crash.
IMHO, CloakDll was probably posted as a prank. You'll find a lot of misinformation like this on the net. If I were you, I would encourage them to use this CloakDll code since it's harmless.
I would suggest you single step your debugger thru CloakDll and you'll understand why its harmless.
I've chosen to do the cloaking in the DLL as opposed to doing something similar to CloakDll.
I won't post fully functional cloaking code since that is frowned upon on this forum. But pobr19 PM me with an email address and I'll send you the complete source which will include a starting point for counter measures that actually do work.
Other way to do it can be by hooking every thread creation through a DLL (when DllMain() receives a DLL_THREAD_ATTACH event in BobS0327's way) and just check if the thread was own created or was externally. You can even kill not allowed threads.
The way to check it was internally created (and not remotely) could be using and internal MyCreateThread() in the DLL, for example:
Code:HANDLE MyCreateThread(...) {
CreateThread(... CREATE_SUSPENDED ...); // Actual creation
// Keep handle into a list of allowed threads
ResumeThread(...); // Make the thread run
}
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) {
if(dwReason == DLL_THREAD_ATTACH) {
if(GetCurrentThread() is NOT in the list)
ExitThread(0); // Kill it!
}
}
Isn't the DllMain located in the Injected DLL? That you do not have an access to the source code?
DLLMain() is the control function (loading/finishing/thread support... events handler, so it's called on every of those events) for every DLL. Every new thread calls the DllMain() of every DLL already loaded at its creation.
The idea is to use your own DLL, loaded at program startup, to detect the injection of other "malicious" DLLs (really, thread injections).
The 'CloakDLL' code is not a "prank" by any means, this method of hiding your module from the PEB list has been a known working means of evading detection by VAC2 (VALVE ANTI-CHEAT) for some years now. If you're familiar with the site OpenRCE as I'm sure you are, Pnluck posted an article detailing this a couple years ago:
http://www.openrce.org/blog/view/844/How_to_hide_dll
in short:
In my research I have written several applications for detecting such methods, to point you in the right direction you will need to use Nt/ZwQueryInformationProcess.Code:while(modulo->BaseAddress != 0)
{
if( (ULONG_PTR)modulo->BaseAddress == DllHandle)
{
if(modulo->InInitializationOrderModuleList.Blink == NULL)
return false;
prec = (LDR_MODULE*)(ULONG_PTR)((ULONG_PTR)modulo->InInitializationOrderModuleList.Blink - 16);
next = (LDR_MODULE*)(ULONG_PTR)((ULONG_PTR)modulo->InInitializationOrderModuleList.Flink - 16);
prec->InInitializationOrderModuleList.Flink = modulo->InInitializationOrderModuleList.Flink;
next->InInitializationOrderModuleList.Blink = modulo->InInitializationOrderModuleList.Blink;
prec = (LDR_MODULE*)modulo->InLoadOrderModuleList.Blink;
next = (LDR_MODULE*)modulo->InLoadOrderModuleList.Flink;
prec->InLoadOrderModuleList.Flink = modulo->InLoadOrderModuleList.Flink;
prec->InMemoryOrderModuleList.Flink = modulo->InMemoryOrderModuleList.Flink;
next->InLoadOrderModuleList.Blink = modulo->InLoadOrderModuleList.Blink;
next->InMemoryOrderModuleList.Blink = modulo->InMemoryOrderModuleList.Blink;
memset( modulo, 0, sizeof( LDR_MODULE ) );
return true;
}
modulo = (LDR_MODULE*)modulo->InLoadOrderModuleList.Flink;
}
Good Luck.