Skip to content

Commit

Permalink
Merge pull request #15 from lapla-cogito/got
Browse files Browse the repository at this point in the history
GOT overwrite
  • Loading branch information
lapla-cogito authored Aug 20, 2024
2 parents ad039a8 + 11ad02b commit e2ef6f9
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 36 deletions.
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.86"
clap = { version = "4.5.3", features = ["derive"] }
memmap2 = "0.9.4"
thiserror = "1.0.63"
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Options:
--comment nullify comment section in the ELF
--section <SECTION> nullify section in the ELF [default: ]
-r, --recursive <RECURSIVE> recursive [default: ]
-g, --got perform GOT overwrite
--got-l <GOT_L> GOT overwrite target library function name [default: ]
--got-f <GOT_F> GOT overwrite target function name [default: ]
-h, --help Print help
-V, --version Print version
```
Expand Down Expand Up @@ -134,6 +137,36 @@ Hex dump of section '.comment':
0x00000020 00000000 00000000 000000 ...........
```

## GOT overwrite

Overwrites the GOT section with a specified value

```
$ cattleya -i bin/got --got --got-l system --got-f secret -o bin/res_got
$ ./bin/res_got
secret function called
```

As shown below, only the system function is called in the main function as far as disassembly is concerned:

```
$ objdump -d bin/res_got
...
00000000004011d2 <main>:
4011d2: f3 0f 1e fa endbr64
4011d6: 55 push %rbp
4011d7: 48 89 e5 mov %rsp,%rbp
4011da: 48 83 ec 10 sub $0x10,%rsp
4011de: 48 8d 05 36 0e 00 00 lea 0xe36(%rip),%rax # 40201b <_IO_stdin_used+0x1b>
4011e5: 48 89 c7 mov %rax,%rdi
4011e8: e8 73 fe ff ff call 401060 <system@plt>
4011ed: 89 45 fc mov %eax,-0x4(%rbp)
4011f0: b8 00 00 00 00 mov $0x0,%eax
4011f5: c9 leave
4011f6: c3 ret
...
```

# Recursive option

By specifying the directory name in the recursive option, the same obfuscation can be applied to all ELF files in that directory:
Expand Down
Binary file added bin/got
Binary file not shown.
14 changes: 14 additions & 0 deletions bin/got.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// gcc got.c -no-pie -o got
#include<stdio.h>
#include<stdlib.h>

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");
}
28 changes: 28 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("invalid option: {0}")]
InvalidOption(&'static str),

#[error("failed to open file: {0}")]
OpenFile(std::io::Error),

#[error("failed to create file: {0}")]
CreateFile(std::io::Error),

#[error("invalid ELF magic")]
InvalidMagic,

#[error("failed to mmap: {0}")]
Mmap(std::io::Error),

#[error("failed in I/O operation: {0}")]
Io(std::io::Error),

#[error("failed to process obfuscation: {0}")]
Obfuscation(&'static str),

#[error("not found: {0}")]
NotFound(String),
}

pub type Result<T> = std::result::Result<T, Error>;
56 changes: 40 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod error;
mod obfus;
mod util;

Expand Down Expand Up @@ -45,15 +46,27 @@ 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() {
fn main() -> crate::error::Result<()> {
use clap::Parser as _;
let args = Args::parse();

if args.recursive.is_empty() {
if args.input.is_empty() {
panic!("input file name is required");
return Err(crate::error::Error::InvalidOption(
"input file name is required",
));
}

let output_path = if !args.output.is_empty() {
Expand All @@ -62,13 +75,15 @@ fn main() {
"obfuscated"
};

exec_obfus(&args.input, output_path, &args).unwrap_or(());
exec_obfus(&args.input, output_path, &args).unwrap();
} else {
if !args.input.is_empty() {
panic!("both input file name and recursive option are not allowed");
return Err(crate::error::Error::InvalidOption(
"both input file name and recursive option are not allowed",
));
}
if !args.output.is_empty() {
println!("output file name will be ignored");
eprintln!("output file name will be ignored");
}

let entries = util::RecursiveDir::new(&args.recursive)
Expand All @@ -83,16 +98,18 @@ fn main() {
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();
}
}

Ok(())
}

fn exec_obfus(input_path: &str, output_path: &str, args: &Args) -> std::io::Result<()> {
fn exec_obfus(input_path: &str, output_path: &str, args: &Args) -> crate::error::Result<()> {
let loader = obfus::Obfuscator::open(input_path, output_path);
let mut obfuscator = loader.unwrap();

match obfuscator.is_elf() && obfuscator.is_64bit() {
match obfuscator.is_elf() {
true => {
println!("start obfuscating {}...", input_path);

Expand All @@ -106,21 +123,28 @@ fn exec_obfus(input_path: &str, output_path: &str, args: &Args) -> std::io::Resu
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 => {
panic!("not a valid ELF file: {}", args.input);
}
false => Err(crate::error::Error::InvalidMagic),
}
}

Expand Down Expand Up @@ -170,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()
Expand All @@ -190,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()
Expand Down
Loading

0 comments on commit e2ef6f9

Please sign in to comment.