package com.sun.jna.examples.win32.ext; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.examples.win32.Kernel32; import com.sun.jna.examples.win32.Kernel32Helper; import com.sun.jna.examples.win32.User32; import com.sun.jna.examples.win32.Kernel32Helper.FunctionCall; import com.sun.jna.examples.win32.W32API.HANDLE; import com.sun.jna.examples.win32.W32API.HWND; import com.sun.jna.examples.win32.W32API.SIZE_T; import com.sun.jna.examples.win32.W32API.WPARAM; import com.sun.jna.ptr.IntByReference; import copyto.paste.chat.miranda.Trace; /** * Allows code to be run inside a remote process address space. * * @author <a href="mailto:phil.kursawe@gmail.com">Philipp Kursawe</a> * */ public class ProcessAddressSpace { public interface ProcessRunnable<R> { R run(int memory) throws Throwable; } public static class SendMessageRunnable implements ProcessRunnable<Integer> { private final int message; private final HWND window; private final WPARAM wParam; public SendMessageRunnable(HWND window, int message, int wParam) { this(window, message, new WPARAM(wParam)); } public SendMessageRunnable(HWND window, int message, WPARAM wParam) { this.window = window; this.message = message; this.wParam = wParam; } public Integer run(int memory) throws Throwable { return User32.INSTANCE.SendMessage(window, message, wParam, memory); } } public static abstract class VoidProcessRunnable implements ProcessRunnable<Object> { public final Object run(int memory) throws Throwable { doRun(memory); return null; } protected abstract void doRun(int memory) throws Throwable; } private final class StructureReader<S extends Structure, R> implements ProcessRunnable<R> { private final ProcessRunnable<R> runnable; private final S structure; private StructureReader(ProcessRunnable<R> runnable, S structure) { this.runnable = runnable; this.structure = structure; } public R run(final int memory) throws Throwable { R result = runnable.run(memory); try { Kernel32Helper.checkedCall(true, new FunctionCall<Boolean>() { public Boolean call() { IntByReference read = new IntByReference(); return Kernel32.INSTANCE.ReadProcessMemory(process, memory, structure.getPointer(), new SIZE_T( structure.size()), read) && read.getValue() == structure.size(); } }); } catch (RuntimeException e) { return null; } structure.read(); return result; } } private int lastSize = 0; private int memory = 0; private final HANDLE process; public ProcessAddressSpace(HANDLE process) { this.process = process; } public void dispose() { freeMemory(); } public HANDLE getProcess() { return process; } public <S extends Structure, R> R run(final S structure, final ProcessRunnable<R> runnable) throws Throwable { structure.write(); R result = run(structure.getPointer(), structure.size(), new StructureReader<S, R>(runnable, structure)); return result; } public <T extends Pointer, R> R run(final T pointer, final int itemSize, ProcessRunnable<R> runnable) throws Throwable { if (lastSize != itemSize) { freeMemory(); } lastSize = itemSize; Trace.dump(pointer, itemSize); try { Kernel32Helper.checkedCall(true, new FunctionCall<Boolean>() { public Boolean call() { final SIZE_T nSize = new SIZE_T(itemSize); IntByReference written = new IntByReference(); boolean success = Kernel32.INSTANCE.WriteProcessMemory( process, getMemory(itemSize), pointer, nSize, written) && written.getValue() == itemSize; return success; } }); } catch (RuntimeException e) { return null; } return runnable.run(memory); } @Override protected void finalize() throws Throwable { dispose(); super.finalize(); } private void freeMemory() { if (memory != 0) { try { Kernel32Helper.checkedVirtualFreeEx(process, memory, Kernel32.MEM_RELEASE); } finally { memory = 0; } } } private int getMemory(int size) { if (0 == memory) { this.memory = Kernel32.INSTANCE.VirtualAllocEx(process, null, new SIZE_T(size), Kernel32.MEM_COMMIT, Kernel32.PAGE_READWRITE); } return memory; } public static String readStringA(HANDLE process, int address) { return readString(process, address, 1); } /** * Reads a null terminated string from the given address. * * <p> * This method assumes that the string at the address is a UNICODE (2 byte * per character) string. * * <p> * The method reads the <i>process</i> memory using * <code>ReadProcessMemory</code> and if it does not encounter a * 0-terminating character it might read memory that it has no access to. In * that case the returned string is automatically cut. * * * @param process * to read the string from. * @param address * in the process' address space to read the string from. * @return the string at at the given <i>address</i>. */ public static String readStringW(HANDLE process, int address) { return readString(process, address, 2); } private static String readString(HANDLE process, int address, int charSize) { if (charSize != 1 && charSize != 2) { throw new IllegalArgumentException("Character size must be 1 or 2."); } StringBuffer buffer = new StringBuffer(); IntByReference read = new IntByReference(); int offset = address; CharByReference c = new CharByReference(); final Kernel32.SIZE_T size = new Kernel32.SIZE_T(charSize); while (true) { if (!Kernel32.INSTANCE.ReadProcessMemory(process, offset, c, size, read) || read.getValue() != charSize) { break; } if (c.getValue() == 0) { break; } buffer.append(c.getValue()); offset += charSize; } return buffer.toString(); } }