0

I hope answer to my question is not game/app specific and can be answered in communities like this.

So, I tried to use some internal functions of a game, like pick a target. No malicious intentions, just wanted to do it work and use it once. I used "nop" as temporally asm code to inject, so I will sure that I do something wrong in my inject method and not in asm code I'm trying to inject. To my surprise is the single "nop" been the reason the game crashed.

My question is why? How can I break something by doing nothing?

Then after "nop" I added "ret" as Google said me. and game didn't crash, tried this on function I was tried to call and also worked fine.

Injector code(c#):

        [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
        public static extern int CloseHandle(int hObject);

        [DllImport("kernel32.dll")]
        public static extern Int32 WriteProcessMemory(int hProcess, int lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesWritten);


        [DllImport("kernel32", EntryPoint = "CreateRemoteThread")]
        public static extern int CreateRemoteThread(int hProcess, int lpThreadAttributes, int dwStackSize, int lpStartAddress, int lpParameter, int dwCreationFlags, ref int lpThreadId);


        [DllImport("kernel32", EntryPoint = "WaitForSingleObject")]
        public static extern int WaitForSingleObject(int hHandle, int dwMilliseconds);


        [DllImport("Kernel32.dll")]
        public static extern System.Int32 VirtualAllocEx(int hProcess, int lpAddress, int dwSize, int flAllocationType, int flProtect);

        [DllImport("Kernel32.dll")]
        public static extern System.Int32 VirtualFreeEx(int hProcess, int lpAddress, int dwSize, int flAllocationType);

        [DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
        public static extern int OpenProcess(int dwDesiredAccess, int bInheritHandle, int dwProcessId);

        private const int PAGE_EXECUTE_READWRITE = 0x4;
        private const int MEM_COMMIT = 4096;
        private const int MEM_RELEASE = 0x8000;
        private const int MEM_DECOMMIT = 0x4000;
        private const int PROCESS_ALL_ACCESS = 0x1f0fff;
        private const int PROCESS_CREATE_THREAD = 0x2;
        private const int PROCESS_VM_OPERATION = 0x8;
        private const int PROCESS_VM_WRITE = 0x20;


        public void Inject(byte[] data,int pid)
        {
            int hwnd = 0;
            int addre = 0;
            int threadhwnd = 0;
            if (pid != 0)
            {
                hwnd = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD | PROCESS_VM_WRITE, 0, pid);
                if (hwnd != 0)
                {
                    addre = VirtualAllocEx(hwnd, 0, data.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                    WriteProcessMemory(hwnd, addre, data, data.Length, 0);
                    int threadid = 0;
                    threadhwnd = CreateRemoteThread(hwnd, 0, 0, addre, 0, 0, ref threadid);
                    int res = WaitForSingleObject(threadhwnd,3000);
                    VirtualFreeEx(hwnd, addre, data.Length, MEM_RELEASE);
                    CloseHandle(threadhwnd);
                    CloseHandle(hwnd);

                }
            }
        }

Thank you in advance!

2

CreateRemoteThread takes 7 arguments and the 4th of them is called lpStartAddress.

Now, from MSDN docs:

lpStartAddress

A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the starting address of the thread in the remote process. The function must exist in the remote process. For more information, see ThreadProc.

So as you see your parameter addre has to point to some function in remote process. Functions in assembly (x86/x64) look like this:

FUNCTION_NAME:
FUNCTION_BODY
ret

And you call them like this:

call FUNCTION_NAME

The ret instruction in the end takes the return address from the stack, which was saved by the call instruction, when call FUNCTION_NAME was executed - it points to the next instruction after that call.

But what happens when you inject only nop? Since you didn't inject ret, your injected function looks like this:

INJECTED_FUNCTION:
nop
SOME_GARBAGE_BYTES

As you see, the program you inject the code into will try to execute the instruction just after nop (since it didn't encounter ret), which may be either some garbage bytes or may even lie outside process virtual memory and may cause access violation - in both cases the program will crash.

2
  • Thank you. Can I also ask what is lpParameter? Because I saw somewhere it is used and somewhere it is zero. I don't understand especially "variable to be passed to the thread function" part. As I know parameters passed with Push instruction like push edx call edi So, when and how use lpParameter? – user224351 Mar 27 at 11:17
  • It is a parameter passed to the function you pass to CreateRemoteThread function. If you don't need to pass anything there, you just set it to NULL. However, it can be a pointer to some data in remote process' memory - it can point, for example, to array of arguments you would normally pass using push instructions – bart1e Mar 28 at 10:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.