Description: Call this to inject a DLL into a process and remove it from the PEB LDR data, so it wont be located by other stuff looking for loaded DLLs. Attach windbg with symbols loaded and type "!peb" to see that it isn't in the PEB->LdrData lists at all.
Code:
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
PVOID ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StdInputHandle;
HANDLE StdOutputHandle;
HANDLE StdErrorHandle;
UNICODE_STRING CurrentDirectoryPath;
HANDLE CurrentDirectoryHandle;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingPositionLeft;
ULONG StartingPositionTop;
ULONG Width;
ULONG Height;
ULONG CharWidth;
ULONG CharHeight;
ULONG ConsoleTextAttributes;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopName;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef struct _PEB_FREE_BLOCK {
_PEB_FREE_BLOCK *Next;
ULONG Size;
} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
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 void (*PPEBLOCKROUTINE)(PVOID PebLock);
typedef PVOID *PPVOID;
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
ULONG EnvironmentUpdateCount;
PPVOID KernelCallbackTable;
PVOID EventLogSection;
PVOID EventLog;
PPEB_FREE_BLOCK FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PPVOID ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
BYTE Spare2[0x4];
LARGE_INTEGER CriticalSectionTimeout;
ULONG HeapSegmentReserve;
ULONG HeapSegmentCommit;
ULONG HeapDeCommitTotalFreeThreshold;
ULONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
PPVOID *ProcessHeaps;
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
PVOID GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
ULONG OSBuildNumber;
ULONG OSPlatformId;
ULONG ImageSubSystem;
ULONG ImageSubSystemMajorVersion;
ULONG ImageSubSystemMinorVersion;
ULONG GdiHandleBuffer[0x22];
ULONG PostProcessInitRoutine;
ULONG TlsExpansionBitmap;
BYTE TlsExpansionBitmapBits[0x80];
ULONG SessionId;
} PEB, *PPEB;
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PPEB PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
typedef enum _PROCESSINFOCLASS {
ProcessBasicInformation = 0,
ProcessWow64Information = 26
} PROCESSINFOCLASS;
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_MODULE, *PLDR_MODULE;
SIZE_T ReadProcessUnicodeString(HANDLE hProcess, UNICODE_STRING *inString, UNICODE_STRING *outString){
SIZE_T dwRead;
outString->Length = inString->Length;
outString->MaximumLength = inString->MaximumLength;
outString->Buffer = (PWSTR)malloc(inString->MaximumLength);
ReadProcessMemory(hProcess, inString->Buffer, outString->Buffer, inString->MaximumLength, &dwRead);
return dwRead;
}
DWORD LoadLibraryInjection(HANDLE proc, PCHAR dllName){
LPVOID RemoteString, LoadLibAddy;
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
RemoteString = (LPVOID)VirtualAllocEx(proc, NULL, strlen(dllName), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if(RemoteString == NULL){
CloseHandle(proc); // Close the process handle.
ErrorExit(TEXT("VirtualAllocEx"), NULL);
}
if(WriteProcessMemory(proc, (LPVOID)RemoteString, dllName,strlen(dllName), NULL) == 0){
VirtualFreeEx(proc, RemoteString, 0, MEM_RELEASE); // Free the memory we were going to use.
CloseHandle(proc); // Close the process handle.
ErrorExit(TEXT("WriteProcessMemory"), NULL);
}
HANDLE hThread;
if((hThread = CreateRemoteThread(proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL)) == NULL){
VirtualFreeEx(proc, RemoteString, 0, MEM_RELEASE); // Free the memory we were going to use.
CloseHandle(proc); // Close the process handle.
ErrorExit(TEXT("CreateRemoteThread"), NULL);
}
DWORD dwThreadExitCode=0;
// Lets wait for the thread to finish 10 seconds is our limit.
// During this wait, DllMain is running in the injected DLL, so
// DllMain has 10 seconds to run.
WaitForSingleObject(hThread, 10000);
// Lets see what it says...
GetExitCodeThread(hThread, &dwThreadExitCode);
// No need for this handle anymore, lets get rid of it.
CloseHandle(hThread);
// Lets clear up that memory we allocated earlier.
VirtualFreeEx(proc, RemoteString, 0, MEM_RELEASE);
// Alright lets remove this DLL from the loaded DLL list!
WCHAR dllNameW[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, dllName, strlen(dllName)+1, dllNameW, MAX_PATH);
typedef NTSTATUS (WINAPI *fpNtQueryInformationProcess)(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);
fpNtQueryInformationProcess NtQueryInformationProcess = (fpNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"),"NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION procinfo;
NtQueryInformationProcess(proc, ProcessBasicInformation, &procinfo, sizeof(procinfo), NULL);
PEB peb;
PEB_LDR_DATA ldrData;
SIZE_T dwRead;
ReadProcessMemory(proc, procinfo.PebBaseAddress, &peb, sizeof(peb), &dwRead);
ReadProcessMemory(proc, peb.LoaderData, &ldrData, sizeof(ldrData), &dwRead);
LDR_MODULE ldrModuleA = {0};
LDR_MODULE ldrModuleOfInterest = {0};
LIST_ENTRY *Flink=ldrData.InInitializationOrderModuleList.Flink;
LIST_ENTRY *Start=Flink;
LIST_ENTRY *Blink = 0;
do{
// Lets find the entry in the InInitializationOrderModuleList first.
ReadProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InInitializationOrderModuleList), &ldrModuleA, sizeof(ldrModuleA), &dwRead);
Blink = ldrModuleA.InInitializationOrderModuleList.Blink;
Flink = ldrModuleA.InInitializationOrderModuleList.Flink;
if(ldrModuleA.InInitializationOrderModuleList.Flink == Start)
break;
UNICODE_STRING FullDllName;
ReadProcessUnicodeString(proc, &ldrModuleA.FullDllName, &FullDllName);
if(wcscmp(FullDllName.Buffer, dllNameW)==0){
// BINGO!
printf("FullDllName: %wZ\n", FullDllName);
// Lets remove this list entry.
LDR_MODULE Previous;
LDR_MODULE Next;
// Overwriting the entry at Flink, so it points to the one behind us.
ReadProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InInitializationOrderModuleList), &Next, sizeof(Next), &dwRead);
Next.InInitializationOrderModuleList.Blink = Blink;
WriteProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InInitializationOrderModuleList), &Next, sizeof(Next), NULL);
// Overwriting the entry at Blink, so it points to the one in front of us.
ReadProcessMemory(proc, CONTAINING_RECORD(Blink, LDR_MODULE, InInitializationOrderModuleList), &Previous, sizeof(Previous), &dwRead);
Previous.InInitializationOrderModuleList.Flink = Flink;
WriteProcessMemory(proc, CONTAINING_RECORD(Blink, LDR_MODULE, InInitializationOrderModuleList), &Previous, sizeof(Previous), NULL);
// Break out, to continue on to the next list...
break;
}
free(FullDllName.Buffer);
}while(TRUE);
Flink=ldrData.InLoadOrderModuleList.Flink;
Start=Flink;
Blink = 0;
do{
// Lets find the entry in the InInitializationOrderModuleList first.
ReadProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InLoadOrderModuleList), &ldrModuleA, sizeof(ldrModuleA), &dwRead);
Blink = ldrModuleA.InLoadOrderModuleList.Blink;
Flink = ldrModuleA.InLoadOrderModuleList.Flink;
if(ldrModuleA.InLoadOrderModuleList.Flink == Start)
break;
UNICODE_STRING FullDllName;
ReadProcessUnicodeString(proc, &ldrModuleA.FullDllName, &FullDllName);
if(wcscmp(FullDllName.Buffer, dllNameW)==0){
// BINGO!
printf("FullDllName: %wZ\n", FullDllName);
// Lets remove this list entry.
LDR_MODULE Previous;
LDR_MODULE Next;
// Overwriting the entry at Flink, so it points to the one behind us.
ReadProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InLoadOrderModuleList), &Next, sizeof(Next), &dwRead);
Next.InLoadOrderModuleList.Blink = Blink;
WriteProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InLoadOrderModuleList), &Next, sizeof(Next), NULL);
// Overwriting the entry at Blink, so it points to the one in front of us.
ReadProcessMemory(proc, CONTAINING_RECORD(Blink, LDR_MODULE, InLoadOrderModuleList), &Previous, sizeof(Previous), &dwRead);
Previous.InLoadOrderModuleList.Flink = Flink;
WriteProcessMemory(proc, CONTAINING_RECORD(Blink, LDR_MODULE, InLoadOrderModuleList), &Previous, sizeof(Previous), NULL);
// Break out, to continue on to the next list...
break;
}
free(FullDllName.Buffer);
}while(TRUE);
Flink=ldrData.InMemoryOrderModuleList.Flink;
Start=Flink;
Blink = 0;
do{
// Lets find the entry in the InInitializationOrderModuleList first.
ReadProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InMemoryOrderModuleList), &ldrModuleA, sizeof(ldrModuleA), &dwRead);
Blink = ldrModuleA.InMemoryOrderModuleList.Blink;
Flink = ldrModuleA.InMemoryOrderModuleList.Flink;
if(ldrModuleA.InMemoryOrderModuleList.Flink == Start)
break;
UNICODE_STRING FullDllName;
ReadProcessUnicodeString(proc, &ldrModuleA.FullDllName, &FullDllName);
if(wcscmp(FullDllName.Buffer, dllNameW)==0){
// BINGO!
printf("FullDllName: %wZ\n", FullDllName);
// Lets remove this list entry.
LDR_MODULE Previous;
LDR_MODULE Next;
// Overwriting the entry at Flink, so it points to the one behind us.
ReadProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InMemoryOrderModuleList), &Next, sizeof(Next), &dwRead);
Next.InMemoryOrderModuleList.Blink = Blink;
WriteProcessMemory(proc, CONTAINING_RECORD(Flink, LDR_MODULE, InMemoryOrderModuleList), &Next, sizeof(Next), NULL);
// Overwriting the entry at Blink, so it points to the one in front of us.
ReadProcessMemory(proc, CONTAINING_RECORD(Blink, LDR_MODULE, InMemoryOrderModuleList), &Previous, sizeof(Previous), &dwRead);
Previous.InMemoryOrderModuleList.Flink = Flink;
WriteProcessMemory(proc, CONTAINING_RECORD(Blink, LDR_MODULE, InMemoryOrderModuleList), &Previous, sizeof(Previous), NULL);
// Break out, to continue on to the next list...
break;
}
free(FullDllName.Buffer);
}while(TRUE);
return dwThreadExitCode;
}
Please note that when running on WoW64, there are actually TWO process environment blocks, this only clear out the 32 bit one, and on normal 64-bit it clears out the ONLY one (which is the 64-bit one). Sorry if that is confusing... (I'm proud of my code