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]
|
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
|
0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,16 @@ impl Mapper for Uxrom {
|
||||||
self.chr_ram[address]
|
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],
|
0xC000..=0xFFFF => self.cart.prg_rom[self.cart.prg_rom.len() - 1][address % 0x4000],
|
||||||
_ => {
|
_ => {
|
||||||
println!("bad address read from UxROM mapper: 0x{:X}", address);
|
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),
|
| 0xFC | 0xFD => self.address_page_cross(old_address, new_address),
|
||||||
0x1E | 0x1F | 0x3E | 0x3F | 0x5E | 0x5F | 0x7E | 0x7F | 0x9D | 0xC3 | 0xC7 | 0xCF
|
0x1E | 0x1F | 0x3E | 0x3F | 0x5E | 0x5F | 0x7E | 0x7F | 0x9D | 0xC3 | 0xC7 | 0xCF
|
||||||
| 0xD3 | 0xD7 | 0xDB | 0xDE | 0xDF | 0xFE | 0xFF => self.clock += 1,
|
| 0xD3 | 0xD7 | 0xDB | 0xDE | 0xDF | 0xFE | 0xFF => self.clock += 1,
|
||||||
_ => panic!("illegal opcode using abs x: {:02x}", current_opcode),
|
_ => self.clock += 1,
|
||||||
}
|
}
|
||||||
new_address
|
new_address
|
||||||
}
|
}
|
||||||
|
|
|
@ -669,7 +669,7 @@ impl Cpu {
|
||||||
self.handle_interrupts();
|
self.handle_interrupts();
|
||||||
|
|
||||||
// back up clock so we know how many cycles we complete
|
// back up clock so we know how many cycles we complete
|
||||||
|
self.addresses_fetched.insert(self.pc);
|
||||||
self.before_clock = self.clock;
|
self.before_clock = self.clock;
|
||||||
let opcode = <usize>::from(self.read(self.pc));
|
let opcode = <usize>::from(self.read(self.pc));
|
||||||
self.addresses_fetched.insert(self.pc);
|
self.addresses_fetched.insert(self.pc);
|
||||||
|
@ -689,7 +689,7 @@ impl Cpu {
|
||||||
|
|
||||||
// memory interface
|
// memory interface
|
||||||
pub fn read(&mut self, address: usize) -> u8 {
|
pub fn read(&mut self, address: usize) -> u8 {
|
||||||
// self.addresses_fetched.insert(address);
|
self.addresses_fetched.insert(address);
|
||||||
let val = match address {
|
let val = match address {
|
||||||
0x0000..=0x1FFF => self.mem[address % 0x0800],
|
0x0000..=0x1FFF => self.mem[address % 0x0800],
|
||||||
0x2000..=0x3FFF => self.read_ppu_reg(address % 8),
|
0x2000..=0x3FFF => self.read_ppu_reg(address % 8),
|
||||||
|
@ -700,14 +700,14 @@ impl Cpu {
|
||||||
0x4000..=0x4017 => 0, // can't read from these APU registers
|
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.
|
0x4018..=0x401F => 0, // APU and I/O functionality that is normally disabled. See CPU Test Mode.
|
||||||
0x4020..=0xFFFF => self.mapper.borrow().read(address),
|
0x4020..=0xFFFF => self.mapper.borrow().read(address),
|
||||||
_ => panic!("invalid read from 0x{:02x}", address),
|
_ => 0, //panic!("invalid read from 0x{:02x}", address),
|
||||||
};
|
};
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
// memory interface
|
// memory interface
|
||||||
fn write(&mut self, address: usize, val: u8) {
|
fn write(&mut self, address: usize, val: u8) {
|
||||||
// self.addresses_fetched.insert(address);
|
self.addresses_fetched.insert(address);
|
||||||
match address {
|
match address {
|
||||||
0x0000..=0x1FFF => self.mem[address % 0x0800] = val,
|
0x0000..=0x1FFF => self.mem[address % 0x0800] = val,
|
||||||
0x2000..=0x3FFF => self.write_ppu_reg(address % 8, val),
|
0x2000..=0x3FFF => self.write_ppu_reg(address % 8, val),
|
||||||
|
@ -716,7 +716,7 @@ impl Cpu {
|
||||||
0x4000..=0x4017 => self.apu.write_reg(address, val),
|
0x4000..=0x4017 => self.apu.write_reg(address, val),
|
||||||
0x4018..=0x401F => (), // APU and I/O functionality that is normally disabled. See CPU Test Mode.
|
0x4018..=0x401F => (), // APU and I/O functionality that is normally disabled. See CPU Test Mode.
|
||||||
0x4020..=0xFFFF => self.mapper.borrow_mut().write(address, val),
|
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) {
|
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
|
// Interrupts
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::Mode;
|
use super::Mode;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct CpuData {
|
pub struct CpuData {
|
||||||
|
@ -8,7 +9,7 @@ pub struct CpuData {
|
||||||
a: u8,
|
a: u8,
|
||||||
x: u8,
|
x: u8,
|
||||||
y: u8,
|
y: u8,
|
||||||
pc: usize,
|
pub(crate) pc: usize,
|
||||||
s: u8,
|
s: u8,
|
||||||
p: u8,
|
p: u8,
|
||||||
clock: u64,
|
clock: u64,
|
||||||
|
@ -19,6 +20,7 @@ pub struct CpuData {
|
||||||
button_number1: u8,
|
button_number1: u8,
|
||||||
button_number2: u8,
|
button_number2: u8,
|
||||||
mode_table: Vec<Mode>,
|
mode_table: Vec<Mode>,
|
||||||
|
pub(crate) addresses_fetched: HashSet<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Cpu {
|
impl super::Cpu {
|
||||||
|
@ -39,6 +41,7 @@ impl super::Cpu {
|
||||||
button_number1: self.button_number1,
|
button_number1: self.button_number1,
|
||||||
button_number2: self.button_number2,
|
button_number2: self.button_number2,
|
||||||
mode_table: self.mode_table.clone(),
|
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_number1 = data.button_number1;
|
||||||
self.button_number2 = data.button_number2;
|
self.button_number2 = data.button_number2;
|
||||||
self.mode_table = data.mode_table;
|
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 {
|
} else {
|
||||||
tas = format!("{}.", tas);
|
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");
|
fs::write(filename, tas).expect("Unable to write file");
|
||||||
|
@ -96,7 +140,7 @@ impl FuzzingInput {
|
||||||
self.frames.push(FrameInput {
|
self.frames.push(FrameInput {
|
||||||
console_action: ConsoleAction::None,
|
console_action: ConsoleAction::None,
|
||||||
player_1_input: rng.next() as u8,
|
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 (check / frames_to_consider as f64) < MUTATION_RATE {
|
||||||
if frame_num < self.frames.len() {
|
if frame_num < self.frames.len() {
|
||||||
self.frames[frame_num].player_1_input = rng.next() as u8;
|
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};
|
use std::{fs, thread};
|
||||||
|
|
||||||
// The number of cpu instances to spawn..
|
// 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
|
// The number of frames to fuzz and process
|
||||||
// A small number exploits the current point more at the expense of
|
// 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...
|
// Same input should generate the same output...
|
||||||
// (I make no guarantee of that at the moment)
|
// (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
|
// 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...
|
// 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..
|
// 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..
|
// The rate at which seed inputs may become soft resets..
|
||||||
const MUTATION_RATE_SOFT_RESET: f64 = 0.00;
|
const MUTATION_RATE_SOFT_RESET: f64 = 0.00;
|
||||||
|
|
||||||
// The number of cases to fuzz from a given seed input at each consideration point..
|
// 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)
|
// 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
|
// Fast forward the seed state to a given frame
|
||||||
const PLAY_FROM: usize = 0;
|
const PLAY_FROM: usize = 1900;
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
let argv = std::env::args().collect::<Vec<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 starting_state = FuzzingState::default(rom_filename.clone());
|
||||||
|
|
||||||
|
let mut previous_states: HashSet<usize> = HashSet::new();
|
||||||
|
|
||||||
if PLAY_FROM > 0 {
|
if PLAY_FROM > 0 {
|
||||||
println!("Playing {} frames prelude...", PLAY_FROM);
|
println!("Playing {} frames prelude...", PLAY_FROM);
|
||||||
let (mut cpu, _) = starting_state.load_state(rom_filename.clone());
|
let (mut cpu, _) = starting_state.load_state(rom_filename.clone());
|
||||||
play_frames(&mut cpu, PLAY_FROM, &seed_input);
|
play_frames(&mut cpu, PLAY_FROM, &seed_input);
|
||||||
starting_state = FuzzingState::save_state(cpu, PLAY_FROM);
|
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();
|
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();
|
let mut loop_count = 0;
|
||||||
previous_states.insert(vec![0; 8192]);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
println!("Prospective Cases: {}", fuzzing_queue.len());
|
println!("Prospective Cases: {}", fuzzing_queue.len());
|
||||||
|
@ -147,21 +151,35 @@ fn main() -> Result<(), String> {
|
||||||
for i in 0..NUM_THREADS {
|
for i in 0..NUM_THREADS {
|
||||||
match result_channels[i].1.recv() {
|
match result_channels[i].1.recv() {
|
||||||
Ok((fuzzing_input, fuzzing_state)) => {
|
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() {
|
for (_j, state) in previous_states.iter().enumerate() {
|
||||||
let similiarty =
|
let similiarty =
|
||||||
hamming::distance(&[state[0x86]], &[fuzzing_state.cpu_state.mem[0x86]]);
|
hamming::distance(&state, &fuzzing_state.cpu_state.mem);
|
||||||
// println!("{} {} {} ",i,j,similiarty);
|
|
||||||
if similiarty < lowest_similarity {
|
if similiarty < lowest_similarity {
|
||||||
lowest_similarity = similiarty
|
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 SINGLE_SHOT {
|
||||||
if lowest_similarity > 0 {
|
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();
|
let mut mutated_input: FuzzingInput = fuzzing_input.clone();
|
||||||
mutated_input.mutate_from(
|
mutated_input.mutate_from(
|
||||||
&mut rng,
|
&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;
|
let mut pixel: Option<(usize, usize, (u8, u8, u8))> = None;
|
||||||
|
|
||||||
|
|
||||||
// Visible scanlines (0-239)
|
// Visible scanlines (0-239)
|
||||||
if rendering && (self.scanline < 240 || self.scanline == 261) {
|
if rendering && (self.scanline < 240 || self.scanline == 261) {
|
||||||
// background-related things
|
// background-related things
|
||||||
|
@ -236,8 +235,6 @@ impl Ppu {
|
||||||
|
|
||||||
// signal time to draw frame
|
// signal time to draw frame
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.line_cycle += 1;
|
self.line_cycle += 1;
|
||||||
|
|
||||||
if self.line_cycle == 341 {
|
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.read(palette_address as usize) as usize;
|
||||||
let pixel = self.palette_ram[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 {
|
if self.emphasize_red {
|
||||||
color.0 = emphasize(&color.0);
|
color.0 = emphasize(&color.0);
|
||||||
color.1 = deemphasize(&color.1);
|
color.1 = deemphasize(&color.1);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct PpuData {
|
pub struct PpuData {
|
||||||
line_cycle: usize,
|
line_cycle: usize,
|
||||||
scanline: usize,
|
scanline: usize,
|
||||||
|
|
Loading…
Reference in New Issue