Add support for binary patching
This commit is contained in:
parent
b2e4ef8522
commit
8829e2803f
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -8,3 +8,17 @@ Cargo.lock
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|
||||||
|
|
||||||
|
# Remove Ghidra related testing files
|
||||||
|
/testing/ghidra
|
||||||
|
|
||||||
|
# Remove .vscode directory
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
*.log
|
||||||
|
|
@ -8,3 +8,4 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced-x86 = "1.18.0"
|
iced-x86 = "1.18.0"
|
||||||
|
clap = "4.2.1"
|
||||||
37
notes/hello_ghidra_out.txt
Normal file
37
notes/hello_ghidra_out.txt
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Project File Name: hello
|
||||||
|
Last Modified: Mon Apr 10 03:28:12 CDT 2023
|
||||||
|
Readonly: false
|
||||||
|
Program Name: hello
|
||||||
|
Language ID: x86:LE:64:default (2.13)
|
||||||
|
Compiler ID: gcc
|
||||||
|
Processor: x86
|
||||||
|
Endian: Little
|
||||||
|
Address Size: 64
|
||||||
|
Minimum Address: 00100000
|
||||||
|
Maximum Address: _elfSectionHeaders::000007bf
|
||||||
|
# of Bytes: 6481
|
||||||
|
# of Memory Blocks: 33
|
||||||
|
# of Instructions: 8
|
||||||
|
# of Defined Data: 105
|
||||||
|
# of Functions: 14
|
||||||
|
# of Symbols: 47
|
||||||
|
# of Data Types: 32
|
||||||
|
# of Data Type Categories: 2
|
||||||
|
Created With Ghidra Version: 10.2.3
|
||||||
|
Date Created: Mon Apr 10 03:28:12 CDT 2023
|
||||||
|
ELF File Type: shared object
|
||||||
|
ELF Original Image Base: 0x0
|
||||||
|
ELF Prelinked: false
|
||||||
|
ELF Required Library [ 0]: libc.so.6
|
||||||
|
ELF Source File [ 0]: Scrt1.o
|
||||||
|
ELF Source File [ 1]: crtstuff.c
|
||||||
|
ELF Source File [ 2]: hello.c
|
||||||
|
ELF Source File [ 3]: crtstuff.c
|
||||||
|
ELF Source File [ 4]:
|
||||||
|
Executable Format: Executable and Linking Format (ELF)
|
||||||
|
Executable Location: /media/garrett/Storage/Projects/chisel/testing/hello
|
||||||
|
Executable MD5: 45ddd8df3ccde5aa5d143ae0d00e542d
|
||||||
|
Executable SHA256: d448eaf9e134d840c4c3c87a9320060a763d3fac0e172818b87b7eda8e661b9a
|
||||||
|
FSRL: file:///media/garrett/Storage/Projects/chisel/testing/hello?MD5=45ddd8df3ccde5aa5d143ae0d00e542d
|
||||||
|
Preferred Root Namespace Category:
|
||||||
|
Relocatable: true
|
||||||
34
src/main.rs
34
src/main.rs
|
|
@ -14,19 +14,39 @@ use std::process::exit;
|
||||||
// Import modules
|
// Import modules
|
||||||
mod elf;
|
mod elf;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod patcher;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Collect our execution args
|
// Collect our execution args
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let mut patch_mode: bool = false;
|
||||||
|
let mut patch_file_path: &String = &"".to_string();
|
||||||
|
|
||||||
// Grab our filepath from our options
|
// Grab our filepath from our options
|
||||||
if &args.len() < &2 {
|
if &args.len() < &2 {
|
||||||
// No file given, terminate
|
// No file given, terminate
|
||||||
println!("[Error] Please provide a filepath to open");
|
util::print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the arguments we have include the patching flag and file
|
||||||
|
if &args.len() > &2 {
|
||||||
|
if &args[2] == "-p" {
|
||||||
|
if &args.len() >= &4 {
|
||||||
|
patch_mode = true;
|
||||||
|
patch_file_path = &args[3];
|
||||||
|
} else {
|
||||||
|
util::print_help();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// More than 1 arg but no patching flag given, terminate
|
||||||
|
util::print_help();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let file_path: &String = &args[1];
|
let file_path: &String = &args[1];
|
||||||
|
|
||||||
if path::Path::new(file_path).exists() {
|
if path::Path::new(file_path).exists() {
|
||||||
|
|
@ -200,6 +220,18 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if patch_mode {
|
||||||
|
|
||||||
|
println!("\n==== Applying Patch To Binary ====\n");
|
||||||
|
|
||||||
|
patcher::patch_binary(
|
||||||
|
bytes,
|
||||||
|
&patch_file_path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
println!("[Error] Could not find magic number, is this an ELF executable?")
|
println!("[Error] Could not find magic number, is this an ELF executable?")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
95
src/patcher.rs
Normal file
95
src/patcher.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
// patcher.rs
|
||||||
|
// Author: Garrett Dickinson
|
||||||
|
// Created: 04/06/2023
|
||||||
|
// Description: Houses binary rewriting and patching functionality for chisel.
|
||||||
|
|
||||||
|
use std::path;
|
||||||
|
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn patch_binary(binary_contents: &Vec<u8>, patch_file_path: &String) {
|
||||||
|
parse_patch_file(patch_file_path);
|
||||||
|
|
||||||
|
// If valid
|
||||||
|
// Apply patch
|
||||||
|
// else
|
||||||
|
// err
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_patch_file(patch_path: &String) {
|
||||||
|
// Load the file from patch_binary() arg
|
||||||
|
// Iterate through file, if line starts with # ignore
|
||||||
|
// Otherwise, parse as such
|
||||||
|
// [ADDRESS] [SPACE] [HEX],[HEX],[HEX],....
|
||||||
|
// [ADDRESS] [SPACE] [STRING]
|
||||||
|
|
||||||
|
if path::Path::new(patch_path).exists() && patch_path.ends_with(".patch") {
|
||||||
|
println!("Patch file exists, reading '{}'...", patch_path);
|
||||||
|
|
||||||
|
let contents = util::read_lines(patch_path.to_string());
|
||||||
|
|
||||||
|
for line in contents {
|
||||||
|
let unwrapped = line.unwrap();
|
||||||
|
if unwrapped.trim().starts_with("#") {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let mut statement = unwrapped.split(":");
|
||||||
|
let address: i32 = statement.next().unwrap().trim().parse::<i32>().unwrap();
|
||||||
|
let data: &str = statement.next().unwrap().trim();
|
||||||
|
|
||||||
|
if !data.is_empty() {
|
||||||
|
if data.contains("\"") {
|
||||||
|
// Value is a string literal
|
||||||
|
let cleaned = data.replace("\"", "");
|
||||||
|
let bytes = cleaned.as_bytes();
|
||||||
|
|
||||||
|
print!("{}: ", address);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
print!("{} ", bytes[i]);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Data is comma seperated list or a single value
|
||||||
|
let byte_str: String = data.replace(",", "");
|
||||||
|
let mut bytes: Vec<u8> = util::decode_hex(&byte_str).unwrap();
|
||||||
|
|
||||||
|
print!("{}: ", address);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
print!("{} ", bytes[i]);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[Error] Patch file '{}' is invalid or cannot be read, exiting...", patch_path);
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn apply_patch() {
|
||||||
|
// Iterate through parsed patch information
|
||||||
|
// Find the address at start of line, then apply following data to section
|
||||||
|
// Make sure within size limits of data, otherwise boundary problems
|
||||||
|
|
||||||
|
// Strings -> Iterate through characters specifically and parse them as individual ASCII chars
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
26
src/util.rs
26
src/util.rs
|
|
@ -5,6 +5,9 @@
|
||||||
// functions.
|
// functions.
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::io::{self, BufReader, BufRead};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
use crate::elf::{self, EndianType, ArchitectureType};
|
use crate::elf::{self, EndianType, ArchitectureType};
|
||||||
|
|
||||||
|
|
@ -328,3 +331,26 @@ pub fn pp_program_header(header: &elf::ProgramHeader, number: i32, ph_type: &Str
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn print_help() {
|
||||||
|
print!("Usage: chisel [EXECUTABLE] [-p] [PATCH_FILE]\n\n \
|
||||||
|
Options:\n \
|
||||||
|
\t-p\t\tToggle binary patching mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn read_lines(filename: String) -> io::Lines<BufReader<File>> {
|
||||||
|
// Open the file in read-only mode.
|
||||||
|
let file = File::open(filename).unwrap();
|
||||||
|
// Read the file line by line, and return an iterator of the lines of the file.
|
||||||
|
return io::BufReader::new(file).lines();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Borrowed from the following Stack Overflow post
|
||||||
|
// https://stackoverflow.com/questions/52987181/how-can-i-convert-a-hex-string-to-a-u8-slice
|
||||||
|
pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
|
||||||
|
(0..s.len())
|
||||||
|
.step_by(2)
|
||||||
|
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
3
testing/patches/hello.patch
Normal file
3
testing/patches/hello.patch
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
2002 : "Hello, World!"
|
||||||
|
20041 : DE,AD,BE,EF
|
||||||
|
45620 : 00,01,02,03
|
||||||
Loading…
Reference in New Issue
Block a user