Skip to content

Commit

Permalink
Merge pull request #60 from Jon-Becker/feat/decode-lib
Browse files Browse the repository at this point in the history
✨ feat: decode libaray
  • Loading branch information
Jon-Becker authored Feb 12, 2023
2 parents c06a4ad + b9cc5aa commit 979cedd
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 12 deletions.
7 changes: 4 additions & 3 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "heimdall-common"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
license = "MIT"
readme = "README.md"
Expand All @@ -17,4 +17,5 @@ tokio = { version = "1", features = ["full"] }
clap = { version = "3.1.18", features = ["derive"] }
ethers = "1.0.0"
reqwest = { version = "0.11.11", features = ["blocking"] }
serde_json = "1.0"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
8 changes: 4 additions & 4 deletions common/src/ether/signatures.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
use ethers::abi::Token;

use crate::utils::{http::get_json_from_url, strings::replace_last};
use serde::{Deserialize, Serialize};


#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedFunction {
pub name: String,
pub signature: String,
pub inputs: Vec<String>,
pub decoded_inputs: Option<Vec<Token>>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedError {
pub name: String,
pub signature: String,
pub inputs: Vec<String>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedLog {
pub name: String,
pub signature: String,
Expand Down
2 changes: 1 addition & 1 deletion config/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "heimdall-config"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
license = "MIT"
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion heimdall/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"]
license = "MIT"
name = "heimdall"
readme = "README.md"
version = "0.3.1"
version = "0.3.2"

[dependencies]
backtrace = "0.3"
Expand Down
2 changes: 1 addition & 1 deletion heimdall/examples/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use heimdall::cfg::CFGBuilder;

fn main() {

// Decompile the bytecode and save the results.
// Trace the bytecode and save the results.
CFGBuilder::new(BYTECODE).generate();

}
Expand Down
10 changes: 10 additions & 0 deletions heimdall/examples/decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use heimdall::decode::decode_calldata;

fn main() {

// Decode the calldata and save the results.
println!("{:#?}", decode_calldata(CALLDATA.to_string()));

}

const CALLDATA: &str = "0xd57eafac000000000000000000000000b59419389d1fb089135a6a2c4bb32e5e8aa8b3330000000000000000000000001b84765de8b7566e4ceaf4d0fd3c5af52d3dde4f000000000000000000000000000000000000000000000f41a839dee4932bd176000000000000000000000000000000000000000000000004afd16002efcae82f0000000000000000000000001116898dda4015ed8ddefb84b6e8bc24528af2d80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000101c599f9f0000000000000000000000000000000000000000000000000000000063eaa06d8514775c2c1663f55720b8c4291fbb33e6524316ebc6919a2d9a459811072867";
102 changes: 102 additions & 0 deletions heimdall/src/decode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,106 @@ pub fn decode(args: DecodeArgs) {
trace.level = 4;
trace.display();

}

/// Decode calldata into a Vec of potential ResolvedFunctions
/// ## Example
/// ```no_run
/// # use crate::heimdall::decode::decode_calldata;
/// const CALLDATA: &'static str = "0xd57e/* snip */2867"
///
/// let potential_matches = decode_calldata(CALLDATA.to_string());
#[allow(deprecated)]
#[allow(dead_code)]
pub fn decode_calldata(calldata: String) -> Option<Vec<ResolvedFunction>> {
let (logger, _) = Logger::new("ERROR");

// parse the two parts of calldata, inputs and selector
let function_selector = calldata.replace("0x", "").get(0..8).unwrap_or("0x00000000").to_string();
let byte_args = match decode_hex(&calldata[8..]) {
Ok(byte_args) => byte_args,
Err(_) => {
logger.error("failed to parse bytearray from calldata.");
std::process::exit(1)
}
};

// get the function signature possibilities
let potential_matches = match resolve_function_signature(&function_selector) {
Some(signatures) => signatures,
None => Vec::new()
};
let mut matches: Vec<ResolvedFunction> = Vec::new();
for potential_match in &potential_matches {

// convert the string inputs into a vector of decoded types
let mut inputs: Vec<ParamType> = Vec::new();

match parse_function_parameters(potential_match.signature.to_owned()) {
Some(type_) => {
for input in type_ {
inputs.push(input);
}
},
None => {}
}

match decode_abi(&inputs, &byte_args) {
Ok(result) => {

// convert tokens to params
let mut params: Vec<Param> = Vec::new();
for (i, input) in inputs.iter().enumerate() {
params.push(Param {
name: format!("arg{}", i),
kind: input.to_owned(),
internal_type: None,
});
}
// build the decoded function to verify it's a match
let decoded_function_call = Function {
name: potential_match.name.to_string(),
inputs: params,
outputs: Vec::new(),
constant: None,
state_mutability: StateMutability::NonPayable,
}.encode_input(&result);
match decoded_function_call {
Ok(decoded_function_call) => {

// decode the function call in trimmed bytes, removing 0s, because contracts can use nonstandard sized words
// and padding is hard
let cleaned_bytes = decoded_function_call.encode_hex().replace("0", "");
let decoded_function_call = match cleaned_bytes.split_once(&function_selector.replace("0", "")) {
Some(decoded_function_call) => decoded_function_call.1,
None => {
logger.debug(&format!("potential match '{}' ignored. decoded inputs differed from provided calldata.", &potential_match.signature).to_string());
continue
}
};

// if the decoded function call matches (95%) the function signature, add it to the list of matches
if similarity(decoded_function_call,&calldata[8..].replace("0", "")).abs() >= 0.90 {
let mut found_match = potential_match.clone();
found_match.decoded_inputs = Some(result);
matches.push(found_match);
}
else {
logger.debug(&format!("potential match '{}' ignored. decoded inputs differed from provided calldata.", &potential_match.signature).to_string());
}

},
Err(_) => { logger.debug(&format!("potential match '{}' ignored. type checking failed", &potential_match.signature).to_string()); }
}

},
Err(_) => { logger.debug(&format!("potential match '{}' ignored. decoding types failed", &potential_match.signature).to_string()); }
}
}

if matches.len() == 0 {
return None;
}

return Some(matches)
}
1 change: 1 addition & 0 deletions heimdall/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod decompile;
pub mod decode;
pub mod cfg;

0 comments on commit 979cedd

Please sign in to comment.