mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 05:45:06 +08:00
GP-6509: Fix ConPtyTests
This commit is contained in:
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -47,9 +47,12 @@ public interface PtySession {
|
|||||||
void destroyForcibly();
|
void destroyForcibly();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a human-readable description of the session
|
* {@return a human-readable description of the session}
|
||||||
*
|
|
||||||
* @return the description
|
|
||||||
*/
|
*/
|
||||||
String description();
|
String description();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the process handle for the session leader}
|
||||||
|
*/
|
||||||
|
ProcessHandle handle();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -57,4 +57,9 @@ public class LocalProcessPtySession implements PtySession {
|
|||||||
public String description() {
|
public String description() {
|
||||||
return "process " + process.pid() + " on " + ptyName;
|
return "process " + process.pid() + " on " + ptyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProcessHandle handle() {
|
||||||
|
return process.toHandle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-2
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.pty.local;
|
package ghidra.pty.local;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
@@ -96,4 +97,15 @@ public class LocalWindowsNativeProcessPtySession implements PtySession {
|
|||||||
public String description() {
|
public String description() {
|
||||||
return "process " + pid + " on " + ptyName;
|
return "process " + pid + " on " + ptyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProcessHandle handle() {
|
||||||
|
IntByReference lpExitCode = new IntByReference();
|
||||||
|
Kernel32.INSTANCE.GetExitCodeProcess(processHandle.getNative(), lpExitCode);
|
||||||
|
Optional<ProcessHandle> result = ProcessHandle.of(pid);
|
||||||
|
if (lpExitCode.getValue() != WinBase.STILL_ACTIVE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result.orElseThrow(() -> new AssertionError());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.pty.windows;
|
package ghidra.pty.windows;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.junit.Assume.assumeTrue;
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.ProcessBuilder.Redirect;
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -31,8 +30,14 @@ import com.sun.jna.LastErrorException;
|
|||||||
import ghidra.framework.OperatingSystem;
|
import ghidra.framework.OperatingSystem;
|
||||||
import ghidra.pty.*;
|
import ghidra.pty.*;
|
||||||
import ghidra.pty.testutil.DummyProc;
|
import ghidra.pty.testutil.DummyProc;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class ConPtyTest extends AbstractPtyTest {
|
public class ConPtyTest extends AbstractPtyTest {
|
||||||
|
public static final int JOIN_TIMEOUT_MS = 3000;
|
||||||
|
|
||||||
|
public static final String CMD = DummyProc.which("cmd.exe");
|
||||||
|
public static final String GDB = DummyProc.which("gdb.exe");
|
||||||
|
public static final String NOTEPAD = DummyProc.which("notepad.exe");
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void checkWindows() {
|
public void checkWindows() {
|
||||||
@@ -40,11 +45,11 @@ public class ConPtyTest extends AbstractPtyTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionCmd() throws IOException, InterruptedException {
|
public void testSessionCmd() throws Exception {
|
||||||
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
||||||
PtySession cmd = pty.getChild().session(new String[] { DummyProc.which("cmd") }, null);
|
PtySession cmd = pty.getChild().session(new String[] { CMD }, null);
|
||||||
pty.getParent().getOutputStream().write("exit\r\n".getBytes());
|
pty.getParent().getOutputStream().write("exit\r\n".getBytes());
|
||||||
assertEquals(0, cmd.waitExited());
|
assertEquals(0, cmd.waitExited(JOIN_TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,12 +65,12 @@ public class ConPtyTest extends AbstractPtyTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionCmdEchoTest() throws IOException, InterruptedException {
|
public void testSessionCmdEchoTest() throws Exception {
|
||||||
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
||||||
PtyParent parent = pty.getParent();
|
PtyParent parent = pty.getParent();
|
||||||
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
||||||
BufferedReader reader = loggingReader(parent.getInputStream());
|
BufferedReader reader = loggingReader(parent.getInputStream());
|
||||||
PtySession cmd = pty.getChild().session(new String[] { DummyProc.which("cmd") }, null);
|
PtySession cmd = pty.getChild().session(new String[] { CMD }, null);
|
||||||
runExitCheck(3, cmd);
|
runExitCheck(3, cmd);
|
||||||
|
|
||||||
writer.println("echo test");
|
writer.println("echo test");
|
||||||
@@ -77,38 +82,37 @@ public class ConPtyTest extends AbstractPtyTest {
|
|||||||
do {
|
do {
|
||||||
line = reader.readLine();
|
line = reader.readLine();
|
||||||
}
|
}
|
||||||
while (!"test".equals(line));
|
while (!"test".equals(line));
|
||||||
} catch (IOException e) {
|
}
|
||||||
// TODO Auto-generated catch block
|
catch (IOException e) {
|
||||||
e.printStackTrace();
|
Msg.info(this, "done reading");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
t.start();
|
t.start();
|
||||||
|
|
||||||
writer.println("exit 3");
|
writer.println("exit 3");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
||||||
assertEquals(3, cmd.waitExited());
|
assertEquals(3, cmd.waitExited(JOIN_TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||||
assertFalse("Content read successfully from the cmd", t.isAlive());
|
t.join(JOIN_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSessionGdbLineLength() throws IOException, InterruptedException {
|
public void testSessionGdbLineLength() throws Exception {
|
||||||
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
||||||
PtyParent parent = pty.getParent();
|
PtyParent parent = pty.getParent();
|
||||||
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
||||||
BufferedReader reader = loggingReader(parent.getInputStream());
|
BufferedReader reader = loggingReader(parent.getInputStream());
|
||||||
PtySession gdb =
|
PtySession gdb = pty.getChild().session(new String[] { GDB }, null);
|
||||||
pty.getChild().session(new String[] { "C:\\msys64\\mingw64\\bin\\gdb.exe" }, null);
|
|
||||||
|
|
||||||
writer.println(
|
writer.println(
|
||||||
"echo This line is cleary much, much, much, much, much, much, much, much, much " +
|
"echo This line is cleary much, much, much, much, much, much, much, much, much " +
|
||||||
" longer than 80 characters");
|
" longer than 80 characters");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
||||||
// set up reading cmd output on a thread since "readLine" is blocking
|
// set up reading cmd output on a thread since "readLine" is blocking
|
||||||
Thread t = new Thread(() -> {
|
Thread t = new Thread(() -> {
|
||||||
String line;
|
String line;
|
||||||
@@ -116,87 +120,145 @@ public class ConPtyTest extends AbstractPtyTest {
|
|||||||
do {
|
do {
|
||||||
line = reader.readLine();
|
line = reader.readLine();
|
||||||
}
|
}
|
||||||
while (!"test".equals(line));
|
while (!"test".equals(line));
|
||||||
} catch (IOException e) {
|
}
|
||||||
// TODO Auto-generated catch block
|
catch (IOException e) {
|
||||||
e.printStackTrace();
|
Msg.info(this, "done reading");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
t.start();
|
t.start();
|
||||||
|
|
||||||
writer.println("exit 3");
|
writer.println("exit 3");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
||||||
assertEquals(3, gdb.waitExited());
|
assertEquals(3, gdb.waitExited(JOIN_TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||||
assertFalse("Content read successfully from the cmd", t.isAlive());
|
t.join(JOIN_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
/**
|
||||||
|
* Verifies that the ConPty is actually necessary to send interrupts to child processes.
|
||||||
|
* <p>
|
||||||
|
* Sending char 3 down the stdin is not sufficient, as demonstrated in this experiment. GDB does
|
||||||
|
* not receive the interrupt, and so the target process remains running, and none of the
|
||||||
|
* subsequent gdb commands are processed. Thus, the target and gdb are still running by the time
|
||||||
|
* we get to the {@link Process#waitFor(long, TimeUnit)} call. It will return false, thus
|
||||||
|
* causing the expected {@link AssertionError}.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
* 'tis a test
|
||||||
|
*/
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
public void testGdbInterruptPlain() throws Exception {
|
public void testGdbInterruptPlain() throws Exception {
|
||||||
ProcessBuilder builder = new ProcessBuilder("C:\\msys64\\mingw64\\bin\\gdb.exe");
|
|
||||||
builder.redirectOutput(Redirect.PIPE);
|
|
||||||
builder.redirectInput(Redirect.PIPE);
|
|
||||||
builder.redirectErrorStream(true);
|
|
||||||
|
|
||||||
Process gdb = builder.start();
|
boolean terminated = false;
|
||||||
|
Process gdb = null;
|
||||||
|
try {
|
||||||
|
ProcessBuilder builder = new ProcessBuilder(GDB);
|
||||||
|
builder.redirectOutput(Redirect.PIPE);
|
||||||
|
builder.redirectInput(Redirect.PIPE);
|
||||||
|
builder.redirectErrorStream(true);
|
||||||
|
|
||||||
PrintWriter writer = new PrintWriter(gdb.getOutputStream());
|
gdb = builder.start();
|
||||||
pump(gdb.getInputStream(), System.err);
|
|
||||||
|
|
||||||
System.out.println("Testing");
|
PrintWriter writer = new PrintWriter(gdb.getOutputStream());
|
||||||
writer.println("echo test");
|
pump(gdb.getInputStream(), System.out);
|
||||||
writer.println("set new-console on");
|
|
||||||
System.out.println("Launching notepad");
|
|
||||||
writer.println("file C:\\\\Windows\\\\notepad.exe");
|
|
||||||
writer.println("run");
|
|
||||||
writer.flush();
|
|
||||||
System.out.println("Waiting");
|
|
||||||
Thread.sleep(3000);
|
|
||||||
System.out.println("Interrupting");
|
|
||||||
writer.write(3);
|
|
||||||
writer.println();
|
|
||||||
writer.flush();
|
|
||||||
System.out.println("Killing");
|
|
||||||
writer.println("kill");
|
|
||||||
writer.flush();
|
|
||||||
writer.println("y");
|
|
||||||
writer.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
Msg.info(this, "Testing");
|
||||||
public void testGdbInterruptConPty() throws Exception {
|
|
||||||
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
|
||||||
PtyParent parent = pty.getParent();
|
|
||||||
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
|
||||||
//BufferedReader reader = loggingReader(parent.getInputStream());
|
|
||||||
PtySession gdb =
|
|
||||||
pty.getChild().session(new String[] { "C:\\msys64\\mingw64\\bin\\gdb.exe" }, null);
|
|
||||||
|
|
||||||
pump(parent.getInputStream(), System.err);
|
|
||||||
|
|
||||||
System.out.println("Testing");
|
|
||||||
writer.println("echo test");
|
writer.println("echo test");
|
||||||
writer.println("set new-console on");
|
writer.println("set new-console on");
|
||||||
System.out.println("Launching notepad");
|
Msg.info(this, "Launching notepad");
|
||||||
writer.println("file C:\\\\Windows\\\\notepad.exe");
|
writer.println("file %s".formatted(NOTEPAD.replace("\\", "\\\\")));
|
||||||
writer.println("run");
|
writer.println("run");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
System.out.println("Waiting");
|
Msg.info(this, "Waiting");
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
System.out.println("Interrupting");
|
Msg.info(this, "Interrupting");
|
||||||
writer.write(3);
|
writer.write(3);
|
||||||
writer.println();
|
writer.println();
|
||||||
writer.flush();
|
writer.flush();
|
||||||
System.out.println("Killing");
|
Thread.sleep(1000);
|
||||||
|
Msg.info(this, "Killing");
|
||||||
writer.println("kill");
|
writer.println("kill");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.println("y");
|
writer.println("y");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
writer.println("quit");
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
Thread.sleep(100000);
|
terminated = gdb.waitFor(JOIN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||||
|
assertTrue("Gdb did not terminate", terminated);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (gdb != null && !terminated) {
|
||||||
|
for (ProcessHandle child : gdb.descendants().toList()) {
|
||||||
|
Msg.info(this, "Killing descendant process: %d".formatted(child.pid()));
|
||||||
|
child.destroyForcibly();
|
||||||
|
}
|
||||||
|
Msg.info(this, "Killing gdb: %d".formatted(gdb.pid()));
|
||||||
|
gdb.destroyForcibly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STRANGENESS: This test will fail if Eclipse was started from git-bash on Windows. I can only
|
||||||
|
* guess this is because of some strange interaction between ConPty (under test here) and the
|
||||||
|
* WinPty hack that git-bash still uses? Specifically, the interrupt (char 3) is not causing the
|
||||||
|
* signal to actually get sent to gdb. I haven't the slightest idea where it goes instead, if
|
||||||
|
* anywhere.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
* 'tis a test
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGdbInterruptConPty() throws Exception {
|
||||||
|
PtySession gdb = null;
|
||||||
|
try (Pty pty = ConPtyFactory.INSTANCE.openpty()) {
|
||||||
|
PtyParent parent = pty.getParent();
|
||||||
|
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
||||||
|
gdb = pty.getChild().session(new String[] { GDB }, null);
|
||||||
|
|
||||||
|
pump(parent.getInputStream(), System.err);
|
||||||
|
|
||||||
|
Msg.info(this, "Testing");
|
||||||
|
writer.println("echo test");
|
||||||
|
writer.println("set new-console on");
|
||||||
|
Msg.info(this, "Launching notepad");
|
||||||
|
writer.println("file %s".formatted(NOTEPAD.replace("\\", "\\\\")));
|
||||||
|
writer.println("run");
|
||||||
|
writer.flush();
|
||||||
|
Msg.info(this, "Waiting");
|
||||||
|
Thread.sleep(3000);
|
||||||
|
|
||||||
|
Msg.info(this, "Interrupting");
|
||||||
|
writer.write(3);
|
||||||
|
writer.println();
|
||||||
|
writer.flush();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
Msg.info(this, "Killing");
|
||||||
|
writer.println("kill");
|
||||||
|
writer.flush();
|
||||||
|
writer.println("y");
|
||||||
|
writer.flush();
|
||||||
|
writer.println("quit");
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
|
gdb.waitExited(JOIN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ProcessHandle handle = gdb.handle();
|
||||||
|
if (handle != null) {
|
||||||
|
for (ProcessHandle child : handle.descendants().toList()) {
|
||||||
|
Msg.info(this, "Killing descendant process: %d".formatted(child.pid()));
|
||||||
|
child.destroyForcibly();
|
||||||
|
}
|
||||||
|
Msg.info(this, "Killing gdb: %d".formatted(handle.pid()));
|
||||||
|
gdb.destroyForcibly();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +269,7 @@ public class ConPtyTest extends AbstractPtyTest {
|
|||||||
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
PrintWriter writer = new PrintWriter(parent.getOutputStream());
|
||||||
//BufferedReader reader = loggingReader(parent.getInputStream());
|
//BufferedReader reader = loggingReader(parent.getInputStream());
|
||||||
PtySession gdb = pty.getChild()
|
PtySession gdb = pty.getChild()
|
||||||
.session(new String[] { "C:\\msys64\\mingw64\\bin\\gdb.exe", "-i", "mi2" },
|
.session(new String[] { GDB, "-i", "mi2" }, null);
|
||||||
null);
|
|
||||||
|
|
||||||
InputStream inputStream = parent.getInputStream();
|
InputStream inputStream = parent.getInputStream();
|
||||||
inputStream = new AnsiBufferedInputStream(inputStream);
|
inputStream = new AnsiBufferedInputStream(inputStream);
|
||||||
@@ -218,8 +279,7 @@ public class ConPtyTest extends AbstractPtyTest {
|
|||||||
writer.println("-interpreter-exec console \"quit\"");
|
writer.println("-interpreter-exec console \"quit\"");
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
||||||
gdb.waitExited();
|
gdb.waitExited(JOIN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||||
//System.out.println("Exited");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -224,6 +224,11 @@ public class ScriptTraceRmiLaunchOfferTest extends AbstractGhidraHeadedDebuggerT
|
|||||||
public String description() {
|
public String description() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProcessHandle handle() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record MockPty() implements Pty {
|
record MockPty() implements Pty {
|
||||||
|
|||||||
Reference in New Issue
Block a user