Page 1 of 2 12 LastLast
Results 1 to 15 of 27

Thread: A different approach V2

  1. #1
    Registered User
    Join Date
    Nov 2002
    Posts
    115

    A different approach V2

    I've got another idea It occurred to me while shaving this morning and mulling over the idea of further stealthing this key sniffer. Without question, the key goal is to run in the address space of the game so it can operate with impunity. V1 accomplished this goal, but it leaves a few faint signatures which leave it open (however slightly) to detection. I don't want to go into the details of the detection process for fear of Verant implementing them before I'm ready ...

    Onto V2 ...

    The idea I've come up with is much MUCH sneakier. RUNDLL32 won't be in the process list, and even if they enumerate the DLL's running in their address space, it won't appear there either. As a matter of fact, it'd be running in their address space, but they'd have no idea where to look for it or how to detect its presence! It won't hijack any DLL entry points, block any API calls or interfere in any way with the game -- won't change anything in the running game binary or any DLL's it uses either.

    I'm not even sure it'll work (although the code to do it is not complex and I may even have a work-in-progress later today), and it'll require a few advanced techniques, but it'll be all straight C/C++ code, no assembly, and be initially injected much like the previous code I wrote. However, once injected, it dumps its payload and exits -- the DLL will be unloaded, but the payload will remain, and RUNDLL32 will exist only long enough to deliver the package.

    I need to do a couple tests to see if it works before I go explaining the details ... if it does, Verant will be extremely hard-pressed (as if they weren't already) to detect this method.

    Maggotboy

  2. #2
    Registered User
    Join Date
    Nov 2002
    Posts
    4

    Cool ..

    For now, I have done something like this

    Lets say my Antivirusshield scanns the EQdir all 10-30 min and saves some data to a viruslogfile

    The transfer is done with a simple patchfile.
    ########################

    In the moment i dry to code a windowssystemservicehook that can handle the problem. (There is a service who scans the HDD all day long for indexing)

    But I think the only "endsolution" is something like yours.

  3. #3
    Registered User
    Join Date
    Dec 2001
    Posts
    752
    You Guys rock !!!

    Thank you !!!
    -- Lord Crush

    Greater Faydark has to be cleaned from all Elves !

    This is a HOTKEY !!!

  4. #4
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    So far, I've had some success and some failure in implementing my idea.

    For one thing, its much more complicated than I originally thought, and it has required 2 lines of inline assembly, which I was hoping to avoid.

    If I can get some of the unresolved issues taken care of, I may post it prematurely in hopes of getting a collaborative effort going.

    Maggotboy

  5. #5
    Developer Ratt's Avatar
    Join Date
    Dec 2001
    Posts
    533
    I'm really impressed with the level of effort towards this ... keep up the killer work

  6. #6
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    I'm almost read to post this ... needs some more tweaking and testing ...

    Essentially it works like this ...

    1. Set a global hook into Windows which auto-injects the DLL into all processes. The DLL is dormant until a process matching the description (in our case "eqgame.exe") attaches.

    2. When the matching process attaches, we begin Phase 2, which is:

    3. Obtain a pointer to the first and last functions in our DLL that pertain to the actual payload. Subtract one from the other to get the length of the code.
    4. Call VirtualAlloc() an allocate some committed virtual memory with the PAGE_EXECUTE_READWRITE flag set, large enough to contain the code from step 3 plus some extra room for data.
    5. Copy the code from our DLL into the new allocated memory, along with a struct containing useful information and API call pointers (these API calls must be explicitly de-referenced because the DLL holding the lookup tables will disappear in 2 steps)
    6. Create a timer which calls into the new relocated code segment.
    7. Detach the DLL and remove the global hook. All instances of the DLL in all processes will be dropped, and RUNDLL32 which was in standby awaiting payload delivery, will end.

    What is all that mess in layman's terms?

    What I've done is taken a block of code and a bit of data with it, and created a NEW code-only memory segment inside the address space of the game. I've copied the bit of code from the DLL into the newly-created memory space and have told Windows to execute a Timer callback procedure inside this new code block. Once the new code block is in place, the DLL no longer needs to remain, so it drops out. As a matter of fact, the system won't even realize the DLL was there in the first place.

    Once the payload is delivered, the global hook is released, thereby causing RUNDLL32.EXE to drop out of the process list.

    The end result ... I've allocated some memory inside the game's address space, injected some code, and set Windows to call on it every half second.

    My tests in XP have it working, sortof. I need to make sure its stable before I post it.

    Some caveats ... I don't know if the darn thing will work with another compiler. I use an inline assembly function:

    {
    LPVOID pvmem;

    __asm mov pvmem, esi

    }

    which at least in my tests, has received the memory block start address and is mandatory in order for the code to get its bearings.

    Since the code is executing in memory other than where it was originally found, its impossible for the code to know where it is unless I use some assembly. This fact alone may deter most people from using the method. However, it is IMO the *most* stealth method I've seen that is in a state to be implemented.

    In order for VI to detect this injected code, they'd need to first figure out where it was allocated. Since the memory isn't associated with any DLL, doesn't have a lock count or a HANDLE, can't be HeapWalk()'ed, and is of arbitrary size, location and offset, they'll have their hands full.

    Maggotboy

  7. #7
    Registered User baelang's Avatar
    Join Date
    May 2002
    Posts
    252
    Am i correct in assuming that this bit of code goes away once eqgame exits and the game's memory space is cleaned up?
    BaeLang
    ---
    "seek and ye shall find." <-- god's way of saying use the damn search button. (or grep)

  8. #8
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    Of course. Some extra RAM is allocated on the running process only, and the code injected into it. Once the program exits, all that memory is freed automatically. The actual game isn't modified in any way, either in its running form or otherwise.

    Maggotboy

  9. #9
    Registered User
    Join Date
    Nov 2002
    Posts
    3
    CreateRemoteThread, baby.

  10. #10
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    Absolutely not!

    When you create a new thread in a process, EVERY DLL IN THAT PROCESS GETS NOTIFIED!

    While its true you can call DisableThreadLibraryCalls(), Verant can easily test to see if the thread library calls are disabled and shut the game down if it detects tampering.

    Maggotboy

  11. #11
    Registered User
    Join Date
    Aug 2002
    Posts
    143
    Devil's advocate here, not suggesting Sony is even looking for sniffers at the moment.

    What do you use for the timer? CreateTimer() runs through the message loop and can be interecepted remember.

    Interesting plan though. As always, the best defense is using VirtualProtect() and kernel mode hooking to determine what's trying to read your protected memory. To be relatively sure of not having your calls intercepted in user land, you should only link against ntdll and not kernel32, user32 or advapi32.

    If I was to implement sniffing countermeasures I'd do something like the following (all kernel mode stuff):

    - Hook OpenProcess and monitor it for eqgame.exe's pid.
    - Protect the key either with GUARD or just no access.
    - Hook VirtualProtectEx and monitor it for eqgame.exe's pid. If the pid matches then look at the user mode call stack to figure if the call matches a known location that eqgame.exe is *supposed* to call it from.
    - Move the key around randomly in memory (which is going to happen anyway if VirtualAlloc is used).

    Alternately just move then entire decryption logic (key exchange and all) into a driver. Ship EQ2 with a hardware dongle that actually does the decrypt (not just supplies a key).

  12. #12
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    The SetTimer() function does generate messages that get processed in the message queue, however ...

    Since I've made it using a callback notification, the timer's callback is executed automatically by the system.

    After doing some research on the queue, I've decided that it probably isn't quite as bullet-proof as it needs to be, so I'll be coming up with an alternative in the very near future. I was under the original impression that WM_TIMER messages were considered non-queued messages -- ones that could not be seen by a PeekMessage call. It appears however, that its not the case, and it *may* be possible to detect those messages on the queue if you time it right.

    As for a kernel-mode driver ...

    API-hooking is a bad idea. It causes undue system overhead and causes each instance of the kernel32.dll to be copied to each process rather than having them all share the same instance. The memory of DLL's is marked with PAGE_EXECUTE_WRITECOPY protection.

    A safer and less intrusive approach would be to write a winsock layered service provider to obtain legal and designed-for access to the socket layer...once inside, check the PID to find out if its EQ, and then sink your teeth into it from there.

    Maggotboy

  13. #13
    Registered User
    Join Date
    Nov 2002
    Posts
    48
    As this is living in the process space couldn't EQ just take a look at the memory footprint and do some checking to see if there is extra code in there?

    That seems to me how virus detection is done....
    Quothe the raven, "Nevermore!" - Poe

  14. #14
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    ... should have read more closely ... you were discussing countermeasures, not sniffing measures

    There's several problems with the countermeasures ...

    1. VirtualAlloc is an expensive call and should not be done in tight loops.
    2. If they move the key to random locations, it is still necessary to keep a pointer to those locations. If you create a GlobalAlloc() with GMEM_MOVEABLE and constantly lock and unlock the page to get the key ... Windows may or may not move it, no guarantee. Even if it was moved after EVERY unlock, you still have to have a constant pointer to the memory's HANDLE ... sniff out the handle, and you have unfettered access to the key.
    3. VirtualProtect() does nothing if you KNOW where the location is. You merely un-protect and un-guard the memory, then read it, then replace the original protection flags.
    4. An injected DLL has the bonus ability of being able to keep track of all threads that get created courtesy DllMain. If you're worried about being monitored, your function can SuspendThread on every thread in the process (except the current one), read the memory, then ResumeThread. The operation would be quick enough that none of the threads would really be aware they were momentarily shorted.

    Maggotboy

  15. #15
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    Not really, Poet.

    In order to make the distinction between memory allocated for code, and memory allocated for use by the application, you'd need to do three things ...

    1. Figure out the difference between memory YOU allocated, and memory allocated by something ELSE. Even if you *can* make this distinction, you CANNOT determine whether the memory allocated by something else is legitimate or not -- and you cannot determine exactly WHO allocated the memory. For example ... when you initialize winsock, it allocates thread-local storage and memory so it can do name lookups, etc. You didn't allocate the memory, you've no idea where it was allocated, when, how much and by who.

    2. If you *could* figure out what other memory was allocated, you'd then need to scan it by calling VirtualQuery() on tiny chunks of it at a time to determine which chunks were marked PAGE_EXECUTE to determine the code. One could walk all the blocks of memory a dozen bytes at a time, but VirtualAlloc() allocates memory in blocks around the locations of loaded DLL's, so you'd have to navigate in and around the loaded modules -- otherwise you could mistake a legitimate DLL for a sniffer. In order to navigate around the DLL's, you'd have to know where they all are in memory, and know how large they are.

    3. While navigating the memory, you'd have to be wary, because not all of it could be committed at the time. You'd have to constantly check for invalid pages, of which you have no idea how large/small they are.

    How is this more difficult than checking for Viruses? I'm glad you asked ...

    The sniffers are 10x more difficult to find than checking for a virus, because they are 10x more polymorphic. Each compiler compiles differently, and depending on the compiler settings, each compiler can compile the code 100 different ways. Add to that the ability to rearrange the code, add unused bits of code, fiddle with the order of stack variables, etc. Everyone who runs a sniffer will be running a DIFFERENT sniffer with a different signature!

    Viruses are compiled once, and are only polymorphic up to a point. They can't 100% reinvent themselves on every infection, and this is why virus scanners can detect them.

    If Verant succeeds in making a program that can detect every variation of code on a sniffer, then they may just have to launch a new Virus Scanning division and kill off McAfee and Norton

    Maggotboy

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