diff --git a/CHANGES.md b/CHANGES.md index ba378b87de..0cd59ccf9b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ Features * [#334](https://github.com/twall/jna/pull/334): Added `com.sun.jna.platform.win32.Shell32.SHGetKnownFolderPath` and `KnownFolders` GUID constants - [@msteiger](https://github.com/msteiger). * [#338](https://github.com/twall/jna/pull/338): Added `com.sun.jna.platform.mac.XAttr` and `com.sun.jna.platform.mac.XAttrUtil` JNA wrapper for `` for Mac OS X - [@rednoah](https://github.com/rednoah). * [#339](https://github.com/twall/jna/pull/339): Added `GetWindowPlacement`, `SetWindowPlacement`, `AdjustWindowRect`, `AdjustWindowRectEx`, `ExitWindowsEx`, and `LockWorkstation` to `com.sun.jna.platform.win32.User32` - [@Timeroot](https://github.com/Timeroot). +* [#286](https://github.com/twall/jna/pull/286): Added in com.sun.jna.platform.win32.Kernel32: CreateRemoteThread, WritePocessMemory and ReadProcessMemory - [@sstokic-tgm](https://github.com/sstokic-tgm). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java index 7d8e854f89..93c981f043 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java @@ -2134,5 +2134,89 @@ boolean SystemTimeToTzSpecificLocalTime(TIME_ZONE_INFORMATION lpTimeZone, * information, call GetLastError. */ boolean SystemTimeToFileTime(SYSTEMTIME lpSystemTime, FILETIME lpFileTime); + /** + * Creates a thread that runs in the virtual address space of another process. + * + * @param hProcess + * A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD, + * PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, + * and may fail without these rights on certain platforms. + * @param lpThreadAttributes + * A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and + * determines whether child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a + * default security descriptor and the handle cannot be inherited. The access control lists (ACL) in the default + * security descriptor for a thread come from the primary token of the creator. + * @param dwStackSize + * The initial size of the stack, in bytes. The system rounds this value to the nearest page. + * If this parameter is 0 (zero), the new thread uses the default size for the executable. + * @param 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. + * @param lpParameter + * A pointer to a variable to be passed to the thread function. + * @param dwCreationFlags + * The flags that control the creation of the thread. + * 0 ... The thread runs immediately after creation. + * CREATE_SUSPENDED => 0x00000004 ... The thread is created in a suspended state, and does not run until the ResumeThread function is called. + * STACK_SIZE_PARAM_IS_A_RESERVATION => 0x00010000 ... The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size. + * @param lpThreadId + * A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned. + * + * @return If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is NULL. + * To get extended error information, call GetLastError. + * + * Note that CreateRemoteThread may succeed even if lpStartAddress points to data, code, or is not accessible. If the start address is + * invalid when the thread runs, an exception occurs, and the thread terminates. Thread termination due to a invalid start address is + * handled as an error exit for the thread's process. This behavior is similar to the asynchronous nature of CreateProcess, where the + * process is created even if it refers to invalid or missing dynamic-link libraries (DLL). + */ + HANDLE CreateRemoteThread(HANDLE hProcess, WinBase.SECURITY_ATTRIBUTES lpThreadAttributes, int dwStackSize, FOREIGN_THREAD_START_ROUTINE lpStartAddress, Pointer lpParameter, DWORD dwCreationFlags, Pointer lpThreadId); + + /** + * Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or the operation fails. + * + * @param hProcess + * A handle to the process memory to be modified. The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION + * access to the process. + * @param lpBaseAddress + * A pointer to the base address in the specified process to which data is written. Before data transfer occurs, the system + * verifies that all data in the base address and memory of the specified size is accessible for write access, and if it is not + * accessible, the function fails. + * @param lpBuffer + * A pointer to the buffer that contains data to be written in the address space of the specified process. + * @param nSize + * The number of bytes to be written to the specified process. + * @param lpNumberOfBytesWritten + * A pointer to a variable that receives the number of bytes transferred into the specified process. This parameter is optional. + * If lpNumberOfBytesWritten is NULL, the parameter is ignored. + * + * @return If the function succeeds, the return value is nonzero. + * If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError. + * The function fails if the requested write operation crosses into an area of the process that is inaccessible. +*/ + boolean WriteProcessMemory(HANDLE hProcess, Pointer lpBaseAddress, Pointer lpBuffer, int nSize, IntByReference lpNumberOfBytesWritten); + + /** + * Reads data from an area of memory in a specified process. The entire area to be read must be accessible or the operation fails. + * + * @param hProcess + * A handle to the process with memory that is being read. The handle must have PROCESS_VM_READ access to the process. + * @param lpBaseAddress + * A pointer to the base address in the specified process from which to read. Before any data transfer occurs, the system + * verifies that all data in the base address and memory of the specified size is accessible for read access, and if it is not + * accessible the function fails. + * @param lpBuffer + * A pointer to a buffer that receives the contents from the address space of the specified process. + * @param nSize + * The number of bytes to be read from the specified process. + * @param lpNumberOfBytesRead + * A pointer to a variable that receives the number of bytes transferred into the specified buffer. + * If lpNumberOfBytesRead is NULL, the parameter is ignored. + * + * @return If the function succeeds, the return value is nonzero. + * If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError. + * The function fails if the requested read operation crosses into an area of the process that is inaccessible. + */ + boolean ReadProcessMemory(HANDLE hProcess, Pointer lpBaseAddress, Pointer lpBuffer, int nSize, IntByReference lpNumberOfBytesRead); } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java b/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java index 37dbd79e10..092672caeb 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java @@ -16,6 +16,7 @@ import java.util.Date; import java.util.List; +import com.sun.jna.Callback; import com.sun.jna.Platform; import com.sun.jna.Pointer; import com.sun.jna.Structure; @@ -903,4 +904,23 @@ public PROCESS_INFORMATION(Pointer memory) { */ int MOVEFILE_WRITE_THROUGH = 0x8; + /** + * Represents a thread entry point local to this process, as a Callback. + */ + public interface THREAD_START_ROUTINE extends Callback{ + public DWORD apply( LPVOID lpParameter ); + } + + /** + * Represents a thread entry point in another process. Can only be expressed as a pointer, as + * the location has no meaning in the Java process. + */ + public class FOREIGN_THREAD_START_ROUTINE extends Structure { + LPVOID foreignLocation; + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "foreignLocation" }); + } + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java index 8c3aff5895..699795ca84 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java @@ -19,6 +19,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -614,4 +615,55 @@ public final void testWritePrivateProfileSection() throws IOException { reader.close(); } + public final void testCreateRemoteThread() throws IOException { + HANDLE hThrd = Kernel32.INSTANCE.CreateRemoteThread(null, null, 0, null, null, null, null); + assertNull(hThrd); + assertEquals(Kernel32.INSTANCE.GetLastError(), WinError.ERROR_INVALID_HANDLE); + } + + public void testWriteProcessMemory() { + Kernel32 kernel = Kernel32.INSTANCE; + + boolean successWrite = kernel.WriteProcessMemory(null, Pointer.NULL, Pointer.NULL, 1, null); + assertFalse(successWrite); + assertEquals(kernel.GetLastError(), WinError.ERROR_INVALID_HANDLE); + + ByteBuffer bufDest = ByteBuffer.allocateDirect(4); + bufDest.put(new byte[]{0,1,2,3}); + ByteBuffer bufSrc = ByteBuffer.allocateDirect(4); + bufSrc.put(new byte[]{5,10,15,20}); + Pointer ptrSrc = Native.getDirectBufferPointer(bufSrc); + Pointer ptrDest = Native.getDirectBufferPointer(bufDest); + + HANDLE selfHandle = kernel.GetCurrentProcess(); + kernel.WriteProcessMemory(selfHandle, ptrDest, ptrSrc, 3, null);//Write only the first three + + assertEquals(bufDest.get(0),5); + assertEquals(bufDest.get(1),10); + assertEquals(bufDest.get(2),15); + assertEquals(bufDest.get(3),3); + } + + public void testReadProcessMemory() { + Kernel32 kernel = Kernel32.INSTANCE; + + boolean successRead = kernel.ReadProcessMemory(null, Pointer.NULL, Pointer.NULL, 1, null); + assertFalse(successRead); + assertEquals(kernel.GetLastError(), WinError.ERROR_INVALID_HANDLE); + + ByteBuffer bufSrc = ByteBuffer.allocateDirect(4); + bufSrc.put(new byte[]{5,10,15,20}); + ByteBuffer bufDest = ByteBuffer.allocateDirect(4); + bufDest.put(new byte[]{0,1,2,3}); + Pointer ptrSrc = Native.getDirectBufferPointer(bufSrc); + Pointer ptrDest = Native.getDirectBufferPointer(bufDest); + + HANDLE selfHandle = kernel.GetCurrentProcess(); + kernel.ReadProcessMemory(selfHandle, ptrSrc, ptrDest, 3, null);//Read only the first three + + assertEquals(bufDest.get(0),5); + assertEquals(bufDest.get(1),10); + assertEquals(bufDest.get(2),15); + assertEquals(bufDest.get(3),3); + } }