Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WinAPI] Wrong declaration of CreateRemoteThread in Kernel32 interface #1115

Closed
apangin opened this issue Jul 20, 2019 · 2 comments · Fixed by #1124
Closed

[WinAPI] Wrong declaration of CreateRemoteThread in Kernel32 interface #1115

apangin opened this issue Jul 20, 2019 · 2 comments · Fixed by #1124
Labels

Comments

@apangin
Copy link

apangin commented Jul 20, 2019

CreateRemoteThread is currently declared as

HANDLE CreateRemoteThread(
        HANDLE hProcess,
        WinBase.SECURITY_ATTRIBUTES lpThreadAttributes,
        int dwStackSize,
        FOREIGN_THREAD_START_ROUTINE lpStartAddress,
        Pointer lpParameter,
        DWORD dwCreationFlags,
        Pointer lpThreadId
);

Win API expects a pointer as a start address argument. However, in the above declaration it is FOREIGN_THREAD_START_ROUTINE which is a structure with a pointer. Therefore JNA creates an extra level of indirection when passing lpStartAddress, and this leads to an immediate crash of the target process with EXCEPTION_ACCESS_VIOLATION.

FOREIGN_THREAD_START_ROUTINE should extend PointerType rather than Structure.

See https://twitter.com/rafaelcodes/status/1152682931569274881?s=20 for an example.

@matthiasblaesing
Copy link
Member

Can you suggest a minimal sample, that could be turned into a unittest for this?

@apangin
Copy link
Author

apangin commented Aug 5, 2019

Yes, here is the test:

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.W32APIOptions;
import junit.framework.Assert;
import org.junit.Test;

import static com.sun.jna.platform.win32.WinNT.*;

public class CreateRemoteThreadTest {

    interface MyKernel32 extends Kernel32 {
        MyKernel32 INSTANCE = Native.load("kernel32", MyKernel32.class, W32APIOptions.DEFAULT_OPTIONS);

        Pointer VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);

        HANDLE CreateRemoteThread(HANDLE hProcess, SECURITY_ATTRIBUTES attr, int stackSize,
                                  Pointer startAddr, Pointer param, DWORD flags, Pointer threadId);

        boolean GetExitCodeThread(HANDLE hThread, IntByReference exitCode);
    }

    @Test
    public void myCreateRemoteThread() {
        Pointer addr = MyKernel32.INSTANCE.VirtualAlloc(null, new SIZE_T(4096),
                new DWORD(MEM_COMMIT | MEM_RESERVE), new DWORD(PAGE_EXECUTE_READWRITE));

        // mov eax, ecx; ret; int3
        addr.setInt(0, 0xccc3c18b);

        HANDLE hThread = MyKernel32.INSTANCE.CreateRemoteThread(
                MyKernel32.INSTANCE.GetCurrentProcess(),
                null, 0, addr, new Pointer(12345), new DWORD(0), null);
        Assert.assertNotNull(hThread);

        int waitResult = MyKernel32.INSTANCE.WaitForSingleObject(hThread, 10_000);
        Assert.assertEquals(WAIT_OBJECT_0, waitResult);

        IntByReference exitCode = new IntByReference();
        boolean exitResult = MyKernel32.INSTANCE.GetExitCodeThread(hThread, exitCode);
        Assert.assertTrue(exitResult);
        Assert.assertEquals(12345, exitCode.getValue());
    }

    @Test
    public void platformCreateRemoteThread() {
        Pointer addr = MyKernel32.INSTANCE.VirtualAlloc(null, new SIZE_T(4096),
                new DWORD(MEM_COMMIT | MEM_RESERVE), new DWORD(PAGE_EXECUTE_READWRITE));

        // mov eax, ecx; ret; int3
        addr.setInt(0, 0xccc3c18b);

        FOREIGN_THREAD_START_ROUTINE startRoutine = new FOREIGN_THREAD_START_ROUTINE();
        startRoutine.foreignLocation.setPointer(addr);

        HANDLE hThread = Kernel32.INSTANCE.CreateRemoteThread(
                MyKernel32.INSTANCE.GetCurrentProcess(),
                null, 0, startRoutine, new Pointer(12345), new DWORD(0), null);
        Assert.assertNotNull(hThread);

        int waitResult = MyKernel32.INSTANCE.WaitForSingleObject(hThread, 10_000);
        Assert.assertEquals(WAIT_OBJECT_0, waitResult);

        IntByReference exitCode = new IntByReference();
        boolean exitResult = MyKernel32.INSTANCE.GetExitCodeThread(hThread, exitCode);
        Assert.assertTrue(exitResult);
        Assert.assertEquals(12345, exitCode.getValue());
    }
}

myCreateRemoteThread works fine while platformCreateRemoteThread fails with 0xC0000005 (EXCEPTION_ACCESS_VIOLATION)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
2 participants