Skip to content

Commit

Permalink
Add dumpbin, refactor.
Browse files Browse the repository at this point in the history
  • Loading branch information
kitlith committed Dec 18, 2019
1 parent 9bd68f9 commit 8af147e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 53 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "microcorruption-wasm"
version = "0.1.0"
version = "0.0.3"
authors = ["Kitlith <[email protected]>"]
edition = "2018"

Expand All @@ -27,6 +27,7 @@ wee_alloc = { version = "0.4.2", optional = true }
target-lexicon = "0.9.0"
goblin = "0.1"
scroll = "0.10"
byteorder = "1.3.2"

serde = "^1.0.59"
serde_json = "^1.0.40"
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
_Liberate yourself from the microcorruption website_

## Usage
You'll need a userscript manager to use this. This userscript was tested with Violentmonkey.
You'll need a userscript manager to use this.
This userscript was tested with Violentmonkey.
Grab the userscript from [jsDelivr](https://cdn.jsdelivr.net/gh/kitlith/microcorruption-dumper/pkg/mcorrupt.user.js).

To dump a microcorruption level, type `dump` in the on-page debugger.
This script will perform a memory dump and parse the page for symbols,
providing an ELF file suitable for viewing in Ghidra for download.

Additionally, the `dumpbin` command just performs a memory dump and downloads
it, for use with tools like
[msp430-emu-uctf](https://github.com/cemeyer/msp430-emu-uctf) that don't
support the elf file format.


## Hacking
Build the wasm component with:
```sh
Expand Down
62 changes: 33 additions & 29 deletions pkg/mcorrupt.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// @namespace Kitlith
// @match https://*.microcorruption.com/cpu/debugger
// @grant GM_getResourceURL
// @version 0.0.1
// @version 0.0.3
// @author Kitlith
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js
// @require microcorruption_wasm.js
// @resource wasm microcorruption_wasm_bg.wasm
// @description Adds a command (dump) to the debugger that performs a memory dump (w/ symbols!) and downloads it as "<level name>.elf"
Expand All @@ -14,35 +15,38 @@
// For now, this works without too much modification.
wasm_bindgen(GM_getResourceURL('wasm'));

unsafeWindow.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];
}
function getMemory() {
// 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];
}

return memory;
}

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;
unsafeWindow.cpu._dump = function (e) {
var symbols = {};
for (elem of document.getElementsByClassName("insnlabel")) {
let addr = parseInt(elem.innerText.slice(0, 4), 16); // first four characters are address in hex
if (isNaN(addr)) {
continue;
}
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) {
var memory = getMemory();
let elf = wasm_bindgen.gen_elf(e.level, memory, symbols);
saveAs(new Blob([elf], {type: "application/octet-stream"}), e.level + ".elf");
});
};

// 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 = unsafeWindow.URL.createObjectURL(new Blob([elf], {type: "application/octet-stream"}));
a.href = url;
a.download = e.level + ".elf";
a.click();
unsafeWindow.URL.revokeObjectURL(url);
});
};
}());
unsafeWindow.cpu._dumpbin = function () {
cpu.get('/whoami', function(e) {
var memory = getMemory();
saveAs(new Blob([memory], {type: "application/octet-stream"}), e.level + ".bin");
});
}
52 changes: 30 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use goblin::{elf, container::{Ctx, Endian, Container}};
use scroll::{Pread, Pwrite};
use target_lexicon::{Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor};
use std::collections::BTreeMap;
use byteorder::{ByteOrder, LE};

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
Expand All @@ -29,6 +30,8 @@ pub fn gen_elf(name: &str, memory: Box<[u8]>, symbols: &JsValue) -> Result<Box<[
binary_format: BinaryFormat::Elf
};

let entry_point = LE::read_u16(&memory[0xfffe..]).into();

let mut artifact = ArtifactBuilder::new(target)
.name(name.to_string())
.library(false)
Expand All @@ -47,39 +50,44 @@ pub fn gen_elf(name: &str, memory: Box<[u8]>, symbols: &JsValue) -> Result<Box<[

// Now let's modify the output of that to create a real executable, so that it works with Ghidra.

// 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())?;
let mut elf: goblin::elf::Elf = bin.pread(0).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())?;
elf.header.e_type = elf::header::ET_EXEC;
elf.header.e_entry = entry_point;
elf.header.e_flags = 0x00000112; // EXEC_P, HAS_SYMS, D_PAGED

// 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;
let pheader = elf.section_headers.iter()
.find(|h| h.sh_flags & elf::section_header::SHF_ALLOC as u64 != 0)
.map(|section| elf::ProgramHeader {
p_type: elf::program_header::PT_LOAD,
p_flags: 7, // rwx, probably // TODO
p_offset: section.sh_offset,
p_vaddr: 0,
p_paddr: 0,
p_filesz: section.sh_size,
p_memsz: section.sh_size,
p_align: 0x10000
}).ok_or("missing load section")?;

let ctx = Ctx::new(Container::Little, Endian::Little);

// 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;
elf.header.e_phoff = bin.len() as u64; // end of file. TODO: alignment?
elf.header.e_phentsize = elf::ProgramHeader::size(ctx) as u16;
elf.header.e_phnum = 1;

let header = elf.header;
drop(elf);

// write the updated header
bin.pwrite_with(header, 0, ctx.le).map_err(|e| e.to_string())?;

// 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())
}

0 comments on commit 8af147e

Please sign in to comment.