/*
Copyright 2014 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.google.security.zynamics.binnavi.Debug.Debugger.Synchronizers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.security.zynamics.binnavi.CUtilityFunctions;
import com.google.security.zynamics.binnavi.Common.CommonTestObjects;
import com.google.security.zynamics.binnavi.Debug.Debugger.DebuggerMessageBuilder;
import com.google.security.zynamics.binnavi.Debug.Debugger.MockDebugger;
import com.google.security.zynamics.binnavi.Debug.Debugger.MockEventListener;
import com.google.security.zynamics.binnavi.Exceptions.MaybeNullException;
import com.google.security.zynamics.binnavi.Log.NaviLogger;
import com.google.security.zynamics.binnavi.debug.connection.packets.parsers.MessageParserException;
import com.google.security.zynamics.binnavi.debug.connection.packets.parsers.RegisterValuesParser;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.AttachReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.AuthenticationFailedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.BreakpointSetReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.BreakpointsRemovedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.DebuggerClosedUnexpectedlyReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.DetachReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.EchoBreakpointSetReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.EchoBreakpointsRemovedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ExceptionOccurredReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.MemoryMapReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ModuleLoadedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ModuleUnloadedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ProcessClosedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ReadMemoryReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.RegistersReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ResumeReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ResumeThreadReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.SetRegisterReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.SingleStepReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.StepBreakpointHitReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.StepBreakpointSetReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.SuspendThreadReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.TargetInformationReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.TerminateReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ThreadClosedReply;
import com.google.security.zynamics.binnavi.debug.connection.packets.replies.ThreadCreatedReply;
import com.google.security.zynamics.binnavi.debug.debugger.AbstractDebugger;
import com.google.security.zynamics.binnavi.debug.debugger.DebugExceptionWrapper;
import com.google.security.zynamics.binnavi.debug.debugger.ModuleTargetSettings;
import com.google.security.zynamics.binnavi.debug.debugger.synchronizers.DebuggerSynchronizer;
import com.google.security.zynamics.binnavi.debug.models.breakpoints.BreakpointManager;
import com.google.security.zynamics.binnavi.debug.models.breakpoints.enums.BreakpointStatus;
import com.google.security.zynamics.binnavi.debug.models.breakpoints.enums.BreakpointType;
import com.google.security.zynamics.binnavi.debug.models.processmanager.MemoryMap;
import com.google.security.zynamics.binnavi.debug.models.processmanager.MemoryModule;
import com.google.security.zynamics.binnavi.debug.models.processmanager.MemorySection;
import com.google.security.zynamics.binnavi.debug.models.processmanager.TargetProcessThread;
import com.google.security.zynamics.binnavi.debug.models.processmanager.ThreadState;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.DebuggerException;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.DebuggerOptions;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.RegisterDescription;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.RegisterValue;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.RegisterValues;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.TargetInformation;
import com.google.security.zynamics.binnavi.debug.models.targetinformation.ThreadRegisters;
import com.google.security.zynamics.binnavi.disassembly.RelocatedAddress;
import com.google.security.zynamics.zylib.disassembly.CAddress;
import com.google.security.zynamics.zylib.general.Pair;
import com.google.security.zynamics.zylib.types.lists.FilledList;
import com.google.security.zynamics.zylib.types.lists.IFilledList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.logging.Level;
/**
* Test class for functions related to debugger synchronization.
*/
@RunWith(JUnit4.class)
public final class CDebuggerSynchronizerTest {
private MockDebugger mockDebugger;
private DebuggerSynchronizer debuggerSynchronizer;
private BreakpointManager breakpointManager;
private final MockEventListener listener = new MockEventListener();
@Before
public void setUp() throws SecurityException, NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
mockDebugger = new MockDebugger(new ModuleTargetSettings(CommonTestObjects.MODULE));
mockDebugger.setAddressTranslator(CommonTestObjects.MODULE, new CAddress(0),
new CAddress(0x1000));
breakpointManager = mockDebugger.getBreakpointManager();
debuggerSynchronizer = new DebuggerSynchronizer(mockDebugger);
debuggerSynchronizer.addListener(listener);
// assign the synchronizer to the internally used one by the debugger so we can test the
// synchronizer itself
// as well as the combination of synchronizer and debugger
final Field synchronizerField = AbstractDebugger.class.getDeclaredField("synchronizer");
synchronizerField.setAccessible(true);
synchronizerField.set(mockDebugger, debuggerSynchronizer);
}
@After
public void tearDown() {
mockDebugger.close();
}
@Test
public void testAddBreakpointEcho() throws DebugExceptionWrapper {
// It is not possible to set echo breakpoint in unconnected debuggers
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.ECHO, CommonTestObjects.BP_ADDRESS_456_SET);
// Immediately try to set the breakpoint in the target process if the debugger is active
assertEquals(BreakpointStatus.BREAKPOINT_INACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.ECHO));
}
@Test
public void testAddBreakpointRegular() throws DebugExceptionWrapper {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_123_SET);
// Nothing happens to new breakpoints when the debugger is not connected
assertEquals(BreakpointStatus.BREAKPOINT_INACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_123, BreakpointType.REGULAR));
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildProcessStartReply(CommonTestObjects.MEMORY_MODULE));
// Immediately try to set the breakpoint in the target process if the debugger is active
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.REGULAR));
}
@Test
public void testAddBreakpointStep() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildProcessStartReply(CommonTestObjects.MEMORY_MODULE));
// Immediately try to set the breakpoint in the target process if the debugger is active
breakpointManager.addBreakpoints(BreakpointType.STEP, CommonTestObjects.BP_ADDRESS_456_SET);
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.STEP));
}
@Test
public void testAttachError() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new AttachReply(0, 5));
assertFalse(mockDebugger.getProcessManager().isAttached());
assertEquals("ERROR_ATTACH/5;", listener.events);
}
@Test
public void testAttachSuccess() throws DebugExceptionWrapper {
// Connect the debugger
mockDebugger.connect();
assertTrue(mockDebugger.isConnected());
debuggerSynchronizer.receivedEvent(new AttachReply(0, 0));
assertTrue(mockDebugger.getProcessManager().isAttached());
}
/**
* This test makes sure that authentication failure replies are handled correctly.
*
* This message is sent if the client can not authenticate itself as a BinNavi debug client. In
* that case BinNavi has to close the connection to the debug client and reset the internal state
* of the process.
*
* @throws DebugExceptionWrapper Thrown if something goes wrong.
*/
@Test
public void testAuthenticationFailed() throws DebugExceptionWrapper {
mockDebugger.connect();
assertTrue(mockDebugger.isConnected());
debuggerSynchronizer.receivedEvent(new AuthenticationFailedReply());
assertFalse(mockDebugger.isConnected());
}
@SuppressWarnings("unchecked")
@Test
public void testBreakpointRemoveErr() {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new BreakpointsRemovedReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 5))));
assertEquals(BreakpointStatus.BREAKPOINT_INVALID, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_123, BreakpointType.REGULAR));
assertEquals("ERROR_REMOVE_BREAKPOINTS/00001123/5;", listener.events);
}
@SuppressWarnings("unchecked")
@Test
public void testBreakpointRemoveSucc() throws DebugExceptionWrapper {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new BreakpointSetReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 0))));
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_123_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_DELETING);
debuggerSynchronizer.receivedEvent(new BreakpointsRemovedReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 0))));
assertEquals(0, breakpointManager.getNumberOfBreakpoints(BreakpointType.REGULAR));
}
@SuppressWarnings("unchecked")
@Test
public void testBreakpointSetErr() {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new BreakpointSetReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 5))));
assertEquals(BreakpointStatus.BREAKPOINT_INVALID, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_123, BreakpointType.REGULAR));
assertEquals("ERROR_SET_BREAKPOINTS/00001123/5;", listener.events);
}
@SuppressWarnings("unchecked")
@Test
public void testBreakpointSetSucc() {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new BreakpointSetReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 0))));
assertEquals(1, breakpointManager.getNumberOfBreakpoints(BreakpointType.REGULAR));
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_123, BreakpointType.REGULAR));
}
@Test
public void testDetachError() {
debuggerSynchronizer.receivedEvent(new DetachReply(0, 5));
assertFalse(mockDebugger.isConnected());
assertEquals("ERROR_DETACH/5;", listener.events);
}
@Test
public void testDetachSucc() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new DetachReply(0, 0));
assertFalse(mockDebugger.isConnected());
}
@Test
public void testDisableBreakpointRegularDisabledDebugger() {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
assertEquals(BreakpointStatus.BREAKPOINT_INACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.REGULAR));
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_DISABLED);
assertEquals("", mockDebugger.requests);
}
@SuppressWarnings("unchecked")
@Test
public void testEchoBreakpointRemoveSucc() throws DebugExceptionWrapper {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.ECHO, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new EchoBreakpointsRemovedReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 0))));
assertEquals(0, breakpointManager.getNumberOfBreakpoints(BreakpointType.ECHO));
}
@SuppressWarnings("unchecked")
@Test
public void testEchoBreakpointSetErr() throws DebugExceptionWrapper {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.ECHO, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new EchoBreakpointSetReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 5))));
assertEquals(0, breakpointManager.getNumberOfBreakpoints(BreakpointType.ECHO));
assertEquals("ERROR_SET_ECHO_BREAKPOINT/00001123/5;", listener.events);
}
@SuppressWarnings("unchecked")
@Test
public void testEchoBreakpointSetSucc() throws DebugExceptionWrapper {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.ECHO, CommonTestObjects.BP_ADDRESS_123_SET);
debuggerSynchronizer.receivedEvent(new EchoBreakpointSetReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 0))));
assertEquals(1, breakpointManager.getNumberOfBreakpoints(BreakpointType.ECHO));
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_123, BreakpointType.ECHO));
}
@Test
public void testEnabledToDisabled() throws DebugExceptionWrapper {
// Scenario:
//
// 1. User sets breakpoint (SET Message is sent to the Debug Client)
// 2. User disables breakpoint (REMOVE Message is sent to the Debug Client)
//
// At this point there are two messages on the way to the debug client which
// are guaranteed to come back in the order they were sent.
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildProcessStartReply(CommonTestObjects.MEMORY_MODULE));
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.REGULAR));
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_DISABLED);
// If the debugger is active, the breakpoint is removed from the target process
assertEquals("CONNECT;READREGS;RESUME;SET_BREAKPOINTS/00000456/REGULAR;"
+ "REMOVE_BREAKPOINTS/00000456/REGULAR;", mockDebugger.requests);
}
@Test
public void testErrorConnectionClosed() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new DebuggerClosedUnexpectedlyReply());
assertFalse(mockDebugger.isConnected());
assertEquals("DEBUGGER_CLOSED/0;", listener.events);
}
@Test
public void testHandleExceptionOccured() throws DebugExceptionWrapper, MaybeNullException {
mockDebugger.connect();
final TargetProcessThread thread = new TargetProcessThread(18, ThreadState.RUNNING);
mockDebugger.getProcessManager().addThread(thread);
debuggerSynchronizer.receivedEvent(new ExceptionOccurredReply(0,
0,
18,
5,
CommonTestObjects.BP_ADDRESS_123_RELOC,
"Test exception"));
assertEquals(thread, mockDebugger.getProcessManager().getActiveThread());
assertEquals(ThreadState.RUNNING, mockDebugger.getProcessManager().getThread(18).getState());
assertEquals(CommonTestObjects.BP_ADDRESS_123_RELOC,
mockDebugger.getProcessManager().getThread(18).getCurrentAddress());
assertEquals("CONNECT;READREGS;", mockDebugger.requests);
assertEquals("EXCEPTION_OCCURRED/5;", listener.events);
}
@Test
public void testHitBreakpoint_UnknownBreakpoint() throws MessageParserException {
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.SUSPENDED));
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildRegularBreakpointHit(CommonTestObjects.BP_ADDRESS_456_RELOC));
assertEquals(0, listener.exception);
}
@Test
public void testHitBreakpoint_UnknownThread() throws MessageParserException {
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildRegularBreakpointHit(CommonTestObjects.BP_ADDRESS_123_RELOC));
assertEquals(0, listener.exception);
}
@Test
public void testHitBreakpoint_Wellformed() throws MessageParserException {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_ACTIVE);
mockDebugger.getProcessManager().addThread(
new TargetProcessThread(CommonTestObjects.THREAD_ID, ThreadState.SUSPENDED));
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildRegularBreakpointHit(CommonTestObjects.BP_ADDRESS_456_RELOC));
assertEquals(0, listener.exception);
assertEquals(BreakpointStatus.BREAKPOINT_HIT, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.REGULAR));
}
@Test
public void testHitEchoBreakpoint() throws DebugExceptionWrapper {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.ECHO, CommonTestObjects.BP_ADDRESS_456_SET);
assertEquals(BreakpointStatus.BREAKPOINT_INACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.ECHO));
}
@Test
public void testHitEchoBreakpoint_Wellformed() throws DebugExceptionWrapper,
MessageParserException {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.ECHO, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET, BreakpointType.ECHO,
BreakpointStatus.BREAKPOINT_ACTIVE);
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.SUSPENDED));
assertEquals(1, breakpointManager.getNumberOfBreakpoints(BreakpointType.ECHO));
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.ECHO));
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildEchoBreakpointHit(CommonTestObjects.BP_ADDRESS_456_RELOC));
assertEquals(0, listener.exception);
assertEquals(1, breakpointManager.getNumberOfBreakpoints(BreakpointType.ECHO));
debuggerSynchronizer.receivedEvent(DebuggerMessageBuilder.buildEchoBreakpointRemoveSucc(
CommonTestObjects.BP_ADDRESS_456_RELOC));
assertEquals(0, breakpointManager.getNumberOfBreakpoints(BreakpointType.ECHO));
}
@Test
public void testInfoString_Malformed() {
debuggerSynchronizer.receivedEvent(new TargetInformationReply(0, 5, null));
assertEquals(0, listener.exception);
}
@Test
public void testInfoString_Wellformed() throws DebugExceptionWrapper, MaybeNullException {
// Set breakpoints while the debugger is not connected
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_123_SET);
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_DISABLED);
assertEquals(BreakpointStatus.BREAKPOINT_INACTIVE,
breakpointManager.getBreakpointStatus(BreakpointType.REGULAR, 0));
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(
DebuggerMessageBuilder.buildProcessStartReply(CommonTestObjects.MEMORY_MODULE));
assertEquals(0, listener.exception);
assertNotNull(mockDebugger.getProcessManager().getThread(CommonTestObjects.THREAD_ID));
assertEquals(ThreadState.RUNNING,
mockDebugger.getProcessManager().getThread(CommonTestObjects.THREAD_ID).getState());
debuggerSynchronizer.receivedEvent(new ThreadClosedReply(0, 0, CommonTestObjects.THREAD_ID));
try {
mockDebugger.getProcessManager().getThread(CommonTestObjects.THREAD_ID);
fail();
} catch (final MaybeNullException exception) {
CUtilityFunctions.logException(exception);
}
// On receiving an info string we request the memory map and the thread created reply triggers
// read registers
assertEquals("CONNECT;READREGS;SET_BREAKPOINTS/"
+ String.format("%08d", CommonTestObjects.THREAD_ID) + "/REGULAR;RESUME;",
mockDebugger.requests);
// Enabled breakpoints are activated
assertEquals(BreakpointStatus.BREAKPOINT_ENABLED,
breakpointManager.getBreakpointStatus(BreakpointType.REGULAR, 0));
assertEquals(BreakpointStatus.BREAKPOINT_DISABLED,
breakpointManager.getBreakpointStatus(BreakpointType.REGULAR, 1));
}
@Test
public void testMemmap() {
final IFilledList<MemorySection> sections = new FilledList<MemorySection>();
sections.add(new MemorySection(new CAddress(100), new CAddress(200)));
sections.add(new MemorySection(new CAddress(300), new CAddress(400)));
debuggerSynchronizer.receivedEvent(new MemoryMapReply(0, 0, new MemoryMap(sections)));
assertEquals(0, listener.exception);
assertEquals(2, mockDebugger.getProcessManager().getMemoryMap().getNumberOfSections());
}
@Test
public void testMemoryErr() {
debuggerSynchronizer.receivedEvent(new ReadMemoryReply(0, 5, null, null));
assertEquals(0, listener.exception);
assertEquals("ERROR_READING_MEMORY/5;", listener.events);
assertFalse(mockDebugger.getProcessManager().getMemory()
.hasData(CommonTestObjects.BP_ADDRESS_123.getAddress().getAddress().toLong(), 6));
}
@Test
public void testMemorySucc() {
debuggerSynchronizer.receivedEvent(new ReadMemoryReply(0, 0,
CommonTestObjects.BP_ADDRESS_123.getAddress().getAddress(), "Hannes".getBytes()));
assertEquals(0, listener.exception);
assertEquals("RECEIVED_MEMORY/00000123/6;", listener.events);
assertTrue(mockDebugger.getProcessManager().getMemory().hasData(0x123, 6));
}
/**
* This test makes sure that the memory module lifecycle (Module Loaded -> Module Unloaded) is
* working and that the process manager of the debugger is updated correctly.
*
* @throws DebugExceptionWrapper
*/
@Test
public void testModuleLifecycle() throws DebugExceptionWrapper {
assertTrue(mockDebugger.getProcessManager().getModules().isEmpty());
mockDebugger.connect();
mockDebugger.getProcessManager().getThreads().clear();
debuggerSynchronizer.receivedEvent(new ThreadCreatedReply(0, 0, 1000, ThreadState.RUNNING));
final MemoryModule module = new MemoryModule("hannes.dll", "C:\\hannes.dll",
new RelocatedAddress(new CAddress(0x1000000)), 1000);
debuggerSynchronizer.receivedEvent(
new ModuleLoadedReply(0, 0, module, new TargetProcessThread(1000, ThreadState.RUNNING)));
mockDebugger.getProcessManager().setTargetInformation(new TargetInformation(
5, Lists.newArrayList(new RegisterDescription("eax", 4, true),
new RegisterDescription("ebx", 4, false)), new DebuggerOptions(false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
12,
0,
new ArrayList<DebuggerException>(),
false,
false,
false)));
assertTrue(mockDebugger.getProcessManager().getModules().size() == 1);
assertTrue(mockDebugger.getProcessManager().getModules().get(0) == module);
debuggerSynchronizer.receivedEvent(new ModuleUnloadedReply(0, 0, module));
assertTrue(mockDebugger.getProcessManager().getModules().isEmpty());
}
@Test
public void testProcessClosed() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new ProcessClosedReply(0, 0));
assertFalse(mockDebugger.isConnected());
assertEquals("PROCESS_CLOSED;", listener.events);
}
@Test
public void testRegisterValues_Malformed() {
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.RUNNING));
NaviLogger.setLevel(Level.OFF);
try {
debuggerSynchronizer.receivedEvent(
new RegistersReply(0, 0, RegisterValuesParser.parse("Hannes".getBytes())));
fail();
} catch (final MessageParserException exception) {
CUtilityFunctions.logException(exception);
} finally {
NaviLogger.setLevel(Level.SEVERE);
}
}
@Test
public void testRegisterValues_UnknownTID() throws MessageParserException {
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.SUSPENDED));
debuggerSynchronizer.receivedEvent(new RegistersReply(0, 0, RegisterValuesParser.parse((
"<Registers><Thread id=\"123\"><Register name=\"EAX\" "
+ "value=\"123\" memory=\"\" /><Register name=\"EBX\" value=\"456\" memory=\"\" "
+ "/><Register name=\"EIP\" value=\"999\" memory=\"\" pc=\"true\" /></Thread>"
+ "</Registers>").getBytes())));
assertEquals(0, listener.exception);
}
@Test
public void testRegisterValues_Wellformed() throws MessageParserException, MaybeNullException {
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.RUNNING));
debuggerSynchronizer.receivedEvent(new RegistersReply(0, 0, RegisterValuesParser.parse((
"<Registers><Thread id=\"123\"><Register name=\"EAX\" "
+ "value=\"123\" memory=\"\" /><Register name=\"EBX\" value=\"456\" memory=\"\" "
+ "/><Register name=\"EIP\" value=\"999\" memory=\"\" pc=\"true\" /></Thread>"
+ "</Registers>").getBytes())));
assertEquals(0, listener.exception);
assertEquals(0x456, mockDebugger
.getProcessManager()
.getThread(123)
.getRegisterValues()
.get(1)
.getValue()
.longValue());
assertEquals(0x999,
mockDebugger.getProcessManager().getThread(123).getCurrentAddress().getAddress().toLong());
}
@SuppressWarnings("unchecked")
@Test
public void testRemoveBreakpoint_Active() throws DebugExceptionWrapper {
mockDebugger.connect();
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_ACTIVE);
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.SUSPENDED));
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_DELETING);
debuggerSynchronizer.receivedEvent(new BreakpointsRemovedReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_456_RELOC, 0))));
assertEquals(0, listener.exception);
assertEquals(0, breakpointManager.getNumberOfBreakpoints(BreakpointType.REGULAR));
}
@SuppressWarnings("unchecked")
@Test
public void testRemoveBreakpoint_Disabled() {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_DISABLED);
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.SUSPENDED));
debuggerSynchronizer.receivedEvent(new BreakpointsRemovedReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_456_RELOC, 0))));
assertEquals(0, listener.exception);
assertEquals(1, breakpointManager.getNumberOfBreakpoints(BreakpointType.REGULAR));
}
@SuppressWarnings("unchecked")
@Test
public void testRemoveBreakpointError_Invalid() {
debuggerSynchronizer.receivedEvent(new BreakpointsRemovedReply(0, 5, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_456_RELOC, 0))));
assertEquals(0, listener.exception);
assertEquals(0, breakpointManager.getNumberOfBreakpoints(BreakpointType.REGULAR));
}
@SuppressWarnings("unchecked")
@Test
public void testRemoveBreakpointError_Valid() {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_ACTIVE);
debuggerSynchronizer.receivedEvent(new BreakpointsRemovedReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_456_RELOC, 5))));
assertEquals(0, listener.exception);
assertEquals(1, breakpointManager.getNumberOfBreakpoints(BreakpointType.REGULAR));
assertEquals(BreakpointStatus.BREAKPOINT_INVALID, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.REGULAR));
}
@Test
public void testResume_UnknownTID() {
debuggerSynchronizer.receivedEvent(new ResumeReply(0, 0));
assertEquals(0, listener.exception);
}
@Test
public void testResume_Wellformed() throws MaybeNullException {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_456_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_456_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_HIT);
final TargetProcessThread thread = new TargetProcessThread(123, ThreadState.RUNNING);
mockDebugger.getProcessManager().addThread(thread);
mockDebugger.getProcessManager().setActiveThread(thread);
debuggerSynchronizer.receivedEvent(new ResumeReply(0, 0));
assertEquals(0, listener.exception);
assertNull(mockDebugger.getProcessManager().getActiveThread());
assertEquals(ThreadState.RUNNING, mockDebugger.getProcessManager().getThread(123).getState());
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_456, BreakpointType.REGULAR));
}
@Test
public void testSetRegisterErr() {
debuggerSynchronizer.receivedEvent(new SetRegisterReply(0, 5, 0, 0));
assertEquals("ERROR_SET_REGISTERS/5;", listener.events);
}
@Test
public void testSetRegisterSucc() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new SetRegisterReply(0, 0, 0, 0));
assertEquals("CONNECT;READREGS;", mockDebugger.requests);
}
@Test
public void testSingleStep_Err() {
debuggerSynchronizer.receivedEvent(new SingleStepReply(0, 5, 0,
new RelocatedAddress(CommonTestObjects.BP_ADDRESS_123.getAddress().getAddress()), null));
assertEquals("ERROR_SINGLE_STEP/5;", listener.events);
}
@Test
public void testSingleStep_Valid() throws MessageParserException, MaybeNullException {
breakpointManager.addBreakpoints(BreakpointType.REGULAR, CommonTestObjects.BP_ADDRESS_333_SET);
breakpointManager.setBreakpointStatus(CommonTestObjects.BP_ADDRESS_333_SET,
BreakpointType.REGULAR, BreakpointStatus.BREAKPOINT_HIT);
mockDebugger.getProcessManager().addThread(new TargetProcessThread(123, ThreadState.SUSPENDED));
debuggerSynchronizer.receivedEvent(new SingleStepReply(0, 0, 123,
new RelocatedAddress(new CAddress(0x999)), RegisterValuesParser.parse((
"<Registers><Thread id=\"123\">" + "<Register name=\"EAX\" value=\"123\" memory=\"\" />"
+ "<Register name=\"EBX\" value=\"456\" memory=\"\" />"
+ "<Register name=\"EIP\" value=\"999\" memory=\"\" pc=\"true\" />"
+ "</Thread></Registers>").getBytes())));
assertEquals(ThreadState.SUSPENDED, mockDebugger.getProcessManager().getThread(123).getState());
assertEquals(0x999,
mockDebugger.getProcessManager().getThread(123).getCurrentAddress().getAddress().toLong());
assertEquals(0x456, mockDebugger
.getProcessManager()
.getThread(123)
.getRegisterValues()
.get(1)
.getValue()
.longValue());
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, breakpointManager.getBreakpointStatus(
CommonTestObjects.BP_ADDRESS_333, BreakpointType.REGULAR));
}
/**
* This test is used to determine whether the step breakpoint lifecycle (Set Step BP -> Hit Step
* BP -> Remove Step BP) works correctly.
*
* @throws DebugExceptionWrapper Thrown if something goes wrong.
*/
@SuppressWarnings("unchecked")
@Test
public void testStepBreakpointLifecycle() throws DebugExceptionWrapper {
mockDebugger.connect();
final TargetProcessThread thread = new TargetProcessThread(0, ThreadState.RUNNING);
mockDebugger.getProcessManager().addThread(thread);
mockDebugger.getBreakpointManager().addBreakpoints(BreakpointType.STEP,
CommonTestObjects.BP_ADDRESS_123_SET);
mockDebugger.getBreakpointManager().addBreakpoints(BreakpointType.STEP,
CommonTestObjects.BP_ADDRESS_456_SET);
debuggerSynchronizer.receivedEvent(new StepBreakpointSetReply(0, 0, Lists.newArrayList(
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_123_RELOC, 0),
new Pair<RelocatedAddress, Integer>(CommonTestObjects.BP_ADDRESS_456_RELOC, 0))));
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, mockDebugger.getBreakpointManager()
.getBreakpointStatus(CommonTestObjects.BP_ADDRESS_123, BreakpointType.STEP));
assertEquals(BreakpointStatus.BREAKPOINT_ACTIVE, mockDebugger.getBreakpointManager()
.getBreakpointStatus(CommonTestObjects.BP_ADDRESS_456, BreakpointType.STEP));
final RegisterValues registerValues =
new RegisterValues(Lists.<ThreadRegisters>newArrayList(new ThreadRegisters(
0, Lists.newArrayList(
new RegisterValue("esp", BigInteger.valueOf(0x123), new byte[0], true, false)))));
debuggerSynchronizer.receivedEvent(new StepBreakpointHitReply(0, 0, 0, registerValues));
listener.toString();
assertTrue(
Iterables.isEmpty(mockDebugger.getBreakpointManager().getBreakpoints(BreakpointType.STEP)));
assertEquals(thread, mockDebugger.getProcessManager().getActiveThread());
assertEquals(0x123, thread.getCurrentAddress().getAddress().toLong());
}
@Test
public void testTerminate() throws DebugExceptionWrapper {
mockDebugger.connect();
assertTrue(mockDebugger.isConnected());
debuggerSynchronizer.receivedEvent(new TerminateReply(0, 0));
assertFalse(mockDebugger.isConnected());
}
@Test
public void testThreadClosed() throws DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new ThreadCreatedReply(0, 0, 18, ThreadState.RUNNING));
assertEquals(1, mockDebugger.getProcessManager().getThreads().size());
debuggerSynchronizer.receivedEvent(new ThreadClosedReply(0, 0, 18));
assertEquals(0, mockDebugger.getProcessManager().getThreads().size());
}
@Test
public void testThreadCreated() throws MaybeNullException, DebugExceptionWrapper {
mockDebugger.connect();
debuggerSynchronizer.receivedEvent(new ThreadCreatedReply(0, 0, 18, ThreadState.RUNNING));
debuggerSynchronizer.receivedEvent(new ThreadCreatedReply(0, 0, 19, ThreadState.SUSPENDED));
assertEquals(ThreadState.RUNNING, mockDebugger.getProcessManager().getThread(18).getState());
assertEquals(ThreadState.SUSPENDED, mockDebugger.getProcessManager().getThread(19).getState());
}
/**
* This test is used to test the thread lifecycle of suspending and resuming threads.
*
* @throws DebugExceptionWrapper Thrown if something goes wrong.
*/
@Test
public void testThreadLifecycle() throws DebugExceptionWrapper {
mockDebugger.connect();
final TargetProcessThread thread = new TargetProcessThread(123, ThreadState.RUNNING);
mockDebugger.getProcessManager().addThread(thread);
debuggerSynchronizer.receivedEvent(new SuspendThreadReply(0, 0, 123));
assertEquals(ThreadState.SUSPENDED, thread.getState());
debuggerSynchronizer.receivedEvent(new ResumeThreadReply(0, 0, 123));
assertEquals(ThreadState.RUNNING, thread.getState());
}
}