Add support for ELF code injection
This commit is contained in:
parent
8aa55bdb23
commit
ab0f273cd9
6
notes/code_injection.md
Normal file
6
notes/code_injection.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# ELF Code injection notes
|
||||||
|
|
||||||
|
- Locate PT_NOTE segments in binary
|
||||||
|
- Locate a suitable `.note.*` section for replacing code
|
||||||
|
- Modify the injection code section type from `SHT_NOTE` to `SHT_PROGBITS`
|
||||||
|
- Update relevant start, offset, and end addresses for new section
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
To Do
|
# To Do
|
||||||
|
|
||||||
Code injection
|
## Code injection
|
||||||
- Have a script with 3 functions, (main, funcA, funcB), and replace refs to funcA with funcB
|
- Have a script with 3 functions, (main, funcA, funcB), and replace refs to funcA with funcB
|
||||||
- Trampoline definitions of a function to a new modified function that gets places in memory
|
- Trampoline definitions of a function to a new modified function that gets places in memory
|
||||||
-
|
|
||||||
|
|
||||||
Injected code: 0x680
|
Injected code: 0x680
|
||||||
|
|
||||||
Call: 0x1160
|
Call: 0x1160
|
||||||
|
|
@ -121,8 +121,9 @@ pub struct FileHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProgramHeader {
|
pub struct ProgramHeader {
|
||||||
|
pub id: u16,
|
||||||
pub program_type: u32,
|
pub program_type: u32,
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
|
|
@ -134,9 +135,10 @@ pub struct ProgramHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SectionHeader {
|
pub struct SectionHeader {
|
||||||
pub name: u32,
|
pub id: u16,
|
||||||
|
pub name_idx: u32,
|
||||||
pub section_type: u32,
|
pub section_type: u32,
|
||||||
pub flags: u64,
|
pub flags: u64,
|
||||||
pub addr: u64,
|
pub addr: u64,
|
||||||
|
|
|
||||||
36
src/main.rs
36
src/main.rs
|
|
@ -20,7 +20,7 @@ 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 inject_mode: bool = false;
|
||||||
let mut patch_file_path: &String = &"".to_string();
|
let mut patch_file_path: &String = &"".to_string();
|
||||||
|
|
||||||
// Grab our filepath from our options
|
// Grab our filepath from our options
|
||||||
|
|
@ -34,7 +34,7 @@ fn main() {
|
||||||
if &args.len() > &2 {
|
if &args.len() > &2 {
|
||||||
if &args[2] == "-p" {
|
if &args[2] == "-p" {
|
||||||
if &args.len() >= &4 {
|
if &args.len() >= &4 {
|
||||||
patch_mode = true;
|
inject_mode = true;
|
||||||
patch_file_path = &args[3];
|
patch_file_path = &args[3];
|
||||||
} else {
|
} else {
|
||||||
util::print_help();
|
util::print_help();
|
||||||
|
|
@ -87,6 +87,7 @@ fn main() {
|
||||||
let shstrtab_section: elf::SectionHeader = util::build_section_header(
|
let shstrtab_section: elf::SectionHeader = util::build_section_header(
|
||||||
bytes,
|
bytes,
|
||||||
shstrtab_offset as usize,
|
shstrtab_offset as usize,
|
||||||
|
file_header.shstrndx,
|
||||||
file_header.is_x86_64
|
file_header.is_x86_64
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -104,17 +105,18 @@ fn main() {
|
||||||
let mut section_table_count: i32 = 0;
|
let mut section_table_count: i32 = 0;
|
||||||
|
|
||||||
// Iterate through number of section headers
|
// Iterate through number of section headers
|
||||||
for _ in 0..file_header.shnum {
|
for i in 0..file_header.shnum {
|
||||||
|
|
||||||
// Build section header data structure
|
// Build section header data structure
|
||||||
let section_header: elf::SectionHeader = util::build_section_header(
|
let section_header: elf::SectionHeader = util::build_section_header(
|
||||||
bytes,
|
bytes,
|
||||||
section_table_offset as usize,
|
section_table_offset as usize,
|
||||||
|
i,
|
||||||
file_header.is_x86_64
|
file_header.is_x86_64
|
||||||
);
|
);
|
||||||
|
|
||||||
// Determine the section name for each section using the shstrtab data
|
// Determine the section name for each section using the shstrtab data
|
||||||
let section_name: String = util::parse_section_name(&shstrtab_data, section_header.name as usize);
|
let section_name: String = util::parse_section_name(&shstrtab_data, section_header.name_idx as usize);
|
||||||
|
|
||||||
util::pp_section_header(§ion_header, section_table_count, §ion_name);
|
util::pp_section_header(§ion_header, section_table_count, §ion_name);
|
||||||
|
|
||||||
|
|
@ -131,19 +133,25 @@ fn main() {
|
||||||
|
|
||||||
let mut program_table_offset = file_header.phoff;
|
let mut program_table_offset = file_header.phoff;
|
||||||
let mut program_table_count: i32 = 0;
|
let mut program_table_count: i32 = 0;
|
||||||
|
let mut pt_note_offset: usize = 0;
|
||||||
|
|
||||||
// Iterate through number of Program Headers
|
// Iterate through number of Program Headers
|
||||||
for _ in 0..file_header.phnum {
|
for i in 0..file_header.phnum {
|
||||||
// Build Program Header data structure
|
// Build Program Header data structure
|
||||||
let program_header: elf::ProgramHeader = util::build_program_header(
|
let program_header: elf::ProgramHeader = util::build_program_header(
|
||||||
bytes,
|
bytes,
|
||||||
program_table_offset as usize,
|
program_table_offset as usize,
|
||||||
|
i,
|
||||||
file_header.is_x86_64
|
file_header.is_x86_64
|
||||||
);
|
);
|
||||||
|
|
||||||
// Parse the program name using the program type
|
// Parse the program name using the program type
|
||||||
let program_name: String = util::parse_program_segment_type(program_header.program_type);
|
let program_name: String = util::parse_program_segment_type(program_header.program_type);
|
||||||
|
|
||||||
|
if (program_name == "PT_NOTE") && (pt_note_offset == 0) {
|
||||||
|
pt_note_offset = program_table_offset as usize;
|
||||||
|
}
|
||||||
|
|
||||||
util::pp_program_header(&program_header, program_table_count, &program_name);
|
util::pp_program_header(&program_header, program_table_count, &program_name);
|
||||||
|
|
||||||
// Update the program header table offset counter based on the program header size
|
// Update the program header table offset counter based on the program header size
|
||||||
|
|
@ -151,6 +159,13 @@ fn main() {
|
||||||
program_table_count += 1;
|
program_table_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let note_segment: elf::ProgramHeader = util::build_program_header(
|
||||||
|
bytes,
|
||||||
|
pt_note_offset,
|
||||||
|
0,
|
||||||
|
file_header.is_x86_64
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// Now that we have all the sections, spit out the .text section and start a linear disassembly
|
// Now that we have all the sections, spit out the .text section and start a linear disassembly
|
||||||
let text_section: &elf::SectionHeader = section_table_map.get(".text").unwrap();
|
let text_section: &elf::SectionHeader = section_table_map.get(".text").unwrap();
|
||||||
|
|
@ -221,14 +236,17 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if patch_mode {
|
if inject_mode {
|
||||||
|
|
||||||
println!("\n==== Applying Patch To Binary ====\n");
|
println!("\n==== Injecting Payload To Binary ====\n");
|
||||||
|
|
||||||
patcher::patch_binary(
|
patcher::patch_binary(
|
||||||
bytes.to_vec(),
|
bytes.to_vec(),
|
||||||
file_path.to_string(),
|
file_path.to_string(),
|
||||||
&patch_file_path
|
&patch_file_path,
|
||||||
|
&file_header,
|
||||||
|
section_table_map,
|
||||||
|
note_segment
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,7 +256,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("[Error] '{}' does not exist", file_path);
|
println!("[Error] File '{}' does not exist", file_path);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
173
src/patcher.rs
173
src/patcher.rs
|
|
@ -4,105 +4,124 @@
|
||||||
// Description: Houses binary rewriting and patching functionality for chisel.
|
// Description: Houses binary rewriting and patching functionality for chisel.
|
||||||
|
|
||||||
use std::path;
|
use std::path;
|
||||||
|
use std::fs;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
use crate::elf;
|
||||||
|
|
||||||
|
|
||||||
pub fn patch_binary(binary_contents: Vec<u8>, binary_name: String, patch_file_path: &String) {
|
pub fn patch_binary(
|
||||||
|
binary_contents: Vec<u8>,
|
||||||
|
binary_name: String,
|
||||||
|
patch_file_path: &String,
|
||||||
|
file_header: &elf::FileHeader,
|
||||||
|
section_header_map: HashMap<String, elf::SectionHeader>,
|
||||||
|
note_segment: elf::ProgramHeader) {
|
||||||
|
|
||||||
let patch_data: HashMap<usize, Vec<u8>> = parse_patch_file(patch_file_path);
|
|
||||||
let mut bytes: Vec<u8> = binary_contents;
|
|
||||||
|
|
||||||
println!("Patch data read successfully, applying...");
|
let patch_result: Result<Vec<u8>,Error> = read_patch_file(patch_file_path);
|
||||||
|
let patch_data: &Vec<u8> = &patch_result.as_ref().unwrap();
|
||||||
|
|
||||||
|
let mut program_data: Vec<u8> = binary_contents;
|
||||||
|
|
||||||
for patch in patch_data {
|
|
||||||
let start_offset = patch.0;
|
|
||||||
let mut i: usize = 0;
|
|
||||||
while i < (patch.1.len()) {
|
|
||||||
bytes[(start_offset + i) as usize] = patch.1[i];
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Done!");
|
// Apply patch to end of binary
|
||||||
|
println!("Patch data read successfully, injecting at end of binary...");
|
||||||
|
|
||||||
let patched_file_name: String = binary_name + "_patched";
|
let injection_offset: usize = program_data.len();
|
||||||
|
let injection_size: usize = patch_data.len();
|
||||||
|
let injection_addr: usize = injection_offset;
|
||||||
|
|
||||||
println!("Writing '{}' to disk...", patched_file_name);
|
program_data.extend_from_slice(patch_data);
|
||||||
|
|
||||||
let mut file = std::fs::File::create(patched_file_name)
|
print!("Done!");
|
||||||
|
|
||||||
|
|
||||||
|
// Locate a note segment
|
||||||
|
println!("Pulling .note.ABI-tag segment data...");
|
||||||
|
let note_section: &elf::SectionHeader = section_header_map.get(".note.ABI-tag")
|
||||||
|
.expect("[Error] Failed to pull ABI-tag section from binary!");
|
||||||
|
print!("Done!\n");
|
||||||
|
|
||||||
|
println!("Note section address: {:#04x}", note_section.addr);
|
||||||
|
println!("Note section offset: {:#04x}", note_section.offset);
|
||||||
|
println!("Note section size: {}", note_section.size);
|
||||||
|
println!("");
|
||||||
|
println!("Injected address: {:#04x}", injection_addr);
|
||||||
|
println!("Injected section offset: {:#04x}", injection_offset);
|
||||||
|
println!("Injected section size: {}", injection_size);
|
||||||
|
|
||||||
|
|
||||||
|
// Rewrite the section header
|
||||||
|
let mut injected_section: elf::SectionHeader = elf::SectionHeader::from(note_section.clone());
|
||||||
|
|
||||||
|
injected_section.section_type = 1;
|
||||||
|
injected_section.addr = injection_addr as u64;
|
||||||
|
injected_section.offset = injection_offset as u64;
|
||||||
|
injected_section.size = injection_size as u64;
|
||||||
|
|
||||||
|
util::overwrite_section_header(
|
||||||
|
&mut program_data,
|
||||||
|
file_header.shoff as usize,
|
||||||
|
file_header.shentsize as usize,
|
||||||
|
injected_section.id as usize,
|
||||||
|
&injected_section,
|
||||||
|
file_header.is_x86_64
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rewrite the section header
|
||||||
|
let mut injected_segment: elf::ProgramHeader = elf::ProgramHeader::from(note_segment.clone());
|
||||||
|
|
||||||
|
injected_segment.program_type = 1;
|
||||||
|
injected_segment.offset = injection_offset as u64;
|
||||||
|
injected_segment.vaddr = injection_offset as u64;
|
||||||
|
injected_segment.paddr = injection_offset as u64;
|
||||||
|
injected_segment.filesz = injection_size as u64;
|
||||||
|
injected_segment.memsz = injection_size as u64;
|
||||||
|
injected_segment.flags = 5;
|
||||||
|
injected_segment.align = 0x1000;
|
||||||
|
|
||||||
|
util::overwrite_segment_header(
|
||||||
|
&mut program_data,
|
||||||
|
file_header.shoff as usize,
|
||||||
|
file_header.shentsize as usize,
|
||||||
|
injected_section.id as usize,
|
||||||
|
&injected_section,
|
||||||
|
file_header.is_x86_64
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
util::overwrite_entrypoint(&mut program_data, injection_offset);
|
||||||
|
|
||||||
|
|
||||||
|
// Spit everything back out
|
||||||
|
let out_file_name: String = binary_name + ".patched";
|
||||||
|
|
||||||
|
println!("Writing '{}' to disk...", out_file_name);
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create(out_file_name)
|
||||||
.expect("[Error] Could not write patched binary to disk");
|
.expect("[Error] Could not write patched binary to disk");
|
||||||
|
|
||||||
file.write_all(&bytes)
|
file.write_all(&program_data)
|
||||||
.expect("[Error] Could not write to patched binary file");
|
.expect("[Error] Could not write to patched binary data file");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn parse_patch_file(patch_path: &String) -> HashMap<usize, Vec<u8>>{
|
fn read_patch_file(patch_path: &String) -> Result<Vec<u8>, std::io::Error> {
|
||||||
// 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") {
|
if path::Path::new(patch_path).exists() && patch_path.ends_with(".bin") {
|
||||||
println!("Patch file exists, reading '{}'...", patch_path);
|
println!("Patch file exists, reading '{}'...", patch_path);
|
||||||
|
|
||||||
let contents = util::read_lines(patch_path.to_string());
|
let contents: Result<Vec<u8>, std::io::Error> = fs::read(patch_path);
|
||||||
let mut patch_data: HashMap<usize, Vec<u8>> = HashMap::new();
|
|
||||||
|
|
||||||
for line in contents {
|
return contents;
|
||||||
let unwrapped = line.unwrap();
|
|
||||||
if unwrapped.trim().starts_with("#") || unwrapped.is_empty() {
|
|
||||||
//Skip
|
|
||||||
} else {
|
|
||||||
let mut statement = unwrapped.split(":");
|
|
||||||
let address: usize = util::hex_to_int(statement.next().unwrap().trim()).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: Vec<u8> = cleaned.as_bytes().to_vec();
|
|
||||||
|
|
||||||
print!("{}: ", address);
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
while i < bytes.len() {
|
|
||||||
print!("{} ", bytes[i]);
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
patch_data.insert(address, bytes);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Data is comma seperated list or a single value
|
|
||||||
let byte_str: String = data.replace(",", "");
|
|
||||||
let bytes: Vec<u8> = util::hex_to_buff(&byte_str).unwrap();
|
|
||||||
|
|
||||||
print!("{}: ", address);
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
while i < bytes.len() {
|
|
||||||
print!("{} ", bytes[i]);
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
patch_data.insert(address, bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return patch_data;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
println!("[Error] Patch file '{}' is invalid or cannot be read, exiting...", patch_path);
|
println!("[Error] Patch file '{}' is invalid or cannot be read, exiting...", patch_path);
|
||||||
|
|
|
||||||
111
src/patcher.rs.bak
Normal file
111
src/patcher.rs.bak
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
// patcher.rs
|
||||||
|
// Author: Garrett Dickinson
|
||||||
|
// Created: 04/06/2023
|
||||||
|
// Description: Houses binary rewriting and patching functionality for chisel.
|
||||||
|
|
||||||
|
use std::path;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn patch_binary(binary_contents: Vec<u8>, binary_name: String, patch_file_path: &String) {
|
||||||
|
|
||||||
|
let patch_data: HashMap<usize, Vec<u8>> = parse_patch_file(patch_file_path);
|
||||||
|
let mut bytes: Vec<u8> = binary_contents;
|
||||||
|
|
||||||
|
println!("Patch data read successfully, applying...");
|
||||||
|
|
||||||
|
for patch in patch_data {
|
||||||
|
let start_offset = patch.0;
|
||||||
|
let mut i: usize = 0;
|
||||||
|
while i < (patch.1.len()) {
|
||||||
|
bytes[(start_offset + i) as usize] = patch.1[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Done!");
|
||||||
|
|
||||||
|
let patched_file_name: String = binary_name + "_patched";
|
||||||
|
|
||||||
|
println!("Writing '{}' to disk...", patched_file_name);
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create(patched_file_name)
|
||||||
|
.expect("[Error] Could not write patched binary to disk");
|
||||||
|
|
||||||
|
file.write_all(&bytes)
|
||||||
|
.expect("[Error] Could not write to patched binary file");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_patch_file(patch_path: &String) -> HashMap<usize, Vec<u8>>{
|
||||||
|
// 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());
|
||||||
|
let mut patch_data: HashMap<usize, Vec<u8>> = HashMap::new();
|
||||||
|
|
||||||
|
for line in contents {
|
||||||
|
let unwrapped = line.unwrap();
|
||||||
|
if unwrapped.trim().starts_with("#") || unwrapped.is_empty() {
|
||||||
|
//Skip
|
||||||
|
} else {
|
||||||
|
let mut statement = unwrapped.split(":");
|
||||||
|
let address: usize = util::hex_to_int(statement.next().unwrap().trim()).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: Vec<u8> = cleaned.as_bytes().to_vec();
|
||||||
|
|
||||||
|
print!("{}: ", address);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
print!("{} ", bytes[i]);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
|
|
||||||
|
patch_data.insert(address, bytes);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Data is comma seperated list or a single value
|
||||||
|
let byte_str: String = data.replace(",", "");
|
||||||
|
let bytes: Vec<u8> = util::hex_to_buff(&byte_str).unwrap();
|
||||||
|
|
||||||
|
print!("{}: ", address);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
print!("{} ", bytes[i]);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
|
|
||||||
|
patch_data.insert(address, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return patch_data;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
println!("[Error] Patch file '{}' is invalid or cannot be read, exiting...", patch_path);
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
163
src/util.rs
163
src/util.rs
|
|
@ -5,9 +5,9 @@
|
||||||
// functions.
|
// functions.
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::io::{self, BufReader, BufRead};
|
// use std::io::{self, BufReader, BufRead};
|
||||||
use std::fs::File;
|
// use std::fs::File;
|
||||||
use std::num::ParseIntError;
|
// use std::num::ParseIntError;
|
||||||
|
|
||||||
use crate::elf::{self, EndianType, ArchitectureType};
|
use crate::elf::{self, EndianType, ArchitectureType};
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ pub fn build_file_header(data: &Vec<u8>) -> elf::FileHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn build_program_header(data: &Vec<u8>, phoffset: usize, is_x86_64: bool) -> elf::ProgramHeader {
|
pub fn build_program_header(data: &Vec<u8>, phoffset: usize, id: u16, is_x86_64: bool) -> elf::ProgramHeader {
|
||||||
|
|
||||||
// Cast the supplied is_x86_64 bool to an array offset
|
// Cast the supplied is_x86_64 bool to an array offset
|
||||||
// 0 : x86
|
// 0 : x86
|
||||||
|
|
@ -51,6 +51,7 @@ pub fn build_program_header(data: &Vec<u8>, phoffset: usize, is_x86_64: bool) ->
|
||||||
let arch: usize = is_x86_64.into();
|
let arch: usize = is_x86_64.into();
|
||||||
|
|
||||||
let program_header: elf::ProgramHeader = elf::ProgramHeader {
|
let program_header: elf::ProgramHeader = elf::ProgramHeader {
|
||||||
|
id: id,
|
||||||
program_type: u32_from_buffer(data, phoffset + elf::PH_TYPE_OFFSET as usize),
|
program_type: u32_from_buffer(data, phoffset + elf::PH_TYPE_OFFSET as usize),
|
||||||
flags: u32_from_buffer(data, phoffset + elf::PH_FLAGS_OFFSET[arch] as usize),
|
flags: u32_from_buffer(data, phoffset + elf::PH_FLAGS_OFFSET[arch] as usize),
|
||||||
offset: u64_from_buffer(data, phoffset + elf::PH_OFFSET_OFFSET[arch] as usize),
|
offset: u64_from_buffer(data, phoffset + elf::PH_OFFSET_OFFSET[arch] as usize),
|
||||||
|
|
@ -65,7 +66,48 @@ pub fn build_program_header(data: &Vec<u8>, phoffset: usize, is_x86_64: bool) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn build_section_header(data: &Vec<u8>, shoffset: usize, is_x86_64: bool) -> elf::SectionHeader {
|
pub fn overwrite_segment_header(program_data: &mut Vec<u8>,
|
||||||
|
stoffset: usize,
|
||||||
|
shentsize: usize,
|
||||||
|
shentidx: usize,
|
||||||
|
new_section: &elf::SectionHeader,
|
||||||
|
is_x86_64: bool) {
|
||||||
|
|
||||||
|
// Cast the supplied is_x86_64 bool to an array offset
|
||||||
|
// 0 : x86
|
||||||
|
// 1 : x64
|
||||||
|
let arch: usize = is_x86_64.into();
|
||||||
|
|
||||||
|
println!("stoffset {}", stoffset);
|
||||||
|
println!("shentsize {}", shentsize);
|
||||||
|
println!("shentidx {}", shentidx);
|
||||||
|
|
||||||
|
let section_addr_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_ADDR_OFFSET[arch] as usize;
|
||||||
|
let section_offset_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_OFFSET_OFFSET[arch] as usize;
|
||||||
|
let section_size_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_SIZE_OFFSET[arch] as usize;
|
||||||
|
let section_type_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_TYPE_OFFSET as usize;
|
||||||
|
|
||||||
|
program_data[section_addr_offset..section_addr_offset+8].copy_from_slice(
|
||||||
|
&new_section.addr.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section addr with {:#04x}", new_section.addr);
|
||||||
|
|
||||||
|
program_data[section_offset_offset..section_offset_offset+8].copy_from_slice(
|
||||||
|
&new_section.offset.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section offset with {:#04x}", new_section.offset as usize);
|
||||||
|
|
||||||
|
program_data[section_size_offset..section_size_offset+8].copy_from_slice(
|
||||||
|
&new_section.size.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section size with {:#04x}", new_section.size as usize);
|
||||||
|
|
||||||
|
program_data[section_type_offset..section_type_offset+4].copy_from_slice(
|
||||||
|
&new_section.section_type.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section type with {:#04x}", new_section.section_type as usize);
|
||||||
|
|
||||||
|
// return section_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn build_section_header(data: &Vec<u8>, stoffset: usize, id: u16, is_x86_64: bool) -> elf::SectionHeader {
|
||||||
|
|
||||||
// Cast the supplied is_x86_64 bool to an array offset
|
// Cast the supplied is_x86_64 bool to an array offset
|
||||||
// 0 : x86
|
// 0 : x86
|
||||||
|
|
@ -73,22 +115,74 @@ pub fn build_section_header(data: &Vec<u8>, shoffset: usize, is_x86_64: bool) ->
|
||||||
let arch: usize = is_x86_64.into();
|
let arch: usize = is_x86_64.into();
|
||||||
|
|
||||||
let section_header: elf::SectionHeader = elf::SectionHeader {
|
let section_header: elf::SectionHeader = elf::SectionHeader {
|
||||||
name: u32_from_buffer(data, shoffset + elf::SH_NAME_OFFSET as usize),
|
id: id,
|
||||||
section_type: u32_from_buffer(data, shoffset + elf::SH_TYPE_OFFSET as usize),
|
name_idx: u32_from_buffer(data, stoffset + elf::SH_NAME_OFFSET as usize),
|
||||||
flags: u64_from_buffer(data, shoffset + elf::SH_FLAGS_OFFSET as usize),
|
section_type: u32_from_buffer(data, stoffset + elf::SH_TYPE_OFFSET as usize),
|
||||||
addr: u64_from_buffer(data, shoffset + elf::SH_ADDR_OFFSET[arch] as usize),
|
flags: u64_from_buffer(data, stoffset + elf::SH_FLAGS_OFFSET as usize),
|
||||||
offset: u64_from_buffer(data, shoffset + elf::SH_OFFSET_OFFSET[arch] as usize),
|
addr: u64_from_buffer(data, stoffset + elf::SH_ADDR_OFFSET[arch] as usize),
|
||||||
size: u64_from_buffer(data, shoffset + elf::SH_SIZE_OFFSET[arch] as usize),
|
offset: u64_from_buffer(data, stoffset + elf::SH_OFFSET_OFFSET[arch] as usize),
|
||||||
link: u32_from_buffer(data, shoffset + elf::SH_LINK_OFFSET[arch] as usize),
|
size: u64_from_buffer(data, stoffset + elf::SH_SIZE_OFFSET[arch] as usize),
|
||||||
info: u32_from_buffer(data, shoffset + elf::SH_INFO_OFFSET[arch] as usize),
|
link: u32_from_buffer(data, stoffset + elf::SH_LINK_OFFSET[arch] as usize),
|
||||||
addralign: u64_from_buffer(data, shoffset + elf::SH_ADDRALIGN_OFFSET[arch] as usize),
|
info: u32_from_buffer(data, stoffset + elf::SH_INFO_OFFSET[arch] as usize),
|
||||||
entsize: u64_from_buffer(data, shoffset + elf::SH_ENTSIZE_OFFSET[arch] as usize)
|
addralign: u64_from_buffer(data, stoffset + elf::SH_ADDRALIGN_OFFSET[arch] as usize),
|
||||||
|
entsize: u64_from_buffer(data, stoffset + elf::SH_ENTSIZE_OFFSET[arch] as usize)
|
||||||
};
|
};
|
||||||
|
|
||||||
return section_header;
|
return section_header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn overwrite_section_header(program_data: &mut Vec<u8>,
|
||||||
|
stoffset: usize,
|
||||||
|
shentsize: usize,
|
||||||
|
shentidx: usize,
|
||||||
|
new_section: &elf::SectionHeader,
|
||||||
|
is_x86_64: bool) {
|
||||||
|
|
||||||
|
// Cast the supplied is_x86_64 bool to an array offset
|
||||||
|
// 0 : x86
|
||||||
|
// 1 : x64
|
||||||
|
let arch: usize = is_x86_64.into();
|
||||||
|
|
||||||
|
println!("stoffset {}", stoffset);
|
||||||
|
println!("shentsize {}", shentsize);
|
||||||
|
println!("shentidx {}", shentidx);
|
||||||
|
|
||||||
|
let section_addr_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_ADDR_OFFSET[arch] as usize;
|
||||||
|
let section_offset_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_OFFSET_OFFSET[arch] as usize;
|
||||||
|
let section_size_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_SIZE_OFFSET[arch] as usize;
|
||||||
|
let section_type_offset: usize = stoffset + (shentsize * shentidx) + elf::SH_TYPE_OFFSET as usize;
|
||||||
|
|
||||||
|
program_data[section_addr_offset..section_addr_offset+8].copy_from_slice(
|
||||||
|
&new_section.addr.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section addr with {:#04x}", new_section.addr);
|
||||||
|
|
||||||
|
program_data[section_offset_offset..section_offset_offset+8].copy_from_slice(
|
||||||
|
&new_section.offset.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section offset with {:#04x}", new_section.offset as usize);
|
||||||
|
|
||||||
|
program_data[section_size_offset..section_size_offset+8].copy_from_slice(
|
||||||
|
&new_section.size.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section size with {:#04x}", new_section.size as usize);
|
||||||
|
|
||||||
|
program_data[section_type_offset..section_type_offset+4].copy_from_slice(
|
||||||
|
&new_section.section_type.to_ne_bytes().to_vec());
|
||||||
|
println!("Overwriting section type with {:#04x}", new_section.section_type as usize);
|
||||||
|
|
||||||
|
// return section_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn overwrite_entrypoint(program_data: &mut Vec<u8>,
|
||||||
|
new_entry_point: usize) {
|
||||||
|
|
||||||
|
let offset: usize = elf::ENTRYPOINT_OFFSET as usize;
|
||||||
|
program_data[offset..offset+8].copy_from_slice(
|
||||||
|
&new_entry_point.to_ne_bytes().to_vec()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn parse_endian(endian: u8) -> elf::EndianType {
|
pub fn parse_endian(endian: u8) -> elf::EndianType {
|
||||||
match endian {
|
match endian {
|
||||||
0x00 => return EndianType::Big,
|
0x00 => return EndianType::Big,
|
||||||
|
|
@ -339,23 +433,28 @@ pub fn print_help() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn read_lines(filename: String) -> io::Lines<BufReader<File>> {
|
// pub fn read_lines(filename: String) -> io::Lines<BufReader<File>> {
|
||||||
// Open the file in read-only mode.
|
// // Open the file in read-only mode.
|
||||||
let file = File::open(filename).unwrap();
|
// let file = File::open(filename).unwrap();
|
||||||
// Read the file line by line, and return an iterator of the lines of the file.
|
// // Read the file line by line, and return an iterator of the lines of the file.
|
||||||
return io::BufReader::new(file).lines();
|
// return io::BufReader::new(file).lines();
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Borrowed from the following Stack Overflow post
|
// // Borrowed from the following Stack Overflow post
|
||||||
// https://stackoverflow.com/questions/52987181/how-can-i-convert-a-hex-string-to-a-u8-slice
|
// // https://stackoverflow.com/questions/52987181/how-can-i-convert-a-hex-string-to-a-u8-slice
|
||||||
pub fn hex_to_buff(s: &str) -> Result<Vec<u8>, ParseIntError> {
|
// pub fn hex_to_buff(s: &str) -> Result<Vec<u8>, ParseIntError> {
|
||||||
(0..s.len())
|
// (0..s.len())
|
||||||
.step_by(2)
|
// .step_by(2)
|
||||||
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
|
// .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
|
||||||
.collect()
|
// .collect()
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
pub fn hex_to_int(s: &str) -> Result<usize, ParseIntError> {
|
// pub fn hex_to_usize(s: &str) -> Result<usize, ParseIntError> {
|
||||||
return usize::from_str_radix(s, 16)
|
// return usize::from_str_radix(s, 16)
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// pub fn usize_to_hex(i: usize) -> String {
|
||||||
|
// return format!("{:X}", i).to_string();
|
||||||
|
// }
|
||||||
BIN
testing/elfpatching/hello_patch.bin
Normal file
BIN
testing/elfpatching/hello_patch.bin
Normal file
Binary file not shown.
32
testing/elfpatching/hello_patch.s
Normal file
32
testing/elfpatching/hello_patch.s
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
BITS 64
|
||||||
|
|
||||||
|
SECTION .text
|
||||||
|
global main
|
||||||
|
|
||||||
|
main:
|
||||||
|
push rax
|
||||||
|
push rcx
|
||||||
|
push rdx
|
||||||
|
push rsi
|
||||||
|
push rdi
|
||||||
|
push r11
|
||||||
|
|
||||||
|
mov rax,1
|
||||||
|
mov rdi,1
|
||||||
|
lea rsi,[rel $+hello-$]
|
||||||
|
mov rdx,[rel $+len-$]
|
||||||
|
syscall
|
||||||
|
|
||||||
|
pop r11
|
||||||
|
pop rdi
|
||||||
|
pop rsi
|
||||||
|
pop rdx
|
||||||
|
pop rcx
|
||||||
|
pop rax
|
||||||
|
|
||||||
|
;push 0x1060 ; jump to original entry point
|
||||||
|
; ; could be replaced dynamically?
|
||||||
|
;ret
|
||||||
|
|
||||||
|
hello: db "Calling injected code >:)",10
|
||||||
|
len: dd 26
|
||||||
BIN
testing/elfpatching/login
Executable file
BIN
testing/elfpatching/login
Executable file
Binary file not shown.
Binary file not shown.
BIN
testing/hello32
BIN
testing/hello32
Binary file not shown.
|
|
@ -1,14 +0,0 @@
|
||||||
# Trampoline segment
|
|
||||||
# Pushes and returns to 0x116b
|
|
||||||
#3f80 : 48C7C00100000048C7C70100000048C7C68007000048C7C20D0000000F05686B110000C3
|
|
||||||
# No syscall
|
|
||||||
1190 : 48C7C00100000048C7C70100000048C7C6C011000048C7C20D000000686B110000C3
|
|
||||||
|
|
||||||
# String data to print from trampoline instruction
|
|
||||||
11C0 : "Hello, World!"
|
|
||||||
|
|
||||||
# Initial jump to trampoline
|
|
||||||
#1160 : 6880060000C3
|
|
||||||
#1160 : 6890110000C3
|
|
||||||
|
|
||||||
2004 : "Hello, Patch!"
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
int main() {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int my_function() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int another_function() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
.file "main.c"
|
|
||||||
.text
|
|
||||||
.section .rodata
|
|
||||||
.LC0:
|
|
||||||
.string "Hello, World!"
|
|
||||||
.text
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
.LFB0:
|
|
||||||
.cfi_startproc
|
|
||||||
endbr64
|
|
||||||
pushq %rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
movq %rsp, %rbp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
leaq .LC0(%rip), %rax
|
|
||||||
movq %rax, %rdi
|
|
||||||
movl $0, %eax
|
|
||||||
call printf@PLT
|
|
||||||
movl $0, %eax
|
|
||||||
popq %rbp
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE0:
|
|
||||||
.size main, .-main
|
|
||||||
.ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
.section .note.gnu.property,"a"
|
|
||||||
.align 8
|
|
||||||
.long 1f - 0f
|
|
||||||
.long 4f - 1f
|
|
||||||
.long 5
|
|
||||||
0:
|
|
||||||
.string "GNU"
|
|
||||||
1:
|
|
||||||
.align 8
|
|
||||||
.long 0xc0000002
|
|
||||||
.long 3f - 2f
|
|
||||||
2:
|
|
||||||
.long 0x3
|
|
||||||
3:
|
|
||||||
.align 8
|
|
||||||
4:
|
|
||||||
Loading…
Reference in New Issue
Block a user