Add support for binary patching

This commit is contained in:
Garrett Dickinson 2023-04-10 14:13:29 -05:00
parent b2e4ef8522
commit 8829e2803f
7 changed files with 209 additions and 1 deletions

14
.gitignore vendored
View File

@ -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

View File

@ -8,3 +8,4 @@ edition = "2018"
[dependencies] [dependencies]
iced-x86 = "1.18.0" iced-x86 = "1.18.0"
clap = "4.2.1"

View 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

View File

@ -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
View 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
}

View File

@ -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()
}

View File

@ -0,0 +1,3 @@
2002 : "Hello, World!"
20041 : DE,AD,BE,EF
45620 : 00,01,02,03