Results 1 to 12 of 12

Thread: Small modification to v2 Stealth Sniffer (XOR before key send)

  1. #1
    Registered User
    Join Date
    Dec 2001
    Posts
    144

    Small modification to v2 Stealth Sniffer (XOR before key send)

    I've been reading some of the other threads, and people are expressing interest in obscuring the key a bit before it is sent.

    Lostinspace included some new SEQ-side code to allow for SEQ to "de XOR" a key that has been obfuscated by XORing prior to transmission.

    I've made a small modification to Maggotboy's 2.05 sniffer code to allow the addition of another parameter at the end of the command line. This additional parameter takes the form of an 8-byte hexadecimal value (0x1234567812345678). The modifications allow the sniffer to XOR the key by this value prior to sending. To send the key straight away, enter 0 for the last value.

    Additional conversations have included the consideration of offsetting the transmitted key somewhere in a larger UDP packet. Sorry -- I didn't get to this one yet!

    I'm posting this code separately. If Maggotboy wants to include it as part of his main thread, that's is fine. If he chooses not to, I won't be insulted. Either way, this code will do the trick for you.

    NOTE: This code does NOT work yet in LCC (of course). This has been tested under VS.Net.

    code starts:
    =============================
    /* eqsniffer2.cpp written by Maggotboy

    Revision 2.06 (from MisterSpock)
    Adds the ability to pass an additional parameter (64 bit, 8 byte) value.
    The sniffer will XOR the key by this value prior to transmitting. If you have made
    LostInSpace's changes to SEQ, you can specify in EQ the same XOR value, and get the
    correct key. To send the key unencrypted, enter 0 for the final parameter.

    Example to XOR the key by 0x1234567812345678:
    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x1234567812345678

    or to not XOR the key at all:
    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x00



    Revision 2.05

    HOW IT WORKS

    This DLL takes advantage of the built-in hooking mechanism of Windows 9x/NT. The DLL hooks into
    keyboard processing messages on all processes in the system using a global keyboard hook. This
    allows the DLL to sit in the address space of all processes that handle keyboard events.

    When the DLL detects that it has latched onto EQGAME.EXE, it does several things ... First, it
    de-hooks itself from the global keyboard hook chain. Then it hooks into the main message pump
    of EQ's main thread. As messages get processed by the game through the normal mechanisms, the
    DLL will be checking the game's memory and sending the encryption key to SEQ when it changes.

    The ways of detecting this program's presence are incredibly complex, and by changing the program
    around before you compile it, you pretty-much eliminate most feasible detection methods.

    IMPORTANT NOTES ON V2
    V2 is more uber stealthy than V1. It is identical in form and function to V1 up to the point
    where it gets loaded into the EQ game's address space. V2 allocates some executable memory,
    injects some code into it, and then DROPS OUT! The DLL disappears, and EQ will never even know
    it was there to begin with.

    COMPILING THE PROGRAM
    Before you compile this program, you need to take several steps in creating a project for it...

    VS.NET Users
    Create a new WIN32 Project
    Project Options, choose DLL Project
    Create the project and copy eqsniffer.cpp and eqsniffer.def into the new folder for it.
    Remove stdafx.h, stdafx.cpp and ReadMe.txt from the project.
    Right-click the project and add the eqsniffer.cpp and eqsniffer.def files to it.
    Right-click the project, and go into Properties
    Change the Configuration to "All Configurations"
    Expand the C/C++ options
    Under "General"
    - Debug Information Format: Disabled
    - Detect 64-bit portability issues: No
    Under "Code Generation"
    - Enable String Pooling: Yes
    - Enable Minimal Rebuild: No
    - Enable C++ Exceptions: No
    - Runtime Library: Single Threaded
    - Buffer Security Check: No
    - Enable Function-Level Linking: Yes
    Under "Precompiled Headers"
    - Create/Use Precompiled Header: Not Using Precompiled Headers
    Expand the Linker options
    Under "Input"[/B]
    - Module Definition File: eqsniffer.def
    Under "Debug"
    - Generate Debug Info: No

    MS VC++ 6.0 Users
    Create a WIN32 DLL Project
    For the options, select Empty Project
    Copy the eqsniffer.cpp and eqsniffer.def file into the project folder.
    Add eqsniffer.cpp and eqsniffer.def to the project (right-click and select "Add Files to project"
    Right-click the project and select Settings
    Change "Settings For" to "All Configurations"
    In the C++ Tab ...
    Category: General
    - Debug Info: None
    Category: C++ Language
    - Enable Exception Handling: UNCHECKED
    - Enable run-time type information: UNCHECKED
    - Disable construction displacements: UNCHECKED
    Category: Code Generation
    - Use run-time library: Single Threaded
    Category: Customize
    - Enable function-level linking: CHECKED
    - Eliminate duplicat strings: CHECKED
    Switch to the "Link" Tab
    Category: General
    - Generate Debug info: UNCHECKED

    As more people get up and running with different compilers I will incorporate those instructions into the CPP file.

    Edit the eqsniffer.def file, and at the top where it says "LIBRARY eqsniffer" change
    the name to the name of your project (no DLL extension).

    ***** The following steps are optional, but highly recommended in order to make your
    ***** build unique and deter Verant from being able to detect the program. The program is
    ***** quite stealthy on its own, but the steps below will give you some added security.

    Rename HookProc, InstallProc and ReleaseProc to something obscure in the DEF file while
    you're there.

    Open the eqsniffer.cpp file and find where the "#define EQHOOKPROC" statement is down below.
    Rename the functions to the same names you gave in the .DEF file in step 4.

    For example ... if you renamed HookProc to TCPNotify, the #define statement would
    read:

    #define EQHOOKPROC TcpNotify

    Do this for all 3 functions, making sure they match the names in the .DEF file.


    RUNNING EQ WITH THE HOOK
    First, you have to get this program up and running.

    To install the hook, use this command line as a template:

    RUNDLL32.EXE mysniffer.dll,InstallHook <ipaddr> <port> <filename> <memaddr>

    Replace "mysniffer.dll" with whatever name you gave the DLL
    Replace "InstallHook" with whatever name you gave to the InstallHook procedure from
    steps 4-5 above.

    <ipaddr> The IP Address of your SEQ box
    <port> The port to send UDP packets to the SEQ box on
    <filename> Either EQGAME.EXE or TESTEQGAME.EXE or any partial derivitive of.
    The code uses strstr() to see if this sequence is in the actual
    filename of the process being attached (case-insensitive)
    <memaddr> The memory address to read the key from ... 0x0078AAD0

    Example:
    RUNDLL32.EXE mysniffer.dll,InstallHook 192.168.1.10 666 eqgame.exe 0x0078AAD0

    RUN EQ normally after the hook has been installed. There's no time limit or restrictions
    on when you can run it. This code will automatically latch onto it and take care of the rest.

    REMOVING THE HOOK:
    Removing the hook is only necessary if you installed it but never ran a program that
    matched the search criteria (in our case, "eqgame.exe"). If you ran that program, the
    hook auto-terminates, so there's no need for manual removal.

    RUNDLL32.EXE mysniffer.dll,ReleaseHook

    Again, rename "mysniffer.dll" to the name of your DLL, and rename "ReleaseHook" to
    whatever name you called it from steps 4-5 above.

    Revision 2.05
    - Fixed my call to GetTempFileName() -- accidentally passed a NULL for the directory name
    where it is supposed to not be NULL according to the MSDN docs.

    Revision 2.04
    - Changed the InjectCode() routine to inject the entire code section of the DLL rather
    than just the code for the InternalHookProc. This was done because many compilers use
    jump tables at the beginning of the code section, and the pointer to the InternalHookProc
    was actually at the beginning of the code section rather than at the end. Also, there
    were some cases where references were being made to code further up in memory, which did
    not exist when the code was injected.
    - Added the _SNIFFDEBUG #define to allow debug output to appear even in release builds.
    Commment the line out when you're done testing the sniffer and ready to use it for real.

    Revision 2.03
    - Added more debugging code to try and isolate why the DLL isn't being released properly
    - Added a safety variable gsh_bInjected to prevent multiple injections of the code into the game

    Revision 2.02
    - Fixed a bug in the offset code calculation code that allocated WAY TOO DAMN MUCH MEMORY!
    - Removed one line of the assembly code that wasn't really necessary to begin with.
    - Slightly modified the beginning of the 2 lines of assembly to fix a Borland compiler issue.

    Revision 2.01
    - Fixed a bug in DllMain that prevented the DLL from loading. TRUE, FALSE, they're the
    same, right?

    Revision 2.0
    - Sneakiness has improved! (200) This version uses a memory-injection technique to inject
    its payload into the process's virtual memory. Once it injects its payload, it drops out
    completely so there's no residual DLL in memory while the game runs.

    Revision 1.3
    - The DLL automatically releases itself from the global hook chain when it successfully hooks
    to the matching process. When it hooks into the target process, it uses a local-only hook.
    - Changed the way the hooking mechanism works. The global hook is triggered by a keypress
    rather then a mouse move.
    - Removed the Timer from the sniffer. The DLL now uses a WH_GETMESSAGE hook local to the target
    process, which hooks into the main thread's message queue. No timer is necessary, and the
    sniffer has improved invisibility
    - Got rid of rand() and srand() for lighter code. Uses GetTickCount() now to get the XOR seed.

    Revision 1.2
    - Removed the try/catch and replaced it with the easier IsBadReadPtr() API call to determine
    whether or not the mem addr is readible.
    - Modified the DoEQProcessing() loop to only open a socket if the key changes. Oops!

    Revision 1.1
    - Added _MSC_VER checks around the __try and __catch exception handlers to avoid non-MS compiler problems
    - Added function prototypes to avoid problems when the code is rearranged.
    - Added #define's to define the name of the HookProc, InstallHook and ReleaseHook methods for easy renaming.
    - Added a #define for the update interval, defaulting to 500ms (half a second). Since this DLL lives
    in the same address space as the game, it doesn't have the overhead of out-of-process methods and therefore
    doesn't require as much time and effort and API calls to get the key. Therefore 500ms is quite acceptable.
    */

    /*
    TODO:
    - Change the name of the InstallHook, ReleaseHook and HookProc functions.
    - Edit the EQSNIFFER.DEF file and update it to reflect the above changes.
    - Rearrange the items in the shared data segment, add some filler nonsense
    variables, etc.
    */

    #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
    #define STRICT

    #define _SNIFFDEBUG // Comment this line out when compiling for release build!

    #include <windows.h>
    #include <tchar.h>
    #include <winsock.h>
    #include <stdlib.h>

    // Includes the WINSOCK library without having to tweak linker settings
    #pragma comment(lib,"wsock32.lib")

    // These are the names of the 3 exported functions. Rename them
    // to something else here and in the corresponding .DEF file.
    // Example:
    // #define EQHOOKPROC TcpNotify
    // Don't forget to change the EQHookProc entry in the .DEF file to TcpNotify

    #define EQHOOKPROC HookProc
    #define INSTALLHOOK InstallHook
    #define RELEASEHOOK ReleaseHook

    // V2
    // This offset determines where the new code is injected in the allocated
    // memory block. This number will be bit-shifted to get a good
    // aligned offset for the code. You may make the number anything from
    // 0x00 - 0xFF (0 - 255 in decimal notation)
    #define INJECT_OFFSET 0x04

    // *** V2 ADDITIONS ***
    // typedef's for functions we're going to call within our injected code
    typedef SIZE_T (WINAPI *VIRTUALQUERY) (LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T);
    typedef BOOL (WINAPI *ISBADREADPTR) (CONST VOID *,UINT_PTR);
    typedef SOCKET (PASCAL FAR *CREATESOCKET) (int, int, int);
    typedef int (PASCAL FAR *SENDTO) (SOCKET, const char FAR *, int, int, const struct sockaddr FAR *, int);
    typedef int (PASCAL FAR *CLOSESOCKET) (SOCKET);
    typedef LRESULT (WINAPI *CALLNEXTHOOKEX) (HHOOK, int, WPARAM, LPARAM);

    // *** V2 ADDITIONS ***
    // This data structure holds data necessary for the injected code to function.
    // When the DLL drops out, the injected code must hold its own without the benefit of
    // API calls (other than those referenced here) or C-runtimes.
    typedef struct _injectstruct
    {
    LPVOID pvmem; // Mem address to read in the process
    SOCKADDR_IN addr; // Socket address to communicate with SEQ
    ULONGLONG ullLastKey; // The last encryption key transmitted
    HHOOK hHook; // Hook handle for passing messages on
    ULONGLONG sentxor; // Value by which the transmitted key will be XORed

    // These are API function pointers which must be explicitly pre-determined
    // because once the DLL drops out, the injected code will be orphaned
    // and won't have a lookup table.
    VIRTUALQUERY func_VirtualQuery;
    ISBADREADPTR func_IsBadReadPtr;
    CREATESOCKET func_socket;
    SENDTO func_sendto;
    CLOSESOCKET func_closesocket;
    CALLNEXTHOOKEX func_CallNextHookEx;
    } INJECTSTRUCT, *LPINJECTSTRUCT;

    /*
    In the shared data segment below, rearrange the items as they appear, and
    don't be afraid to add more nonsense globals with nonsense values, such as
    DWORD gsh_dwFiller = 0xBEEFD00D;
    And so on and so forth.
    */

    // Shared data segment across all processes attached to the DLL
    #pragma data_seg(".shared")
    HHOOK gsh_hHook = NULL; // Global handle to our place in the hook chain
    TCHAR gsh_szEvent[MAX_PATH] = {_T("\0")}; // Global named event for signalling termination
    SOCKADDR_IN gsh_SEQAddr = {0}; // Socket address of the SEQ box
    LPVOID gsh_pvEQKey = NULL; // Memory address to read the key from
    DWORD gsh_xorby = 0; // Random number to XOR the strings and memory loc by to randomize it.
    TCHAR gsh_szFileName[MAX_PATH] = {_T("\0")}; // Filename (EQGAME.EXE or TESTEQGAME.EXE) to test for.
    BOOL gsh_bInjected = FALSE; // Safety measure to prevent multiple injections
    ULONGLONG gsh_sentxor = 0x0000000000000000; // Global value - transmitted key XOR
    #pragma data_seg()

    // Module handle for this DLL (process dependent)
    HMODULE g_hMod = NULL;

    // Function prototypes. These first few funcs remain static and don't require any name change
    // because they are not exported.
    BOOL APIENTRY DllMain (HANDLE, DWORD, LPVOID);
    void xormem (LPVOID, DWORD, DWORD);
    BOOL InjectCode ();
    LRESULT WINAPI InternalHookProc(int, WPARAM, LPARAM);

    // These prototypes use the #define'd names above and declare the 3 exported functions.
    LRESULT CALLBACK EQHOOKPROC (int, WPARAM, LPARAM);
    void CALLBACK INSTALLHOOK (HWND, HINSTANCE, LPTSTR, int);
    void CALLBACK RELEASEHOOK (HWND, HINSTANCE, LPTSTR, int);

    // DLL Entrypoint. Records our module handle.
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
    {
    if (dwReason == DLL_PROCESS_ATTACH)
    {
    TCHAR szModule[MAX_PATH];
    TCHAR szCmp[MAX_PATH];
    g_hMod = (HMODULE)hModule;

    // Get the process's filename
    GetModuleFileName(NULL, szModule, MAX_PATH);
    CharUpper(szModule);

    // Get the filename we're going to be comparing it to, and de-XOR it.
    CopyMemory(szCmp, gsh_szFileName, MAX_PATH);
    xormem(szCmp, gsh_xorby, MAX_PATH);

    // Check to see if the process is the EQ game
    if (_tcsstr(szModule, szCmp) != 0 && szCmp[0])
    {
    #ifdef _SNIFFDEBUG
    OutputDebugString(_T("Found EQ Process!\n"));
    #endif
    // V2 - Injects the sniffer code into memory, then releases the hook and exits with
    // a FALSE message, which prevents the DLL from loading into the actual game's process space.
    // The process will be none the wiser and will never even know the DLL was loading to begin with.
    if (!gsh_bInjected) InjectCode();

    gsh_bInjected = TRUE;

    RELEASEHOOK(NULL, (HINSTANCE)hModule, NULL, 0);

    return FALSE;
    }
    #ifdef _SNIFFDEBUG
    else
    {
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Ignoring process attach request for %s\n"), szModule);
    OutputDebugString(szMsg);
    }
    #endif

    // Clear out our temp variable for safety sake.
    ZeroMemory(szCmp, MAX_PATH);
    ZeroMemory(szModule, MAX_PATH);
    }

    return TRUE;
    }

    // RUNDLL32 entrypoint to install the hook procedure.
    // RUNDLL32.EXE <dllname>, InstallHook <ipaddr> <port> <eqgame.exe> <memaddr>
    void CALLBACK INSTALLHOOK(HWND hWnd, HINSTANCE hInst, LPTSTR pszCmdLine, int nCmdShow)
    {
    if (!gsh_hHook) // Only do this if the hook isn't already installed
    {
    HANDLE hEvent;
    int n = 0;
    LPTSTR pszTok;

    gsh_xorby = GetTickCount(); // XOR our globals as a simplistic scrambler to prevent memory reading

    pszTok = _tcstok(pszCmdLine, _T(" "));
    while(pszTok)
    {
    switch(n)
    {
    case 0:
    gsh_SEQAddr.sin_family = AF_INET;
    gsh_SEQAddr.sin_addr.s_addr = inet_addr(pszTok);
    break;
    case 1:
    gsh_SEQAddr.sin_port = htons(_ttoi(pszTok));
    break;
    case 2:
    lstrcpy(gsh_szFileName, pszTok);
    CharUpper(gsh_szFileName);
    xormem(gsh_szFileName, gsh_xorby, MAX_PATH);
    break;
    case 3:
    gsh_pvEQKey = (LPVOID)_tcstoul(pszTok, NULL, 16);
    xormem(&gsh_pvEQKey, gsh_xorby, sizeof(gsh_pvEQKey));
    break;
    case 4:
    gsh_sentxor = _strtoui64(pszTok, NULL, 16);
    break;
    }
    n ++;
    pszTok = _tcstok(NULL, _T(" "));
    }
    if (n < 5) return; // Not enough arguments.

    // Get a temporary variable to hold the name of the global event.
    GetTempFileName(_T("."), NULL, 0, gsh_szEvent);
    DeleteFile(gsh_szEvent);

    #ifdef _SNIFFDEBUG
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Creating event handle \"%s\"\n"), &gsh_szEvent[2]);
    OutputDebugString(szMsg);
    #endif

    // Create an event handle to wait on.
    hEvent = CreateEvent(NULL, FALSE, FALSE, &gsh_szEvent[2]);
    if (hEvent)
    {
    // Set the global hook procedure
    gsh_hHook = SetWindowsHookEx(WH_KEYBOARD, EQHOOKPROC, g_hMod, 0);

    // Put RUNDLL32.EXE to sleep and wait for the event to be triggered.
    WaitForSingleObject(hEvent, INFINITE);

    // Close the event
    CloseHandle(hEvent);

    // Unhook the windows event hook
    UnhookWindowsHookEx(gsh_hHook);
    gsh_hHook = NULL;
    }
    #ifdef _SNIFFDEBUG
    else
    {
    OutputDebugString(_T("Unable to create event handle!\n"));
    }
    #endif
    }
    }

    // RUNDLL32 entrypoint to release the hook procedure.
    // This function merely opens the global event and triggers it, which causes the
    // waiting RUNDLL32 instance to wake up and release the hook.
    void CALLBACK RELEASEHOOK(HWND hWnd, HINSTANCE hInst, LPSTR pszCmdLine, int nCmdShow)
    {
    if (gsh_hHook) // Only do this if the hook is installed.
    {
    #ifdef _SNIFFDEBUG
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Opening global event \"%s\"\n"), &gsh_szEvent[2]);
    OutputDebugString(szMsg);
    #endif
    // Open the event by name
    HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, TRUE, &gsh_szEvent[2]);
    if (hEvent != NULL)
    {
    SetEvent(hEvent); // Set the event to active, which wakes up the original hook installer.
    CloseHandle(hEvent); // Close our copy of the event handle before exiting.
    }
    #ifdef _SNIFFDEBUG
    else
    {
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Unable to open global event \"%s\"\n"), &gsh_szEvent[2]);
    OutputDebugString(szMsg);
    }
    #endif
    }
    }

    // Global hook procedure which captures all mouse events for all processes.
    LRESULT CALLBACK EQHOOKPROC(int nCode, WPARAM wParam, LPARAM lParam)
    {
    // Do-nothing hook procedure ...
    return CallNextHookEx(gsh_hHook, nCode, wParam, lParam);
    }

    // V2 - Allocates memory, injects our sniffer code into it, and gets it started.
    BOOL InjectCode()
    {
    LPVOID pvCode;
    LPVOID pvMem;
    INJECTSTRUCT inj;
    LPVOID pvStart;
    DWORD dwLen;
    MEMORY_BASIC_INFORMATION mbi;
    DWORD dwOffset = MAKELONG(MAKEWORD(0, INJECT_OFFSET), 0);
    DWORD dwFuncOffset;

    // The start of the function we're injecting
    pvStart = (LPVOID)InternalHookProc;

    // Figure out how large our memory block is that contains our sniffer code.
    VirtualQuery(pvStart, &mbi, sizeof(mbi));
    dwFuncOffset = (DWORD)pvStart - (DWORD)mbi.BaseAddress;

    // Determine the length of the code to inject, and add the size of the offset to it.
    dwLen = (DWORD)mbi.RegionSize + dwOffset;

    #ifdef _SNIFFDEBUG
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Injecting code length %d ...\n"), dwLen);
    OutputDebugString(szMsg);
    #endif

    // Allocate a writeable memory block in preparation for injection ...
    pvCode = VirtualAlloc(NULL, dwLen, MEM_COMMIT, PAGE_READWRITE);
    if (!pvCode) return FALSE; // Failed to allocate memory

    #ifdef _SNIFFDEBUG
    wsprintf(szMsg, _T("Code allocated at 0x%8.8X\n"), pvCode);
    OutputDebugString(szMsg);
    #endif

    // Get the memory address to sniff for, and de-xor it.
    pvMem = gsh_pvEQKey;
    xormem(&pvMem, gsh_xorby, sizeof(pvMem));

    // Clear and fill out the struct with pointers to our API calls and some other useful stuff
    // such as the SEQ box socket addr, the memory pointer to sniff, etc.
    ZeroMemory(&inj, sizeof(inj));
    inj.addr = gsh_SEQAddr;
    inj.pvmem = pvMem;
    inj.ullLastKey = MAXDWORD;
    inj.sentxor = gsh_sentxor; // pass the injected code the value by which to XOR the key before sending
    inj.func_VirtualQuery = (VIRTUALQUERY) GetProcAddress(GetModuleHandle(_T("KERNEL32")), "VirtualQuery");
    inj.func_IsBadReadPtr = (ISBADREADPTR) GetProcAddress(GetModuleHandle(_T("KERNEL32")), "IsBadReadPtr");
    inj.func_socket = (CREATESOCKET) GetProcAddress(GetModuleHandle(_T("WSOCK32")), "socket");
    inj.func_sendto = (SENDTO) GetProcAddress(GetModuleHandle(_T("WSOCK32")), "sendto");
    inj.func_closesocket = (CLOSESOCKET) GetProcAddress(GetModuleHandle(_T("WSOCK32")), "closesocket");
    inj.func_CallNextHookEx = (CALLNEXTHOOKEX)GetProcAddress(GetModuleHandle(_T( "USER32")), "CallNextHookEx");

    // Write the injection struct to the beginning of the memory page.
    CopyMemory(pvCode, &inj, sizeof(inj));

    // Copy our DLL code into the memory page starting at the offset specified.
    CopyMemory((LPBYTE)pvCode + dwOffset, mbi.BaseAddress, dwLen - dwOffset);

    // Mark the code's memory to allow execution.
    VirtualProtect(pvCode, dwLen, PAGE_EXECUTE_READWRITE, &dwLen);

    #ifdef _SNIFFDEBUG
    OutputDebugString(_T("Setting hook procedure...\n"));
    #endif

    // Set a hook into the message pump of the process's main thread.
    ((LPINJECTSTRUCT)pvCode)->hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)((LPBYTE)pvCode + dwOffset + dwFuncOffset), NULL, GetCurrentThreadId());

    return TRUE;
    }

    // XOR routine to scramble our in-memory variables to prevent sniffing
    void xormem(LPVOID pvmem, DWORD dwXorVal, DWORD dwSize)
    {
    while (dwSize > 3)
    {
    ((LPDWORD)pvmem)[0] ^= dwXorVal;
    dwSize -=4 ;
    pvmem = &((LPDWORD)pvmem)[1];
    }

    while (dwSize > 1)
    {
    ((LPWORD)pvmem)[0] ^= LOWORD(dwXorVal);
    dwSize -= 2;
    pvmem = &((LPWORD)pvmem)[1];
    }

    if (dwSize) ((LPBYTE)pvmem)[0] ^= (LOBYTE(LOWORD(dwXorVal)));
    }

    /* Called periodically while EQ is running to keep the encryption key updated.
    This code is injected into allocated memory in the address space of the main process.
    It is not executed where it is originally intended.

    Because of this, we cannot call any API calls directly! Instead, we have to rely on
    a pointer to some memory above this codeblock which gives us pointers to the API calls we
    need, along with state information so we can make some decisions.

    Remember, the DLL that injected this code is no longer resident in the main process, so
    we do not have it to fall back on! This function must be entirely self-contained, and any
    API calls must be provided by the structure at the beginning of this memory block.
    */
    LRESULT WINAPI InternalHookProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    LPVOID pvmem;
    SOCKET s = INVALID_SOCKET;
    ULONGLONG ullKey = 0;
    LPINJECTSTRUCT pinj;
    SOCKADDR_IN addr;
    DWORD dwOffset = MAKELONG(MAKEWORD(0, INJECT_OFFSET), 0);
    ULONGLONG sentval; // will hold the final transmitted value (key ^ xorvalue)

    // Gets the EIP register, essentially.
    // We need to know where in memory we are, in order to find out where our INJECTSTRUCT is.
    __asm {
    call $ + 5
    pop pvmem
    }

    // Round the number down a bit to account for function call overhead
    pvmem = (LPVOID)((DWORD)pvmem & 0xFFFF0000);

    // Get the position of the struct containing our needed junk
    pinj = (LPINJECTSTRUCT)((DWORD)pvmem);

    // Execute this code, provided the given pointer references memory we can actually read.
    if (!pinj->func_IsBadReadPtr(pinj->pvmem, sizeof(ULONGLONG)))
    {
    ullKey = ((ULONGLONG *)pinj->pvmem)[0]; // Retrieve the key from memory

    if (ullKey != pinj->ullLastKey) // If its different from the last key, send it to SEQ.
    {
    addr = pinj->addr;
    sentval = (ULONGLONG)ullKey ^ pinj->sentxor; // xor the key with the selected xor value

    s = pinj->func_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s != SOCKET_ERROR)
    {
    // Send the 8 bytes along to the SEQ box
    pinj->func_sendto(s, (char *)&sentval, sizeof(ULONGLONG), 0, (LPSOCKADDR)&addr, sizeof(SOCKADDR_IN));
    pinj->func_closesocket(s);
    }
    // Record the XOR'd key for future reference.
    pinj->ullLastKey = ullKey;
    }
    }
    return pinj->func_CallNextHookEx(pinj->hHook, nCode, wParam, lParam);
    }




    ========================
    end of code

    Attached is a zip file. I've called it eqsniffer206 to distinguish it from the main release. Hope this is okay, Maggotboy
    Attached Files Attached Files

  2. #2
    Registered User
    Join Date
    Dec 2001
    Posts
    144

    Limited offset capability added (2.07)

    Okay -- I had a few minutes this morning to mess with the idea of adding an offset (e.g. adding a certain number of bytes to the UDP packet before the key).

    I came up with a really nice and easy method to shift the key around within the packet. Unfortunately, when it was employed in the injected code, it crashed 100% of the time. I suspect this is due to the function I called (CopyMemory) calling another function who's lookup we didn't send... I'm looking in to it. However, in order to get something out there for people to experiment with, I came up with another method.

    This version adds (yet another) command line parameter to determine the number of 8-byte blocks of garbage to add before the real data is sent. Currently, the code limits this value to between 0 and 5.

    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x00 0

    will send the key as normal, no XOR, etc.

    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x00 1

    will add 8 bytes of garbage before the key, and so on.


    I've called this version 2.07 to distinguish it from others. Again, this is Maggotboy's code, so it is up to him if he wants to include these mods or not.


    =====================================

    /* eqsniffer2.cpp written by Maggotboy

    Revision 2.07 (from MisterSpock)
    Adds the ability of offset the key location within the UDP frame sent. Another
    command line parameter has been added that specifies the number of ULONGLONG sized blocks (8 bytes, normally)
    that will appear BEFORE the key is sent. Entering 0 sends the key in the normal manner.
    Entering 1 adds 8 bytes (technically, adds 1* sizeof(ULONGLONG)) BEFORE the key,
    2 adds 16 bytes, and so forth. The maximum offset value currently accepted is 5. I know this is a bit
    of slop, but the elegant method using CopyMemory() crashed repeatedly.


    Example to send a non xor'ed key offset by 2 8-byte blocks (e.g. 16 bytes of garbage, then key):
    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x00 2

    Example to send everything normally (no offset, no xor)
    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x00 0



    Revision 2.06 (from MisterSpock)
    Adds the ability to pass an additional parameter (64 bit, 8 byte) value.
    The sniffer will XOR the key by this value prior to transmitting. If you have made
    LostInSpace's changes to SEQ, you can specify in EQ the same XOR value, and get the
    correct key. To send the key unencrypted, enter 0 for the final parameter.

    Example to XOR the key by 0x1234567812345678:
    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x1234567812345678

    or to not XOR the key at all:
    rundll32 eqsniffer2.dll,InstallHook 192.168.1.1 12345 eqgame.exe 0x0078AAD0 0x00



    Revision 2.05

    HOW IT WORKS

    This DLL takes advantage of the built-in hooking mechanism of Windows 9x/NT. The DLL hooks into
    keyboard processing messages on all processes in the system using a global keyboard hook. This
    allows the DLL to sit in the address space of all processes that handle keyboard events.

    When the DLL detects that it has latched onto EQGAME.EXE, it does several things ... First, it
    de-hooks itself from the global keyboard hook chain. Then it hooks into the main message pump
    of EQ's main thread. As messages get processed by the game through the normal mechanisms, the
    DLL will be checking the game's memory and sending the encryption key to SEQ when it changes.

    The ways of detecting this program's presence are incredibly complex, and by changing the program
    around before you compile it, you pretty-much eliminate most feasible detection methods.

    IMPORTANT NOTES ON V2
    V2 is more uber stealthy than V1. It is identical in form and function to V1 up to the point
    where it gets loaded into the EQ game's address space. V2 allocates some executable memory,
    injects some code into it, and then DROPS OUT! The DLL disappears, and EQ will never even know
    it was there to begin with.

    COMPILING THE PROGRAM
    Before you compile this program, you need to take several steps in creating a project for it...

    VS.NET Users
    Create a new WIN32 Project
    Project Options, choose DLL Project
    Create the project and copy eqsniffer.cpp and eqsniffer.def into the new folder for it.
    Remove stdafx.h, stdafx.cpp and ReadMe.txt from the project.
    Right-click the project and add the eqsniffer.cpp and eqsniffer.def files to it.
    Right-click the project, and go into Properties
    Change the Configuration to "All Configurations"
    Expand the C/C++ options
    Under "General"
    - Debug Information Format: Disabled
    - Detect 64-bit portability issues: No
    Under "Code Generation"
    - Enable String Pooling: Yes
    - Enable Minimal Rebuild: No
    - Enable C++ Exceptions: No
    - Runtime Library: Single Threaded
    - Buffer Security Check: No
    - Enable Function-Level Linking: Yes
    Under "Precompiled Headers"
    - Create/Use Precompiled Header: Not Using Precompiled Headers
    Expand the Linker options
    Under "Input"[/B]
    - Module Definition File: eqsniffer.def
    Under "Debug"
    - Generate Debug Info: No

    MS VC++ 6.0 Users
    Create a WIN32 DLL Project
    For the options, select Empty Project
    Copy the eqsniffer.cpp and eqsniffer.def file into the project folder.
    Add eqsniffer.cpp and eqsniffer.def to the project (right-click and select "Add Files to project"
    Right-click the project and select Settings
    Change "Settings For" to "All Configurations"
    In the C++ Tab ...
    Category: General
    - Debug Info: None
    Category: C++ Language
    - Enable Exception Handling: UNCHECKED
    - Enable run-time type information: UNCHECKED
    - Disable construction displacements: UNCHECKED
    Category: Code Generation
    - Use run-time library: Single Threaded
    Category: Customize
    - Enable function-level linking: CHECKED
    - Eliminate duplicat strings: CHECKED
    Switch to the "Link" Tab
    Category: General
    - Generate Debug info: UNCHECKED

    As more people get up and running with different compilers I will incorporate those instructions into the CPP file.

    Edit the eqsniffer.def file, and at the top where it says "LIBRARY eqsniffer" change
    the name to the name of your project (no DLL extension).

    ***** The following steps are optional, but highly recommended in order to make your
    ***** build unique and deter Verant from being able to detect the program. The program is
    ***** quite stealthy on its own, but the steps below will give you some added security.

    Rename HookProc, InstallProc and ReleaseProc to something obscure in the DEF file while
    you're there.

    Open the eqsniffer.cpp file and find where the "#define EQHOOKPROC" statement is down below.
    Rename the functions to the same names you gave in the .DEF file in step 4.

    For example ... if you renamed HookProc to TCPNotify, the #define statement would
    read:

    #define EQHOOKPROC TcpNotify

    Do this for all 3 functions, making sure they match the names in the .DEF file.


    RUNNING EQ WITH THE HOOK
    First, you have to get this program up and running.

    To install the hook, use this command line as a template:

    RUNDLL32.EXE mysniffer.dll,InstallHook <ipaddr> <port> <filename> <memaddr>

    Replace "mysniffer.dll" with whatever name you gave the DLL
    Replace "InstallHook" with whatever name you gave to the InstallHook procedure from
    steps 4-5 above.

    <ipaddr> The IP Address of your SEQ box
    <port> The port to send UDP packets to the SEQ box on
    <filename> Either EQGAME.EXE or TESTEQGAME.EXE or any partial derivitive of.
    The code uses strstr() to see if this sequence is in the actual
    filename of the process being attached (case-insensitive)
    <memaddr> The memory address to read the key from ... 0x0078AAD0

    Example:
    RUNDLL32.EXE mysniffer.dll,InstallHook 192.168.1.10 666 eqgame.exe 0x0078AAD0

    RUN EQ normally after the hook has been installed. There's no time limit or restrictions
    on when you can run it. This code will automatically latch onto it and take care of the rest.

    REMOVING THE HOOK:
    Removing the hook is only necessary if you installed it but never ran a program that
    matched the search criteria (in our case, "eqgame.exe"). If you ran that program, the
    hook auto-terminates, so there's no need for manual removal.

    RUNDLL32.EXE mysniffer.dll,ReleaseHook

    Again, rename "mysniffer.dll" to the name of your DLL, and rename "ReleaseHook" to
    whatever name you called it from steps 4-5 above.

    Revision 2.05
    - Fixed my call to GetTempFileName() -- accidentally passed a NULL for the directory name
    where it is supposed to not be NULL according to the MSDN docs.

    Revision 2.04
    - Changed the InjectCode() routine to inject the entire code section of the DLL rather
    than just the code for the InternalHookProc. This was done because many compilers use
    jump tables at the beginning of the code section, and the pointer to the InternalHookProc
    was actually at the beginning of the code section rather than at the end. Also, there
    were some cases where references were being made to code further up in memory, which did
    not exist when the code was injected.
    - Added the _SNIFFDEBUG #define to allow debug output to appear even in release builds.
    Commment the line out when you're done testing the sniffer and ready to use it for real.

    Revision 2.03
    - Added more debugging code to try and isolate why the DLL isn't being released properly
    - Added a safety variable gsh_bInjected to prevent multiple injections of the code into the game

    Revision 2.02
    - Fixed a bug in the offset code calculation code that allocated WAY TOO DAMN MUCH MEMORY!
    - Removed one line of the assembly code that wasn't really necessary to begin with.
    - Slightly modified the beginning of the 2 lines of assembly to fix a Borland compiler issue.

    Revision 2.01
    - Fixed a bug in DllMain that prevented the DLL from loading. TRUE, FALSE, they're the
    same, right?

    Revision 2.0
    - Sneakiness has improved! (200) This version uses a memory-injection technique to inject
    its payload into the process's virtual memory. Once it injects its payload, it drops out
    completely so there's no residual DLL in memory while the game runs.

    Revision 1.3
    - The DLL automatically releases itself from the global hook chain when it successfully hooks
    to the matching process. When it hooks into the target process, it uses a local-only hook.
    - Changed the way the hooking mechanism works. The global hook is triggered by a keypress
    rather then a mouse move.
    - Removed the Timer from the sniffer. The DLL now uses a WH_GETMESSAGE hook local to the target
    process, which hooks into the main thread's message queue. No timer is necessary, and the
    sniffer has improved invisibility
    - Got rid of rand() and srand() for lighter code. Uses GetTickCount() now to get the XOR seed.

    Revision 1.2
    - Removed the try/catch and replaced it with the easier IsBadReadPtr() API call to determine
    whether or not the mem addr is readible.
    - Modified the DoEQProcessing() loop to only open a socket if the key changes. Oops!

    Revision 1.1
    - Added _MSC_VER checks around the __try and __catch exception handlers to avoid non-MS compiler problems
    - Added function prototypes to avoid problems when the code is rearranged.
    - Added #define's to define the name of the HookProc, InstallHook and ReleaseHook methods for easy renaming.
    - Added a #define for the update interval, defaulting to 500ms (half a second). Since this DLL lives
    in the same address space as the game, it doesn't have the overhead of out-of-process methods and therefore
    doesn't require as much time and effort and API calls to get the key. Therefore 500ms is quite acceptable.
    */

    /*
    TODO:
    - Change the name of the InstallHook, ReleaseHook and HookProc functions.
    - Edit the EQSNIFFER.DEF file and update it to reflect the above changes.
    - Rearrange the items in the shared data segment, add some filler nonsense
    variables, etc.
    */

    #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
    #define STRICT

    #define _SNIFFDEBUG // Comment this line out when compiling for release build!

    #include <windows.h>
    #include <tchar.h>
    #include <winsock.h>
    #include <stdlib.h>

    // Includes the WINSOCK library without having to tweak linker settings
    #pragma comment(lib,"wsock32.lib")

    // These are the names of the 3 exported functions. Rename them
    // to something else here and in the corresponding .DEF file.
    // Example:
    // #define EQHOOKPROC TcpNotify
    // Don't forget to change the EQHookProc entry in the .DEF file to TcpNotify

    #define EQHOOKPROC HookProc
    #define INSTALLHOOK InstallHook
    #define RELEASEHOOK ReleaseHook

    // V2
    // This offset determines where the new code is injected in the allocated
    // memory block. This number will be bit-shifted to get a good
    // aligned offset for the code. You may make the number anything from
    // 0x00 - 0xFF (0 - 255 in decimal notation)
    #define INJECT_OFFSET 0x04

    // *** V2 ADDITIONS ***
    // typedef's for functions we're going to call within our injected code
    typedef SIZE_T (WINAPI *VIRTUALQUERY) (LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T);
    typedef BOOL (WINAPI *ISBADREADPTR) (CONST VOID *,UINT_PTR);
    typedef SOCKET (PASCAL FAR *CREATESOCKET) (int, int, int);
    typedef int (PASCAL FAR *SENDTO) (SOCKET, const char FAR *, int, int, const struct sockaddr FAR *, int);
    typedef int (PASCAL FAR *CLOSESOCKET) (SOCKET);
    typedef LRESULT (WINAPI *CALLNEXTHOOKEX) (HHOOK, int, WPARAM, LPARAM);

    // *** V2 ADDITIONS ***
    // This data structure holds data necessary for the injected code to function.
    // When the DLL drops out, the injected code must hold its own without the benefit of
    // API calls (other than those referenced here) or C-runtimes.
    typedef struct _injectstruct
    {
    LPVOID pvmem; // Mem address to read in the process
    SOCKADDR_IN addr; // Socket address to communicate with SEQ
    ULONGLONG ullLastKey; // The last encryption key transmitted
    HHOOK hHook; // Hook handle for passing messages on
    ULONGLONG sentxor; // Value by which the transmitted key will be XORed
    BYTE offset; // Value by which to offset the transmitted key

    // These are API function pointers which must be explicitly pre-determined
    // because once the DLL drops out, the injected code will be orphaned
    // and won't have a lookup table.
    VIRTUALQUERY func_VirtualQuery;
    ISBADREADPTR func_IsBadReadPtr;
    CREATESOCKET func_socket;
    SENDTO func_sendto;
    CLOSESOCKET func_closesocket;
    CALLNEXTHOOKEX func_CallNextHookEx;
    } INJECTSTRUCT, *LPINJECTSTRUCT;

    /*
    In the shared data segment below, rearrange the items as they appear, and
    don't be afraid to add more nonsense globals with nonsense values, such as
    DWORD gsh_dwFiller = 0xBEEFD00D;
    And so on and so forth.
    */

    // Shared data segment across all processes attached to the DLL
    #pragma data_seg(".shared")
    HHOOK gsh_hHook = NULL; // Global handle to our place in the hook chain
    TCHAR gsh_szEvent[MAX_PATH] = {_T("\0")}; // Global named event for signalling termination
    SOCKADDR_IN gsh_SEQAddr = {0}; // Socket address of the SEQ box
    LPVOID gsh_pvEQKey = NULL; // Memory address to read the key from
    DWORD gsh_xorby = 0; // Random number to XOR the strings and memory loc by to randomize it.
    TCHAR gsh_szFileName[MAX_PATH] = {_T("\0")}; // Filename (EQGAME.EXE or TESTEQGAME.EXE) to test for.
    BOOL gsh_bInjected = FALSE; // Safety measure to prevent multiple injections
    ULONGLONG gsh_sentxor = 0x0000000000000000; // Global value - transmitted key XOR
    BYTE gsh_offset = 0;
    #pragma data_seg()

    // Module handle for this DLL (process dependent)
    HMODULE g_hMod = NULL;

    // Function prototypes. These first few funcs remain static and don't require any name change
    // because they are not exported.
    BOOL APIENTRY DllMain (HANDLE, DWORD, LPVOID);
    void xormem (LPVOID, DWORD, DWORD);
    BOOL InjectCode ();
    LRESULT WINAPI InternalHookProc(int, WPARAM, LPARAM);

    // These prototypes use the #define'd names above and declare the 3 exported functions.
    LRESULT CALLBACK EQHOOKPROC (int, WPARAM, LPARAM);
    void CALLBACK INSTALLHOOK (HWND, HINSTANCE, LPTSTR, int);
    void CALLBACK RELEASEHOOK (HWND, HINSTANCE, LPTSTR, int);

    // DLL Entrypoint. Records our module handle.
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
    {
    if (dwReason == DLL_PROCESS_ATTACH)
    {
    TCHAR szModule[MAX_PATH];
    TCHAR szCmp[MAX_PATH];
    g_hMod = (HMODULE)hModule;

    // Get the process's filename
    GetModuleFileName(NULL, szModule, MAX_PATH);
    CharUpper(szModule);

    // Get the filename we're going to be comparing it to, and de-XOR it.
    CopyMemory(szCmp, gsh_szFileName, MAX_PATH);
    xormem(szCmp, gsh_xorby, MAX_PATH);

    // Check to see if the process is the EQ game
    if (_tcsstr(szModule, szCmp) != 0 && szCmp[0])
    {
    #ifdef _SNIFFDEBUG
    OutputDebugString(_T("Found EQ Process!\n"));
    #endif
    // V2 - Injects the sniffer code into memory, then releases the hook and exits with
    // a FALSE message, which prevents the DLL from loading into the actual game's process space.
    // The process will be none the wiser and will never even know the DLL was loading to begin with.
    if (!gsh_bInjected) InjectCode();

    gsh_bInjected = TRUE;

    RELEASEHOOK(NULL, (HINSTANCE)hModule, NULL, 0);

    return FALSE;
    }
    #ifdef _SNIFFDEBUG
    else
    {
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Ignoring process attach request for %s\n"), szModule);
    OutputDebugString(szMsg);
    }
    #endif

    // Clear out our temp variable for safety sake.
    ZeroMemory(szCmp, MAX_PATH);
    ZeroMemory(szModule, MAX_PATH);
    }

    return TRUE;
    }

    // RUNDLL32 entrypoint to install the hook procedure.
    // RUNDLL32.EXE <dllname>, InstallHook <ipaddr> <port> <eqgame.exe> <memaddr>
    void CALLBACK INSTALLHOOK(HWND hWnd, HINSTANCE hInst, LPTSTR pszCmdLine, int nCmdShow)
    {
    if (!gsh_hHook) // Only do this if the hook isn't already installed
    {
    HANDLE hEvent;
    int n = 0;
    LPTSTR pszTok;

    gsh_xorby = GetTickCount(); // XOR our globals as a simplistic scrambler to prevent memory reading

    pszTok = _tcstok(pszCmdLine, _T(" "));
    while(pszTok)
    {
    switch(n)
    {
    case 0:
    gsh_SEQAddr.sin_family = AF_INET;
    gsh_SEQAddr.sin_addr.s_addr = inet_addr(pszTok);
    break;
    case 1:
    gsh_SEQAddr.sin_port = htons(_ttoi(pszTok));
    break;
    case 2:
    lstrcpy(gsh_szFileName, pszTok);
    CharUpper(gsh_szFileName);
    xormem(gsh_szFileName, gsh_xorby, MAX_PATH);
    break;
    case 3:
    gsh_pvEQKey = (LPVOID)_tcstoul(pszTok, NULL, 16);
    xormem(&gsh_pvEQKey, gsh_xorby, sizeof(gsh_pvEQKey));
    break;
    case 4:
    gsh_sentxor = _strtoui64(pszTok, NULL, 16);
    break;
    case 5:
    gsh_offset = _tstoi(pszTok);
    // check and adjust offset value to fall between 0 and 5
    // this limits the padding to between 0 and 40 bytes
    if (gsh_offset > 5)
    {
    gsh_offset = 5;
    }
    if (gsh_offset <= 0)
    {
    gsh_offset = 0;
    }
    break;


    }
    n ++;
    pszTok = _tcstok(NULL, _T(" "));
    }
    if (n < 6) return; // Not enough arguments.

    // Get a temporary variable to hold the name of the global event.
    GetTempFileName(_T("."), NULL, 0, gsh_szEvent);
    DeleteFile(gsh_szEvent);

    #ifdef _SNIFFDEBUG
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Creating event handle \"%s\"\n"), &gsh_szEvent[2]);
    OutputDebugString(szMsg);
    #endif

    // Create an event handle to wait on.
    hEvent = CreateEvent(NULL, FALSE, FALSE, &gsh_szEvent[2]);
    if (hEvent)
    {
    // Set the global hook procedure
    gsh_hHook = SetWindowsHookEx(WH_KEYBOARD, EQHOOKPROC, g_hMod, 0);

    // Put RUNDLL32.EXE to sleep and wait for the event to be triggered.
    WaitForSingleObject(hEvent, INFINITE);

    // Close the event
    CloseHandle(hEvent);

    // Unhook the windows event hook
    UnhookWindowsHookEx(gsh_hHook);
    gsh_hHook = NULL;
    }
    #ifdef _SNIFFDEBUG
    else
    {
    OutputDebugString(_T("Unable to create event handle!\n"));
    }
    #endif
    }
    }

    // RUNDLL32 entrypoint to release the hook procedure.
    // This function merely opens the global event and triggers it, which causes the
    // waiting RUNDLL32 instance to wake up and release the hook.
    void CALLBACK RELEASEHOOK(HWND hWnd, HINSTANCE hInst, LPSTR pszCmdLine, int nCmdShow)
    {
    if (gsh_hHook) // Only do this if the hook is installed.
    {
    #ifdef _SNIFFDEBUG
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Opening global event \"%s\"\n"), &gsh_szEvent[2]);
    OutputDebugString(szMsg);
    #endif
    // Open the event by name
    HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, TRUE, &gsh_szEvent[2]);
    if (hEvent != NULL)
    {
    SetEvent(hEvent); // Set the event to active, which wakes up the original hook installer.
    CloseHandle(hEvent); // Close our copy of the event handle before exiting.
    }
    #ifdef _SNIFFDEBUG
    else
    {
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Unable to open global event \"%s\"\n"), &gsh_szEvent[2]);
    OutputDebugString(szMsg);
    }
    #endif
    }
    }

    // Global hook procedure which captures all mouse events for all processes.
    LRESULT CALLBACK EQHOOKPROC(int nCode, WPARAM wParam, LPARAM lParam)
    {
    // Do-nothing hook procedure ...
    return CallNextHookEx(gsh_hHook, nCode, wParam, lParam);
    }

    // V2 - Allocates memory, injects our sniffer code into it, and gets it started.
    BOOL InjectCode()
    {
    LPVOID pvCode;
    LPVOID pvMem;
    INJECTSTRUCT inj;
    LPVOID pvStart;
    DWORD dwLen;
    MEMORY_BASIC_INFORMATION mbi;
    DWORD dwOffset = MAKELONG(MAKEWORD(0, INJECT_OFFSET), 0);
    DWORD dwFuncOffset;

    // The start of the function we're injecting
    pvStart = (LPVOID)InternalHookProc;

    // Figure out how large our memory block is that contains our sniffer code.
    VirtualQuery(pvStart, &mbi, sizeof(mbi));
    dwFuncOffset = (DWORD)pvStart - (DWORD)mbi.BaseAddress;

    // Determine the length of the code to inject, and add the size of the offset to it.
    dwLen = (DWORD)mbi.RegionSize + dwOffset;

    #ifdef _SNIFFDEBUG
    TCHAR szMsg[MAX_PATH];
    wsprintf(szMsg, _T("Injecting code length %d ...\n"), dwLen);
    OutputDebugString(szMsg);
    #endif

    // Allocate a writeable memory block in preparation for injection ...
    pvCode = VirtualAlloc(NULL, dwLen, MEM_COMMIT, PAGE_READWRITE);
    if (!pvCode) return FALSE; // Failed to allocate memory

    #ifdef _SNIFFDEBUG
    wsprintf(szMsg, _T("Code allocated at 0x%8.8X\n"), pvCode);
    OutputDebugString(szMsg);
    #endif

    // Get the memory address to sniff for, and de-xor it.
    pvMem = gsh_pvEQKey;
    xormem(&pvMem, gsh_xorby, sizeof(pvMem));

    // Clear and fill out the struct with pointers to our API calls and some other useful stuff
    // such as the SEQ box socket addr, the memory pointer to sniff, etc.
    ZeroMemory(&inj, sizeof(inj));
    inj.addr = gsh_SEQAddr;
    inj.pvmem = pvMem;
    inj.ullLastKey = MAXDWORD;
    inj.sentxor = gsh_sentxor; // pass the injected code the value by which to XOR the key before sending
    inj.offset = gsh_offset; // pass the offset value to the injected code
    inj.func_VirtualQuery = (VIRTUALQUERY) GetProcAddress(GetModuleHandle(_T("KERNEL32")), "VirtualQuery");
    inj.func_IsBadReadPtr = (ISBADREADPTR) GetProcAddress(GetModuleHandle(_T("KERNEL32")), "IsBadReadPtr");
    inj.func_socket = (CREATESOCKET) GetProcAddress(GetModuleHandle(_T("WSOCK32")), "socket");
    inj.func_sendto = (SENDTO) GetProcAddress(GetModuleHandle(_T("WSOCK32")), "sendto");
    inj.func_closesocket = (CLOSESOCKET) GetProcAddress(GetModuleHandle(_T("WSOCK32")), "closesocket");
    inj.func_CallNextHookEx = (CALLNEXTHOOKEX)GetProcAddress(GetModuleHandle(_T( "USER32")), "CallNextHookEx");

    // Write the injection struct to the beginning of the memory page.
    CopyMemory(pvCode, &inj, sizeof(inj));

    // Copy our DLL code into the memory page starting at the offset specified.
    CopyMemory((LPBYTE)pvCode + dwOffset, mbi.BaseAddress, dwLen - dwOffset);

    // Mark the code's memory to allow execution.
    VirtualProtect(pvCode, dwLen, PAGE_EXECUTE_READWRITE, &dwLen);

    #ifdef _SNIFFDEBUG
    OutputDebugString(_T("Setting hook procedure...\n"));
    #endif

    // Set a hook into the message pump of the process's main thread.
    ((LPINJECTSTRUCT)pvCode)->hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)((LPBYTE)pvCode + dwOffset + dwFuncOffset), NULL, GetCurrentThreadId());

    return TRUE;
    }

    // XOR routine to scramble our in-memory variables to prevent sniffing
    void xormem(LPVOID pvmem, DWORD dwXorVal, DWORD dwSize)
    {
    while (dwSize > 3)
    {
    ((LPDWORD)pvmem)[0] ^= dwXorVal;
    dwSize -=4 ;
    pvmem = &((LPDWORD)pvmem)[1];
    }

    while (dwSize > 1)
    {
    ((LPWORD)pvmem)[0] ^= LOWORD(dwXorVal);
    dwSize -= 2;
    pvmem = &((LPWORD)pvmem)[1];
    }

    if (dwSize) ((LPBYTE)pvmem)[0] ^= (LOBYTE(LOWORD(dwXorVal)));
    }

    /* Called periodically while EQ is running to keep the encryption key updated.
    This code is injected into allocated memory in the address space of the main process.
    It is not executed where it is originally intended.

    Because of this, we cannot call any API calls directly! Instead, we have to rely on
    a pointer to some memory above this codeblock which gives us pointers to the API calls we
    need, along with state information so we can make some decisions.

    Remember, the DLL that injected this code is no longer resident in the main process, so
    we do not have it to fall back on! This function must be entirely self-contained, and any
    API calls must be provided by the structure at the beginning of this memory block.
    */
    LRESULT WINAPI InternalHookProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    LPVOID pvmem;
    SOCKET s = INVALID_SOCKET;
    ULONGLONG ullKey = 0;
    LPINJECTSTRUCT pinj;
    SOCKADDR_IN addr;
    DWORD dwOffset = MAKELONG(MAKEWORD(0, INJECT_OFFSET), 0);
    ULONGLONG sentval; // will hold the final transmitted value (key ^ xorvalue)
    ULONGLONG bpad[6]; // make some space to hold the offset + the key

    // Gets the EIP register, essentially.
    // We need to know where in memory we are, in order to find out where our INJECTSTRUCT is.
    __asm {
    call $ + 5
    pop pvmem
    }

    // Round the number down a bit to account for function call overhead
    pvmem = (LPVOID)((DWORD)pvmem & 0xFFFF0000);

    // Get the position of the struct containing our needed junk
    pinj = (LPINJECTSTRUCT)((DWORD)pvmem);

    FillMemory(&bpad, 40, 0xFC); // fill with something -- change this to make your code different

    // Execute this code, provided the given pointer references memory we can actually read.
    if (!pinj->func_IsBadReadPtr(pinj->pvmem, sizeof(ULONGLONG)))
    {
    ullKey = ((ULONGLONG *)pinj->pvmem)[0]; // Retrieve the key from memory

    if (ullKey != pinj->ullLastKey) // If its different from the last key, send it to SEQ.
    {
    addr = pinj->addr;
    sentval = (ULONGLONG)ullKey ^ pinj->sentxor; // xor the key with the selected xor value

    //offset the key by pinj->offset * sizeof(ULONGLONG) bytes

    bpad[pinj->offset] = sentval;

    //CopyMemory(&bpad[pinj->offset], &sentval, sizeof(ULONGLONG)); would be much more graceful, but crashes

    s = pinj->func_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s != SOCKET_ERROR)
    {
    // Send the pad bytes + the key along to the SEQ box
    pinj->func_sendto(s, (char *)&bpad[0], (pinj->offset * sizeof(ULONGLONG)+sizeof(ULONGLONG)), 0, (LPSOCKADDR)&addr, sizeof(SOCKADDR_IN));
    pinj->func_closesocket(s);
    }
    // Record the XOR'd key for future reference.
    pinj->ullLastKey = ullKey;
    }
    }
    return pinj->func_CallNextHookEx(pinj->hHook, nCode, wParam, lParam);
    }



    ========================================
    end of code


    Zip file included for your convenience
    Attached Files Attached Files

  3. #3
    Registered User
    Join Date
    Aug 2002
    Posts
    15
    MisterSpock;

    Wanted to ask for help since you appear to have this code working with VC++NET. Could you check the thread about people crashing using the MaggotBoy V2 sniffer and see if you have any thoughts to suggest? There are about 6 or 7 posts there from people having the exact same crash when they press a keyboard key the first time. All people involved (so far) have reported that they have WinXP Pro and are using VC++NET to compile.

    http://seq.sourceforge.net/showthrea...&threadid=2505

  4. #4
    Registered User
    Join Date
    Dec 2001
    Posts
    144
    BlackJack,

    I've been looking at that thread. Unfortunately, I've not been able to replicate the problems. I have both VS.NET and VC++ 6.0 at my disposal, and can test on 2K and XP. If I can get the problem to show up even once, I might be able to nail it.

    However, the only code I've been able to get to crash under either of these is some of my own experimental versions of this code. The most recent crash being the unexpected crash of CopyMemory when injected (FillMemory and ZeroMemory work). I'm looking in to this. Perhaps they are somehow related.

  5. #5
    Registered User
    Join Date
    Nov 2002
    Posts
    16
    MisterSpock,

    I get the following errors when I try to compile your v.2.07 of maggotboy's sniffer:

    Deleting intermediate files and output files for project 'XXXXXXXX - Win32 Release'.
    --------------------Configuration: XXXXXXXX - Win32 Release--------------------
    Compiling...
    eqsniffer2.cpp
    C:\Program Files\Microsoft Visual Studio\MyProjects\XXXXXXXX\eqsniffer2.cpp(413) : error C2065: '_strtoui64' : undeclared identifier
    C:\Program Files\Microsoft Visual Studio\MyProjects\XXXXXXXX\eqsniffer2.cpp(416) : error C2065: '_tstoi' : undeclared identifier
    Error executing cl.exe.

    XXXXXXXX.dll - 2 error(s), 0 warning(s)

    This is with VC++ 6.0, on Win 2k.

  6. #6
    Registered User
    Join Date
    Nov 2002
    Posts
    7
    Hi MisterSpock,

    I tried out the XOR feature from version 2.06 but it doesn't seem to be working for me at all. The console indicates a key being sent, but the zone does not decode. The code works fine if I use 0x00 option to disable to XOR feature.

    BTW, I may have hit on the problem with the program kicking out right at the EULA screen. When I compiled it the first time, the dll would load but kick me out everytime I select 'accept' on the EULA screen. I went back in and double checked all the compiler settings. Apparently, the Properties->Code Generation->Basic Runtime Check was set to Both instead of default. I changed it to default and recompiled and the program works. The previous dll was 54k in size, the working dll was 46k.

    I'm using MSVS 2002 .NET + win2k. I also have MSVS 6.0 + winxp and both compiled without problems.

  7. #7
    Registered User
    Join Date
    Dec 2001
    Posts
    144
    Hi foo --

    Did you set the XOR value in the XML file and add lostinspace's XOR code to the SEQ box? Sorry if this is a dumb question, but I gotta ask it.

    Runtime Check needs to DEFAULT and buffer security must be set to NO.

  8. #8
    Registered User
    Join Date
    Nov 2002
    Posts
    7
    Hi MisterSpock,

    Yep. I modified the XML and src files from Lostinspace's example and make and make install. I also changed the keyxor value from "0" to "0x100A00A1" and used the same value in the keysniffer parameter setting.

    Something I just realized. The seqdef.xml keyxor value and keyoffset value are integer value. And the parameters I pass to the sniffer is hex.

    I've since used version 2.07 and compiled it. Keyxor doesn't work for me as yet.

  9. #9
    Registered User
    Join Date
    Nov 2002
    Posts
    48
    Long time reader/lurker...first time poster....be gentle...

    Originally posted by Raelik
    MisterSpock,

    I get the following errors when I try to compile your v.2.07 of maggotboy's sniffer:

    Deleting intermediate files and output files for project 'XXXXXXXX - Win32 Release'.
    --------------------Configuration: XXXXXXXX - Win32 Release--------------------
    Compiling...
    eqsniffer2.cpp
    C:\Program Files\Microsoft Visual Studio\MyProjects\XXXXXXXX\eqsniffer2.cpp(413) : error C2065: '_strtoui64' : undeclared identifier
    C:\Program Files\Microsoft Visual Studio\MyProjects\XXXXXXXX\eqsniffer2.cpp(416) : error C2065: '_tstoi' : undeclared identifier
    Error executing cl.exe.

    XXXXXXXX.dll - 2 error(s), 0 warning(s)

    This is with VC++ 6.0, on Win 2k.
    I get the same errors....Any fix for this? Seems that the two functions in question are not in the stdlib.h where they should be according to the MSDN info I have...

    VC++6 SP5 on XP Sp1
    HM

  10. #10
    Registered User
    Join Date
    Nov 2002
    Posts
    16
    Originally posted by Hendrix_Morton
    Long time reader/lurker...first time poster....be gentle...

    I get the same errors....Any fix for this? Seems that the two functions in question are not in the stdlib.h where they should be according to the MSDN info I have...

    VC++6 SP5 on XP Sp1
    HM
    Nope. Still got nothing.

  11. #11
    Registered User
    Join Date
    Dec 2001
    Posts
    144
    For those running in to problems with VC6...

    I was able to replicate your problem, though I'm not sure if there is a fix or a workaround yet.

    It seems that '_strtoui64' and '_tstoi' are not in the CRT (C Runtime Library) of VC6. Perhaps there is an update that can be downloaded.

    --------------------

    I have not spent a lot of time on this code recently, though. Fee indicated that he wasn't planning on including the XOR option in the next CVS of SEQ. If there is enough interest, I'll continue to work on it.

  12. #12
    Registered User
    Join Date
    Nov 2002
    Posts
    48
    I think there will be a strong enough interest to continue with it...

    Any extra security that doesnt really cost anything cant hurt to add...

    And if Fee doesnt want to add the XOR code to the CVS base, then lostinspace has listed the changes that manually need to be made in another thread, and I for one would be more than happy to make those manual changes to the code each time I update before doing the make...

    Security is a good thing...I dont see a reason not to use it...

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

You may post new threads
You may post replies
You may post attachments
You may edit your posts
HTML code is Off
vB code is On
Smilies are On
[IMG] code is Off