GP-24_emteere fixing disassembly during emulation

This commit is contained in:
emteere 2020-06-30 16:56:33 -04:00
parent b3b7bab4ca
commit 27f9b750db
4 changed files with 403 additions and 54 deletions

View file

@ -0,0 +1,282 @@
/* ###
* IP: GHIDRA
*
* 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 ghidra.program.model.mem;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.task.TaskMonitorAdapter;
public class WrappedMemoryBufferTest extends AbstractGhidraHeadedIntegrationTest {
private Program program;
private MemBuffer memBuf;
public WrappedMemoryBufferTest() {
super();
}
private void loadProgram(String name) throws Exception {
ProgramBuilder builder = new ProgramBuilder(name, ProgramBuilder._TOY);
builder.createMemory("ram", "0x0", 100000);
program = builder.getProgram();
memBuf = new MemoryBufferImpl(program.getMemory(), program.getMinAddress());
memBuf = new WrappedMemBuffer(memBuf, 20, 0);
}
@Test
public void testGetBytes() throws Exception {
loadProgram("notepad");
Address minAddr = program.getMinAddress();
Address maxAddr = program.getMaxAddress();
setBytes(minAddr, new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 });
setBytes(maxAddr.subtract(7), new byte[] { 11, 12, 13, 14, 15, 16, 17, 18 });
memBuf = new MemoryBufferImpl(program.getMemory(), minAddr);
memBuf = new WrappedMemBuffer(memBuf, 5, 0);
byte[] bytes = new byte[4];
assertEquals(4, memBuf.getBytes(bytes, 0));
Assert.assertArrayEquals("Unexpected bytes read from memBuf", new byte[] { 1, 2, 3, 4 },
bytes);
assertEquals(4, memBuf.getBytes(bytes, 4));
Assert.assertArrayEquals("Unexpected bytes read from memBuf", new byte[] { 5, 6, 7, 8 },
bytes);
Arrays.fill(bytes, (byte) 0);
assertEquals(2, memBuf.getBytes(bytes, 99998));
Assert.assertArrayEquals("Unexpected bytes read from memBuf", new byte[] { 17, 18, 0, 0 },
bytes);
assertEquals(4, memBuf.getBytes(bytes, 99996));
Assert.assertArrayEquals("Unexpected bytes read from memBuf", new byte[] { 15, 16, 17, 18 },
bytes);
assertEquals(4, memBuf.getBytes(bytes, 99995));
Assert.assertArrayEquals("Unexpected bytes read from memBuf", new byte[] { 14, 15, 16, 17 },
bytes);
Arrays.fill(bytes, (byte) 0);
assertEquals(0, memBuf.getBytes(bytes, 100000));
Assert.assertArrayEquals("Unexpected bytes read from memBuf", new byte[] { 0, 0, 0, 0 },
bytes);
}
private void setBytes(Address addr, byte[] bytes) throws MemoryAccessException {
Memory mem = program.getMemory();
int txId = program.startTransaction("Test");
try {
mem.setBytes(addr, bytes);
}
finally {
program.endTransaction(txId, true);
}
}
@Test
public void testSetPosition() throws Exception {
loadProgram("notepad");
Memory mem = program.getMemory();
Address addr = program.getMinAddress();
memBuf = new MemoryBufferImpl(mem, program.getMinAddress());
WrappedMemBuffer wrapBuf = new WrappedMemBuffer(memBuf, 5, 0);
for (int i = 0; i < 5000; i++) {
assertEquals(mem.getByte(addr), wrapBuf.getByte(0));
assertEquals(mem.getByte(addr.add(1)), wrapBuf.getByte(1));
assertEquals(mem.getByte(addr.add(2)), wrapBuf.getByte(2));
assertEquals(mem.getByte(addr.add(3)), wrapBuf.getByte(3));
addr = addr.add(5);
int offset = (int) addr.subtract(program.getMinAddress());
wrapBuf.setBaseOffset(offset);
}
addr = program.getMinAddress();
memBuf = new MemoryBufferImpl(mem, program.getMinAddress());
wrapBuf = new WrappedMemBuffer(memBuf, 5, 0);
for (int i = 0; i < 500; i++) {
assertEquals(mem.getByte(addr), memBuf.getByte(0));
assertEquals(mem.getByte(addr.add(1)), memBuf.getByte(1));
assertEquals(mem.getByte(addr.add(2)), memBuf.getByte(2));
assertEquals(mem.getByte(addr.add(3)), memBuf.getByte(3));
addr = addr.add(50);
int offset = (int) addr.subtract(program.getMinAddress());
wrapBuf.setBaseOffset(offset);
}
addr = program.getMinAddress();
memBuf = new MemoryBufferImpl(mem, program.getMinAddress());
wrapBuf = new WrappedMemBuffer(memBuf, 5, 0);
for (int i = 0; i < 10; i++) {
assertEquals(mem.getByte(addr), wrapBuf.getByte(0));
assertEquals(mem.getByte(addr.add(1)), wrapBuf.getByte(1));
assertEquals(mem.getByte(addr.add(2)), wrapBuf.getByte(2));
assertEquals(mem.getByte(addr.add(3)), wrapBuf.getByte(3));
addr = addr.add(2000);
int offset = (int) addr.subtract(program.getMinAddress());
wrapBuf.setBaseOffset(offset);
}
}
@Test
public void testProgram20bit() throws Exception {
program =
createDefaultProgram(testName.getMethodName(), ProgramBuilder._X86_16_REAL_MODE, this);
Address start = program.getAddressFactory().getAddress("0000:0000");
InputStream is = new InputStream() {
private int pos = 0;
@Override
public int read() throws IOException {
if (pos < 0x10000) {
++pos;
return 0xaa;
}
if (pos < 0x20000) {
++pos;
return 0xbb;
}
if (pos < 0x30000) {
++pos;
return 0xcc;
}
return 0;
}
};
int id = program.startTransaction("add block");
try {
program.getMemory().createInitializedBlock(".test", start, is, 0x30000,
TaskMonitorAdapter.DUMMY_MONITOR, false);
}
finally {
program.endTransaction(id, true);
}
MemoryBufferImpl buf = new MemoryBufferImpl(program.getMemory(), start);
WrappedMemBuffer wrapBuf = new WrappedMemBuffer(buf, 0);
for (int i = 0; i < 0x30000; i++) {
int b = 0xff & wrapBuf.getByte(i);
if (i < 0x10000) {
assertEquals(0xaa, b);
continue;
}
if (i < 0x20000) {
assertEquals(0xbb, b);
continue;
}
if (i < 0x30000) {
assertEquals(0xcc, b);
continue;
}
}
wrapBuf = new WrappedMemBuffer(buf, 10, 0);
for (int i = 0; i < 0x30000; i++) {
int b = 0xff & wrapBuf.getByte(i);
if (i < 0x10000) {
assertEquals(0xaa, b);
continue;
}
if (i < 0x20000) {
assertEquals(0xbb, b);
continue;
}
if (i < 0x30000) {
assertEquals(0xcc, b);
continue;
}
}
}
@Test
public void testBoundary() throws Exception {
loadProgram("notepad");
Memory mem = program.getMemory();
Address addr = program.getMaxAddress();
memBuf = new MemoryBufferImpl(mem, program.getMaxAddress());
WrappedMemBuffer wrapBuf = new WrappedMemBuffer(memBuf, 0);
assertEquals(mem.getByte(addr), wrapBuf.getByte(0));
wrapBuf.setBaseOffset(1);
try {
wrapBuf.getByte(0);
Assert.fail("Should not have been able to get byte");
}
catch (MemoryAccessException e) {
// good
}
wrapBuf = new WrappedMemBuffer(memBuf, 20, 0);
assertEquals(mem.getByte(addr), wrapBuf.getByte(0));
wrapBuf.setBaseOffset(1);
try {
wrapBuf.getByte(0);
Assert.fail("Should not have been able to get byte");
}
catch (MemoryAccessException e) {
// good
}
}
@Test
public void testBoundary1() throws Exception {
loadProgram("notepad");
Memory mem = program.getMemory();
Address addr = program.getMaxAddress();
memBuf = new MemoryBufferImpl(mem, program.getMaxAddress());
WrappedMemBuffer wrapBuf = new WrappedMemBuffer(memBuf, 0);
assertEquals(mem.getByte(addr), wrapBuf.getByte(0));
try {
wrapBuf.getByte(1);
Assert.fail("Should not have been able to get byte");
}
catch (MemoryAccessException e) {
// good
}
wrapBuf = new WrappedMemBuffer(memBuf, 20, 0);
assertEquals(mem.getByte(addr), wrapBuf.getByte(0));
try {
wrapBuf.getByte(1);
Assert.fail("Should not have been able to get byte");
}
catch (MemoryAccessException e) {
// good
}
}
}

View file

@ -920,9 +920,8 @@ public class Disassembler implements DisassemblerConflictHandler {
disassemblerContext.flowToAddress(addr);
// TODO: An overall better caching of bytes for this block could be done instead
// the previous buffering done here was not doing any buffering
MemBuffer instrMemBuffer = new DumbMemBufferImpl(blockMemBuffer.getMemory(), addr);
MemBuffer instrMemBuffer = new WrappedMemBuffer(blockMemBuffer,
(int) addr.subtract(blockMemBuffer.getAddress()));
adjustPreParseContext(instrMemBuffer);

View file

@ -15,12 +15,11 @@
*/
package ghidra.program.model.mem;
import ghidra.program.model.address.*;
import ghidra.util.GhidraBigEndianDataConverter;
import ghidra.util.GhidraLittleEndianDataConverter;
import java.math.BigInteger;
import ghidra.program.model.address.*;
import ghidra.util.GhidraDataConverter;
/**
* MemBufferImpl implements the MemBuffer interface. It buffers up N bytes
* at time, reducing the overall number of calls to Memory, greatly reducing
@ -34,8 +33,9 @@ import java.math.BigInteger;
public class MemoryBufferImpl implements MutableMemBuffer {
private final GhidraDataConverter converter;
private static final int DEFAULT_BUFSIZE = 1024;
private static final int THRESH = 8;
private Memory mem;
private Address startAddr;
@ -43,6 +43,7 @@ public class MemoryBufferImpl implements MutableMemBuffer {
private int startAddrIndex = 0;
private int minOffset = 0;
private int maxOffset = -1;
private int threshold = 0;
/**
* Construct a new MemoryBufferImpl
@ -62,6 +63,9 @@ public class MemoryBufferImpl implements MutableMemBuffer {
public MemoryBufferImpl(Memory mem, Address addr, int bufSize) {
this.mem = mem;
buffer = new byte[bufSize];
threshold = bufSize / 100; // 1/100 of buffer size
this.converter = GhidraDataConverter.getInstance(mem.isBigEndian());
setPosition(addr);
}
@ -81,7 +85,7 @@ public class MemoryBufferImpl implements MutableMemBuffer {
if (minOffset <= maxOffset) {
if (addr.getAddressSpace().equals(startAddr.getAddressSpace())) {
long diff = addr.subtract(startAddr);
if (diff >= minOffset && diff < maxOffset - THRESH) {
if (diff >= minOffset && diff < maxOffset - threshold) {
startAddr = addr;
minOffset -= (int) diff;
maxOffset -= (int) diff;
@ -155,35 +159,22 @@ public class MemoryBufferImpl implements MutableMemBuffer {
@Override
public short getShort(int offset) throws MemoryAccessException {
if (mem.isBigEndian()) {
return GhidraBigEndianDataConverter.INSTANCE.getShort(this, offset);
}
return GhidraLittleEndianDataConverter.INSTANCE.getShort(this, offset);
return converter.getShort(this, offset);
}
@Override
public int getInt(int offset) throws MemoryAccessException {
if (mem.isBigEndian()) {
return GhidraBigEndianDataConverter.INSTANCE.getInt(this, offset);
}
return GhidraLittleEndianDataConverter.INSTANCE.getInt(this, offset);
return converter.getInt(this, offset);
}
@Override
public long getLong(int offset) throws MemoryAccessException {
if (mem.isBigEndian()) {
return GhidraBigEndianDataConverter.INSTANCE.getLong(this, offset);
}
return GhidraLittleEndianDataConverter.INSTANCE.getLong(this, offset);
return converter.getLong(this, offset);
}
@Override
public BigInteger getBigInteger(int offset, int size, boolean signed)
throws MemoryAccessException {
if (mem.isBigEndian()) {
return GhidraBigEndianDataConverter.INSTANCE.getBigInteger(this, offset, size, signed);
return converter.getBigInteger(this, offset, size, signed);
}
return GhidraLittleEndianDataConverter.INSTANCE.getBigInteger(this, offset, size, signed);
}
}

View file

@ -19,13 +19,24 @@ import java.math.BigInteger;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.util.GhidraDataConverter;
public class WrappedMemBuffer implements MemBuffer {
private final GhidraDataConverter converter;
private final MemBuffer memBuffer;
private int baseOffset;
private Address address;
private static final int DEFAULT_BUFSIZE = 10;
private byte[] buffer;
private int subBufferIndex = 0;
private int minOffset = 0;
private int maxOffset = -1;
private int threshold = 0;
/**
* Construct a wrapped MemBuffer with an adjustable base offset
* @param buf memory buffer
@ -33,23 +44,60 @@ public class WrappedMemBuffer implements MemBuffer {
* @throws AddressOutOfBoundsException
*/
public WrappedMemBuffer(MemBuffer buf, int offset) throws AddressOutOfBoundsException {
this(buf, DEFAULT_BUFSIZE, offset);
}
/**
* Construct a wrapped MemBuffer with an adjustable base offset
* @param buf memory buffer
* @buffersize size of cache buffer - specify 0 for no buffering
* @param offset base offset for this buffer relative to buf's address
* @throws AddressOutOfBoundsException
*/
public WrappedMemBuffer(MemBuffer buf, int bufferSize, int offset)
throws AddressOutOfBoundsException {
this.memBuffer = buf;
this.converter = GhidraDataConverter.getInstance(buf.isBigEndian());
buffer = new byte[bufferSize];
threshold = bufferSize / 100; // 1/100 of buffer size
setBaseOffset(offset);
}
@Override
public Address getAddress() {
return address;
}
/**
* Set new base offset relative to the associated MemBuffer's address
* @param offset new base offset of this buffer
* @throws AddressOutOfBoundsException
*/
public void setBaseOffset(int offset) throws AddressOutOfBoundsException {
this.baseOffset = offset;
this.address = memBuffer.getAddress().add(baseOffset);
}
this.address = memBuffer.getAddress().add(offset);
@Override
public Address getAddress() {
return address;
// already set, changing position
if (minOffset <= maxOffset) {
long diff = offset - baseOffset;
if (diff >= minOffset && diff < (maxOffset - threshold)) {
baseOffset = offset;
minOffset -= (int) diff;
maxOffset -= (int) diff;
subBufferIndex += diff;
return;
}
}
this.baseOffset = offset;
if (buffer.length > 0) {
subBufferIndex = 0;
minOffset = 0;
maxOffset = -1;
maxOffset = memBuffer.getBytes(buffer, baseOffset) - 1;
}
}
/**
@ -70,11 +118,40 @@ public class WrappedMemBuffer implements MemBuffer {
@Override
public byte getByte(int offset) throws MemoryAccessException {
// no buffering, just get the byte
if (buffer.length <= 0) {
return memBuffer.getByte(computeOffset(offset));
}
// byte found in buffer
if ((offset >= minOffset) && (offset <= maxOffset)) {
return buffer[subBufferIndex + offset];
}
// fill the buffer
int nRead = memBuffer.getBytes(buffer, computeOffset(offset));
subBufferIndex = -offset;
minOffset = offset;
maxOffset = offset + nRead - 1;
if (nRead == 0) {
throw new MemoryAccessException();
}
return buffer[0];
}
@Override
public int getBytes(byte[] b, int offset) {
if (buffer.length > 0) {
// bytes are contained in the buffer
if (offset >= minOffset && (b.length + offset) <= maxOffset) {
System.arraycopy(buffer, subBufferIndex + offset, b, 0, b.length);
return b.length;
}
}
// grab from wrapped buffer, too many bytes, or no buffer
try {
return memBuffer.getBytes(b, computeOffset(offset));
}
@ -83,34 +160,34 @@ public class WrappedMemBuffer implements MemBuffer {
}
}
@Override
public int getInt(int offset) throws MemoryAccessException {
return memBuffer.getInt(computeOffset(offset));
}
@Override
public long getLong(int offset) throws MemoryAccessException {
return memBuffer.getLong(computeOffset(offset));
}
@Override
public BigInteger getBigInteger(int offset, int size, boolean signed)
throws MemoryAccessException {
return memBuffer.getBigInteger(computeOffset(offset), size, signed);
}
@Override
public Memory getMemory() {
return memBuffer.getMemory();
}
@Override
public short getShort(int offset) throws MemoryAccessException {
return memBuffer.getShort(computeOffset(offset));
}
@Override
public boolean isBigEndian() {
return memBuffer.isBigEndian();
}
@Override
public short getShort(int offset) throws MemoryAccessException {
return converter.getShort(this, computeOffset(offset));
}
@Override
public int getInt(int offset) throws MemoryAccessException {
return converter.getInt(this, computeOffset(offset));
}
@Override
public long getLong(int offset) throws MemoryAccessException {
return converter.getLong(this, computeOffset(offset));
}
@Override
public BigInteger getBigInteger(int offset, int size, boolean signed)
throws MemoryAccessException {
return converter.getBigInteger(this, computeOffset(offset), size, signed);
}
}