Skip to content

Instantly share code, notes, and snippets.

@kitlith
Last active December 8, 2019 09:31
Show Gist options
  • Save kitlith/a26a1021c268b34bef60d57cfc6cc19f to your computer and use it in GitHub Desktop.
Save kitlith/a26a1021c268b34bef60d57cfc6cc19f to your computer and use it in GitHub Desktop.
Memory Dump for Microcorruption
// ==UserScript==
// @name Memory Dump for Microcorruption
// @namespace Kitlith
// @match https://*.microcorruption.com/cpu/debugger
// @grant none
// @version 1.0
// @author Kitlith
// @require http://localhost:8000/microcorruption_wasm.js
// @description Adds a command (dump) to the debugger that performs a memory dump and downloads it as "mcorrupt_dump.bin"
// ==/UserScript==
var wasm;
// TODO: embed the final wasm file?
wasm_bindgen('http://localhost:8000/microcorruption_wasm_bg.wasm').then(o => console.log(wasm_bindgen.example()));
window.cpu._dump = (function () {
// File download code from: https://stackoverflow.com/a/19328891
var a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
return function (e) {
// cpu.memory is a sparse js 'array', let's convert it to a full 64KiB array before downloading
var memory = new Uint8Array(0x10000);
for (key in cpu.memory) {
memory[key] = cpu.memory[key];
}
var symbols = {};
// for (elem of document.getElementsByClassName("insnlabel")) {
// let addr = parseInt(elem.innerText.slice(0, 4), 16); // first four characters are address in hex
// let name = elem.textContent.slice(7, -2); // skip the addr, skip ' <', and leave out '>'
// symbols[name] = addr;
// }
console.log(symbols);
// By querying whoami, we can get the current level name. woo!
cpu.get('/whoami', function(e) {
let elf = wasm_bindgen.gen_elf(e.level, memory, symbols);
var url = window.URL.createObjectURL(new Blob([elf], {type: "application/octet-stream"}));
a.href = url;
a.download = e.level + ".elf";
a.click();
window.URL.revokeObjectURL(url);
});
};
}());
mod utils;
use wasm_bindgen::prelude::*;
use faerie::{ArtifactBuilder, Data, DataType, Decl, artifact::DefinedDecl, SectionKind};
use goblin::{elf, container::{Ctx, Endian, Container}};
use scroll::{Pread, Pwrite};
use target_lexicon::{Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor};
use std::collections::BTreeMap;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
// #[wasm_bindgen]
// extern {
// fn alert(s: &str);
// }
#[wasm_bindgen]
pub fn gen_elf(name: &str, memory: Box<[u8]>, symbols: &JsValue) -> Result<Box<[u8]>, JsValue> {
let symbols: BTreeMap<String, u64> = symbols.into_serde().map_err(|_| "Bad symbol input")?;
let target = Triple {
architecture: Architecture::Msp430,
vendor: Vendor::Unknown,
operating_system: OperatingSystem::None_,
environment: Environment::Unknown,
binary_format: BinaryFormat::Elf
};
let mut artifact = ArtifactBuilder::new(target)
.name(name.to_string())
.library(false)
.finish();
let section = Decl::section(SectionKind::Text)
.with_datatype(DataType::Bytes)
.with_writable(true)
.with_executable(true)
.with_loaded(true);
artifact.declare("flash", Decl::Defined(DefinedDecl::Section(section))).map_err(|e| e.to_string())?;
artifact.define_with_symbols("flash", Data::Blob(memory.into_vec()), symbols).map_err(|e| e.to_string())?;
let mut bin = artifact.emit().map_err(|e| e.to_string())?;
// Now let's modify the output of that to create a real executable.
// read header
let ctx = Ctx::new(Container::Little, Endian::Little);
let mut header: elf::Header = bin.pread_with(0, ctx.le).map_err(|e: goblin::error::Error| e.to_string())?;
// make it an executable
header.e_type = elf::header::ET_EXEC;
header.e_entry = 0x4400;
header.e_flags = 0x00000112; // EXEC_P, HAS_SYMS, D_PAGED
// read section header
let sheader: elf::SectionHeader = bin.pread_with((header.e_shoff + (header.e_shentsize as u64 * 3)) as usize, ctx).map_err(|e: goblin::error::Error| e.to_string())?;
// build a ProgramHeader
let mut pheader: elf::ProgramHeader = elf::ProgramHeader::new();
pheader.read(); pheader.write(); pheader.executable(); // rwx
pheader.p_offset = sheader.sh_offset;
pheader.p_vaddr = 0;
pheader.p_paddr = 0;
pheader.p_filesz = sheader.sh_size;
pheader.p_memsz = sheader.sh_size;
// locate the ProgramHeader
header.e_phoff = bin.len() as u64; // end of file. TODO: alignment?
header.e_phentsize = elf::ProgramHeader::size(ctx) as u16;
header.e_phnum = 1;
// write ProgramHeader
let mut phbin = vec![0; header.e_phentsize as usize];
phbin.pwrite_with(pheader, 0, ctx).map_err(|e| e.to_string())?;
bin.extend(phbin);
// write the updated header
bin.pwrite_with(header, 0, ctx.le).map_err(|e| e.to_string())?;
Ok(bin.into_boxed_slice())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment