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
|
||||
**/*.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]
|
||||
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
|
||||
mod elf;
|
||||
mod util;
|
||||
mod patcher;
|
||||
|
||||
|
||||
fn main() {
|
||||
// Collect our execution args
|
||||
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
|
||||
if &args.len() < &2 {
|
||||
// No file given, terminate
|
||||
println!("[Error] Please provide a filepath to open");
|
||||
util::print_help();
|
||||
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];
|
||||
|
||||
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 {
|
||||
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.
|
||||
|
||||
use std::mem;
|
||||
use std::io::{self, BufReader, BufRead};
|
||||
use std::fs::File;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use crate::elf::{self, EndianType, ArchitectureType};
|
||||
|
||||
|
|
@ -328,3 +331,26 @@ pub fn pp_program_header(header: &elf::ProgramHeader, number: i32, ph_type: &Str
|
|||
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