diff --git a/bin/got b/bin/got new file mode 100755 index 0000000..8c5f99a Binary files /dev/null and b/bin/got differ diff --git a/bin/got.c b/bin/got.c new file mode 100644 index 0000000..46d61f1 --- /dev/null +++ b/bin/got.c @@ -0,0 +1,14 @@ +// gcc got.c -no-pie -o got +#include +#include + +int secret(char* s) { + if (s[0] == 's' && s[1] == 'e' && s[2] == 'c' && s[3] == 'r' && s[4] == 'e' && s[5] == 't') { + puts("secret function called"); + } + return 0; +} + +int main() { + int x=system("secret"); +} diff --git a/src/error.rs b/src/error.rs index c46c4d3..80e4b6b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -18,8 +18,11 @@ pub enum Error { #[error("failed in I/O operation: {0}")] Io(std::io::Error), - #[error("")] - ExitWithCode(std::process::ExitCode), + #[error("failed to process obfuscation: {0}")] + Obfuscation(&'static str), + + #[error("not found: {0}")] + NotFound(String), } pub type Result = std::result::Result; diff --git a/src/main.rs b/src/main.rs index 47e57e0..5ac4e46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,16 @@ struct Args { section: String, #[arg(short, long, help = "recursive", default_value = "")] recursive: String, + #[arg(short, long, help = "perform GOT overwrite", default_value = "false")] + got: bool, + #[arg( + long, + help = "GOT overwrite target library function name", + default_value = "" + )] + got_l: String, + #[arg(long, help = "GOT overwrite target function name", default_value = "")] + got_f: String, } fn main() -> crate::error::Result<()> { @@ -65,7 +75,7 @@ fn main() -> crate::error::Result<()> { "obfuscated" }; - exec_obfus(&args.input, output_path, &args).unwrap_or(()) + exec_obfus(&args.input, output_path, &args).unwrap(); } else { if !args.input.is_empty() { return Err(crate::error::Error::InvalidOption( @@ -88,7 +98,7 @@ fn main() -> crate::error::Result<()> { std::fs::create_dir_all(dir).unwrap(); std::fs::File::create(&output_path).unwrap(); - exec_obfus(entry.to_str().unwrap(), &output_path, &args).unwrap_or(()); + exec_obfus(entry.to_str().unwrap(), &output_path, &args).unwrap(); } } @@ -113,21 +123,28 @@ fn exec_obfus(input_path: &str, output_path: &str, args: &Args) -> crate::error: obfuscator.nullify_sec_hdr(); } if args.symbol { - obfuscator.nullify_section(".strtab"); + obfuscator.nullify_section(".strtab")?; } if args.comment { - obfuscator.nullify_section(".comment"); + obfuscator.nullify_section(".comment")?; } if !args.section.is_empty() { - obfuscator.nullify_section(&args.section); + obfuscator.nullify_section(&args.section)?; + } + if args.got { + if args.got_l.is_empty() || args.got_f.is_empty() { + return Err(crate::error::Error::InvalidOption( + "both library and function names are required", + )); + } + + obfuscator.got_overwrite(&args.got_l, &args.got_f)?; } println!("obfuscation done!"); Ok(()) } - false => { - return Err(crate::error::Error::InvalidMagic); - } + false => Err(crate::error::Error::InvalidMagic), } } @@ -177,7 +194,7 @@ mod tests { fn null_symbol_name() { let loader = crate::obfus::Obfuscator::open("bin/test_64bit", "bin/res_symbol"); let mut obfuscator = loader.unwrap(); - obfuscator.nullify_section(".strtab"); + obfuscator.nullify_section(".strtab").unwrap(); let output = std::process::Command::new("readelf") .args(["-x29", "bin/res_symbol"]) .output() @@ -197,7 +214,7 @@ mod tests { fn null_comment() { let loader = crate::obfus::Obfuscator::open("bin/test_64bit", "bin/res_comment"); let mut obfuscator = loader.unwrap(); - obfuscator.nullify_section(".comment"); + obfuscator.nullify_section(".comment").unwrap(); let output = std::process::Command::new("readelf") .args(["-x27", "bin/res_comment"]) .output() diff --git a/src/obfus.rs b/src/obfus.rs index b820782..e800d62 100644 --- a/src/obfus.rs +++ b/src/obfus.rs @@ -29,6 +29,8 @@ pub struct Obfuscator { sec_hdr_size: u64, sec_hdr_offset: u64, sec_table: u64, + dyn_strings: String, + string_table: String, } impl Obfuscator { @@ -111,7 +113,7 @@ impl Obfuscator { let sec_hdr = String::from_utf8_lossy(&data_copy).to_string(); - Ok(Obfuscator { + let mut obfus = Obfuscator { input, output, sec_hdr, @@ -119,7 +121,21 @@ impl Obfuscator { sec_hdr_size, sec_hdr_offset, sec_table, - }) + dyn_strings: String::new(), + string_table: String::new(), + }; + + let (section_addr, section_size, _, _) = obfus.get_section(".dynstr").unwrap(); + obfus.dyn_strings = + String::from_utf8_lossy(&obfus.input[section_addr..section_addr + section_size]) + .to_string(); + + let (section_addr, section_size, _, _) = obfus.get_section(".strtab").unwrap(); + obfus.string_table = + String::from_utf8_lossy(&obfus.input[section_addr..section_addr + section_size]) + .to_string(); + + Ok(obfus) } pub fn is_elf(&self) -> bool { @@ -131,13 +147,19 @@ impl Obfuscator { } fn is_enable_pie(&self) -> bool { - self.input[16] == 2 + self.input[16] != 2 } fn is_stripped(&self) -> bool { self.get_section(".symtab").unwrap().0 == 0 } + fn v2p(&self, virtual_addr: usize, section: &str) -> usize { + let (section_addr, _, _, vaddr) = self.get_section(section).unwrap(); + + section_addr + virtual_addr - vaddr + } + // (section_addr, section_size, entry_size, vaddr) fn get_section(&self, section: &str) -> crate::error::Result<(usize, usize, usize, usize)> { let searched_idx = self.sec_hdr.find(section).unwrap_or(usize::MAX); @@ -203,23 +225,95 @@ impl Obfuscator { Ok(()) } - pub fn got_overwrite(&self, function: &str, new_func_addr: &str) { + fn get_dyn_func_id(&self, function: &str) -> u64 { + let idx = self.dyn_strings.find(function).unwrap(); + let (section_addr, section_size, entry_size, _) = self.get_section(".dynsym").unwrap(); + + let dynsym_section = &self.input[section_addr..section_addr + section_size]; + + for i in 0..section_size / entry_size { + let entry = &dynsym_section[i * entry_size..(i + 1) * entry_size]; + let name_offset = u32::from_le_bytes(entry[0..4].try_into().unwrap()); + if name_offset == idx as u32 { + return i as u64; + } + } + + 0 + } + + fn get_func_addr_by_name(&self, function: &str) -> crate::error::Result { + let idx = self.string_table.find(function).unwrap(); + let (section_addr, section_size, entry_size, _) = self.get_section(".symtab").unwrap(); + + let dynsym_section = &self.input[section_addr..section_addr + section_size]; + + for i in 0..section_size / entry_size { + let entry = &dynsym_section[i * entry_size..(i + 1) * entry_size]; + if self.is_64bit() { + if u32::from_le_bytes(entry[0..4].try_into().unwrap()) == idx as u32 { + return Ok(u64::from_le_bytes(entry[8..16].try_into().unwrap())); + } + } else if u32::from_le_bytes(entry[0..4].try_into().unwrap()) == idx as u32 { + return Ok(u32::from_le_bytes(entry[4..8].try_into().unwrap()) as u64); + } + } + + Err(crate::error::Error::NotFound( + "function not found".to_owned() + function, + )) + } + + pub fn got_overwrite( + &mut self, + function: &str, + new_func_addr: &str, + ) -> crate::error::Result<()> { if self.is_enable_pie() { - println!("replacing GOT get will no effect with PIE enabled") + return Err(crate::error::Error::InvalidOption( + "replacing GOT get will no effect with PIE enabled", + )); } else if self.is_stripped() { - println!("cannot overwrite GOT with stripped binary") + return Err(crate::error::Error::InvalidOption( + "cannot overwrite GOT with stripped binary", + )); } + let id = self.get_dyn_func_id(function); + if self.is_64bit() { - let (section_addr, section_size, entry_size, vaddr) = + let (section_addr, section_size, entry_size, _) = self.get_section(".rela.plt").unwrap(); for i in 0..section_size / entry_size { let entry = &self.input[section_addr..section_addr + section_size] [i * entry_size..(i + 1) * entry_size]; + let info = u64::from_le_bytes(entry[8..16].try_into().unwrap()) >> 32; + if info == id { + let offset = u64::from_le_bytes(entry[0..8].try_into().unwrap()); + let addr = self.v2p(offset as usize, ".got.plt"); + let new_func_addr = self.get_func_addr_by_name(new_func_addr); + self.output[addr..addr + 8] + .copy_from_slice(&new_func_addr.unwrap().to_le_bytes()); + return Ok(()); + } } } else { - let (section_addr, section_size, entry_size, vaddr) = - self.get_section(".rel.plt").unwrap(); + let (section_addr, section_size, entry_size, _) = self.get_section(".rel.plt").unwrap(); + for i in 0..section_size / entry_size { + let entry = &self.input[section_addr..section_addr + section_size] + [i * entry_size..(i + 1) * entry_size]; + let info = (u32::from_le_bytes(entry[8..16].try_into().unwrap()) >> 8) as u64; + if info == id { + let offset = u32::from_le_bytes(entry[0..4].try_into().unwrap()); + let addr = self.v2p(offset as usize, ".got.plt"); + let new_func_addr = self.get_func_addr_by_name(new_func_addr); + self.output[addr..addr + 4] + .copy_from_slice(&new_func_addr.unwrap().to_le_bytes()); + return Ok(()); + } + } } + + Err(crate::error::Error::Obfuscation("failed to overwrite GOT")) } }