Playing with success cases / defenses against arbitrary code execution reading/writing weird addresses :)
This commit is contained in:
parent
9c785f3f68
commit
5c86ead1d3
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
49
src/input.rs
49
src/input.rs
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue