Skip to content

Commit

Permalink
Switch to Capstone, since we use weird instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
mkeeter committed Oct 2, 2024
1 parent ab68de7 commit b6ad2f7
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 73 deletions.
42 changes: 21 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ bitflags = { version = "2.5.0", default-features = false }
bstringify = { version = "0.1.2", default-features = false }
byteorder = { version = "1.3.4", default-features = false }
bzip2-rs = { version = "0.1.2", default-features = false }
capstone = { version = "0.12.0", default-features = false, features = ["full"] }
cargo_metadata = { version = "0.12.0", default-features = false }
cfg-if = { version = "1", default-features = false }
chrono = { version = "0.4", default-features = false }
Expand Down Expand Up @@ -115,8 +116,6 @@ toml = { version = "0.7", default-features = false, features = ["parse", "displa
toml_edit = { version = "0.19", default-features = false }
vcell = { version = "0.1.2", default-features = false }
walkdir = { version = "2.0.0", default-features = false }
yaxpeax-arm = { version = "0.3.0", default-features = false }
yaxpeax-arch = { version = "0.3.2", default-features = false }
zerocopy = { version = "0.6.1", default-features = false }
zeroize = { version = "1.5.7", default-features = false, features = ["zeroize_derive"] }
zip = { version = "0.6", default-features = false, features = ["bzip2"] }
Expand Down
2 changes: 1 addition & 1 deletion app/gimlet/base.toml
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ name = "task-control-plane-agent"
priority = 6
max-sizes = {flash = 131072, ram = 32768}
# This can probably overkill and can be tuned later
stacksize = 4096
stacksize = 6000
start = true
uses = ["usart1"]
task-slots = [
Expand Down
3 changes: 1 addition & 2 deletions build/xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ toml_edit = { workspace = true }
# for dist
byteorder = { workspace = true }
bzip2-rs = { workspace = true }
capstone = { workspace = true }
ctrlc = { workspace = true }
dunce = { workspace = true }
filetime = { workspace = true }
Expand All @@ -40,8 +41,6 @@ tlvc = { workspace = true }
tlvc-text = { workspace = true }
toml = { workspace = true }
walkdir = { workspace = true }
yaxpeax-arm = { workspace = true }
yaxpeax-arch = { workspace = true }
zerocopy = { workspace = true }
zip = { workspace = true }

Expand Down
100 changes: 53 additions & 47 deletions build/xtask/src/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,9 +1064,6 @@ fn build_task(cfg: &PackageConfig, name: &str) -> Result<()> {
}

fn task_can_overflow(cfg: &PackageConfig, task_name: &str) -> Result<bool> {
use yaxpeax_arch::{AddressDisplay, Decoder, ReadError, Reader, U8Reader};
use yaxpeax_arm::armv7::{DecodeError, InstDecoder, Opcode, Operand};

// Open the statically-linked ELF file
let f = cfg.dist_file(format!("{task_name}.tmp"));
let data = std::fs::read(f).context("could not open ELF file")?;
Expand Down Expand Up @@ -1120,6 +1117,18 @@ fn task_can_overflow(cfg: &PackageConfig, task_name: &str) -> Result<bool> {
text_regions.insert(addr, is_text);
}

use capstone::{
arch::{arm, ArchOperand, BuildsCapstone, BuildsCapstoneExtraMode},
Capstone, InsnGroupId, InsnGroupType,
};
let cs = Capstone::new()
.arm()
.mode(arm::ArchMode::Thumb)
.extra_mode(std::iter::once(arm::ArchExtraMode::MClass))
.detail(true)
.build()
.map_err(|e| anyhow!("failed to initialize disassembler: {e:?}"))?;

let mut fns = BTreeMap::new();
for sym in elf.syms.iter() {
if sym.st_name == 0 || !sym.is_function() || sym.st_size == 0 {
Expand All @@ -1142,55 +1151,52 @@ fn task_can_overflow(cfg: &PackageConfig, task_name: &str) -> Result<bool> {
let offset = (val - text.sh_addr + text.sh_offset) as usize;
let text = &data[offset..][..sym.st_size as usize];

// Disassemble, looking for BL operations
let mut reader = U8Reader::new(text);

let decoder = InstDecoder::default_thumb();
let mut calls = vec![];
loop {
// Before reading, check to see if we're in a data section and skip
// over bytes if that's the case.
let addr = base_addr
+ <U8Reader as Reader<u32, u8>>::total_offset(&mut reader);
// Split the text region into instruction-only chunks
let mut chunks = vec![];
let mut chunk = None;
for (i, b) in text.iter().enumerate() {
let addr = base_addr + i as u32;
let mut iter = text_regions.range(..=addr);
if !iter.next_back().unwrap().1 {
match <U8Reader as Reader<u32, u8>>::next(&mut reader) {
Err(ReadError::ExhaustedInput) => break,
Err(e) => bail!("got read error: {e:?}"),
Ok(_) => continue,
// Check if this is a code or data byte
if *iter.next_back().unwrap().1 {
if chunk.is_none() {
chunk = Some((addr, vec![]));
}
chunk.as_mut().unwrap().1.push(*b);
} else if let Some(c) = chunk.take() {
chunks.push(c);
}
}
// Process data in the trailing chunk
if let Some(c) = chunk {
chunks.push(c);
}

// Decode the instruction and get the post-instruction address
// (used for pc-relative jumps)
let decode_res = decoder.decode(&mut reader);
let addr = base_addr
+ <U8Reader as Reader<u32, u8>>::total_offset(&mut reader);

match decode_res {
Ok(ref inst) => match (inst.opcode, inst.operands[0]) {
(Opcode::BL, Operand::BranchThumbOffset(i)) => {
let target = addr.checked_add_signed(i * 2).unwrap();
calls.push(target)
}
(Opcode::BL, v) => {
println!(
"{}: cannot find call target with offset {v:?}",
addr.show()
)
let mut calls = vec![];
for (addr, chunk) in chunks {
let instrs = cs
.disasm_all(&chunk, addr.into())
.map_err(|e| anyhow!("disassembly failed: {e:?}"))?;
for instr in instrs.iter() {
let detail = cs.insn_detail(instr).map_err(|e| {
anyhow!("could not get instruction details: {e}")
})?;
if detail.groups().iter().any(|g| {
g == &InsnGroupId(InsnGroupType::CS_GRP_CALL as u8)
}) {
let arch = detail.arch_detail();
let ops = arch.operands();
let op = ops.last().unwrap_or_else(|| {
panic!("missing operand!");
});

let ArchOperand::ArmOperand(op) = op else {
panic!("bad operand type: {op:?}");
};
// We can't resolve indirect calls, alas
if let arm::ArmOperandType::Imm(a) = op.op_type {
calls.push(a.try_into().unwrap());
}
_ => (),
},
Err(DecodeError::Incomplete) => {
// yaxpeax is not complete, alas
continue;
}
Err(DecodeError::ExhaustedInput) => {
break;
}
Err(e) => {
println!(" {}: decode error in {name}: {e}", addr.show());
break;
}
}
}
Expand Down

0 comments on commit b6ad2f7

Please sign in to comment.