1pub use pcode_ir::{AddressSpaceId, DecodeError, Instruction, PcodeOp, Varnode};
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub enum Architecture {
47 X86_64,
49 X86_32,
51 AArch64,
53 ARM32,
55 MIPS32,
57 RiscV64,
59}
60
61impl Architecture {
62 pub fn addr_size(&self) -> u32 {
64 match self {
65 Architecture::X86_64 | Architecture::AArch64 | Architecture::RiscV64 => 8,
66 Architecture::X86_32 | Architecture::ARM32 | Architecture::MIPS32 => 4,
67 }
68 }
69
70 pub fn register_name(&self, offset: u64, size: u32) -> Option<&'static str> {
74 match self {
75 Architecture::X86_64 => x86_root::register_name(offset, size),
76 Architecture::X86_32 => x86_32_root::register_name(offset, size),
77 Architecture::AArch64 => aarch64_root::register_name(offset, size),
78 Architecture::ARM32 => arm32_root::register_name(offset, size),
79 Architecture::MIPS32 => mips_root::register_name(offset, size),
80 Architecture::RiscV64 => riscv_root::register_name(offset, size),
81 }
82 }
83}
84
85pub struct Decoder {
90 arch: Architecture,
91 inner: DecoderInner,
92}
93
94enum DecoderInner {
95 X86_64 {
96 context: x86_root::ContextMemory,
97 global_set: x86_root::GlobalSet,
98 },
99 X86_32 {
100 context: x86_32_root::ContextMemory,
101 global_set: x86_32_root::GlobalSet,
102 },
103 AArch64 {
104 context: aarch64_root::ContextMemory,
105 global_set: aarch64_root::GlobalSet,
106 },
107 ARM32 {
108 context: arm32_root::ContextMemory,
109 global_set: arm32_root::GlobalSet,
110 },
111 MIPS32 {
112 context: mips_root::ContextMemory,
113 global_set: mips_root::GlobalSet,
114 },
115 RiscV64 {
116 context: riscv_root::ContextMemory,
117 global_set: riscv_root::GlobalSet,
118 },
119}
120
121impl Decoder {
122 pub fn new(arch: Architecture) -> Self {
124 let inner = match arch {
125 Architecture::X86_64 => {
126 let mut ctx = x86_root::ContextMemory::default();
127 ctx.write_longMode(1);
129 ctx.write_addrsize(2);
130 ctx.write_opsize(1);
131 let gs = x86_root::GlobalSet::new({
132 let mut c = x86_root::ContextMemory::default();
133 c.write_longMode(1);
134 c.write_addrsize(2);
135 c.write_opsize(1);
136 c
137 });
138 DecoderInner::X86_64 {
139 context: ctx,
140 global_set: gs,
141 }
142 }
143 Architecture::X86_32 => {
144 let mut ctx = x86_32_root::ContextMemory::default();
147 ctx.write_addrsize(1);
148 ctx.write_opsize(1);
149 let gs = x86_32_root::GlobalSet::new({
150 let mut c = x86_32_root::ContextMemory::default();
151 c.write_addrsize(1);
152 c.write_opsize(1);
153 c
154 });
155 DecoderInner::X86_32 {
156 context: ctx,
157 global_set: gs,
158 }
159 }
160 Architecture::AArch64 => DecoderInner::AArch64 {
161 context: aarch64_root::ContextMemory::default(),
162 global_set: aarch64_root::GlobalSet::new(aarch64_root::ContextMemory::default()),
163 },
164 Architecture::ARM32 => DecoderInner::ARM32 {
165 context: arm32_root::ContextMemory::default(),
166 global_set: arm32_root::GlobalSet::new(arm32_root::ContextMemory::default()),
167 },
168 Architecture::MIPS32 => DecoderInner::MIPS32 {
169 context: mips_root::ContextMemory::default(),
170 global_set: mips_root::GlobalSet::new(mips_root::ContextMemory::default()),
171 },
172 Architecture::RiscV64 => DecoderInner::RiscV64 {
173 context: riscv_root::ContextMemory::default(),
174 global_set: riscv_root::GlobalSet::new(riscv_root::ContextMemory::default()),
175 },
176 };
177 Self { arch, inner }
178 }
179
180 pub fn architecture(&self) -> Architecture {
182 self.arch
183 }
184
185 pub fn decode(&mut self, bytes: &[u8], addr: u64) -> Result<Instruction, DecodeError> {
194 match &mut self.inner {
199 DecoderInner::X86_64 {
200 context,
201 global_set,
202 } => {
203 if let Some(inst) = fallback_x86_64_mov_from_rsp_sib(bytes, addr) {
204 return Ok(inst);
205 }
206
207 let mut ctx = *context;
208 if let Some((inst_next, display, mut ops)) =
209 x86_root::parse_instruction(bytes, &mut ctx, addr, global_set)
210 {
211 pcode_ir::optimize(&mut ops);
212 Ok(Instruction {
213 len: inst_next - addr,
214 disassembly: format_display(&display),
215 ops,
216 constructor: None,
217 })
218 } else {
219 Err(DecodeError::UnknownInstruction)
220 }
221 }
222 DecoderInner::X86_32 {
223 context,
224 global_set,
225 } => {
226 let mut ctx = *context;
227 let addr32 = addr as u32;
228 let (inst_next, display, mut ops) =
229 x86_32_root::parse_instruction(bytes, &mut ctx, addr32, global_set)
230 .ok_or(DecodeError::UnknownInstruction)?;
231 pcode_ir::optimize(&mut ops);
232 Ok(Instruction {
233 len: (inst_next - addr32) as u64,
234 disassembly: format_display(&display),
235 ops,
236 constructor: None,
237 })
238 }
239 DecoderInner::AArch64 {
240 context,
241 global_set,
242 } => {
243 let mut ctx = *context;
244 let (inst_next, display, mut ops) =
245 aarch64_root::parse_instruction(bytes, &mut ctx, addr, global_set)
246 .ok_or(DecodeError::UnknownInstruction)?;
247 pcode_ir::optimize(&mut ops);
248 Ok(Instruction {
249 len: inst_next - addr,
250 disassembly: format_display(&display),
251 ops,
252 constructor: None,
253 })
254 }
255 DecoderInner::ARM32 {
256 context,
257 global_set,
258 } => {
259 let mut ctx = *context;
260 let addr32 = addr as u32;
261 let (inst_next, display, mut ops) =
262 arm32_root::parse_instruction(bytes, &mut ctx, addr32, global_set)
263 .ok_or(DecodeError::UnknownInstruction)?;
264 pcode_ir::optimize(&mut ops);
265 Ok(Instruction {
266 len: (inst_next - addr32) as u64,
267 disassembly: format_display(&display),
268 ops,
269 constructor: None,
270 })
271 }
272 DecoderInner::MIPS32 {
273 context,
274 global_set,
275 } => {
276 let mut ctx = *context;
277 let addr32 = addr as u32;
278 let (inst_next, display, mut ops) =
279 mips_root::parse_instruction(bytes, &mut ctx, addr32, global_set)
280 .ok_or(DecodeError::UnknownInstruction)?;
281 pcode_ir::optimize(&mut ops);
282 Ok(Instruction {
283 len: (inst_next - addr32) as u64,
284 disassembly: format_display(&display),
285 ops,
286 constructor: None,
287 })
288 }
289 DecoderInner::RiscV64 {
290 context,
291 global_set,
292 } => {
293 let mut ctx = *context;
294 let (inst_next, display, mut ops) =
295 riscv_root::parse_instruction(bytes, &mut ctx, addr, global_set)
296 .ok_or(DecodeError::UnknownInstruction)?;
297 pcode_ir::optimize(&mut ops);
298 Ok(Instruction {
299 len: inst_next - addr,
300 disassembly: format_display(&display),
301 ops,
302 constructor: None,
303 })
304 }
305 }
306 }
307}
308
309fn fallback_x86_64_mov_from_rsp_sib(bytes: &[u8], addr: u64) -> Option<Instruction> {
310 let mut pos = 0usize;
311 let mut rex = 0u8;
312 if bytes
313 .first()
314 .copied()
315 .is_some_and(|b| (0x40..=0x4f).contains(&b))
316 {
317 rex = bytes[0];
318 pos += 1;
319 }
320 if bytes.get(pos).copied()? != 0x8b {
321 return None;
322 }
323 pos += 1;
324
325 let modrm = bytes.get(pos).copied()?;
326 pos += 1;
327 let mode = modrm >> 6;
328 let reg = ((modrm >> 3) & 7) | ((rex & 0x04) << 1);
329 let rm = modrm & 7;
330 if rm != 4 || !matches!(mode, 1 | 2) {
331 return None;
332 }
333
334 let sib = bytes.get(pos).copied()?;
335 pos += 1;
336 let index = (sib >> 3) & 7;
337 let base = (sib & 7) | ((rex & 0x01) << 3);
338 if index != 4 || (rex & 0x02) != 0 || !matches!(base, 4 | 12) {
339 return None;
340 }
341
342 let disp = match mode {
343 1 => {
344 let d = *bytes.get(pos)? as i8 as i64;
345 pos += 1;
346 d
347 }
348 2 => {
349 let raw = bytes.get(pos..pos + 4)?;
350 pos += 4;
351 i32::from_le_bytes(raw.try_into().ok()?) as i64
352 }
353 _ => return None,
354 };
355
356 let size = if rex & 0x08 != 0 { 8 } else { 4 };
357 let dest = x86_reg_varnode(reg, size)?;
358 let base_vn = x86_reg_varnode(base, 8)?;
359 let size_name = if size == 8 { "qword" } else { "dword" };
360 let disassembly = if disp == 0 {
361 format!(
362 "MOV {},{} ptr [{}]",
363 x86_reg_name(reg, size)?,
364 size_name,
365 x86_reg_name(base, 8)?,
366 )
367 } else {
368 format!(
369 "MOV {},{} ptr [{} {} {:#x}]",
370 x86_reg_name(reg, size)?,
371 size_name,
372 x86_reg_name(base, 8)?,
373 if disp < 0 { "-" } else { "+" },
374 disp.unsigned_abs()
375 )
376 };
377
378 let mut ops = Vec::new();
379 let ptr = if disp == 0 {
380 base_vn
381 } else {
382 let ptr = Varnode::unique((addr << 16).wrapping_add(0x8000), 8);
383 ops.push(PcodeOp::IntAdd {
384 out: ptr,
385 left: base_vn,
386 right: Varnode::constant(disp as u64, 8),
387 });
388 ptr
389 };
390 ops.push(PcodeOp::Load {
391 out: dest,
392 space: pcode_ir::AddressSpaceId::Ram,
393 ptr,
394 });
395
396 Some(Instruction {
397 len: pos as u64,
398 disassembly,
399 ops,
400 constructor: None,
401 })
402}
403
404fn x86_reg_varnode(reg: u8, size: u32) -> Option<Varnode> {
405 let offset = match reg {
406 0..=7 => u64::from(reg) * 8,
407 8..=15 => 0x80 + (u64::from(reg) - 8) * 8,
408 _ => return None,
409 };
410 Some(Varnode::register(offset, size))
411}
412
413fn x86_reg_name(reg: u8, size: u32) -> Option<&'static str> {
414 const R32: [&str; 16] = [
415 "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI", "R8D", "R9D", "R10D", "R11D",
416 "R12D", "R13D", "R14D", "R15D",
417 ];
418 const R64: [&str; 16] = [
419 "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12",
420 "R13", "R14", "R15",
421 ];
422 match size {
423 4 => R32.get(reg as usize).copied(),
424 8 => R64.get(reg as usize).copied(),
425 _ => None,
426 }
427}
428
429fn format_display(elements: &[impl core::fmt::Display]) -> String {
431 elements.iter().map(|d| format!("{}", d)).collect()
432}
433
434#[cfg(test)]
435mod tests {
436 use super::*;
437
438 #[test]
439 fn x86_64_fallback_decodes_mov_r12d_rsp_disp32() {
440 let mut dec = Decoder::new(Architecture::X86_64);
441 let inst = dec
442 .decode(&[0x44, 0x8b, 0xa4, 0x24, 0x88, 0x00, 0x00, 0x00], 0x1000)
443 .expect("decode MOV R12D,[RSP+0x88]");
444
445 assert_eq!(inst.len, 8);
446 assert!(inst.disassembly.contains("R12D"), "{}", inst.disassembly);
447 assert!(inst.ops.iter().any(|op| {
448 matches!(
449 op,
450 PcodeOp::Load {
451 out,
452 space: pcode_ir::AddressSpaceId::Ram,
453 ..
454 } if *out == Varnode::register(0xa0, 4)
455 )
456 }));
457 }
458}