 Deviant
Join Date: Mar 2006
Location: Hungary - near to the gates of hell
Posts: 46
|
Very nice work! Once we had an idea to implement something like this, but we were lazy asses to write code. It will be even better if you make a windowing system similar to that of win32 + a dialog builder (control anchoring to auto layout controls on resize etc...). ;) With windowed mode plugin it could be useful to be able to detach the window from the starcraft main window as a child wnd of the main wnd. I like projects like this so much, so sad that I have zero time to develop game tools.
One thing I noticed in the code is the hidemodule part. You can replace that to this piece of code, which is win9x compatible, not only winnt. Another good practice is loading the dll without LoadLibrary().
VC++6, Iam lazy ass to make the masm version...
hidemodule.c:
Code:
#include <windows.h>
#include <winnt.h>
#include <stdio.h>
/*
// No we do some manual exception handling xD
//
// Safe functions must call SetupSEH before they put
// anything to the stack, so the return function must
// be on top of stack. This func must return by
// CleanupSEHasm_ret with ReturnCode in eax.
// If the function causes an exception then it
// will return automatically with eax==0
// A function must be __cdecl to use SetupSEH
// and CleanupSEHasm_ret cause CleanupSEH does
// not clean parameters from stack, only returns.
*/
/*
// Size of stack space required for SEH frame
// created by SetupSEH.
*/
#define SEH_STACK (2*4 + 8*4 + 4)
__declspec(naked) void __stdcall SetupSEH (void *ErrorSafeLoc)
{
__asm
{
// Setup SEH frame
pop eax // ret address
pushad
// zero eax: return value on exception
and dword ptr [esp+1CH], 0
push offset seh_handler_func
push dword ptr fs:[0]
mov dword ptr fs:[0], esp
jmp eax
#define EH_NONCONTINUABLE 00000001H
#define EH_UNWINDING 00000002H
#define EH_EXIT_UNWIND 00000004H
#define EH_STACK_INVALID 00000008H
#define EH_NESTED_CALL 00000010H
seh_handler_func:
mov eax, dword ptr [esp+4]
test dword ptr [eax+EXCEPTION_RECORD.ExceptionFlags], EH_NONCONTINUABLE or EH_UNWINDING or EH_EXIT_UNWIND
jnz nextseh
mov edx, dword ptr [esp+12] // eax = PCONTEXT
mov ecx, dword ptr [esp+8] // ecx = pEstablisherFrame
mov dword ptr [edx+CONTEXT.Esp], ecx
mov ecx, dword ptr [ecx+10*4] // ecx = safe_loc_offs
mov dword ptr [edx+CONTEXT.Eip], ecx
xor eax, eax
retn
nextseh:
xor eax, eax
inc eax // eax == 1 (ExceptionContinueSearch)
retn
}
}
__declspec(naked) void __cdecl CleanupSEHasm_ret ()
{
__asm
{
xor ecx, ecx
mov esp, dword ptr fs:[ecx] // extra safety
pop dword ptr fs:[ecx]
pop ecx // dummy
mov dword ptr [esp+1CH], eax
popad
add esp, 4 // dummy
retn
}
}
/*
// This will not work with some of the kernel functions,
// but you will be able to get those that are exported
// only by ordinal. It works well with normal dlls too.
// Those nasty functions can not be retrieved just from
// the export directory. That should never happen to
// simple DLLs.
*/
__declspec(naked) FARPROC __cdecl MyGetProcAddress (HMODULE hModule, char *lpProcName)
{
__asm
{
push offset scloader_GetProcAddress_exit_fail
call SetupSEH
mov eax, dword ptr [esp + SEH_STACK + 4] // hModule
cmp word ptr [eax+IMAGE_DOS_HEADER.e_magic], IMAGE_DOS_SIGNATURE
jne scloader_GetProcAddress_exit_fail
mov ecx, dword ptr [eax+IMAGE_DOS_HEADER.e_lfanew]
add ecx, eax
cmp dword ptr [ecx+IMAGE_NT_HEADERS.Signature], IMAGE_NT_SIGNATURE
jne scloader_GetProcAddress_exit_fail
cmp word ptr [ecx+IMAGE_NT_HEADERS.OptionalHeader.Magic], IMAGE_NT_OPTIONAL_HDR32_MAGIC
jne scloader_GetProcAddress_exit_fail
// sizeof (IMAGE_DATA_DIRECTORY) == 8, IMAGE_DIRECTORY_ENTRY_EXPORT == 0
// Getting RVA of export directory:
mov ecx, [ecx + (IMAGE_DIRECTORY_ENTRY_EXPORT * 8) + IMAGE_NT_HEADERS.OptionalHeader.DataDirectory]
// eax: ImageBase
// ecx: IMAGE_EXPORT_DIRECTORY RVA that can be zero!!!
jecxz scloader_GetProcAddress_exit_fail
xchg ebx, eax // mov ebx, eax with 1 byte opcode
lea edi, [ebx+ecx] // edi: export_dir VA (not RVA!!!)
mov eax, dword ptr [esp + SEH_STACK + 8] // lpProcName
cmp eax, 0FFFFH
jbe scloader_GetProcAddress_ordinal
// eax: lpProcName
// ebx: ImageBase
// edi: export_dir VA
// search by name
mov ecx, [edi+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
mov edx, [edi+IMAGE_EXPORT_DIRECTORY.AddressOfNames]
add edx, ebx
// edx: AddresOfNames VA
// eax: lpFuncName
// ecx: ciklusváltozó
scloader_GetProcAddress_search_name_loop:
jecxz scloader_GetProcAddress_exit_fail
dec ecx
pushad
mov edx, dword ptr [edx+ecx*4]
add edx, ebx
push edx
push eax
call dword ptr [lstrcmp]
or eax, eax
popad
jnz scloader_GetProcAddress_search_name_loop
mov eax, [edi+IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals]
add eax, ebx
movzx eax, word ptr [eax+ecx*2]
jmp short scloader_GetProcAddress_func_by_index
scloader_GetProcAddress_ordinal:
// search by ordinal
sub eax, [edi+IMAGE_EXPORT_DIRECTORY.Base]
jc scloader_GetProcAddress_exit_fail // !!!!!! bugfix
scloader_GetProcAddress_func_by_index: // !!!!! this label was under the following 2 instructions.
cmp eax, [edi+IMAGE_EXPORT_DIRECTORY.NumberOfFunctions]
jae scloader_GetProcAddress_exit_fail
// eax: index to AddressOfFunctions array
mov ecx, [edi+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
add ecx, ebx
mov eax, [ecx+eax*4]
add eax, ebx
jmp short scloader_GetProcAddress_exit
// The exception handler will jump here on error.
scloader_GetProcAddress_exit_fail:
xor eax, eax
scloader_GetProcAddress_exit: // eax: return value
jmp CleanupSEHasm_ret
}
}
/*
// Hiding and unhiding a module:
//
// HMODULE hMyDll = LoadLibrary ("xenominator.dll");
// if (hMyDll)
// {
// DWORD UnhideHandle = HideModule (hMyDll);
// UnhideModule (UnhideHandle);
// FreeLibrary (hMyDll);
// }
//
// The return value of HideModule is zero on error.
*/
__declspec(naked) DWORD __cdecl HideModule (HMODULE hModule)
{
static char kernel32_name[] = "KERNEL32";
static FARPROC kernel32_23 = NULL;
__asm
{
cmp dword ptr [esp+4], 0 // hModule
jne hide_mod_nozero_mod
xor eax, eax
retn
hide_mod_nozero_mod:
push offset hide_mod_not_found
call SetupSEH
mov ax, ds
test al, 4
mov eax, dword ptr [esp + SEH_STACK + 4] // hModule
mov ebx, dword ptr fs:[30H]
jnz win9x
mov ebx, dword ptr [ebx+0CH]
add ebx, 0CH
mov edx, dword ptr [ebx]
hide_mod_search_loop:
cmp edx, ebx
je hide_mod_not_found
cmp dword ptr [edx+18H], eax
je hide_mod_found
mov edx, dword ptr [edx]
jmp short hide_mod_search_loop
hide_mod_found:
push 2
pop ecx ; ciklusváltozó.
hide_mod_loop:
mov esi, dword ptr [edx+ecx*8]
mov edi, dword ptr [edx+ecx*8+4]
mov dword ptr [esi+4], edi
mov dword ptr [edi], esi
dec ecx
jns hide_mod_loop
jmp hide_mod_end
hide_mod_not_found:
xor edx, edx
hide_mod_end:
xchg eax, edx // mov eax,edx
jmp CleanupSEHasm_ret
win9x:
/*
// eax: hModule
// ebx: PDB
// Getting pointer to kernel32's internal module
// table pointer list. It can be done universally
// only with a dirty trick: after calling
// kernel32#23 ecx will contain the correct pointer.
// This was tested on both Win95 and Win98SE
*/
xchg edi, eax // mov edi,eax
mov eax, kernel32_23
or eax, eax
jnz hide_mod_k32_23_ok // address is OK.
push offset kernel32_name
call dword ptr [GetModuleHandle]
or eax, eax
jz hide_mod_not_found
push 23
push eax
call MyGetProcAddress
pop ecx
pop ecx // Clean stack
mov kernel32_23, eax
hide_mod_k32_23_ok:
or eax, eax
jz hide_mod_not_found
push 0
call eax
/*
// ecx: pointer to PIMTE table regardless of
// the succes of kernel32#23 :)
// We will walk through the MODREF list
// and search for our handle using PIMTE
// since module handles are only in PIMTEs
// and MODREFs contain only indexes to the
// PIMTE table.
*/
lea esi, dword ptr [ebx+4CH]
hide_mod_search_loop2:
mov eax, dword ptr [esi] // PMODREF
or eax, eax
jz hide_mod_not_found
movzx edx, word ptr [eax+10H]
// edx: index to PIMTE table
mov edx, dword ptr [ecx+edx*4]
// edx: PIMTE of actual MODREF
mov edx, dword ptr [edx+4] // PIMAGE_NT_HEADERS
cmp dword ptr [edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase], edi
je hide_mod_found2
xchg esi, eax // mov esi,eax
jmp short hide_mod_search_loop2
hide_mod_found2:
// Unlink current modref (eax)
mov ecx, dword ptr [eax]
mov dword ptr [esi], ecx
jmp CleanupSEHasm_ret
}
}
__declspec(naked) void __cdecl UnhideModule (DWORD unhide)
{
__asm
{
cmp dword ptr [esp+4], 0
jne hide_mod_not_null
retn
hide_mod_not_null:
push offset CleanupSEHasm_ret
call SetupSEH
mov ax, ds
test al, 4
mov eax, [esp+SEH_STACK+4]
mov ebx, dword ptr fs:[30H]
jnz win9x
mov ebx, dword ptr [ebx+0CH]
add ebx, 0CH
push 3
pop ecx
unhide_mod_loop:
mov edx, dword ptr [ebx]
mov dword ptr [eax], edx
mov dword ptr [eax+4], ebx
mov dword ptr [ebx], eax
mov dword ptr [edx+4], eax
add eax, 8
add ebx, 8
loop unhide_mod_loop
jmp CleanupSEHasm_ret
win9x: // eax: PMODREF
// Just re-link the PMODREF to head of the list
mov ecx, dword ptr [ebx+4CH]
mov dword ptr [eax], ecx
mov dword ptr [ebx+4CH], eax
jmp CleanupSEHasm_ret
}
}
int main ()
{
static char cDllName[] = "dll.dll";
printf ("Loading %s ...\n", cDllName);
HMODULE hDll = LoadLibrary (cDllName);
if (!hDll)
{
printf ("Error loading dll!\n");
return -1;
}
printf ("Hiding module...\n");
DWORD unhide = HideModule (hDll);
printf ("UnhideHandle: %x\n", unhide);
// Testing module function that should not work like
// Process32First and Process32Next...
char cBuff[MAX_PATH];
printf ("GetModuleFileName result: %d\n", GetModuleFileName (hDll, cBuff, sizeof cBuff));
printf ("Unhiding module...\n");
UnhideModule (unhide);
// Now module functions should should work again:
printf ("GetModuleFileName result: %d\n", GetModuleFileName (hDll, cBuff, sizeof cBuff));
printf ("Hiding module again...\n");
unhide = HideModule (hDll);
printf ("UnhideHandle: %x\n", unhide);
// FreeLibrary should fail.
printf ("FreeLibrary: %d\n", FreeLibrary (hDll));
printf ("Unhiding module...\n");
UnhideModule (unhide);
// FreeLibrary should rock:
printf ("FreeLibrary: %d\n", FreeLibrary (hDll));
return 0;
// You should always unhide modules at least before
// you exit the process to let windows free the library.
// You can temporarily unhide a module to use functions
// that require a HMODULE
}
|