View Single Post

Old 06-30-2008, 09:57 AM   #2 (permalink)
XeNotRoN

Deviant
 
XeNotRoN's Avatar
 
Join Date: Mar 2006
Location: Hungary - near to the gates of hell
Posts: 46
XeNotRoN is on a distinguished road
Default

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
}
XeNotRoN 15 0FF11|\|3   Reply With Quote