183 lines
6.0 KiB
Java
183 lines
6.0 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.
|
|
*/
|
|
//Finds undefined functions by searching for common
|
|
//byte patterns used by compilers for function entry points.
|
|
//
|
|
//Only Intel GCC, Windows, and PowerPC are currently
|
|
//handled.
|
|
//
|
|
//Please feel free to change this script and add
|
|
//different byte patterns.
|
|
//
|
|
//When the byte pattern is found, the instructions
|
|
//will be disassembled and a function will be created.
|
|
//
|
|
//Please note: this will NOT find all undefined functions!
|
|
//@category Functions
|
|
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.program.model.address.Address;
|
|
import ghidra.program.model.lang.CompilerSpecID;
|
|
import ghidra.program.model.lang.Processor;
|
|
import ghidra.program.model.listing.Data;
|
|
import ghidra.program.model.mem.MemoryAccessException;
|
|
import ghidra.program.model.mem.MemoryBlock;
|
|
import ghidra.program.model.symbol.SourceType;
|
|
import ghidra.program.model.symbol.Symbol;
|
|
|
|
public class FindUndefinedFunctionsScript extends GhidraScript {
|
|
|
|
@Override
|
|
public void run() throws Exception {
|
|
PatternMatcher[] expectedPatterns = getPatterns();
|
|
|
|
boolean doIT =
|
|
askYesNo("Find and Create Functions?", "Would you like find and create functions?");
|
|
if (!doIT) {
|
|
return;
|
|
}
|
|
|
|
for (PatternMatcher expectedPattern : expectedPatterns) {
|
|
Address address = currentProgram.getMinAddress();
|
|
while (true) {
|
|
if (monitor.isCancelled()) {
|
|
break;
|
|
}
|
|
|
|
Data nextUndefined =
|
|
currentProgram.getListing().getUndefinedDataAfter(address, monitor);
|
|
if (nextUndefined == null) {
|
|
break;
|
|
}
|
|
Address undefinedAddress = nextUndefined.getMinAddress();
|
|
|
|
MemoryBlock block = currentProgram.getMemory().getBlock(undefinedAddress);
|
|
if (!block.isExecute()) {
|
|
address = undefinedAddress;
|
|
continue;
|
|
}
|
|
|
|
if (expectedPattern.isMatch(undefinedAddress)) {
|
|
disassemble(undefinedAddress);
|
|
createFunction(undefinedAddress, null);
|
|
address = undefinedAddress.add(1);
|
|
}
|
|
else {
|
|
address = undefinedAddress;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private PatternMatcher[] getPatterns() {
|
|
if (currentProgram == null) {
|
|
return null;
|
|
}
|
|
|
|
Processor processor = currentProgram.getLanguage().getProcessor();
|
|
|
|
if (processor.equals(Processor.findOrPossiblyCreateProcessor("x86"))) {
|
|
CompilerSpecID compilerSpecID = currentProgram.getCompilerSpec().getCompilerSpecID();
|
|
if (compilerSpecID.equals(new CompilerSpecID("windows"))) {
|
|
return new PatternMatcher[] { new PatternMatcher(new byte[] { (byte) 0x55,
|
|
(byte) 0x8b, (byte) 0xec }, false), };
|
|
}
|
|
if (compilerSpecID.equals(new CompilerSpecID("gcc"))) {
|
|
return new PatternMatcher[] { new PatternMatcher(new byte[] { (byte) 0x55,
|
|
(byte) 0x89, (byte) 0xe5 }, false), };
|
|
}
|
|
}
|
|
|
|
// Endianness OK here?
|
|
if (processor.equals(Processor.findOrPossiblyCreateProcessor("PowerPC"))) {
|
|
return new PatternMatcher[] { new PatternMatcher(new byte[] { (byte) 0x7c, (byte) 0x08,
|
|
(byte) 0x02, (byte) 0xa6 }, false),//
|
|
};
|
|
}
|
|
if (processor.equals(Processor.findOrPossiblyCreateProcessor("ARM"))) {
|
|
return new PatternMatcher[] {
|
|
//new PatternMatcher(new byte[]{(byte)0x00,(byte)0x00,(byte)0x50,(byte)0xe3}, true),//only check 'cmp' at function entry
|
|
//new PatternMatcher(new byte[]{(byte)0x00,(byte)0x00,(byte)0x51,(byte)0xe3}, true),//only check 'cmp' at function entry
|
|
//new PatternMatcher(new byte[]{(byte)0x00,(byte)0x00,(byte)0x53,(byte)0xe3}, true),//only check 'cmp' at function entry
|
|
new PatternMatcher(
|
|
new byte[] { (byte) 0xf0, (byte) 0x40, (byte) 0x2d, (byte) 0xe9 }, false),//stmdb sp!{r4 r5 r6 r7 lr}
|
|
new PatternMatcher(
|
|
new byte[] { (byte) 0xb0, (byte) 0x40, (byte) 0x2d, (byte) 0xe9 }, false),//stmdb sp!{r4 r5 r7 lr}
|
|
new PatternMatcher(
|
|
new byte[] { (byte) 0x90, (byte) 0x40, (byte) 0x2d, (byte) 0xe9 }, false),//stmdb sp!{r4 r7 lr}
|
|
new PatternMatcher(
|
|
new byte[] { (byte) 0x80, (byte) 0x40, (byte) 0x2d, (byte) 0xe9 }, false),//stmdb sp!{r7 lr}
|
|
};
|
|
}
|
|
throw new RuntimeException("Unsupported language.");
|
|
}
|
|
|
|
private class PatternMatcher {
|
|
byte[] expectedBytes;
|
|
boolean requiresEntyPoint;
|
|
|
|
PatternMatcher(byte[] expectedBytes, boolean requiresEntryPoint) {
|
|
this.expectedBytes = expectedBytes;
|
|
this.requiresEntyPoint = requiresEntryPoint;
|
|
}
|
|
|
|
boolean isMatch(Address undefinedAddress) throws MemoryAccessException {
|
|
byte[] actualBytes = new byte[expectedBytes.length];
|
|
currentProgram.getMemory().getBytes(undefinedAddress, actualBytes);
|
|
|
|
if (equals(expectedBytes, actualBytes)) {
|
|
if (requiresEntyPoint) {
|
|
// return currentProgram.getSymbolTable().isExternalEntryPoint(undefinedAddress);
|
|
Symbol primarySymbol =
|
|
currentProgram.getSymbolTable().getPrimarySymbol(undefinedAddress);
|
|
return (primarySymbol != null) &&
|
|
(primarySymbol.getSource() == SourceType.IMPORTED);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean equals(byte[] expected, byte[] actual) {
|
|
if (expected.length != actual.length) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < expected.length; ++i) {
|
|
if (expected[i] != actual[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// private String toString(byte [][] bytes) {
|
|
// StringBuffer buffer = new StringBuffer();
|
|
// for (byte [] array : bytes) {
|
|
// buffer.append('[');
|
|
// for (byte b : array) {
|
|
// buffer.append(toHexString(b, true, true));
|
|
// buffer.append(',');
|
|
// }
|
|
// buffer.deleteCharAt(buffer.length()-1);
|
|
// buffer.append(']');
|
|
// buffer.append('\n');
|
|
// }
|
|
// return buffer.toString();
|
|
// }
|
|
|
|
}
|