Playing with success cases / defenses against arbitrary code execution reading/writing weird addresses :)

This commit is contained in:
Sarah Jamie Lewis 2021-11-23 23:03:11 -08:00
parent 9c785f3f68
commit 5c86ead1d3
11 changed files with 106 additions and 31 deletions

View File

@ -131,7 +131,7 @@ impl Mapper for Mmc3 {
self.cart.prg_rom[chunk_num][chunk_half + offset_8k]
}
_ => {
println!("bad address read from MMC3: 0x{:X}", address);
//println!("bad address read from MMC3: 0x{:X}", address);
0
}
};

View File

@ -26,7 +26,16 @@ impl Mapper for Uxrom {
self.chr_ram[address]
}
}
0x8000..=0xBFFF => self.cart.prg_rom[self.bank_select][address % 0x4000],
0x8000..=0xBFFF => {
let address = address % 0x4000;
if self.bank_select > self.cart.prg_rom.len() {
return 0;
}
match self.cart.prg_rom[self.bank_select].get(address) {
None => 0,
Some(x) => *x,
}
}
0xC000..=0xFFFF => self.cart.prg_rom[self.cart.prg_rom.len() - 1][address % 0x4000],
_ => {
println!("bad address read from UxROM mapper: 0x{:X}", address);

View File

@ -16,7 +16,7 @@ impl super::Cpu {
| 0xFC | 0xFD => self.address_page_cross(old_address, new_address),
0x1E | 0x1F | 0x3E | 0x3F | 0x5E | 0x5F | 0x7E | 0x7F | 0x9D | 0xC3 | 0xC7 | 0xCF
| 0xD3 | 0xD7 | 0xDB | 0xDE | 0xDF | 0xFE | 0xFF => self.clock += 1,
_ => panic!("illegal opcode using abs x: {:02x}", current_opcode),
_ => self.clock += 1,
}
new_address
}

View File

@ -669,7 +669,7 @@ impl Cpu {
self.handle_interrupts();
// back up clock so we know how many cycles we complete
self.addresses_fetched.insert(self.pc);
self.before_clock = self.clock;
let opcode = <usize>::from(self.read(self.pc));
self.addresses_fetched.insert(self.pc);
@ -689,7 +689,7 @@ impl Cpu {
// memory interface
pub fn read(&mut self, address: usize) -> u8 {
// self.addresses_fetched.insert(address);
self.addresses_fetched.insert(address);
let val = match address {
0x0000..=0x1FFF => self.mem[address % 0x0800],
0x2000..=0x3FFF => self.read_ppu_reg(address % 8),
@ -700,14 +700,14 @@ impl Cpu {
0x4000..=0x4017 => 0, // can't read from these APU registers
0x4018..=0x401F => 0, // APU and I/O functionality that is normally disabled. See CPU Test Mode.
0x4020..=0xFFFF => self.mapper.borrow().read(address),
_ => panic!("invalid read from 0x{:02x}", address),
_ => 0, //panic!("invalid read from 0x{:02x}", address),
};
val
}
// memory interface
fn write(&mut self, address: usize, val: u8) {
// self.addresses_fetched.insert(address);
self.addresses_fetched.insert(address);
match address {
0x0000..=0x1FFF => self.mem[address % 0x0800] = val,
0x2000..=0x3FFF => self.write_ppu_reg(address % 8, val),
@ -716,7 +716,7 @@ impl Cpu {
0x4000..=0x4017 => self.apu.write_reg(address, val),
0x4018..=0x401F => (), // APU and I/O functionality that is normally disabled. See CPU Test Mode.
0x4020..=0xFFFF => self.mapper.borrow_mut().write(address, val),
_ => panic!("invalid write to {:02x}", address),
_ => {} //panic!("invalid write to {:02x}", address),
}
}

View File

@ -553,7 +553,7 @@ impl super::Cpu {
}
pub fn bad(&mut self, _address: usize, _mode: Mode) {
panic!("illegal opcode: 0x{:02X}", self.read(self.pc)); // this won't be the illegal opcode because the PC somehow hasn't been updated yet
//panic!("illegal opcode: 0x{:02X}", self.read(self.pc)); // this won't be the illegal opcode because the PC somehow hasn't been updated yet
}
// Interrupts

View File

@ -1,6 +1,7 @@
use super::Mode;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CpuData {
@ -8,7 +9,7 @@ pub struct CpuData {
a: u8,
x: u8,
y: u8,
pc: usize,
pub(crate) pc: usize,
s: u8,
p: u8,
clock: u64,
@ -19,6 +20,7 @@ pub struct CpuData {
button_number1: u8,
button_number2: u8,
mode_table: Vec<Mode>,
pub(crate) addresses_fetched: HashSet<usize>,
}
impl super::Cpu {
@ -39,6 +41,7 @@ impl super::Cpu {
button_number1: self.button_number1,
button_number2: self.button_number2,
mode_table: self.mode_table.clone(),
addresses_fetched: self.addresses_fetched.clone(),
}
}
@ -58,5 +61,6 @@ impl super::Cpu {
self.button_number1 = data.button_number1;
self.button_number2 = data.button_number2;
self.mode_table = data.mode_table;
self.addresses_fetched = data.addresses_fetched;
}
}

View File

@ -83,7 +83,51 @@ impl FuzzingInput {
} else {
tas = format!("{}.", tas);
}
tas = format!("{}|||\n", tas);
tas = format!("{}|", tas);
if input.player_2_input >> 7 == 1 {
tas = format!("{}R", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 6) & 0x01 == 1 {
tas = format!("{}L", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 5) & 0x01 == 1 {
tas = format!("{}D", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 4) & 0x01 == 1 {
tas = format!("{}U", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 3) & 0x01 == 1 {
tas = format!("{}T", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 2) & 0x01 == 1 {
tas = format!("{}S", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 1) & 0x01 == 1 {
tas = format!("{}B", tas);
} else {
tas = format!("{}.", tas);
}
if (input.player_2_input >> 0) & 0x01 == 1 {
tas = format!("{}A", tas);
} else {
tas = format!("{}.", tas);
}
tas = format!("{}||\n", tas);
}
fs::write(filename, tas).expect("Unable to write file");
@ -96,7 +140,7 @@ impl FuzzingInput {
self.frames.push(FrameInput {
console_action: ConsoleAction::None,
player_1_input: rng.next() as u8,
player_2_input: 0,
player_2_input: rng.next() as u8,
});
}
@ -105,6 +149,7 @@ impl FuzzingInput {
if (check / frames_to_consider as f64) < MUTATION_RATE {
if frame_num < self.frames.len() {
self.frames[frame_num].player_1_input = rng.next() as u8;
self.frames[frame_num].player_2_input = rng.next() as u8;
}
}

View File

@ -27,7 +27,7 @@ use std::sync::{mpsc, Arc, Mutex};
use std::{fs, thread};
// The number of cpu instances to spawn..
const NUM_THREADS: usize = 1;
const NUM_THREADS: usize = 28;
// The number of frames to fuzz and process
// A small number exploits the current point more at the expense of
@ -36,26 +36,26 @@ const FRAMES_TO_CONSIDER: usize = 500;
// Same input should generate the same output...
// (I make no guarantee of that at the moment)
const RNG_SEED: u32 = 0x35234623;
const RNG_SEED: u32 = 0x1334357;
// If set to a low number, this disables start presses after the given frame
// Useful for some games where pausing does nothing to advance the game...
const DISABLE_START_PRESSES_AFTER: usize = usize::MAX;
const DISABLE_START_PRESSES_AFTER: usize = 100;
// The rate at which seed inputs become corrupted..
const MUTATION_RATE: f64 = 0.05;
const MUTATION_RATE: f64 = 0.10;
// The rate at which seed inputs may become soft resets..
const MUTATION_RATE_SOFT_RESET: f64 = 0.00;
// The number of cases to fuzz from a given seed input at each consideration point..
const SEED_CASES: usize = 500;
const SEED_CASES: usize = 10;
// Only add the original seed case top the queue (hammer a single state)
const SINGLE_SHOT: bool = false;
const SINGLE_SHOT: bool = true;
// Fast forward the seed state to a given frame
const PLAY_FROM: usize = 0;
const PLAY_FROM: usize = 1900;
fn main() -> Result<(), String> {
let argv = std::env::args().collect::<Vec<String>>();
@ -81,11 +81,16 @@ fn main() -> Result<(), String> {
let mut starting_state = FuzzingState::default(rom_filename.clone());
let mut previous_states: HashSet<usize> = HashSet::new();
if PLAY_FROM > 0 {
println!("Playing {} frames prelude...", PLAY_FROM);
let (mut cpu, _) = starting_state.load_state(rom_filename.clone());
play_frames(&mut cpu, PLAY_FROM, &seed_input);
starting_state = FuzzingState::save_state(cpu, PLAY_FROM);
for addr in starting_state.cpu_state.addresses_fetched.iter() {
if previous_states.insert(*addr) {}
}
}
let mut fuzzing_queue: PriorityQueue<FuzzingInputState, u64> = PriorityQueue::new();
@ -121,8 +126,7 @@ fn main() -> Result<(), String> {
});
}
let mut previous_states: HashSet<Vec<u8>> = HashSet::new();
previous_states.insert(vec![0; 8192]);
let mut loop_count = 0;
loop {
println!("Prospective Cases: {}", fuzzing_queue.len());
@ -147,21 +151,35 @@ fn main() -> Result<(), String> {
for i in 0..NUM_THREADS {
match result_channels[i].1.recv() {
Ok((fuzzing_input, fuzzing_state)) => {
let mut lowest_similarity = u64::MAX;
/* let mut lowest_similarity = u64::MAX;
for (_j, state) in previous_states.iter().enumerate() {
let similiarty =
hamming::distance(&[state[0x86]], &[fuzzing_state.cpu_state.mem[0x86]]);
// println!("{} {} {} ",i,j,similiarty);
hamming::distance(&state, &fuzzing_state.cpu_state.mem);
if similiarty < lowest_similarity {
lowest_similarity = similiarty
}
}
previous_states.insert(fuzzing_state.cpu_state.mem.clone());
previous_states.insert(fuzzing_state.cpu_state.mem.clone());*/
let mut lowest_similarity = 0u64;
for addr in fuzzing_state.cpu_state.addresses_fetched.iter() {
if previous_states.insert(*addr) {
lowest_similarity += 1;
}
}
if i == 3 && loop_count == 3 {
fuzzing_input.export(
"megaman_interestng.fm2".parse().unwrap(),
fuzzing_state.frames,
);
}
if SINGLE_SHOT {
if lowest_similarity > 0 {
for i in 0..lowest_similarity.log10() {
println!("{} ", lowest_similarity);
for i in 0..lowest_similarity {
let mut mutated_input: FuzzingInput = fuzzing_input.clone();
mutated_input.mutate_from(
&mut rng,
@ -239,6 +257,7 @@ fn main() -> Result<(), String> {
);
}
}
loop_count += 1;
}
}

View File

@ -171,7 +171,6 @@ impl Ppu {
}
let mut pixel: Option<(usize, usize, (u8, u8, u8))> = None;
// Visible scanlines (0-239)
if rendering && (self.scanline < 240 || self.scanline == 261) {
// background-related things
@ -236,8 +235,6 @@ impl Ppu {
// signal time to draw frame
self.line_cycle += 1;
if self.line_cycle == 341 {

View File

@ -124,7 +124,7 @@ impl super::Ppu {
}
// let pixel = self.read(palette_address as usize) as usize;
let pixel = self.palette_ram[palette_address as usize] as usize;
let mut color: (u8, u8, u8) = super::PALETTE_TABLE[pixel];
let mut color: (u8, u8, u8) = super::PALETTE_TABLE[pixel % 64];
if self.emphasize_red {
color.0 = emphasize(&color.0);
color.1 = deemphasize(&color.1);

View File

@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PpuData {
line_cycle: usize,
scanline: usize,