Thread: Find Injected DLLs In A Process?

  1. #31
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    The 'CloakDLL' code is not a "prank" by any means, this method of hiding your module from the PEB list has been a known working means of evading detection by VAC2 (VALVE ANTI-CHEAT) for some years now. If you're familiar with the site OpenRCE as I'm sure you are, Pnluck posted an article detailing this a couple years ago:
    http://www.openrce.org/blog/view/844/How_to_hide_dll

    Please refer to the CloakDll referenced in one of the previous posts in this thread. In particular, pay close attention to how the PEB address is calculated and compare this to how the PEB address is calculated in your link. The PEB address in the above Cloakdll is based on a "local" memory calculation. The code in the link has the PEB address calculated on the "remote" targeted memory. That is because it is calculated in the injected DLL. The PEB of the "remote" targeted memory is what is required to cloak a module. Secondly, look at the naked function in the above listed CloakDll. Do you notice all the C statements in this naked function????? Those C statements should be translated to inline assembly.

    A cursory review of the code in your posted link gives me the impression that it probably would work. Although I have not tested it.

    But anyway, here is a "sanitized" version of my Cloaking util which has only been tested on XP SP2 and lower. It has not been tested on Vista. It will only enumerate the loaded modules in the remote process. It will NOT cloak any modules because I've removed the necessary statements that actually cloak the module since this is a PG13 board and they frown on anything hacking related.

    Code:
    #pragma comment(lib, "advapi32.lib")
    #pragma comment(lib, "shlwapi.lib")
    #include<windows.h>
    #include <stdio.h>
    #include <tlhelp32.h>
    #include <shlwapi.h>
    
    typedef struct _UNICODE_STRING
    {
        USHORT Length;
        USHORT MaximumLength;
        PWSTR Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    
    typedef struct _LDR_MODULE
    {
        LIST_ENTRY              InLoadOrderModuleList;
        LIST_ENTRY              InMemoryOrderModuleList;
        LIST_ENTRY              InInitializationOrderModuleList;
        PVOID                   BaseAddress;
        PVOID                   EntryPoint;
        ULONG                   SizeOfImage;
        UNICODE_STRING          FullDllName;
        UNICODE_STRING          BaseDllName;
        ULONG                   Flags;
        SHORT                   LoadCount;
        SHORT                   TlsIndex;
        LIST_ENTRY              HashTableEntry;
        ULONG                   TimeDateStamp;
    } LDR_DATA_ENTRY , *PLDR_DATA_ENTRY;
    
    typedef struct _PEB_LDR_DATA
    {
        ULONG               Length;
        BOOLEAN             Initialized;
        PVOID               SsHandle;
        LIST_ENTRY          InLoadOrderModuleList; 
        LIST_ENTRY          InMemoryOrderModuleList;
        LIST_ENTRY          InInitializationOrderModuleList;
    } PEB_LDR_DATA, *PPEB_LDR_DATA;
    
    
    typedef struct _PEB
    {
        BOOLEAN                      InheritedAddressSpace; 
        BOOLEAN                      ReadImageFileExecOptions;
        BOOLEAN                      BeingDebugged;           
        BOOLEAN                      SpareBool;               
        HANDLE                       Mutant;                   
        HMODULE                      ImageBaseAddress;         
        PPEB_LDR_DATA                LdrData;                  
        int *ProcessParameters;                
        PVOID                        SubSystemData;                
        HANDLE                       ProcessHeap;                  
        PRTL_CRITICAL_SECTION        FastPebLock;                  
        PVOID                        FastPebLockRoutine;           
        PVOID                        FastPebUnlockRoutine;         
        ULONG                        EnvironmentUpdateCount;       
        PVOID                        KernelCallbackTable;          
        PVOID                        EventLogSection;              
        PVOID                        EventLog;                     
        PVOID                        FreeList;                     
        ULONG                        TlsExpansionCounter;          
        int                          TlsBitmap;                      
        ULONG                        TlsBitmapBits[2];           
        PVOID                        ReadOnlySharedMemoryBase;   
        PVOID                        ReadOnlySharedMemoryHeap;   
        PVOID                       *ReadOnlyStaticServerData;   
        PVOID                        AnsiCodePageData;           
        PVOID                        OemCodePageData;            
        PVOID                        UnicodeCaseTableData;       
        ULONG                        NumberOfProcessors;         
        ULONG                        NtGlobalFlag;               
        BYTE                         Spare2[4];                  
        LARGE_INTEGER                CriticalSectionTimeout;     
        ULONG                        HeapSegmentReserve;         
        ULONG                        HeapSegmentCommit;              
        ULONG                        HeapDeCommitTotalFreeThreshold; 
        ULONG                        HeapDeCommitFreeBlockThreshold;  
        ULONG                        NumberOfHeaps;                   
        ULONG                        MaximumNumberOfHeaps;            
        PVOID                       *ProcessHeaps;                    
        PVOID                        GdiSharedHandleTable;            
        PVOID                        ProcessStarterHelper;            
        PVOID                        GdiDCAttributeList;              
        PVOID                        LoaderLock;                      
        ULONG                        OSMajorVersion;                  
        ULONG                        OSMinorVersion;                  
        ULONG                        OSBuildNumber;                   
        ULONG                        OSPlatformId;                    
        ULONG                        ImageSubSystem;                  
        ULONG                        ImageSubSystemMajorVersion;      
        ULONG                        ImageSubSystemMinorVersion;      
        ULONG                        ImageProcessAffinityMask;        
        ULONG                        GdiHandleBuffer[34];             
        ULONG                        PostProcessInitRoutine;          
        ULONG                        TlsExpansionBitmap;              
        ULONG                        TlsExpansionBitmapBits[32];      
        ULONG                        SessionId;                       
    } PEB, *PPEB;
    
    VOID DisplayError( CHAR * pMessage )
    {
        DWORD eNum;
        CHAR sysMsg[256] = {0};
        CHAR* p;
    
        eNum = GetLastError( );
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, eNum,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
            sysMsg, 256, NULL );
        p = sysMsg;
        while( ( *p > 31 ) || ( *p == 9 ) )
            ++p;
        do { *p-- = 0; } while( ( p >= sysMsg ) &&
            ( ( *p == '.' ) || ( *p < 33 ) ) );
        printf( "\n%s error %d (%s)", pMessage, eNum, sysMsg );
    }
    
    BOOL EnableDebugPrivilege( BOOL Enable ) 
    {
        BOOL Success = FALSE;
        HANDLE hToken = NULL;
        do
        {
            if( !OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken ) )
            {
                DisplayError("OpenProcessToken failed");
                break;
            }
            TOKEN_PRIVILEGES tp; 
            tp.PrivilegeCount = 1;
            if( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid ) )
            {
                DisplayError("LookupPrivilegeValue failed");
                break;
            }
    
            tp.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
            if( !AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(tp), NULL, NULL ) )
            {
                DisplayError("AdjusttokenPrivileges failed");
                break;
            }
            Success = TRUE;
        }
        while( 0 );
        if( hToken != NULL ) 
        {
            if( !CloseHandle( hToken ) )
                DisplayError("CloseHandle failed");    
        }
        return Success;
    }
    
    DWORD GetProcessId( CHAR *pProcessName )
    {
        PROCESSENTRY32 pe;
        HANDLE thSnapshot;
        BOOL bReturnValue;
        DWORD dwPID = 0;
    
        thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if(thSnapshot == INVALID_HANDLE_VALUE)
        {
            DisplayError("CreateToolhelp32Snapshot failed");
            printf("Unable to create toolhelp snapshot\n");
            return 0;
        }
        pe.dwSize = sizeof(PROCESSENTRY32);
        bReturnValue = Process32First(thSnapshot, &pe);
        while(bReturnValue)
        {
            if(StrStrI(pe.szExeFile, pProcessName) )
            {
                dwPID = pe.th32ProcessID;
                break;
            }
            bReturnValue    = Process32Next(thSnapshot,&pe);
        }
        return dwPID;
    }
    
    BOOL HideDLL( CHAR *pProcess, CHAR *pDLL2Hide )
    {
        char szBuffer[256] = {0};
        char szUnicodeBuffer[512] = {0};
        DWORD (*addressOfRtlGetCurrentPeb)();
        DWORD dwPID = 0;
        DWORD dwTargetTID;
        PEB * pPebAddress;
        PEB * pPebAddress1;
        PEB_LDR_DATA * PebLdrData;
        LDR_DATA_ENTRY module,lastModule,nextModule;
        LIST_ENTRY * ListEntryPointer2FirstModule;
        HANDLE hProcess,hRemoteThread;
    
        dwPID = GetProcessId(pProcess);
        if(dwPID == 0)
        {
            printf("Process: %s not found\n", pProcess);
            return FALSE;
        }
        if((addressOfRtlGetCurrentPeb = (DWORD (__cdecl*) (VOID)) GetProcAddress(GetModuleHandle("ntdll"),"RtlGetCurrentPeb"))==NULL)
        {
            DisplayError("GetProcAddress failed");
            return FALSE;
        }
        if(EnableDebugPrivilege( TRUE ) == FALSE )
        {
            DisplayError("EnableDebugPrivilege failed");
            return FALSE;
        }
        if((hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID))==NULL)
        {
            DisplayError("OpenProcess failed");
            return FALSE;
        }
        if((hRemoteThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)addressOfRtlGetCurrentPeb,NULL,0,&dwTargetTID))==NULL)
        {
            DisplayError("CreateRemoteThread failed");
            CloseHandle( hProcess );
            return FALSE;
        }
        WaitForSingleObject(hRemoteThread,INFINITE);
        GetExitCodeThread(hRemoteThread,(LPDWORD)&pPebAddress1);
    
        if(!ReadProcessMemory(hProcess,((BYTE*)pPebAddress1)+0x0c,&PebLdrData,4,NULL))
        {  
            DisplayError("ReadProcessMemory #1 failed");
            CloseHandle( hProcess );
            return FALSE;
        }
        if(!ReadProcessMemory(hProcess,((BYTE*)PebLdrData)+0x0c,&ListEntryPointer2FirstModule,4,NULL))
        {
            DisplayError("ReadProcessMemory #2 failed");
            CloseHandle( hProcess );
            return FALSE;
        }
        if(!ReadProcessMemory(hProcess,((BYTE*)ListEntryPointer2FirstModule),&module,sizeof(LDR_DATA_ENTRY),NULL))
        {
            DisplayError("ReadProcessMemory #3 failed");
            CloseHandle( hProcess );
            return FALSE;
        }
        if(!ReadProcessMemory(hProcess,((BYTE*)module.BaseDllName.Buffer),szUnicodeBuffer,module.BaseDllName.Length,NULL))
        {
            DisplayError("ReadProcessMemory #4 failed");
            CloseHandle( hProcess );
            return FALSE;
        }
        WideCharToMultiByte(CP_ACP,0,(LPCWSTR)szUnicodeBuffer,-1,szBuffer,256,NULL,NULL);
        do{
            printf("%-30s 0x%2x    0x%2x\n",szBuffer, module.BaseAddress, module.SizeOfImage);
            if((BYTE *)module.InLoadOrderModuleList.Flink==(((BYTE*)PebLdrData)+0x0c))
                break;
            if(!ReadProcessMemory(hProcess,((BYTE*)module.InLoadOrderModuleList.Flink),&module,sizeof(LDR_DATA_ENTRY),NULL))
            {
                DisplayError("ReadProcessMemory #5 failed");
                CloseHandle( hProcess );
                return FALSE;
            }
            ZeroMemory(szUnicodeBuffer,512);
            if(!ReadProcessMemory(hProcess,((BYTE*)module.BaseDllName.Buffer),szUnicodeBuffer,module.BaseDllName.Length,NULL))
            {
                DisplayError("ReadProcessMemory #6 failed");
                CloseHandle( hProcess );
                return FALSE;
            }
            WideCharToMultiByte(CP_ACP,0,(LPCWSTR)szUnicodeBuffer,-1,szBuffer,256,NULL,NULL);
        }while(1);
        return TRUE;
    }
    
    INT main( INT argc, CHAR **argv )
    {
        HideDLL(argv[1], argv[2]);
        return 0;
    }
    But anyway, if you wish to discuss this further, please post a reference to this issue on the SysInternals site. I won't stopping in here too often.

  2. #32
    Registered User
    Join Date
    Feb 2009
    Posts
    2
    Quote Originally Posted by BobS0327 View Post
    http://www.openrce.org/blog/view/844/How_to_hide_dll

    Please refer to the CloakDll referenced in one of the previous posts in this thread. In particular, pay close attention to how the PEB address is calculated and compare this to how the PEB address is calculated in your link. The PEB address in the above Cloakdll is based on a "local" memory calculation. The code in the link has the PEB address calculated on the "remote" targeted memory. That is because it is calculated in the injected DLL. The PEB of the "remote" targeted memory is what is required to cloak a module. Secondly, look at the naked function in the above listed CloakDll. Do you notice all the C statements in this naked function????? Those C statements should be translated to inline assembly.

    A cursory review of the code in your posted link gives me the impression that it probably would work. Although I have not tested it.

    ...

    But anyway, if you wish to discuss this further, please post a reference to this issue on the SysInternals site. I won't stopping in here too often.
    On second look at the 'CloakDll' example, I see you were correct. I skimmed over it too quickly once I saw what was being attempted and assumed they did it correctly. However, the method of unlinking/zero'ing out of your module in the linked PEB list in order to hide/cloak yourself works in this manner when done correctly.

    If I recall correctly, there was even an exploit in XP (NT/2000?) that would escalate you to ring0 (physical/mapped memory exploit) and do something very similar (TEB/PEB patching) for a running process which went un-patched for several years and still works on many systems however with some negative side affects.

  3. #33
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,058
    If I recall correctly, there was even an exploit in XP (NT/2000?) that would escalate you to ring0 (physical/mapped memory exploit) and do something very similar (TEB/PEB patching) for a running process which went un-patched for several years and still works on many systems however with some negative side affects.
    Yea, I remember reading something about this in the recent past when I was doing some research on this CloakDLL. Unfortunately, I just can't remember exactly where I saw it. May have been an issue of Phrack or possibly BlackHat.

    Also, if you wish to review the "unsanitized" full version of my cloaking app, I would suggest that you start a thread requesting info on cloaking utils on Sysinternals.com in the development section. I'll post the full version on that forum because IMHO, it's a more appropriate forum for this type of discussion.

  4. #34
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230
    Quote Originally Posted by BobS0327 View Post
    Yea, I remember reading something about this in the recent past when I was doing some research on this CloakDLL. Unfortunately, I just can't remember exactly where I saw it. May have been an issue of Phrack or possibly BlackHat.

    Also, if you wish to review the "unsanitized" full version of my cloaking app, I would suggest that you start a thread requesting info on cloaking utils on Sysinternals.com in the development section. I'll post the full version on that forum because IMHO, it's a more appropriate forum for this type of discussion.
    I found the thread on sysinternals, thanks for posting it, was an interesting read. I have a lot to learn still, so I'm still getting my head around this kind of thing, but nevertheless, it was interesting =)
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  5. #35
    Registered User
    Join Date
    Jan 2010
    Posts
    412
    First, let me apologize for bumping such an old thread but I found this thread by accident while searching for something completely different on google and I felt like I had to clear up some apparent confusion
    Secondly, excuse my sometimes poor spelling. English isn't my primary language. I hope this will be understandable enough anyways. If not please reply and I'll try to explain further.

    Quote Originally Posted by BobS0327 View Post
    Please refer to the CloakDll referenced in one of the previous posts in this thread. In particular, pay close attention to how the PEB address is calculated and compare this to how the PEB address is calculated in your link. The PEB address in the above Cloakdll is based on a "local" memory calculation. The code in the link has the PEB address calculated on the "remote" targeted memory. That is because it is calculated in the injected DLL. The PEB of the "remote" targeted memory is what is required to cloak a module.
    Actually CloakDll does unlink the module successfully and the PEB calculation is correct because the CloakDll_stub function is copied in to the remote process' address space before being executed.
    Relevant parts:
    Code:
    void *stubAddress = VirtualAllocEx(hProcess, NULL, stubLen, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    ...
    WriteProcessMemory(hProcess, stubAddress, CloakDll_stub, stubLen, NULL);
    ...
    CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)stubAddress, hMod, 0, NULL);
    Quote Originally Posted by BobS0327 View Post
    Secondly, look at the naked function in the above listed CloakDll. Do you notice all the C statements in this naked function????? Those C statements should be translated to inline assembly.
    Code:
    __declspec(naked) void CD_stubend() { }
    doesn't contain any code at all, the function body is empty.
    The only purpose of this function is that it is a "marker" used to calculate the length of the CloakDll_stub function. See:
    Code:
    unsigned int stubLen = (unsigned long)CD_stubend - (unsigned long)CloakDll_stub;
    Note; While there is nothing in the C/C++ specifications that says that a compiled binary has to have all the functions in the same order as written in the source files it at least holds true for all versions of Microsoft's C++ compiler that I have tried.
    Quote Originally Posted by BobS0327 View Post
    Unfortunately, CloakDll always causes the targeted app to crash when it is executed. Attaching OllyDbg to the running process prior to executing Cloakdll to determine the cause of the crash just doesn't provide any really helpful info.
    I'm just guessing here, but your crash issue is most likely because of /INCREMENTAL (Link Incrementally)
    (The 2nd point is the critical one, the 1st one just increases the amount of code being copied)
    An incrementally linked program is functionally equivalent to a program that is nonincrementally linked. However, because it is prepared for subsequent incremental links, an incrementally linked executable (.exe) file or dynamic-link library (DLL):
    * Is larger than a nonincrementally linked program because of padding of code and data. (Padding allows the linker to increase the size of functions and data without recreating the .exe file.)
    * May contain jump thunks to handle relocation of functions to new addresses.
    If trying to run CloakDll make sure that incremental linking is disabled.
    It could possibly also be a race condition if you have code that happens to do a scan of the module lists at the same time as CloakDll's thread unlinks the target module.

  6. #36
    Registered User
    Join Date
    Jan 2010
    Posts
    412
    @BobS0327:
    Because of the forum rollback the post where you asked me to check the return values of the API calls in cloakdll is now missing. But anyways, I added some basic logging and here's what I get.
    Code:
    OpenProcess returned 0x60 | GetLastError(): 0
    VirtualAllocEx returned 0x1c0000 | GetLastError(): 0
    WriteProcessMemory returned 0x1 | 192 bytes written | GetLastError(): 0
    GetRemoteModuleHandle returned 0x76ae0000 | GetLastError(): 0
    CreateRemoteThread returned 0x6c | GetLastError(): 0
    Are you by any chance running cloakdll as a restricted user and that's why the APIs are failing? Do they still fail if you obtain an SeDebugPrivilege token?

    I also debugged it to find the source of the crashes. In CloakDll_stub() replace the 2 memset at the end with
    Code:
    DWORD_PTR Address1= (DWORD_PTR)module->fullPath.Buffer;
    DWORD Length1 = module->fullPath.Length;
    DWORD_PTR Address2 = (DWORD_PTR)module;
    DWORD Length2 = sizeof(ModuleInfoNode);
    __asm
    {
    	mov ecx, Length1
    	mov edi, Address1
    	mov eax, 0
    	rep stosb
    	mov ecx, Length2
    	mov edi, Address2
    	rep stosb
    }
    Seems that memset is not being inlined even though the comments says it will be. And btw, I would hardly call that a "major modification"

    I'm not saying that this piece of code is perfect, but I don't understand why you're claiming that "it never has worked and never will" (not an exact quote, can't remember exactly how you worded it) when it clearly works for me.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 10-15-2008, 09:24 AM
  2. Process manipulation questions
    By Sfel in forum Windows Programming
    Replies: 7
    Last Post: 05-17-2008, 12:39 PM
  3. Retrieving binary file name from a process
    By maxhavoc in forum Linux Programming
    Replies: 5
    Last Post: 04-12-2006, 02:50 PM
  4. Redirecting STDOUT to STDIN of a child process
    By 0xception in forum Linux Programming
    Replies: 4
    Last Post: 09-13-2005, 11:58 PM
  5. Replies: 12
    Last Post: 05-17-2003, 05:58 AM