Hidding Module from the Virtual Address Descriptor Tree

Hi =)
Recently I read a post of Ivanlef0u : LdrpHashTable where he was showing how to hide a Dll from this table. So by reading this post we can say that we know 4 techniques to hide a Dll :

  1. Unlink from InLoadOrderModuleList.
  2. Unlink from InMemoryOrderModuleList.
  3. Unlink from InInitializationOrderModuleList.
  4. Unlink from LdrpHashTable.

Well my purpose is to show you a new one. This technique is based on the Virtual Address Descriptors. So let’s take a look to the functioning of these descriptors management.

Understanding Virtual Address Descriptors :

Actually, each process has VADs located in kernel land and there is a VAD to describe each process’s address space. A VAD is constituted by a MMVAD structure:

typedef struct MMVAD {
/*0x000*/     ULONG32      StartingVpn;
/*0x004*/     ULONG32      EndingVpn;
/*0x008*/     struct _MMVAD* Parent;
/*0x00C*/     struct _MMVAD* LeftChild;
/*0x010*/     struct _MMVAD* RightChild;
              union
              {
/*0x014*/         ULONG32      LongFlags;
/*0x014*/         struct _MMVAD_FLAGS VadFlags;
              }u;
/*0x018*/     struct _CONTROL_AREA* ControlArea;
/*0x01C*/     struct _MMPTE* FirstPrototypePte;
/*0x020*/     struct _MMPTE* LastContiguousPte;
              union
              {
/*0x024*/         ULONG32      LongFlags2;
/*0x024*/         struct _MMVAD_FLAGS2 VadFlags2;
              }u2;
}MMVAD, *PMMVAD;

Now I catch your attention on these three fields :

/*0x008*/     struct _MMVAD* Parent;
/*0x00C*/     struct _MMVAD* LeftChild;
/*0x010*/     struct _MMVAD* RightChild;

We see that a VAD has a parent VAD and two children VADs. That is to say that the VADs are organised like a tree.
Now an other useful field is :

/*0x018*/     struct _CONTROL_AREA* ControlArea;

And the structure definition is :

struct _CONTROL_AREA* ControlArea;
typedef struct CONTROL_AREA {
/*0x000*/     struct _SEGMENT* Segment;
/*0x004*/     struct _LIST_ENTRY DereferenceList;
/*0x00C*/     ULONG32      NumberOfSectionReferences;
/*0x010*/     ULONG32      NumberOfPfnReferences;
/*0x014*/     ULONG32      NumberOfMappedViews;
/*0x018*/     UINT16       NumberOfSubsections;
/*0x01A*/     UINT16       FlushInProgressCount;
/*0x01C*/     ULONG32      NumberOfUserReferences;
              union
              {
/*0x020*/         ULONG32      LongFlags;
/*0x020*/         struct _MMSECTION_FLAGS Flags;
              }u;
/*0x024*/     struct _FILE_OBJECT* FilePointer;
/*0x028*/     struct _EVENT_COUNTER* WaitingForDeletion;
/*0x02C*/     UINT16       ModifiedWriteCount;
/*0x02E*/     UINT16       NumberOfSystemCacheViews;
}CONTROL_AREA, *PCONTROL_AREA;

So we see we can have a pointer on the _FILE_OBJECT which can allow us, if the memory space corresponds to a module loaded, to retrieve its name :

nt!_FILE_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x008 Vpb              : Ptr32 _VPB
   +0x00c FsContext        : Ptr32 Void
   +0x010 FsContext2       : Ptr32 Void
   +0x014 SectionObjectPointer : Ptr32 _SECTION_OBJECT_POINTERS
   +0x018 PrivateCacheMap  : Ptr32 Void
   +0x01c FinalStatus      : Int4B
   +0x020 RelatedFileObject : Ptr32 _FILE_OBJECT
   +0x024 LockOperation    : UChar
   +0x025 DeletePending    : UChar
   +0x026 ReadAccess       : UChar
   +0x027 WriteAccess      : UChar
   +0x028 DeleteAccess     : UChar
   +0x029 SharedRead       : UChar
   +0x02a SharedWrite      : UChar
   +0x02b SharedDelete     : UChar
   +0x02c Flags            : Uint4B
   +0x030 FileName         : _UNICODE_STRING
   +0x038 CurrentByteOffset : _LARGE_INTEGER
   +0x040 Waiters          : Uint4B
   +0x044 Busy             : Uint4B
   +0x048 LastLock         : Ptr32 Void
   +0x04c Lock             : _KEVENT
   +0x05c Event            : _KEVENT
   +0x06c CompletionContext : Ptr32 _IO_COMPLETION_CONTEXT

Okay so I made a schema to demonstrate it :

Moreover, windbg allow you to list the VADs of a process. Here is an example :

kd> !process 0 0 calc.exe
PROCESS 8163b020  SessionId: 0  Cid: 01d0    Peb: 7ffde000  ParentCid: 0654
    DirBase: 0d6c3000  ObjectTable: e12a18f8  HandleCount:  40.
    Image: calc.exe
 
kd> dt _EPROCESS 8163b020
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER 0x1cabeaf`5509f2b0
   +0x078 ExitTime         : _LARGE_INTEGER 0x0
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : 0x000001d0
   +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x805604d8 - 0x811b4ca0 ]
   +0x090 QuotaUsage       : [3] 0x870
   +0x09c QuotaPeak        : [3] 0x898
   +0x0a8 CommitCharge     : 0xee
   +0x0ac PeakVirtualSize  : 0x2258000
   +0x0b0 VirtualSize      : 0x1d31000
   +0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0xf9fb5014 - 0x8114a0d4 ]
   +0x0bc DebugPort        : (null)
   +0x0c0 ExceptionPort    : 0xe13bea18
   +0x0c4 ObjectTable      : 0xe12a18f8 _HANDLE_TABLE
   +0x0c8 Token            : _EX_FAST_REF
   +0x0cc WorkingSetLock   : _FAST_MUTEX
   +0x0ec WorkingSetPage   : 0x346
   +0x0f0 AddressCreationLock : _FAST_MUTEX
   +0x110 HyperSpaceLock   : 0
   +0x114 ForkInProgress   : (null)
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x810051c8
...
kd> !vad 0x810051c8
VAD     level      start      end    commit
8112a328 ( 2)         10       10         1 Private      READWRITE
811260a0 ( 1)         20       20         1 Private      READWRITE
811e1008 ( 4)         30       30         1 Private      READWRITE
811294f0 ( 3)         40       7f         4 Private      READWRITE
8113f288 ( 2)         80       81         2 Private      EXECUTE_READWRITE
810761c0 ( 3)         90       92         0 Mapped       READONLY
810051c8 ( 0)         a0       a1         0 Mapped       READONLY
810cc678 ( 4)         b0      1af        18 Private      READWRITE
810f15e8 ( 3)        1b0      1bf         6 Private      READWRITE
81148d50 ( 4)        1c0      1cf         0 Mapped       READWRITE
81104cf0 ( 2)        1d0      1e5         0 Mapped       READONLY
810ec5f0 ( 5)        1f0      22c         0 Mapped       READONLY
810d3ad0 ( 4)        230      270         0 Mapped       READONLY
810e9008 ( 3)        280      285         0 Mapped       READONLY
81158288 ( 6)        290      357         0 Mapped       EXECUTE_READ
81652628 ( 7)        360      360         1 Private      READWRITE
81168268 ( 8)        370      37f         6 Private      READWRITE
8165d448 ( 9)        380      381         0 Mapped       READONLY
81609178 (10)        3a0      3a1         0 Mapped       READONLY
81142510 (11)        3b0      3b0         0 Mapped       READWRITE
81124aa8 ( 5)        3c0      3cf         8 Private      READWRITE
81123390 ( 4)        3d0      3df         4 Private      READWRITE
81130cf8 ( 6)        3e0      3e2         0 Mapped       READONLY
81656ad8 ( 5)        3f0      42f         3 Private      READWRITE
810ca450 ( 7)        430      532         0 Mapped       READONLY
8112d478 ( 6)        540      83f         0 Mapped       EXECUTE_READ
81140350 ( 7)        840      8bf         1 Private      READWRITE
810d6750 ( 8)        8c0      90f         0 Mapped       READONLY
81139540 ( 9)        910      931        28 Mapped  Exe  EXECUTE_WRITECOPY
812119b0 (10)        940      a3f         4 Private      NO_ACCESS
815f43b0 ( 1)       1000     101e         3 Mapped  Exe  EXECUTE_WRITECOPY
810f01f8 ( 8)      595b0    59779        10 Mapped  Exe  EXECUTE_WRITECOPY
810d0530 ( 9)      5b090    5b0c7         2 Mapped  Exe  EXECUTE_WRITECOPY
8168d668 ( 7)      5cea0    5cec5        20 Mapped  Exe  EXECUTE_WRITECOPY
815ef448 ( 9)      76960    76a14         3 Mapped  Exe  EXECUTE_WRITECOPY
810cc180 ( 8)      76ae0    76b0e         3 Mapped  Exe  EXECUTE_WRITECOPY
810cb9c0 (10)      770e0    7716b         4 Mapped  Exe  EXECUTE_WRITECOPY
8114aef8 (11)      77390    77491         2 Mapped  Exe  EXECUTE_WRITECOPY
81639090 ( 9)      774a0    775dc         8 Mapped  Exe  EXECUTE_WRITECOPY
816a3078 (10)      77bb0    77bc4         2 Mapped  Exe  EXECUTE_WRITECOPY
810cf3d8 (11)      77bd0    77bd7         1 Mapped  Exe  EXECUTE_WRITECOPY
810ab7d8 ( 6)      77be0    77c37         7 Mapped  Exe  EXECUTE_WRITECOPY
810bccd0 ( 5)      77d10    77d9f         5 Mapped  Exe  EXECUTE_WRITECOPY
816c80a0 ( 4)      77da0    77e4b         6 Mapped  Exe  EXECUTE_WRITECOPY
810b9008 ( 5)      77e50    77ee0         2 Mapped  Exe  EXECUTE_WRITECOPY
81143f18 ( 6)      77ef0    77f36         2 Mapped  Exe  EXECUTE_WRITECOPY
811372c0 ( 7)      77f40    77fb5         2 Mapped  Exe  EXECUTE_WRITECOPY
810f83a0 ( 3)      7c800    7c903        13 Mapped  Exe  EXECUTE_WRITECOPY
8168fdc0 ( 2)      7c910    7c9c6         5 Mapped  Exe  EXECUTE_WRITECOPY
816c1770 ( 5)      7c9d0    7d1f2        31 Mapped  Exe  EXECUTE_WRITECOPY
81154fd8 ( 4)      7f6f0    7f7ef         0 Mapped       EXECUTE_READ
8161b2e0 ( 3)      7ffb0    7ffd3         0 Mapped       READONLY
816a3990 ( 5)      7ffdd    7ffdd         1 Private      READWRITE
8169a128 ( 4)      7ffde    7ffde         1 Private      READWRITE 
 
Total VADs:    54  average level:    6  maximum depth: 11

Well now we will manipulate the VAD tree ourselves.

Walking into the VAD Tree :

I’ve shown you have to walk in the VAD manually so now we are able to create a driver that will list all the VADs of a process.

First we have to get a pointer on the first VAD (VadRoot) from the EPROCESS structure :

PMMVAD GetVAD(PUCHAR szProcessName)
{
       PMMVAD pVadRoot = NULL;
       PEPROCESS pEprocess = NULL;
 
       pEprocess = GetEprocess(szProcessName);
 
       DbgPrint("\n\nEPROCESS : 0x%x \n\n", pEprocess);
 
       if(pEprocess == 0x0)
       {
                    DbgPrint("\nProcess not found\n");
                    return 0x0;
       }
       pVadRoot = (PMMVAD)*(PULONG)((PUCHAR)pEprocess+0x11C);
 
       DbgPrint("\n\nVADRoot : 0x%x\n\n", pVadRoot);
 
       return pVadRoot;
}

And now we can list the VAD children and so on. But you may think it’s a bit difficult because there is a right child and a left child which both have right and left child… The trick is to use recursion. I obtain this function :

VOID ListVAD(PMMVAD pParentVad, LONG level)
{
      PMMVAD pVadLeft = NULL;
      PMMVAD pVadRight = NULL;
      VAD_INFO VadInfo;
 
      if(pParentVad == 0x0)
                    return;
 
      VadInfo.level = level;
      SetVadInfo(pParentVad, &VadInfo);
      DisplayVadInfo(&VadInfo);
 
      pVadLeft = (PMMVAD)pParentVad->LeftChild;
      pVadRight = (PMMVAD)pParentVad->RightChild; 
 
      if(pVadLeft != 0x0)
      {
                  ListVAD(pVadLeft, level+1);
      }
      if(pVadRight != 0x0)
      {
                  ListVAD(pVadRight, level+1);
      }
 
      return;
}

You see the VADs will have different levels depending of their position in the tree(parents, children, grandchildren, … =) ).
For each VAD I display few informations and the result is :

Lilxam VADTree driver OK
 
EPROCESS : 0x8163b020 
 
VADRoot : 0x810051c8
 
[+]0x810051c8
      Level : 0
      Control Area : 0x811c44e0
      File Object : 0x0
      Name : (null)
 
[+]0x811260a0
      Level : 1
      Control Area : 0xa070004
      File Object : 0x0
      Name : (null)
 
[+]0x8112a328
      Level : 2
      Control Area : 0x170004
      File Object : 0x0
      Name : (null)
 
[+]0x8113f288
      Level : 2
      Control Area : 0xa060004
      File Object : 0x0
      Name : (null)
 
...
 
[+]0x815f43b0
      Level : 1
      Control Area : 0x81650338
      File Object : 0x81641840
      Name : \WINDOWS\system32\calc.exe
 
[+]0x81104cf0
      Level : 2
      Control Area : 0x81695e60
      File Object : 0x81695de8
      Name : \WINDOWS\system32\unicode.nls
 
...
 
[+]0x8168fdc0
      Level : 2
      Control Area : 0x817b62d8
      File Object : 0x81791898
      Name : \WINDOWS\system32\ntdll.dll
 
[+]0x810f83a0
      Level : 3
      Control Area : 0x81642c98
      File Object : 0x81697488
      Name : \WINDOWS\system32\kernel32.dll
 
[+]0x816c80a0
      Level : 4
      Control Area : 0x817048b8
      File Object : 0x8166d798
      Name : \WINDOWS\system32\advapi32.dll
 
[+]0x810bccd0
      Level : 5
      Control Area : 0x8162d388
      File Object : 0x816c3520
      Name : \WINDOWS\system32\user32.dll
 
...
 
Driver exited

Okay, you understand that with this method we can list modules and find the one which are unlinked from the 4 previous lists !

Ivanlef0u owned ;p.

But what if we want to hide a module from this new list ?

Hiding from VAD :

So it is not very difficult now to hide a dll from the VAD. For example we can parse the VADs looking for our dll and when we find it, we just replace its parent pointer with a null pointer and it is going to be impossible to find it with our previous function. This technique is not so clean because all the others VAD depending to the one we hide will be hidden too. We could have create a fake VAD that would have takend the place of the hidden one for example, it would have been cleaner . But I don’t really have the time so I’ve just implemented the first method :

VOID HideDllFromVAD(PMMVAD pParentVad, LONG level, PUNICODE_STRING pDllName)
{
      PMMVAD pVadLeft = NULL;
      PMMVAD pVadRight = NULL;
      PUNICODE_STRING pVadName = NULL;
 
      if((LONG)pParentVad == 0x0)
                    return;
 
      pVadLeft = (PMMVAD)pParentVad->LeftChild;
      pVadRight = (PMMVAD)pParentVad->RightChild; 
 
      /* Check left VAD */
      pVadName = GetVADName(pVadLeft);
 
      if(pVadName != 0x0)
      {
                  if(wcsstr(pVadName->Buffer, pDllName->Buffer))
                  {
                                      DbgPrint("\nDll found : %wZ", pVadName);
                                      DbgPrint("\nErasing from Vad list...");
                                      (ULONG)pParentVad->LeftChild = 0x0;
 
                                      return;
                  }
      } 
 
      /* Check Right VAD */
      pVadName = GetVADName(pVadRight);
 
      if(pVadName != 0x0)
      {
                  if(wcsstr(pVadName->Buffer, pDllName->Buffer))
                  {
                                      DbgPrint("\nDll found : %wZ", pVadName);
                                      DbgPrint("\nErasing from Vad list...");
                                      (ULONG)pParentVad->RightChild = 0x0;
 
                                      return;
                  }
      }                           
 
      if(pVadLeft != 0x0)
      {
                  HideDllFromVAD(pVadLeft, level+1, pDllName);
      }
      if(pVadRight != 0x0)
      {
                  HideDllFromVAD(pVadRight, level+1, pDllName);
      }
 
      return;
}

Good, it’s the end of this article, you can view online the sources here : VADTree.c & structs.h

Or download Sources+Binary here : VAD_Tree.zip

Cya !

    • hasde
    • March 10th, 2010

    Franchement bien :)
    J’avais encore jamais vue cette histoire de VAD.

    • Flopik
    • April 5th, 2010

    Est-ce que ca peux causer des problemes de stabilités un effacement du VAD comme ca ?

    • lilxam
    • April 7th, 2010

    Salut,
    et bien je n’ai encore eu aucun problème, mais je suis parfaitement conscient que ma méthode est un peu “barbare”.
    En fait il serait beaucoup plus propre et stable de modifier seulement la structure FILE_OBJECT. C’est ce que j’aurais mieux fait de faire.
    Ensuite, je pense, il y a d’autres champs à modifier parce que même en effaçant la structure FILE_OBJECT, le VAD comporte des flags qui permettent de savoir que l’espace mémoire correspondant est exécutable et donc on peut savoir qu’il s’agit d’un module. C’est un sujet qui mérite d’être développé, si j’ai le temps j’essaierai de faire un code un peu mieux.
    Cordialement,
    Lilxam.

    • Fury
    • June 11th, 2011

    En effet c’est très interresant merci pour cette article. Sa serait sympa si tu pouvais expliqué un peu plus sur ce FILE_OBJECT et manipuler les flags.

  1. No trackbacks yet.

Comment are closed.