First of all, code principle
1. Use CreateProcess to create a process marked with create "suspend
The natural process will be suspended after the DLL is created and loaded. The create? Suspend tag was originally prepared for the debugger.
2. Then use GetThreadContext to get the thread context of the process and save it (important)....
3. Use VirtualAllocEx to apply for a section of dll path length + 32 bytes of memory in process space (1K is directly applied here) to store shellcode
And dll path.
4. Use WriteProcessMemory to write the DLL path to the requested memory address
5. Then combine and calculate shellcode code (calculation will not be explained here, see code Notes for details)
6. Write shellcode to the memory address of the application with WriteProcessMemory
7. Modify the Eip of context state to the memory address just applied for by VirtualAllocEx
8. Set thread context state with SetThreadContext
9. Resume the thread with ResumeThread.
Here are two classes of code: an injection and an API
How to use
Win32.Injection.EipInject EI = new Win32.Injection.EipInject();
EI.CreateProcess(@"D:\Test.exe",@"D:\Test.dll");
using System; using System.Runtime.InteropServices; namespace Win32.Injection { public static class WinApi { public const int CREATE_SUSPENDED = 0x00000004; public const UInt32 MEM_COMMIT = 0x00001000; public const UInt32 CONTEXT_i386 = 0x00010000; // this assumes that i386 and public const UInt32 CONTEXT_CONTROL = (CONTEXT_i386 | 0x00000001); // SS:SP, CS:IP, FLAGS, BP [DllImport("kernel32")] public static extern IntPtr LoadLibrary(string lpLibFileName); [DllImport("kernel32")] public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); [DllImport("kernel32", EntryPoint = "VirtualAllocEx")] public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 flAllocationType, PAGE_EXECUTE_ENUM flProtect); [DllImport("kernel32", EntryPoint = "GetThreadContext")] public static extern int GetThreadContext(IntPtr hThread, ref CONTEXT lpContext); [DllImport("kernel32")] public static extern Int32 WriteProcessMemory(IntPtr hProcess, IntPtr lpBassAddress, byte[] lpBuffer, Int32 nSize, IntPtr lpNumberOfBytesRead); [DllImport("kernel32", EntryPoint = "SetThreadContext")] public static extern int SetThreadContext(IntPtr hThread, ref CONTEXT lpContext); [DllImport("kernel32", EntryPoint = "ResumeThread")] public static extern int ResumeThread(IntPtr hThread); public enum PAGE_EXECUTE_ENUM { PAGE_EXECUTE_READ = 0x20, PAGE_EXECUTE_READWRITE = 0x40 } public struct STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public int lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [DllImport("kernel32", EntryPoint = "CreateProcess")] public static extern int CreateProcess( string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, String lpCurrentDriectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation); public struct FLOATING_SAVE_AREA { public UInt32 ControlWord; public UInt32 StatusWord; public UInt32 TagWord; public UInt32 ErrorOffset; public UInt32 ErrorSelector; public UInt32 DataOffset; public UInt32 DataSelector; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] public Byte[] RegisterArea; public UInt32 Spare0; } /// <summary> ///Register context /// </summary> public struct CONTEXT { public UInt32 ContextFlags; // // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL. // public UInt32 Dr0; public UInt32 Dr1; public UInt32 Dr2; public UInt32 Dr3; public UInt32 Dr6; public UInt32 Dr7; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. // public FLOATING_SAVE_AREA FloatSave; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_SEGMENTS. // public UInt32 SegGs; public UInt32 SegFs; public UInt32 SegEs; public UInt32 SegDs; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_INTEGER. // public UInt32 Edi; public UInt32 Esi; public UInt32 Ebx; public UInt32 Edx; public UInt32 Ecx; public UInt32 Eax; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_CONTROL. // public UInt32 Ebp; public UInt32 Eip; public UInt32 SegCs; // MUST BE SANITIZED public UInt32 EFlags; // MUST BE SANITIZED public UInt32 Esp; public UInt32 SegSs; // // This section is specified/returned if the ContextFlags word // contains the flag CONTEXT_EXTENDED_REGISTERS. // The format and contexts are processor specific // //BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public Byte[] ExtendedRegisters; } } }
using System; using System.Text; namespace Win32.Injection { public class EipInject { public EipInject() { // pushad ShellCode[0] = 0x60; // pushfd ShellCode[1] = 0x9c; // push ShellCode[2] = 0x68; //call ShellCode[7] = 0xE8; // popfd ShellCode[12] = 0x9d; // popad ShellCode[13] = 0x61; // jmp ShellCode[14] = 0xE9; hModule = WinApi.LoadLibrary("Kernel32.dll"); //Get MessageBoxA address LoadLibrary_addr = WinApi.GetProcAddress(hModule, "LoadLibraryA"); } /// <summary> ///Create the specified process and load the DLL /// </summary> /// <param name="AppPath"></param> /// <param name="DllPath"></param> /// <returns></returns> public bool CreateProcess(String AppPath, String DllPath) { WinApi.CONTEXT context = new WinApi.CONTEXT(); Byte[] DllPathData = Encoding.Default.GetBytes(DllPath); if (!_CreateProcess(AppPath)) { return false; } //Get thread context context.ContextFlags = WinApi.CONTEXT_CONTROL; if (WinApi.GetThreadContext(hThread, ref context) != 0) { Int32 dwSize = 1024; //Request memory IntPtr pProcessMem = AllocMemory(dwSize); if (pProcessMem == IntPtr.Zero) { return false; } //DLL path address pprocesmem + 0x100 BuilderValue(ShellCode, 3, pProcessMem + 0x100); //LoadLibrary address is equal to CALL absolute address - next instruction address BuilderValue(ShellCode, 8, LoadLibrary_addr - (pProcessMem.ToInt32() + 12)); // Skip back to the old EIP address ct.eip - (pprocesmem + 19); (absolute address next instruction address) BuilderValue(ShellCode, 15, (int)context.Eip - (pProcessMem.ToInt32() + 19)); //First write the ShellCode in the address WinApi.WriteProcessMemory(hProcess, pProcessMem, ShellCode, ShellCode.Length, IntPtr.Zero); //Write DLL path to memory, starting from 0x100(256) WinApi.WriteProcessMemory(hProcess, (pProcessMem + 0x100), DllPathData, DllPathData.Length, IntPtr.Zero); //Rewrite EIP context.Eip = (UInt32)pProcessMem; //Write thread context back WinApi.SetThreadContext(hThread, ref context); //Resume suspended threads WinApi.ResumeThread(hThread); return true; } else { return false; } } /// <summary> ///Responsible for creating a suspended process /// </summary> /// <param name="AppPath"></param> /// <returns></returns> private bool _CreateProcess(String AppPath) { WinApi.STARTUPINFO si = new WinApi.STARTUPINFO(); WinApi.PROCESS_INFORMATION PI = new WinApi.PROCESS_INFORMATION(); int HResult = WinApi.CreateProcess( null, // Program path command line AppPath, IntPtr.Zero, IntPtr.Zero, false, // Suspend process only debug this process WinApi.CREATE_SUSPENDED, IntPtr.Zero, //working directory System.IO.Path.GetDirectoryName(AppPath), ref si, ref PI); if (HResult == 0) return false; hProcess = PI.hProcess; hThread = PI.hThread; return true; } /// <summary> ///Responsible for applying for a section of memory /// </summary> /// <param name="Size"></param> /// <returns></returns> private IntPtr AllocMemory(Int32 Size) { return WinApi.VirtualAllocEx(hProcess, IntPtr.Zero, Size, WinApi.MEM_COMMIT, WinApi.PAGE_EXECUTE_ENUM.PAGE_EXECUTE_READWRITE); } /// <summary> ///Responsible for writing four byte integers into shellcode /// </summary> /// <param name="ShellCode"></param> /// <param name="Index"></param> /// <param name="Value"></param> private void BuilderValue(Byte[] ShellCode, Int32 Index, IntPtr Value) { Byte[] DATA = BitConverter.GetBytes(Value.ToInt32()); Array.Copy(DATA, 0, ShellCode, Index, 4); } /// <summary> ///Responsible for writing four byte integers into shellcode /// </summary> /// <param name="ShellCode"></param> /// <param name="Index"></param> /// <param name="Value"></param> private void BuilderValue(Byte[] ShellCode, Int32 Index, Int32 Value) { Byte[] DATA = BitConverter.GetBytes(Value); Array.Copy(DATA, 0, ShellCode, Index, 4); } /// <summary> ///Create process handle /// </summary> public IntPtr hProcess = IntPtr.Zero; /// <summary> ///Create process thread handle /// </summary> public IntPtr hThread = IntPtr.Zero; private IntPtr hModule = IntPtr.Zero; private IntPtr LoadLibrary_addr = IntPtr.Zero; private Byte[] ShellCode = new byte[32]; } }