mirror of
https://github.com/DMaroo/GhidRust.git
synced 2025-10-06 04:53:57 +02:00
221 lines
6.6 KiB
Java
221 lines
6.6 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.
|
|
*/
|
|
package ghidrust.analyzer;
|
|
|
|
import generic.jar.ResourceFile;
|
|
import ghidra.app.services.AbstractAnalyzer;
|
|
import ghidra.app.services.AnalysisPriority;
|
|
import ghidra.app.services.AnalyzerType;
|
|
import ghidra.app.util.importer.MessageLog;
|
|
import ghidra.program.model.address.AddressSetView;
|
|
import ghidra.program.model.listing.Program;
|
|
import ghidra.program.model.mem.MemoryBlock;
|
|
import ghidra.util.exception.CancelledException;
|
|
import ghidra.util.task.TaskMonitor;
|
|
import ghidra.feature.fid.db.FidFileManager;
|
|
import ghidra.framework.Application;
|
|
|
|
import java.io.BufferedWriter;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
|
|
public class RustStdAnalyzer extends AbstractAnalyzer {
|
|
private static final String filePath = "/home/dhruv/Education/CS4900/Work/GhidRust/tmp/logs";
|
|
private static BufferedWriter writer;
|
|
private static final byte[][] rust_artifacts = {
|
|
"run with `RUST_BACKTRACE=1` environment variable".getBytes(),
|
|
"called `Option::unwrap()` on a `None` value".getBytes(),
|
|
"called `Result::unwrap()` on an `Err` value".getBytes()
|
|
};
|
|
private static final String ENABLED_PROPERTY = "DecompilerParameterAnalyzer.enabled";
|
|
|
|
public RustStdAnalyzer() {
|
|
super("Detect Rust libstd functions",
|
|
"Detects Rust standard library functions from saved signatures and saves analysis time.\n\nProvided by GhidRust",
|
|
AnalyzerType.FUNCTION_ANALYZER);
|
|
|
|
/*
|
|
* This is just one above the priority at which the Function ID analyzer runs
|
|
* (FUNCTION_ID_ANALYSIS - 1)
|
|
* We need to run before the Function ID analyzer runs because we are populating
|
|
* the Function ID analyzer
|
|
* with our own Function ID databases.
|
|
*/
|
|
setPriority(AnalysisPriority.FUNCTION_ID_ANALYSIS.before().before());
|
|
|
|
try {
|
|
writer = new BufferedWriter(new FileWriter(filePath, true));
|
|
} catch (IOException exc) {
|
|
// pass
|
|
}
|
|
}
|
|
|
|
private static boolean contains(byte[] hay, byte[] needle) {
|
|
if (hay.length < needle.length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i <= hay.length - needle.length; i++) {
|
|
int match_len = 0;
|
|
for (int j = 0; j < needle.length; j++) {
|
|
if (needle[j] == hay[i + j]) {
|
|
match_len++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (match_len == needle.length) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static private boolean getNextChunk(InputStream stream, byte[] dest, int size) {
|
|
byte end;
|
|
|
|
try {
|
|
if (stream.available() <= 0) {
|
|
return false;
|
|
}
|
|
|
|
end = (byte) stream.read();
|
|
} catch (IOException exc) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < size - 1; i++) {
|
|
dest[i] = dest[i + 1];
|
|
}
|
|
|
|
dest[size - 1] = end;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean getDefaultEnablement(Program program) {
|
|
// Make sure the property has not been disabled
|
|
String default_enabled = System.getProperty(ENABLED_PROPERTY);
|
|
if (default_enabled != null && !Boolean.parseBoolean(default_enabled)) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Be enabled by default so that we can make sure the analysis of Rust functions
|
|
* takes place
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean canAnalyze(Program program) {
|
|
return isRustBinary(program);
|
|
}
|
|
|
|
@Override
|
|
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
|
throws CancelledException {
|
|
FidFileManager ffm = FidFileManager.getInstance();
|
|
if (ffm == null) {
|
|
return false;
|
|
}
|
|
|
|
ResourceFile data_dir;
|
|
try {
|
|
data_dir = Application.getModuleDataSubDirectory("");
|
|
} catch (IOException exc) {
|
|
log.appendException(exc);
|
|
return false;
|
|
}
|
|
|
|
ResourceFile[] libs = data_dir.listFiles();
|
|
for (ResourceFile lib : libs) {
|
|
monitor.checkCanceled();
|
|
ffm.addUserFidFile(lib.getFile(true));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void analysisEnded(Program program) {
|
|
super.analysisEnded(program);
|
|
|
|
try {
|
|
writer.close();
|
|
} catch (IOException exc) {
|
|
// pass
|
|
}
|
|
}
|
|
|
|
/* For exposing the Rust checking code */
|
|
public static boolean isRustBinary(Program program) {
|
|
/*
|
|
* Taken from
|
|
* https://github.com/mandiant/capa-rules/blob/master/compiler/rust/compiled-
|
|
* with-rust.yml
|
|
*/
|
|
|
|
/* We know that the strings would be found in the .rodata section */
|
|
MemoryBlock rodata = program.getMemory().getBlock(".rodata");
|
|
|
|
InputStream stream = rodata.getData();
|
|
|
|
int search_len = 0;
|
|
for (byte[] artifact : rust_artifacts) {
|
|
int artifact_len = artifact.length;
|
|
|
|
if (artifact_len > search_len) {
|
|
search_len = artifact_len;
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
byte[] chunk = new byte[search_len];
|
|
|
|
try {
|
|
while (stream.available() > 0 && i < chunk.length) {
|
|
chunk[i] = (byte) stream.read();
|
|
i++;
|
|
}
|
|
} catch (IOException exc) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
while (stream.available() > 0) {
|
|
if (!getNextChunk(stream, chunk, search_len)) {
|
|
break;
|
|
}
|
|
|
|
for (byte[] artifact : rust_artifacts) {
|
|
if (contains(chunk, artifact)) {
|
|
/* This is a Rust binary */
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|
|
} catch (IOException exc) {
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|