VirtualQuery hooking/mem block stealthing menu

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 15 of 21
  1. #1
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    VirtualQuery hooking/mem block stealthing

    Here's a bit of code I wrote to stealth memory blocks thru VirtualQueryEx hooking (VirtualQuery calls VQE, so you can just detour VQE).

    It ain't pretty, but it works. By "ain't pretty" I mean -- I ignore all sorts of return codes, I do a brute force search for the previous block, and right now it only stealths one block at a time; I assume anyone who's competent enough to understand the code is competent enough to make it pretty and safe.

    It works by merging blocks; if the block being searched for is the block before your "stealth" block, and it's free, this returns the actual results plus the size of the stealth block. If the block being searched for is the stealth block, the fields are set to indicate that it's free. If the next block is free, its size is added to the size of the stealth block (and possibly also the previous block).

    Note that because of the way VQE works, you can't search "backwards" in memory without a walk from zero to the address in question (otherwise region sizes/bases will be wrong). This makes this detour inefficient for frequent VQE's, but for occasional hits (like, say, Warden might do... which is why I was asking about what it does these days, which led to being called a retard ) it should be fine.

    Note that this is far from uncounterable; I can count off at least two or three ways that Warden could counter this, in the future.

    Still, imperfect as it is, it's a start. I searched the forums to see if this was prior art, but I didn't see it anywhere. It may be overkill, but if you coupled this with hooks on VirtualAlloc and the lovely PEB LDR DLL-hiding code floating around here on the forums, it should make it even less likely that Warden will see your code caves/injected DLL's.

    Code:
    MEMORY_BASIC_INFORMATION stealthBlock; // obviously you have to provide this
    
    bool AddressIsIn(PMEMORY_BASIC_INFORMATION pMBI, DWORD dwAddx)
    {	
    	return (dwAddx >= (DWORD)pMBI->BaseAddress) && (dwAddx < (DWORD)pMBI->BaseAddress + pMBI->RegionSize);
    }
    
    bool GetPreviousRegion(PMEMORY_BASIC_INFORMATION pCurrentMBI, PMEMORY_BASIC_INFORMATION pPrev)
    {
    	// HACKHACK: sanity check params
    	DWORD dwAddx = 0;
    	MEMORY_BASIC_INFORMATION mbi;
    	HANDLE hCurProc = GetCurrentProcess();
    	while (pVQE_Trampoline(hCurProc, (LPCVOID)dwAddx, &mbi, sizeof(MEMORY_BASIC_INFORMATION)))
    	{
    		dwAddx = (DWORD)mbi.BaseAddress + mbi.RegionSize;
    
    		if (dwAddx >= (DWORD)pCurrentMBI->BaseAddress) // HACKHACK: doesn't account for exceptional cases
    		{
    			memcpy(pPrev, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    			return true;
    		}
    	}
    
    	return false;
    }
    
    void StealthMBI(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION pMBI)
    {
    	if (pMBI)
    	{
    		MEMORY_BASIC_INFORMATION next;
    		HANDLE hCurProc = GetCurrentProcess();
    		//GetPreviousRegion(&stealthBlock, &prev); // HACKHACK: should check for failure
    		pVQE_Trampoline(hCurProc, (LPCVOID)((DWORD)stealthBlock.BaseAddress + stealthBlock.RegionSize), &next, sizeof(MEMORY_BASIC_INFORMATION));
    		
                    if (AddressIsIn(&stealthBlock, (DWORD)pMBI->BaseAddress + pMBI->RegionSize) && (pMBI->State == MEM_FREE)) // if the next block is stealth block, we're in the block before stealth (duh)
    		{
    			// we're querying the block BEFORE the stealth block
    			pMBI->RegionSize += stealthBlock.RegionSize; // add the stealth block size to this (previous) block size
    
    			if (next.State == MEM_FREE) // now, if the NEXT block is free too...
    			{
    				pMBI->RegionSize += next.RegionSize; // add its size to the pool
    			}
    		}
    		else if (AddressIsIn(&stealthBlock, (DWORD)lpAddress))
    		{
    			// we're querying the stealthed block
    			pMBI->AllocationBase = 0;
    			pMBI->AllocationProtect = 0;
    			pMBI->State = MEM_FREE;
    			pMBI->Protect = PAGE_NOACCESS;
    			pMBI->Type = 0;
    
    			if (next.State == MEM_FREE)
    			{
    				// the next block is free too, so we should merge the stealthed block and the next block
    				pMBI->RegionSize += next.RegionSize;
    			}
    		}
    	}
    }
    
    SIZE_T WINAPI VirtualQueryEx_Detour(
      __in      HANDLE hProcess,
      __in_opt  LPCVOID lpAddress,
      __out     PMEMORY_BASIC_INFORMATION lpBuffer,
      __in      SIZE_T dwLength
    )
    {
    	SIZE_T result = pVQE_Trampoline(hProcess, lpAddress, lpBuffer, dwLength); // first call the real VQE
    	if (result && (GetProcessId(hProcess) == GetCurrentProcessId())) // only care about THIS process
    	{
    		StealthMBI(lpAddress, lpBuffer);
    	}
    	return result;
    }
    Here's it at work. This is a raw dump of a sequence of MEMORY_BASIC_INFORMATION structures, before and after activation. The target block is a page of code I injected at 0x20000 (splitting the free block at 0x20000).

    Note that the block at 0x20000 shows a size of 0x1000 (4096 bytes), and a type of 0x20000 (MEM_PRIVATE).

    Code:
    ab: 00000000 ap: 00000000 ba: 00000000 pr: 00000001 rs: 00010000 st: 00010000 ty: 00000000
    ab: 00010000 ap: 00000004 ba: 00010000 pr: 00000004 rs: 00010000 st: 00001000 ty: 00040000
    ab: 00020000 ap: 00000004 ba: 00020000 pr: 00000004 rs: 00001000 st: 00001000 ty: 00020000
    ab: 00000000 ap: 00000000 ba: 00021000 pr: 00000001 rs: 0000F000 st: 00010000 ty: 00000000
    ab: 00030000 ap: 00000004 ba: 00030000 pr: 00000004 rs: 00003000 st: 00001000 ty: 00020000
    And afterwards:

    Code:
    ab: 00000000 ap: 00000000 ba: 00000000 pr: 00000001 rs: 00010000 st: 00010000 ty: 00000000
    ab: 00010000 ap: 00000004 ba: 00010000 pr: 00000004 rs: 00010000 st: 00001000 ty: 00040000
    ab: 00000000 ap: 00000000 ba: 00020000 pr: 00000001 rs: 00010000 st: 00010000 ty: 00000000
    ab: 00030000 ap: 00000004 ba: 00030000 pr: 00000004 rs: 00003000 st: 00001000 ty: 00020000
    Note that now it looks like there's a single free block (type 0) at 0x20000, size 0x10000 -- absorbing the following free block.

    Enjoy.
    Last edited by amadmonk; 05-03-2009 at 12:50 PM.

    VirtualQuery hooking/mem block stealthing
  2. #2
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Actually, I just realized that due to VQE's lack of backwards-reading, I can skip the brute force search for the "real" previous block, and just use the value passed in. Makes it a zillion times more efficient. I'll tweak the code...
    Last edited by amadmonk; 05-03-2009 at 12:50 PM.

  3. #3
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just wanted to point out that you could fairly easily bypass this with a call into the NT API. Its a fairly straightforward port to the NT API so I'd probably detour those instead, just for a bit of extra security (obviously you can still do a manually syscall but that's a mega pain to maintain and write for all OS versions).

  4. #4
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Harko View Post
    Or by looking for the detour code at the VirtualQuery API.

    For a public application such idea is only suitable if it is done in kernel mode like gliders VirtualAlloc / NtAllocateVirtualMemory hook.
    Except that restricts you to x86 which is a pretty huge deal right now. Also, W7 is probably going to be the last Windows version with x86 builds (they've already dropped x86 for W7 Server builds). Working in kernel mode also makes your OS a lot less stable unless you're a kernel mode expert (think: Glider BSODs).


  5. #5
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Harko View Post
    As far as I know the glider team worked on a 64bit solution at least mercury looked at the vista internals and the possibility that the customer turns off the driver integrity check.

    But yes the problem with unstable code remains.

    Disabling the driver integrity check (i.e. test mode) only solves the problem of loading an unsigned driver.

    Afaik it doesn't solve the problem of KPP. Otherwise malware could just disable it by forcing on the test flag on boot.

  6. #6
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PatchGuard (er, is it KPP now?) and driver signing is precisely why I wanted to do this in userland

    I used to have this working in my x86 rootkit back in the Windows XP days, but that's obviously not much use anymore (at least, not to me on Vista 64). I miss being able to hack my own system

    I've been half tempted to switch to Linux so I can run a custom Wine and get this ability all over again. But every time I try to run Linux as my main desktop, a million things break that "just work" in Vista, so I end up giving up on it.

  7. #7
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Cypher View Post
    Just wanted to point out that you could fairly easily bypass this with a call into the NT API. Its a fairly straightforward port to the NT API so I'd probably detour those instead, just for a bit of extra security (obviously you can still do a manually syscall but that's a mega pain to maintain and write for all OS versions).
    Good point. Also since one of the attack vectors against this would be to VirtualAlloc each supposedly "free" page, if you wanted to be extra paranoid, you could detour that too so that it returned the "right" error codes for your stealthed memory (not entirely sure what the "right" thing to do is when someone asks, by address, for a block of memory you've said is "free").

    And of course WoW could just try a direct memory read at page+0 for each supposedly free page (wrapped in EH, of course) to look for "weird" pages. But I think that might give false positives because there's no guarantee you won't hit a race where you VQE it, then another thread legitimately allocs it for some reason (virus scanner, who knows), and then you read.

    I'm just uber-paranoid about Warden, I guess It's why I have yet to actually RUN an in-process bot engine.

  8. #8
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Kernel Patch Protection is the official name afaik:
    Kernel Patch Protection: Frequently Asked Questions

    Usermode rootkits aren't hard to write. I've been working on one regularly for a while now. Got it working on both x86 and x64 (code examples posted on my blog). The biggest hurdle I had was getting stable API hooking working that was SMP safe and that I could eject at any time without blowing up the process (that, and upgrading my x86 code to x64, which was easy, except for a few things which are obvious with hindsight).

    So far got processes, windows, files, and modules. Next will be registry, then services, drivers, etc. Will be adding memory support to the module cloaker soon too.

    The point is, you can hack usermode all day long. If your bot is private you can pretty much **** warden all you want if it has to use an API to look for something. As long as you don't modify code in warden itself you're pretty safe (and you can even protect some public bots with a private hook).

  9. #9
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah, I realized this after I took off for work; I think PatchGuard was what we called it back when I was on the Visual Studio team, many eons ago. Everything changes names all the time, at the Mothership...

    So Warden doesn't check any of the API offsets at all? That seems... well, great for us, but sort of insecure for them since anyone who knows anything about Windows knows you have to go through the NtXXX functions to get into the kernel (unless you wanna manually do an int 2e, or whatever the syscall vector is now). But hey, I won't complain... that makes things WAY easier since you only really have to worry about unpatching WoW-space hooks when Warden wakes up. I totally see now why D3D hooking would be the best way to patch your bot into a per-frame heartbeat (before that seemed too easily detectable to me).

    I think maybe I was more skeert of Warden than I needed to be.

  10. #10
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You are indeed correct. Warden doesn't even do IAT or EAT checks on APIs, let alone inline hook checks or the likes.

    Yes, D3D is the way to go for getting into the main thread, they can't really ban for it because so much legit software uses D3D hooks (Fraps, Xfire, etc etc).

    Also, doing a manual call into the kernel is not something you want to do in production software due to the fact you'd have to write different stubs for every single OS version.

  11. #11
    kynox's Avatar Member
    Reputation
    830
    Join Date
    Dec 2006
    Posts
    888
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Cypher View Post
    You are indeed correct. Warden doesn't even do IAT or EAT checks on APIs, let alone inline hook checks or the likes.

    Yes, D3D is the way to go for getting into the main thread, they can't really ban for it because so much legit software uses D3D hooks (Fraps, Xfire, etc etc).

    Also, doing a manual call into the kernel is not something you want to do in production software due to the fact you'd have to write different stubs for every single OS version.
    That's not true. If they wanted to, they could; the functionality is there. The data however, is not.

  12. #12
    amadmonk's Avatar Active Member
    Reputation
    124
    Join Date
    Apr 2008
    Posts
    772
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Little fix to patch the "it's not at the end of the usermode chain" issue Cypher mentioned.

    This also allows potentially patching for the call with the MemorySectionName enum value, although I'm not 100% sure this is ever used (conflicting info; I found this enum in Nebbett's Native API Reference).

    Code:
    typedef enum _MEMORY_INFORMATION_CLASS {
    	MemoryBasicInformation,
    	MemoryWorkingSetList,
    	MemorySectionName,
    	MemoryBasicVlmInformation
    } MEMORY_INFORMATION_CLASS;
    
    typedef NTSYSAPI NTSTATUS (NTAPI *ZwQueryVirtualMemory_ptr)(HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, ULONG, PULONG);
    
    ZwQueryVirtualMemory_ptr pQVM_Original = NULL;
    ZwQueryVirtualMemory_ptr pQVM_Trampoline = NULL;
    
    void StealthMBI(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION pMBI)
    {
    	if (pMBI)
    	{
    		MEMORY_BASIC_INFORMATION next;
    		HANDLE hCurProc = GetCurrentProcess();
    		pQVM_Trampoline(hCurProc, (PVOID)((DWORD)stealthBlock.BaseAddress + stealthBlock.RegionSize), MemoryBasicInformation, &next, sizeof(MEMORY_BASIC_INFORMATION), NULL);
    		
                   if (AddressIsIn(&stealthBlock, (DWORD)pMBI->BaseAddress + pMBI->RegionSize) && (pMBI->State == MEM_FREE)) // if the next block is stealth block, we're in the block before stealth (duh)
    		{
    			// we're querying the block BEFORE the stealth block
    			pMBI->RegionSize += stealthBlock.RegionSize; // add the stealth block size to this (previous) block size
    
    			if (next.State == MEM_FREE) // now, if the NEXT block is free too...
    			{
    				pMBI->RegionSize += next.RegionSize; // add its size to the pool
    			}
    		}
    		else if (AddressIsIn(&stealthBlock, (DWORD)lpAddress))
    		{
    			// we're querying the stealthed block
    			pMBI->AllocationBase = 0;
    			pMBI->AllocationProtect = 0;
    			pMBI->State = MEM_FREE;
    			pMBI->Protect = PAGE_NOACCESS;
    			pMBI->Type = 0;
    
    			if (next.State == MEM_FREE)
    			{
    				// the next block is free too, so we should merge the stealthed block and the next block
    				pMBI->RegionSize += next.RegionSize;
    			}
    		}
    	}
    }
    
    NTSYSAPI
    NTSTATUS
    NTAPI 
    ZwQueryVirtualMemory_Detour(
    	IN HANDLE ProcessHandle,
    	IN PVOID BaseAddress,
    	IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
    	OUT PVOID MemoryInformation,
    	IN ULONG MemoryInformationLength,
    	OUT PULONG ReturnLength OPTIONAL
    	)
    {
    	NTSTATUS result = pQVM_Trampoline(ProcessHandle, BaseAddress, MemoryInformationClass, MemoryInformation, MemoryInformationLength, ReturnLength);
    	if (result >= 0)
    	{
    		if ((GetProcessId(ProcessHandle) == GetCurrentProcessId()) && (MemoryInformationClass == MemoryBasicInformation))
    		{			
    			StealthMBI(BaseAddress, (PMEMORY_BASIC_INFORMATION)MemoryInformation);
    		}
    	}
    	return result;
    }
    
    ... later...
    
    	pQVM_Original = (ZwQueryVirtualMemory_ptr)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQueryVirtualMemory");
    	pQVM_Trampoline = (ZwQueryVirtualMemory_ptr)DetourFunction((PBYTE)pQVM_Original, (PBYTE)ZwQueryVirtualMemory_Detour);
    ...

  13. #13
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by kynox View Post
    That's not true. If they wanted to, they could; the functionality is there. The data however, is not.

    I didn't say they can't, I said they aren't. There's a difference. Theoretically they could also do full out of process scans, kick for D3D hooks on nonstandard functions (DIP, etc), check for improperly hidden modules, do a full crc on read-only memory, etc etc. There's lots they COULD do, but currently they don't.

    Also, theres probably a good reason the functionality is disabled. How do you distinguish between a malware infection (using a usermode rootkit) that does IAT hooks and a hack (using a usermode rootkit) that does IAT hooks. There is no way they'd ban on sight for that if your module was private and the hook functions couldn't be hashed.
    Last edited by Cypher; 05-05-2009 at 01:42 AM.

  14. #14
    kynox's Avatar Member
    Reputation
    830
    Join Date
    Dec 2006
    Posts
    888
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Cypher View Post

    I didn't say they can't, I said they aren't. There's a difference. Theoretically they could also do full out of process scans, kick for D3D hooks on nonstandard functions (DIP, etc), check for improperly hidden modules, do a full crc on read-only memory, etc etc. There's lots they COULD do, but currently they don't.

    Also, theres probably a good reason the functionality is disabled. How do you distinguish between a malware infection (using a usermode rootkit) that does IAT hooks and a hack (using a usermode rootkit) that does IAT hooks. There is no way they'd ban on sight for that if your module was private and the hook functions couldn't be hashed.

    What i'm saying is, the scanning routine is IN THE ACTIVE MODULE. So while they technically aren't scanning, the code is right there waiting for a request.

    In response to your malware question, they hash a relative chunk of data based on the destination of the hook and send it back.

  15. #15
    Cypher's Avatar Kynox's Sister's Pimp
    Reputation
    1358
    Join Date
    Apr 2006
    Posts
    5,368
    Thanks G/R
    0/6
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Exactly. So how could they ban if you're using a private anti-anti-cheat usermode rootkit?

    Answer: They can't. Silly noob, trix are for kids.

Page 1 of 2 12 LastLast

Similar Threads

  1. [Tut] Hooking threads without mem writes or breakpoints
    By Master674 in forum WoW Memory Editing
    Replies: 2
    Last Post: 03-26-2015, 04:47 PM
  2. sap a mage with out comeing out of stealth & no calldown
    By bait in forum World of Warcraft Exploits
    Replies: 16
    Last Post: 09-02-2006, 06:06 AM
  3. How to find a get a rogue stealthed -- Warlock only
    By koalaz2004 in forum World of Warcraft Exploits
    Replies: 5
    Last Post: 08-26-2006, 10:53 AM
  4. Expose Stealthed Rogues
    By Matt in forum World of Warcraft Exploits
    Replies: 9
    Last Post: 07-17-2006, 11:00 PM
  5. Block resurrection of the opposite faction in battle grounds
    By Matt in forum World of Warcraft Exploits
    Replies: 5
    Last Post: 06-08-2006, 03:19 AM
All times are GMT -5. The time now is 01:26 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search