use crate::test_tools::test_path;
use gaia_types::{helpers::Architecture, GaiaError};
use pe_assembler::{helpers::PeBuilder, types::SubsystemType};
use std::{
fs::{create_dir_all, write},
process::Command,
};
use x86_64_assembler::{
builder::ProgramBuilder,
instruction::{Instruction, Operand, Register},
};
#[test]
fn test_hello_world() -> Result<(), GaiaError> {
let generated_dir = test_path("generated");
create_dir_all(&generated_dir).ok();
let msg = "Hello world!\n";
let x86_bytes = easy_console_log(Architecture::X86, msg).expect("Failed to generate x86 hello world PE bytes");
let x86_path = generated_dir.join("echo_hello_world_x86.exe");
write(&x86_path, &x86_bytes).expect("Failed to write x86 exe");
let x86_out = Command::new(&x86_path).output().expect("Failed to execute generated x86 PE file");
assert!(x86_out.status.success(), "x86 process did not exit with code 0: {:?}", x86_out.status);
let x86_stdout = String::from_utf8_lossy(&x86_out.stdout);
assert!(x86_stdout.contains("Hello world"), "x86 stdout does not contain 'hello world': {}", x86_stdout);
let x64_bytes = easy_console_log(Architecture::X86_64, msg).expect("Failed to generate x64 hello world PE bytes");
let x64_path = generated_dir.join("echo_hello_world_x64.exe");
write(&x64_path, &x64_bytes).expect("Failed to write x64 exe");
let x64_out = Command::new(&x64_path).output().expect("Failed to execute generated x64 PE file");
assert!(x64_out.status.success(), "x64 process did not exit with code 0: {:?}", x64_out.status);
let x64_stdout = String::from_utf8_lossy(&x64_out.stdout);
println!("x64 stdout: {:?}", x64_stdout);
assert!(x64_stdout.contains("Hello world"), "x64 stdout does not contain 'hello world': {}", x64_stdout);
Ok(())
}
pub fn easy_console_log(arch: Architecture, message: &str) -> Result<Vec<u8>, GaiaError> {
match arch {
Architecture::X86 => generate_console_log_x86(message),
Architecture::X86_64 => generate_console_log_x64(message),
_ => Err(GaiaError::unsupported_architecture(arch)),
}
}
fn generate_console_log_x86(message: &str) -> Result<Vec<u8>, GaiaError> {
let mut program = ProgramBuilder::new(Architecture::X86);
program.push_imm(-11)?.call_indirect(0)?;
let msg_len = message.len() as i64;
program
.push_imm(msg_len) ?
.push_label("msg".to_string()) ?
.pop_reg(Register::EBX) ?
.pop_reg(Register::ECX) ?
.mov_reg_reg(Register::EDI, Register::EBX) ?
.add_reg_imm(Register::EBX, (msg_len + 1) as i64) ?
.push_imm(0) ?
.push_reg(Register::EBX) ?
.push_imm(msg_len) ?
.push_reg(Register::EDI) ?
.push_reg(Register::EAX) ?
.call_indirect(1) ?;
program.push_imm(0)?.call_indirect(2)?.ret()?;
let code = program.compile_instructions()?;
let mut data = Vec::new();
data.extend_from_slice(message.as_bytes());
data.push(0);
data.extend_from_slice(&[0, 0, 0, 0]);
PeBuilder::new()
.architecture(Architecture::X86)
.subsystem(SubsystemType::Console)
.entry_point(0x1000)
.image_base(0x400000)
.import_functions("kernel32.dll", &["GetStdHandle", "WriteFile", "ExitProcess"])
.code(code)
.data(data)
.generate()
}
fn generate_console_log_x64(message: &str) -> Result<Vec<u8>, GaiaError> {
let mut program = ProgramBuilder::new(Architecture::X86_64);
program.sub_reg_imm(Register::RSP, 40)?;
program.mov_reg_imm(Register::RCX, -11)?;
program.call_indirect(0)?;
program.mov_reg_reg(Register::RCX, Register::RAX)?;
program.add_instruction(Instruction::Lea { dst: Register::RDX, displacement: 0, rip_relative: true });
let len = message.len() as i64;
program.mov_reg_imm(Register::R8D, len)?;
let pad = (4 - ((message.len() + 1) % 4)) % 4;
let written_offset = (message.len() + 1 + pad) as i32;
program.add_instruction(Instruction::Lea { dst: Register::R9, displacement: written_offset, rip_relative: true });
program.mov_reg_imm(Register::RAX, 0)?;
program.add_instruction(Instruction::Mov {
dst: Operand::Mem { base: Some(Register::RSP), index: None, scale: 1, displacement: 32 },
src: Operand::Reg(Register::RAX),
});
program.call_indirect(1)?;
program.mov_reg_imm(Register::RCX, 0)?;
program.call_indirect(2)?;
program.add_reg_imm(Register::RSP, 40)?;
program.ret()?;
let code = program.compile_instructions()?;
let mut data = Vec::new();
data.extend_from_slice(message.as_bytes());
data.push(0);
let pad = (4 - (data.len() % 4)) % 4;
for _ in 0..pad {
data.push(0);
}
data.extend_from_slice(&[0u8; 4]);
PeBuilder::new()
.architecture(Architecture::X86_64)
.subsystem(SubsystemType::Console)
.entry_point(0x1000)
.image_base(0x140000000)
.import_functions("kernel32.dll", &["GetStdHandle", "WriteFile", "ExitProcess"])
.code(code)
.data(data)
.generate()
}