CloakDll - by Darawk Win7

On viernes, 28 de enero de 2011 0 comentarios

// This modification is based on Darawk's CloakDll instrumentation with
// some tweaks from me to get it working on Windows 7 and through the
// use of my own DLL - nitrogrlie

//              CloakDll - by Darawk
//        Featured @ www.RealmGX.com & www.Darawk.com
//
//   The purpose of CloakDll is to allow the user to hide any loaded
//   module from the windows API.  It works by accessing the modules
//   list stored in the PEB, and subsequently unlinking the module
//   in question from all 4 of the doubly-linked lists that it's a
//   node of.  It then zeroes out the structure and the path/file
//   name of the module in memory.  So that even if the memory where
//   the data about this module used to reside is scanned there will
//   still be no conclusive evidence of it's existence.  At present
//   there is only one weakness that I have found in this method.
//   I'll describe how it may still be possible to discover at least
//   that a module has been hidden, after a brief introduction to how
//   the GetModuleHandle function works.
//
//   *The following information is not documented by Microsoft.  This
//    information consists of my findings while reverse-engineering
//    these functions and some of them may be incorrect and/or
//    subject to change at any time(and is almost definitely different
//    in different versions of windows, and maybe even in different
//    service packs).  I've tried to make my code as version independant
//    as possible but certain parts of it may not work on older versions
//    of windows.  I've tested it on XP SP2 and there i'll guarantee
//    that it works, but on any other versions of windows, it's anyone's
//    guess.*
//
//   GetModuleHandle eventually calls GetModuleHandleExW, which in
//   turn accesses the native API function GetDllHandle, which calls
//   GetDllHandleEx.  And it's not until here, that we actually see
//   anything even begin to look up information about loaded modules.
//   Whenever GetModuleHandle is called, it saves the address of the
//   last ModuleInfoNode structure that it found in a global variable
//   inside of ntdll.  This global variable is the first thing
//   checked on all subsequent calls to GetModuleHandle.  If the
//   handle being requested is not the one that was requested the last
//   time GetDllHandleEx calls the LdrpCheckForLoadedDll function.
//   LdrpCheckForLoadedDll begins by converting the first letter of the
//   module name being requested to uppercase, decrementing it by 1 and
//   AND'ing it with 0x1F.  This effectively creates a 0-based index
//   beginning with the letter 'A'.  The purpose of this is so that
//   the module can first be looked up in a hash table.  The hash table
//   consists entirely of LIST_ENTRY structures.  One for each letter
//   'A' through 'Z'.  The LIST_ENTRY structure points to the first
//   and last modules loaded that begin with the letter assigned to
//   that entry in the hash table.  The Flink member being the first
//   loaded beginning with that letter, and the Blink member being the
//   last.  The code scans through this list until it finds the module
//   that it's looking for.  On the off-chance that it doesn't find it
//   there, or if the boolean argument UseLdrpHashTable is set to false
//   it will begin going through one of the other three lists.  If, at
//   this point it still doesn't find it, it will admit defeat and return
//   0 for the module handle.
//
//   Weakness:  The global variable inside ntdll that caches the pointer
//   to the last module looked up could be used to at least detect the
//   fact that a module has been hidden.  The LdrUnloadDll() function
//   will set this value to 0 when it unloads a module, so if the cache
//   variable points to an empty structure, the only logical conclusion
//   would be a hidden module somewhere in the process.  This could be
//   resolved by using the static address of this variable and simply
//   zeroing it out.  However, this would make the code specific to only
//   one version of windows.  You could also scan the address space of
//   ntdll for any occurences of the base address(aka module handle)
//   of the module you're hiding.  However, this would be slow and it
//   would clutter up the CloakDll_stub function, because it'd have to
//   all be done manually.  And i'd have to either use a static base
//   address for ntdll...which would probably work on most versions
//   of windows, however I really don't like using static addresses.
//   Or i'd have to manually locate it by writing my own unicode
//   string comparison code, to lookup ntdll in the list by it's name.
//   Realistically though anyone trying to detect this way would run
//   into the same problem.  That their code would not be version
//   independant.  So, it's unlikely to see any largescale deployment
//   of such a technique.  However, anyone who would like to solve
//   this problem themselves is perfectly free, and encouraged to do
//   so.

#include
#include
#include
#include
#include

#pragma comment(lib, "shlwapi.lib")

#define UPPERCASE(x) if((x) >= 'a' && (x) <= 'z') (x) -= 'a' - 'A'
#define UNLINK(x) (x).Blink->Flink = (x).Flink; \
   (x).Flink->Blink = (x).Blink;

#pragma pack(push, 1)
typedef struct _UNICODE_STRING {
  USHORT  Length;
  USHORT  MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _ModuleInfoNode
{
   LIST_ENTRY LoadOrder;
   LIST_ENTRY InitOrder;
   LIST_ENTRY MemoryOrder;
   HMODULE baseAddress;      //   Base address AKA module handle
   unsigned long entryPoint;
   unsigned int size;         //   Size of the modules image
   UNICODE_STRING fullPath;
   UNICODE_STRING name;
   unsigned long flags;
   unsigned short LoadCount;
   unsigned short TlsIndex;
   LIST_ENTRY HashTable;   //   A linked list of any other modules that have the same first letter
   unsigned long timestamp;
} ModuleInfoNode, *pModuleInfoNode;

typedef struct _ProcessModuleInfo
{
   unsigned int size;         //   Size of a ModuleInfo node?
   unsigned int initialized;
   HANDLE SsHandle;
   LIST_ENTRY LoadOrder;
   LIST_ENTRY InitOrder;
   LIST_ENTRY MemoryOrder;
} ProcessModuleInfo, *pProcessModuleInfo;
#pragma pack(pop)

// Forward prototypes
void CloakDll( HMODULE hMod );
bool CloakModule( ModuleInfoNode * module );

// Globals
HMODULE hDLL = 0;

// Define the DLL's main function
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReason, LPVOID lpReserved) {
    // Get rid of compiler warnings since we do not use this parameter
    UNREFERENCED_PARAMETER(lpReserved);

   switch( ulReason ) {
    // If we are attaching to a process
   case DLL_PROCESS_ATTACH:
      //MessageBox(0, "DLL Locked and Loaded.", "DLL Injection Works!", 0);
      hDLL = hModule;
      break;
   case DLL_THREAD_ATTACH:
      break;
   case DLL_THREAD_DETACH:
      break;
   case DLL_PROCESS_DETACH:
      break;
    }

    return (TRUE);
}

extern "C" __declspec(dllexport) void Initialize() {
   DWORD ThreadID;
   CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&CloakDll, GetModuleHandle("GDI32.dll"), 0, &ThreadID);
}

void CloakDll(HMODULE hMod) {
   ProcessModuleInfo *pmInfo;
   ModuleInfoNode *module;

   _asm {
      mov eax, fs:[18h]      // TEB
      mov eax, [eax + 30h]   // PEB
      mov eax, [eax + 0Ch]   // PROCESS_MODULE_INFO
      mov pmInfo, eax
   }

   ModuleInfoNode* origModule = 0, *tohideModule = 0;

   module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
   while( module->baseAddress ) {
      if( module->baseAddress == hMod ) {
         tohideModule = module;
      }
      else if ( module->baseAddress == hDLL ) {
         origModule = module;
      }
      module = (ModuleInfoNode *)(module->LoadOrder.Flink);
   }

   // Cloak the modules, if we found them, call return true/false but I don't check
   // not sure why you would necessarily want to
   CloakModule( origModule );
   CloakModule( tohideModule );

   // Check our work by printing all the modules for the PE we are injected
   FILE * file;
   file = fopen("C:\\Users\\Public\\Cloak.txt", "w");
   module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
   while(module->baseAddress) {
      fprintf(file, "Module: %S\n", module->fullPath.Buffer);
      fflush(file);
      module = (ModuleInfoNode *)(module->LoadOrder.Flink);
   }

   // Close our file handles
   fflush(file);
   fclose(file);

   // Exit our thread and free our DLL
   if( hDLL ) {
      __asm {
         push -2
         push 0
         push hDLL
         mov eax, TerminateThread
         push eax
         mov eax, FreeLibrary
         jmp eax
         }
   }
}

bool CloakModule( ModuleInfoNode * module ) {
   // Quick error checking in-case some module wasn't found
   if( module->baseAddress ) {
      //   Remove the module entry from the list here
      /////////////////////////////////////////////////// 
      //   Unlink from the load order list
      UNLINK(module->LoadOrder);
      //   Unlink from the init order list
      UNLINK(module->InitOrder);
      //   Unlink from the memory order list
      UNLINK(module->MemoryOrder);
      //   Unlink from the hash table
      UNLINK(module->HashTable);

      //   This code will pretty much always be optimized into a rep stosb/stosd pair
      //   so it shouldn't cause problems for relocation.
      //   Zero out the module name
      DWORD oldProt;
      VirtualProtect(module->fullPath.Buffer, module->fullPath.Length, PAGE_READWRITE, &oldProt);
      memset(module->fullPath.Buffer, 0, module->fullPath.Length);
      VirtualProtect(module->fullPath.Buffer, module->fullPath.Length, oldProt, &oldProt);
      //   Zero out the memory of this module's node
      VirtualProtect(module, sizeof(ModuleInfoNode), PAGE_READWRITE, &oldProt);
      memset(module, 0, sizeof(ModuleInfoNode)); 
      VirtualProtect(module, sizeof(ModuleInfoNode), oldProt, &oldProt);
      return true;
   }
   return false;
}

0 comentarios:

Publicar un comentario