Page 2 of 6 FirstFirst 1234 ... LastLast
Results 16 to 30 of 81

Thread: Question about stealth methods

  1. #16
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    Not at all, Sodom. To hook into explorer.exe you use the same SetWindowsHookEx() method that I used to hook into the EQ game in my previos sniffer techniques.

    You set the hook globally, which causes the DLL to load into explorer.exe. You then increment the reference count on yourself, unhook your global hook, spawn a worker thread in explorer's address space....and have a party.

    Maggotboy

  2. #17
    Registered User
    Join Date
    Oct 2002
    Posts
    59
    Ok, I did some research connected with only real threat that I see in detecting key reader - and that is interception of API calls.

    Basically, if SOE can intercept API calls, they can catch any variation of keyreader so far. Most probable APIs that they can hook on and detect keyreaders based on parametar (handle will be their process handle) are:
    - OpenProcess
    - readProcessMemory
    - CloseHandle
    - VirtualProtectEx

    So, how can they (or anyone ) intercept API call? Well, there is long chain of pointers and calls between compiler API procedure and real procedure deep in for example NTOSKRNL. They can basically change any pointer in that chain, or change code that invoke those pointers. Simple example for ReadProcessMemory:

    *** USER MODE ***
    1) compiler has ReadProcessMemory mapped on some fixed address in its own code , where it only read its own process table and jump to next step (KERNEL32 part of same procedure). In case of my compiler, that is:
    APP.readProcessMemory: $4xxxxx
    jmp mem4[$4xxxxyy] -> 77e61a54 (KERNEL32.ReadProcessMemory)

    2) in KERNEL32 it put parameters on stack andll just redirect to deeper level, ntdll, and still in user mode.
    KERNEL32.ReadProcessMemory: 77e61a54
    call mem4[$77e613fc] -> 77f7ef53 (ntdll.NtReadVirtualMemory)

    3) in ntdll there is last element in chain in USER mode, it is basically just setting system procedure number into EAX (in this case it is $BA) and transferring control to real procedure in NTOSKRNL in ring 0 , with SYSENTER.

    *** KERNEL MODE ****

    4) service handler receive service call, and based on number in EAX it looks into System Service Dispatch Table, and jump to that address

    5) finally, actuall code for procedure is reached and it does what it need to do, then return along chain back to user app.


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

    Where they can intercept call? Well, they can do it in user or kernel mode.

    In user mode they can do it at any of points 1-3, but since point 1 is usually compiler based, its more probable that they would change table that KERNEL32 is using. Note, even if its called KERNEL32, it works in ring 3 user mode, so any program like one we use to hook DLL can read that memory and write its own data.

    In kernel mode they need ring 0 access, which they can gain either thru device driver that loads at ring 0, or using KeServiceDescriptorTable() . They can either change SSDT for $BA entry to point (redirect ) to their code (easiest), or change code where SSDT points normally.

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

    How can we protect ourselves? Well, one thing that i can think of is just by initially reading all this memory and pointers in chain for each of 4 APi functions and saving it to file. Later, with every execution of read, we first reread that chain memory footprint and pointers and see if it is identical to what it was when we originally saved ( since Kernel DLLs always go in same spot in memory, it is doable). If all is same as before, noone hooked and we are safe. If anything changed, just show Warning message and exit.

    So far i did that part for USER mode, and I'm still looking for easy way to get to ring 0 to check KERNEL mode. I found that KeServiceDescriptorTable is most often used method , but since I'm not using C and its undocumented export, so far I couldnt link it in NTOSKERNL.EXE, but I'll keep trying.

    Also, same technique that they can use for inserting API hook interceptor on kernel level, or that we need to use to check SSDT tables, could be used to make ring 0 key reader

  3. #18
    Registered User
    Join Date
    Oct 2002
    Posts
    59
    Finally I think that I have non-detectable keyreader.

    Whatever SOE do to try and detect keyreaders , it can come up with three kinds of results:

    - everything is OK ( whatever that means )

    - there is something unusuall, but they are not sure what it is. Example is detecting unusuall DLL attached to their process. It is unusuall, but they can not draw any conclusion based on that - it could be anything like mouse drivers, virus scanner etc...

    - there is something that is tempering with EQgame. Example is if they catch something using API calls related to their process, like ReadProcessMemory, OpenProcess ... presence of EQ process handle is positive proof enough that something is targeting EQgame.

    As I pointed before, interception of APi call is only positive way for them to do it, and I tried to make sure they did not intercept. Then I hit ring 3 <-> ring 0 barrier ... basically, I couldnt check if they hook on service dispatch table in kernel.

    So I finally installed that VC6 and DDK and made my simple driver which enabled me access to ring 0 and Service Dispatch tables. But while I was reading and comparing SSDT, I did small test - read of user process memory (like reading key from EQ) from my kernel driver . And I got very positive result :


    Reading user process memory from kernel driver DOES NOT trigger VirtualProtect traps !


    What does that means? Basically it means that now I can read EQ key memory without calling ANY API function that has any direct refference to EQgame process. Since I still didnt find out how to switch linear addressing from one proccess to other in kernel driver (they run in caller procces linear address space usually, at least mine does since its high level driver), I must call it from EQgame process. So, here is what I do:

    1) set GlobalHookEx as I did previously, from hookshell.exe. Hook will be on hook.dll
    2) in hook.dll initialization part I check if this is EQgame
    3) if this was EQ, in hooked event I periodically read memory from key area, using ioCtl function from my driver
    4) I return key data to outside program hookshell.exe
    5) hookshell.exe does sending UDP packets or writing to shared samba folder or showing key on screen, according to INI file

    Notice that NONE of those operations use handle of EQgame, of its process or anything else.

    Previously my Hook version did same thing, only used normal memory read from in-process. Danger of that was if SOE VirtualProtect key area in next patch. Solution to that was to use my own VirtualProtect to unprotect/reprotect around my read. Danger to that was if SOE intercept calls to VirtualProtect API. But now i read memory area from kernel, and it will not trigger even if SOE use VirtualProtect.

  4. #19
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    Lets have a look at it!

    Maggotboy

  5. #20
    Registered User
    Join Date
    Dec 2001
    Posts
    204
    Very nice!

    I'm no coder (As I've said as such before), but this sounds like quite a pickel for SoE (even if they can) to unravel!



    He he he, when are you going to post it?

  6. #21
    Registered User
    Join Date
    Oct 2002
    Posts
    59
    Well, I didnt post source because:

    a) if someone know how to install VC6 and DDK, and how to compile and use sample drivers , then previous text was enough. If someone dont know to do that, then source wont help

    b) I changed about 5% of already existing sample driver code for random driver. So posting all source would be like posting Microsoft sample driver

    But anyway, here is my procedure. You choose any sample driver, and in Dispatch( IN PDEVICE_OBJECT pDO, IN PIRP pIrp ) function add or change existing pIrpStack->MajorFunction to call your function : myIoctlReadTst.

    NOTE: while read of memory from kernel will not trip guard, it still fails if memory is VirtualProtected, BUT that failure will not make hooked DLL detected. It is possible to use VirtualProtect counterparts from kernel to also unprotect memory if needed, but that so far is not needed. Important advantage is : if they suddenly add protection, this simple read will only fail reading, and will not trigger trap.

    ---------------------------------------------------------------------------------
    // KERNEL DRIVER part (note, only part, rest is same as sample in ntddk/src

    // INPUT buffer format
    // I used 12 byte input buffer, in form
    // DWORD callType
    // PVOID adrToRead
    // DWORD flags
    // although flags are optional
    // only type 8 is for memory read, others are for test


    // OUTPUT buffer format
    // for callType 8 = memory read, i simply return requested number of bytes
    // CHAR result[N] , where N=OutBufferSize
    //
    // for other callTypes (like 1=version), i return
    // PVOID adrFromWhichIReaded
    // CHAR result[N] , whene N=4 for version


    // --> TEST <--
    NTSTATUS
    myIoctlReadTst(
    IN PLOCAL_DEVICE_INFO pLDI,
    IN PIRP pIrp,
    IN PIO_STACK_LOCATION IrpStack,
    IN ULONG IoctlCode )



    {
    PULONG pIOBuffer; // Pointer to transfer buffer
    ULONG InBufferSize; // Amount of data avail. from caller.
    ULONG OutBufferSize; // Max data that caller can accept.
    ULONG nPort; // Port number to read
    DWORD64 nOut; // return integer
    ULONG flags; // input flags
    ULONG DataBufferSize;
    VOID UNALIGNED *pSource, *pDest;
    PVOID pAddress;
    PULONG pUlong;
    PDWORD64 p64bit;

    PAGED_CODE();

    // Size of buffer containing data from application
    InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength;

    // Size of buffer for data to be sent to application
    OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;


    // NT copies inbuf here before entry and copies this to outbuf after
    // return, for METHOD_BUFFERED IOCTL's.
    pIOBuffer = (PULONG)pIrp->AssociatedIrp.SystemBuffer;


    // check input & output
    nPort = *pIOBuffer; // Get read(call) type
    // return result based on type. Only ReadMemory call type (8) remained in listing
    switch (nPort){
    case 8: if (InBufferSize<8) return STATUS_INVALID_PARAMETER;
    p64bit= (PDWORD64) *(pIOBuffer+1);
    if (InBufferSize>=12) flags=*(pIOBuffer+2); else flags=0;
    // should I check for Memory validation?
    if ((flags& 0x01)==0){
    pAddress= (PVOID) (((ULONG)p64bit & 0xFFFFF000));
    if (!MmIsAddressValid(pAddress)) return STATUS_ACCESS_VIOLATION;
    }
    nOut= *p64bit;
    pSource = (VOID UNALIGNED *)&nOut; // int64 from ptr->
    if (OutBufferSize>8) DataBufferSize=8; else DataBufferSize=OutBufferSize;
    break;

    default:
    return STATUS_INVALID_PARAMETER;
    }
    pDest=pIOBuffer;
    if (DataBufferSize>OutBufferSize) return STATUS_INVALID_PARAMETER;
    RtlCopyMemory (pDest, pSource , DataBufferSize);

    //
    // Indicate # of bytes read
    //

    pIrp->IoStatus.Information = DataBufferSize;

    return STATUS_SUCCESS;
    }



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

    //You use memory read from USER application (like hook.dll) with:

    BOOL IoctlResult;
    HANDLE hndFile; // Handle to device, obtain from CreateFile
    LONG IoctlCode;
    DWORD ReturnedLength; // Number of bytes returned
    DWORD64 resultKey;

    struct TinDrv // Declare inDrv struct type
    {
    DWORD callType;
    PVOID adrToRead;
    DWORD flags;
    } drvIn; // Define object to hold input data for driver

    hndFile = CreateFile(
    "\\\\.\\nameOfDev", // Open the Device "file"
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    0,
    NULL
    );

    if (hndFile == INVALID_HANDLE_VALUE) // Was the device opened?
    {
    printf("Unable to open the device.\n");
    exit(1);
    }

    IoctlCode = IOCTL_GPD_READ_TST2;
    drvIn.callType=8;
    drvIn.adrToRead= <address that you want to read, = offset in EQ> ;
    drvIn.flags=0;

    DataLength = sizeof(DataBuffer.CharData);
    IoctlResult = DeviceIoControl(
    hndFile, // Handle to device
    IoctlCode, // IO Control code for Read
    &drvIn, // Buffer to driver.
    sizeof(drvIn), // Length of buffer in bytes.
    &resultKey, // return Buffer from driver.
    8, // Length of buffer in bytes.
    &ReturnedLength, // Bytes placed in DataBuffer.
    NULL // NULL means wait till op. completes.
    );

    if (IoctlResult) // Did the IOCTL succeed?
    {
    // here do whatever you want to do with your key
    printf("and the key is .... %x", resultKey);

    }else
    printf("Read failed with code %ld\n", GetLastError() );

    if (!CloseHandle(hndFile)) printf("Failed to close device.\n");
    exit(0);
    }

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

  7. #22
    Registered User
    Join Date
    Aug 2002
    Posts
    143
    Impressive. The next steps are:

    i) Work through the steps to access another process's memory from ring0.
    ii) Create a 2nd virtual memory page mapping to the memory inside eqgame.exe which has read access.

    This would leave Sony the only options for detection:

    - Obfuscate the key better. This can be reverse engineered but can be painful if changed frequently.
    - Write specific code to detect the ioctl calls to your driver (probably another driver), which you could then detect and subvert, which they could detect etc.
    - Switch to hardware based protection (USB or LPT dongle) which has the hardware itself doing the decrypt.
    - Lobby for Palladium and register itself into the DRM trusted channel stuff which code unsigned from Microsoft won't be able to touch (how they are going to protect from ring 0 drivers is beyond me though). This is pretty irrelavent to the present problem, but is a problem in future.

  8. #23
    Registered User
    Join Date
    Nov 2002
    Posts
    115
    I'll install the DDK from my MSDN Universal subscription and have a look at the samples. Ideally, I'd like to start a timer in kernel mode to read the memory location, which would allow the original hook DLL to terminate after initiating the driver.

    I've also done some research on accessing memory (all of it, not just from a specific process) from kernel mode ... Virtual Memory internals are not for the weak of heart, though.

    Maggotboy

  9. #24
    Registered User
    Join Date
    Oct 2002
    Posts
    59
    Yes, there are kernel equivalents of functions that enumerate processes ( like Process32First) , or read other process memory. Maybe I'll look into those, or any other way to switch linear address space to other process from kernel.

    About those options that Sony have, I see only first one as viable option, because:

    - even if they can detect ioctl call, they will never know what is driver that is called. Name of driver is arbitrary, so are ioctl codes. And tons of windows stuff that application normally use, also use ioctl calls ( to network drivers, graphic drivers....). With all those layered drivers etc, application can never know for sure what some ioctl call really does, even if they somehow catch it.

    - dongle does not seem as option, because it would require 400000 people to buy it ... and that was not advertised on box when you originally bought it. Not to mention that dongle would be high pain to SOE support stuff , and it would not be profit to Sony but to dongle maker

    - about Palladium and DRM I dont know anything, but I believe that is not option they have now

    About memory accessing under win2000, I also searched a lot on web, and found one book that looked very interesting, judging on articles posted on this url:

    http://www.awprofessional.com/catalo...5B9F6E080AC%7D

  10. #25
    Registered User
    Join Date
    Oct 2002
    Posts
    59
    Well, today I tried few other approaches from kernel driver, and one of them looks interesting, and even works

    I added two functions to my driver:

    case 9: if (InBufferSize<8) return STATUS_INVALID_PARAMETER;
    p64bit= (PDWORD64) *(pIOBuffer+1);
    pa= MmGetPhysicalAddress(p64bit);
    pSource = (VOID UNALIGNED *)&pa; // get Physical address
    DataBufferSize=sizeof(pa);
    break;
    case 10: if (InBufferSize<12) return STATUS_INVALID_PARAMETER;
    pa= * (PPHYSICAL_ADDRESS) (pIOBuffer+1);
    p64bit2= MmMapIoSpace(pa, 8, MmNonCached );
    nOut= *p64bit2;
    MmUnmapIoSpace( p64bit2, 8);
    pSource = (VOID UNALIGNED *)&nOut; // read from Phys addr
    DataBufferSize=sizeof(nOut);
    break;



    Function (9) return Physical address based on Virtual address, and I call it only once when my hook.DLL detect that it is EQ process. Then resulting Physical address is returned to hookshell.exe and dll terminate immediatelly.

    Function (10) read from given Physical Address. It can be called from ANY process, and in my case i call it from hookshell.exe whenewer i want to read key.

    Important advantages of this approach:

    - no need for attached DLL to EQgame
    - ability to read memory from more comfortable external EXE (which also send UDP etc..)
    - reading does not trigger eventual VirtualProtect, and .....
    - reading CAN READ key even if it is VirtualProtected !


    Possible disadvantages:

    - IF physical memory is paged out, key reading will be wrong. BUT it wont make any exception or trigger anything, it will just return wrong key. And it is highly unlikely that code/data part of running active process will get paged out and I can always reattach temporarily hook.dll to read new address and detach immediattely. LIke having <Refresh> button in hookshell.exe

  11. #26
    Registered User
    Join Date
    Dec 2001
    Posts
    752

    Thumbs up

    OMG - i was away from forum few hrs - You guys rock !!!!

    Thank you /bow
    -- Lord Crush

    Greater Faydark has to be cleaned from all Elves !

    This is a HOTKEY !!!

  12. #27
    Registered User
    Join Date
    Nov 2002
    Posts
    22

    Memory Management

    I picked up a copy of this book - it has an excellent chapter on memory management.

    http://www.readmedoc.com/bookdetails...SBN=0201721872

    The memory management issue may not be too tough. Given a virtual address - the corresponding PTE used to access memory is known.

    The process has a vector containing the PTEs that can be quickly loaded into hardware during context switch.

    As I recall there are working pages available at driver level - these are normally mapped to the calling process to manage buffers etc.

    You should be able to force the proper mapping by copying the correct PTE from eq - into your ring-0 context. This is a little dicey. Most drivers I have seen allocate internal buffers and copy memory to or from user context in chunks.

    Once you have your own VM mapping - nothing can see you. However - you also have NO way of knowing if the page is in memory, or worse yet if the physical location of the page changes. To be safe - you really need to access the PTE vector for EQ on every read. This is not a huge issue. EQ trys to dominate the machine - and if you have enough memory chances are there is no swapping occurring. But to be safe - it should probably be handled.
    Wiz60
    The Lurker at the Threshold

  13. #28
    Registered User
    Join Date
    Aug 2002
    Posts
    143
    wiz, messing with page tables isn't a great idea. There's better ways.

    lostinspace, look at firing an APC in the eqgame.exe process to read the memory location and then return it to your driver. Will probably require a bit more modification of the stock driver but will essentially eliminate the need for any DLL injections in user space.

    For the attacks, I was just tossing things out there that were possible, however improbable. Any of them could be *made* to happen but whether they are practical is Sony's problem. I would never expect them to take the obvious route to securing their process though...

    Ioctl from another unrelated process may as well be undetectable if the process and driver are polymorphic. However the driver must be compiled with the DDK so there may be something there to hook onto in a virusscan style way?

    Dongles are a customer service pain, but a possibility for EQ2. I certainly doubt they will send them out to current clients.

    Palladium won't be out for a while. Still interested to see what they do with it.

    Moving the key around is just silliness but probably the only real option left that will be effective for long.

  14. #29
    Registered User
    Join Date
    Oct 2002
    Posts
    59
    After testing my new implementation of key reader, i found few problems ... guess anyone who try to implement it that way will hit those so i'll post solution.

    Namely, my new keyreader did following:
    1) Hookshell.exe is main prog, it set hook with hook.dll
    2) when EQgame starts, in hook.dll i detect it, read Physical Address of key area [ using function 9 of myDriver.sys] , return it to Hookshell.exe and set ExitCode <>0 so DLL does not load at all
    3) in Hookshell.exe I use physical address to read key whenever i need it (i do it every N sec) [ using function 10 of myDriver.sys], and send it to SEQ when it change

    That is very good solution since , as I already mentioned, I dont use any detectable API call, DLL does not attach to process, and I can read key even if it is VirtualProtected.

    But .... as I mentioned, I experienced 2 problems:

    1) first one is fact that if I return <>0 exit code so DLL does not attach, and if hook is still active (as it is), DLL will attach again on EQgame...and again...and again - until I unhook .
    First solution was to unhook in DLL when i detect EQgame.... but I didnt want to use any exotic API call from same process as eqgame, and UnhookWindowsHookEx is sure exotic
    Second solution was not to detach DLL with exit code <>0, but as soon as i receive Phys address in Hookshell.exe, I detach hook there - detaching hook detach DLL too. So DLL remains attached for about 1sec, but I'm ok with that

    2) second problem was far more serious ... and was not manifested in EQgame, but while reading key from my appTest.exe where I tested variations with relocated keys and virtualProtected keys.
    Problem was that sometimes i ccould read key even when it was VirtualProtected, and sometimes I could not. Further investigation showed that i can ALWAYS read key from physical memory regardless of protect status, BUT I can not get initiall physical address from virtual key address if key was already protected.
    Function I used was MmGetPhysicalAddress, and there is no lower kernel function that can do that virtual<->physical conversion. No VirtualProtect function is exported in kernel. So I thought I'm stuck there, until I started experimenting with my own GetPhysicalAddress function ... that needed to decode physical address in same way that Intel processor does. After reading some of already posted materials about paging memory system , I finally made my own function that returns physical address EVEN if virtual address is locked

    Here is code, I added it into kernel driver as function #13:

    case 13: // my custom GetPhysicalAddress
    if (InBufferSize<8) return STATUS_INVALID_PARAMETER;
    dw= *(pIOBuffer+1); // Virtual(linear) addres for which we want PA
    if ((dw >= 0x80000000)&&(dw<0xA0000000)){
    // here is 4MB paged kernel range. I dont need it, but ...
    nOut= dw & 0x1FFFFFFF;
    } else {
    // here is part for 4kb pages, read PTE at 0xC0000000
    pUlong= (PULONG) (0xC0000000 + ((dw >> 12) * 4)) ; // calculate PTE address
    if (!MmIsAddressValid(pUlong)) return STATUS_ACCESS_VIOLATION;
    nOut= *pUlong; // read start of Phys page from PTE
    nOut= (nOut & 0xFFFFF000) | (dw & 0xFFF); // last bits are retained from virt. addr
    }
    pSource = (VOID UNALIGNED *)&nOut;
    DataBufferSize=sizeof(nOut);
    break;

  15. #30
    Registered User
    Join Date
    Nov 2002
    Posts
    14
    Just a note:

    Dongles are not even a possibility as more and more motherboards (like mine) are coming without ANY legacy ports. That means no serial/PS2 ports and no parallel ports. Just USB and Firewire. I've never heard of a dongles for those ports.

    I have major doubts that Sony will do anything of the sort.

    Pokesfan

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