Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GOT overwrite #15

Merged
merged 5 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading