177 lines
5.9 KiB
Java
177 lines
5.9 KiB
Java
/* ###
|
|
* 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.
|
|
*/
|
|
//Thoroughly test the assembler by attempting to assemble and match EVERY instruction code unit.
|
|
//NOTE: I do not de-duplicate, since the address of the instruction may affect the output.
|
|
//@category Assembly
|
|
|
|
import java.awt.Color;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.PrintStream;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
|
|
import javax.swing.ImageIcon;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import ghidra.app.plugin.assembler.*;
|
|
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
|
import ghidra.app.plugin.processors.sleigh.SleighDebugLogger;
|
|
import ghidra.app.plugin.processors.sleigh.SleighDebugLogger.SleighDebugMode;
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.app.util.PseudoInstruction;
|
|
import ghidra.program.model.address.*;
|
|
import ghidra.program.model.lang.*;
|
|
import ghidra.program.model.listing.BookmarkManager;
|
|
import ghidra.program.model.listing.Instruction;
|
|
import ghidra.program.model.mem.ByteMemBufferImpl;
|
|
import ghidra.program.model.mem.MemBuffer;
|
|
import ghidra.util.NumericUtilities;
|
|
import resources.ResourceManager;
|
|
|
|
public class AssemblyThrasherDevScript extends GhidraScript {
|
|
public static final String BOOKMARK_FAIL = "AssemblyFailure";
|
|
public static final String BOOKMARK_PASS = "AssemblySuccess";
|
|
|
|
class Accept extends RuntimeException {
|
|
// custom exception
|
|
}
|
|
|
|
class AllMatchByTextSelector extends AssemblySelector {
|
|
private Instruction orig;
|
|
@SuppressWarnings("unused")
|
|
private Address addr;
|
|
private String text;
|
|
|
|
public void setExpected(Instruction ins) {
|
|
this.orig = ins;
|
|
this.addr = ins.getAddress();
|
|
this.text = ins.toString().trim();
|
|
}
|
|
|
|
@Override
|
|
public AssemblyResolvedConstructor select(AssemblyResolutionResults rr,
|
|
AssemblyPatternBlock ctx) throws AssemblySemanticException {
|
|
StringBuilder sb = new StringBuilder();
|
|
boolean gotOne = false;
|
|
boolean failedOne = false;
|
|
for (AssemblyResolution ar : rr) {
|
|
if (ar.isError()) {
|
|
continue;
|
|
}
|
|
AssemblyResolvedConstructor can = (AssemblyResolvedConstructor) ar;
|
|
if (can.getContext().combine(ctx) == null) {
|
|
continue;
|
|
}
|
|
for (byte[] ins : can.possibleInsVals(ctx)) {
|
|
String cantext = disassemble(orig, ins).toString().trim();
|
|
if (cantext.equals(text)) {
|
|
gotOne = true;
|
|
}
|
|
else {
|
|
sb.append("Mismatch: " + NumericUtilities.convertBytesToString(ins) +
|
|
" => " + cantext + ". ");
|
|
failedOne = true;
|
|
}
|
|
}
|
|
}
|
|
if (!gotOne) {
|
|
throw new AssemblySemanticException("No match found");
|
|
}
|
|
if (failedOne) {
|
|
throw new AssemblySemanticException(sb.toString());
|
|
}
|
|
// Don't return, to avoid modifying the binary.
|
|
throw new Accept();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void run() throws Exception {
|
|
clearBackgroundColor(currentProgram.getMemory().getAllInitializedAddressSet());
|
|
|
|
BookmarkManager bm = currentProgram.getBookmarkManager();
|
|
ImageIcon myIcon = ResourceManager.loadImage("images/warning.png");
|
|
bm.defineType(BOOKMARK_FAIL, myIcon, new Color(255, 255, 0), 0);
|
|
bm.removeBookmarks(BOOKMARK_FAIL);
|
|
|
|
monitor.setMessage("Constructing Assembler");
|
|
AllMatchByTextSelector checker = new AllMatchByTextSelector();
|
|
Assembler asm = Assemblers.getAssembler(currentProgram, checker);
|
|
Set<String> done = new HashSet<>();
|
|
AddressSet uniques = createAddressSet();
|
|
for (Instruction ins : currentProgram.getListing().getInstructions(currentAddress, true)) {
|
|
if (monitor.isCancelled()) {
|
|
return;
|
|
}
|
|
|
|
SleighDebugLogger debug =
|
|
new SleighDebugLogger(currentProgram, ins.getAddress(), SleighDebugMode.MASKS_ONLY);
|
|
String linenos = StringUtils.join(debug.getConstructorLineNumbers(), " ");
|
|
monitor.setMessage("Assembling " + ins.getAddress());
|
|
if (!done.add(linenos)) {
|
|
continue;
|
|
}
|
|
uniques.add(ins.getAddress());
|
|
checker.setExpected(ins);
|
|
try {
|
|
asm.assemble(ins.getAddress(), ins.toString());
|
|
}
|
|
catch (Accept e) {
|
|
bm.setBookmark(ins.getAddress(), BOOKMARK_PASS, "Assembly Succeeded",
|
|
e.getMessage());
|
|
}
|
|
catch (AssemblySemanticException e) {
|
|
bm.setBookmark(ins.getAddress(), BOOKMARK_FAIL, "Semantic Error", e.getMessage());
|
|
}
|
|
catch (AssemblySyntaxException e) {
|
|
bm.setBookmark(ins.getAddress(), BOOKMARK_FAIL, "Syntax Error", e.getMessage());
|
|
}
|
|
catch (UnsupportedOperationException e) {
|
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
|
PrintStream p = new PrintStream(buf);
|
|
e.printStackTrace(p);
|
|
bm.setBookmark(ins.getAddress(), BOOKMARK_FAIL, "Unfinished",
|
|
new String(buf.toByteArray()));
|
|
}
|
|
catch (Exception e) {
|
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
|
PrintStream p = new PrintStream(buf);
|
|
e.printStackTrace(p);
|
|
bm.setBookmark(ins.getAddress(), BOOKMARK_FAIL, "Severe Error",
|
|
new String(buf.toByteArray()));
|
|
}
|
|
}
|
|
|
|
println("Unique instructions by constructor: " + done.size());
|
|
setBackgroundColor(uniques, new Color(255, 128, 128));
|
|
}
|
|
|
|
protected PseudoInstruction disassemble(Instruction orig, byte[] ins) {
|
|
try {
|
|
Address at = orig.getAddress();
|
|
Language language = currentProgram.getLanguage();
|
|
MemBuffer buf = new ByteMemBufferImpl(at, ins, language.isBigEndian());
|
|
InstructionPrototype ip = language.parse(buf, orig, false);
|
|
return new PseudoInstruction(at, ip, buf, orig);
|
|
}
|
|
catch (InsufficientBytesException | UnknownInstructionException
|
|
| AddressOverflowException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
}
|