[C#] ASM Injection (SetThreadContext) menu

User Tag List

Results 1 to 4 of 4
  1. #1
    Xartrick's Avatar Active Member
    Reputation
    24
    Join Date
    May 2011
    Posts
    29
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [C#] ASM Injection (SetThreadContext)

    Hello.

    In the last entry, we saw how to inject and execute ASM (shellcode) by the use of a new thread using CreateRemoteThread.
    The problem is: if a function is self-threaded and cannot be called from a foreign thread, we're f*cked...
    To do this injection, we will use the main thread to do our things.

    We will open the main thread and redirect the execution flow to our shellcode and then, go back to our previous position.
    This can be done using GetThreadContext and SetThreadContext to get and change EIP.

    The scheme is almost the same:

    * Get process handle using OpenProcess
    * Allocate memory for our shellcode using VirtualAllocEx
    * Write our shellcode using WriteProcessMemory
    * Open the main thread using OpenThread
    * Suspend main thread using SuspendThread
    * Get EIP using GetThreadContext (and keep it)
    * Set EIP to our shellcode's address using SetThreadContext
    * Resume main thread using ResumeThread
    * 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 bool CloseHandle(IntPtr hObject);
    
    [DllImport("kernel32.dll")]
    public static extern int ResumeThread(IntPtr hThread);
    
    [DllImport("kernel32.dll")]
    public static extern int SuspendThread(IntPtr hThread);
    
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenThread(ThreadAccessFlags dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
    
    [DllImport("kernel32.dll")]
    public static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext);
    
    [DllImport("kernel32.dll")]
    public static extern bool SetThreadContext(IntPtr hThread, ref CONTEXT lpContext);
    Code:
    public enum CONTEXT_FLAGS : uint {
    	CONTEXT_i386               = 0x10000,
    	CONTEXT_i486               = 0x10000,
    	CONTEXT_CONTROL            = CONTEXT_i386 | 0x01,
    	CONTEXT_INTEGER            = CONTEXT_i386 | 0x02,
    	CONTEXT_SEGMENTS           = CONTEXT_i386 | 0x04,
    	CONTEXT_FLOATING_POINT     = CONTEXT_i386 | 0x08,
    	CONTEXT_DEBUG_REGISTERS    = CONTEXT_i386 | 0x10,
    	CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20,
    	CONTEXT_FULL               = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS,
    	CONTEXT_ALL                = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS
    }
    
    [Flags]
    public enum ThreadAccessFlags : int  {
    	TERMINATE            = 0x0001,
    	SUSPEND_RESUME       = 0x0002,
    	GET_CONTEXT          = 0x0008,
    	SET_CONTEXT          = 0x0010,
    	SET_INFORMATION      = 0x0020,
    	QUERY_INFORMATION    = 0x0040,
    	SET_THREAD_TOKEN     = 0x0080,
    	IMPERSONATE          = 0x0100,
    	DIRECT_IMPERSONATION = 0x0200
    }
    Code:
    [StructLayout(LayoutKind.Sequential)]
    public struct CONTEXT {
    	public uint               ContextFlags;
    	public uint               Dr0;
    	public uint               Dr1;
    	public uint               Dr2;
    	public uint               Dr3;
    	public uint               Dr6;
    	public uint               Dr7;
    	public FLOATING_SAVE_AREA FloatSave;
    	public uint               SegGs;
    	public uint               SegFs;
    	public uint               SegEs;
    	public uint               SegDs;
    	public uint               Edi;
    	public uint               Esi;
    	public uint               Ebx;
    	public uint               Edx;
    	public uint               Ecx;
    	public uint               Eax;
    	public uint               Ebp;
    	public uint               Eip;
    	public uint               SegCs;
    	public uint               EFlags;
    	public uint               Esp;
    	public uint               SegSs;
    	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] 
    	public byte[]             ExtendedRegisters;
    }
    We keep the same shellcode as the previous entry, don't forget to correctly align the stack!
    The process opening and shellcode writing is done by the same method, so I will skip this part...

    Now, we can begin the interresting part, the threads operations.
    We will now get and open the main thread.
    The main thread is the first thread of the Threads property of the Process class.
    To open the thread, we have to get the thread's ID and then, call OpenThread.

    Code:
    Process process   = /* get your process*/;
    uint    iThreadId = process.Threads[0].Id;
    IntPtr  hThread   = OpenThread(ThreadAccessFlags.SUSPEND_RESUME | ThreadAccessFlags.SET_CONTEXT | ThreadAccessFlags.GET_CONTEXT, false, iThreadId);
    
    if (hThread == IntPtr.Zero)
        throw new ApplicationException("Cannot get thread handle.");
    We suspend the process to get no trouble during EIP changing:

    Code:
    CONTEXT context = new CONTEXT();
    
    if (!GetThreadContext(hThread, ref context))
        throw new ApplicationException("Cannot get thread context.");
    Now, we've EIP in the CONTEXT structure.
    We're a going to modify the shellcode on the fly to get the whole thing working.
    If you carefuly understand how we redirect the flow, it should be clear.
    We're to save all registers and flags to restore them after the shellcode execution to go back to our previous code (old EIP).
    So, we use PUSHAD (push registers on the stack), PUSHFD (push flags on the stack) and then POPFD and POPAD (LIFO order (Last In First Out)).
    At the top of the shellcode, we push the old EIP to jump it back using a RET.

    I wrote a function who give me the modified shellcode (ugly-fastly-done code):

    Code:
    private byte[] ModifyShellcode(byte[] bytes, uint eip) {
    	byte[] bytesEip = BitConverter.GetBytes(eip);
    	byte[] start = new byte[] {
    		0x60, // PUSHAD
    		0x9C  // PUSHFD
    	};
    
    	byte[] end = new byte[] {
    		0x9D, // POPFD
    		0x61, // POPAD
    		0xC3  // RET
    	};
    
    	IEnumerable<byte> result = Enumerable.Empty<byte>();
    
    	result = result.Concat(new byte[] { 0x68 }); // PUSH
    	result = result.Concat(bytesEip);            // EIP
    	result = result.Concat(start);
    	result = result.Concat(bytes);
    	result = result.Concat(end);
    
    	return result.ToArray();
    }
    Now, we can inject our modified shellcode like the previous entry.
    Change EIP is a child game!

    Code:
    context.Eip = (uint)hAlloc;
    You have to set the (new) context of the thread using SetThreadContext.

    Code:
    if (SetThreadContext(hThread, ref context) == false)
        throw new ApplicationException("Cannot set thread context.");
    Just resume the process using ResumeThread.

    Code:
    if (ResumeThread(hThread) == -1)
        throw new ApplicationException("Cannot resume thread.");]
    And voilà, our shellcode is executed in the main thread of an application.

    And to finish, close the thread handle!

    Code:
    if (!CloseHandle(hThread))
        throw new ApplicationException("Cannot close thread handle.");
    Go further:

    * Check when the shellcode reach the end to clean out the memory.

    Thanks:

    * Ivanlef0u [FRENCH] (provide me some advice about thread suspending/resuming)

    Edit:

    * [WoW][C#]RemoteThreadHijack (alternative to BlackMagic's InjectAndExecute) (this is the same method as I did)
    Last edited by Xartrick; 07-15-2013 at 07:11 PM.

    [C#] ASM Injection (SetThreadContext)
  2. #2
    Frosttall's Avatar Active Member
    Reputation
    64
    Join Date
    Feb 2011
    Posts
    261
    Thanks G/R
    16/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks once again.

    Do you have any experience about the performance using that method? It was horrible the last time I've checked SuspendThread.

  3. #3
    Xartrick's Avatar Active Member
    Reputation
    24
    Join Date
    May 2011
    Posts
    29
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I never tested my code in real condition, I just try (and publish) some POC I made.

  4. #4
    Seifer's Avatar Site Donator
    Reputation
    129
    Join Date
    Apr 2007
    Posts
    270
    Thanks G/R
    1/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've messed around with something similar to this, but instead I was messing with the stack and its return values. From what I found this is highly unstable and will cause a bunch of issues that'll make it less than favourable in any production environment when compared to the more "traditional" methods.

Similar Threads

  1. [Release] [C# DLL] iHook, EndScene ASM Injection!
    By -Ryuk- in forum WoW Memory Editing
    Replies: 142
    Last Post: 09-19-2022, 09:06 PM
  2. [C#] ASM Injection (CreateRemoteThread)
    By Xartrick in forum WoW Memory Editing
    Replies: 7
    Last Post: 07-23-2013, 10:57 PM
  3. [question] basic asm injection
    By abuckau907 in forum WoW Memory Editing
    Replies: 12
    Last Post: 03-07-2012, 05:38 AM
  4. About CTM via asm injection
    By N1ghtmaree in forum WoW Memory Editing
    Replies: 11
    Last Post: 08-08-2010, 10:57 AM
  5. Injecting ASM problems
    By lanman92 in forum WoW Memory Editing
    Replies: 33
    Last Post: 03-16-2009, 06:46 AM
All times are GMT -5. The time now is 05:47 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search