Skip to content

Commit

Permalink
feat(decompile): proper constant, improved internal call decompilation (
Browse files Browse the repository at this point in the history
#450)

* feat(decompile): proper constant decompilation, internal call decompilation

* feat(decompile): proper decompilation of constants

* feat(decompile): replace getters/constants w/ corresponding resolved name
  • Loading branch information
Jon-Becker authored Jun 26, 2024
1 parent 76ce334 commit b76687b
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 233 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions crates/common/src/ether/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,12 @@ pub async fn get_trace(transaction_hash: &str, rpc_url: &str) -> Result<BlockTra
.await
{
Ok(traces) => traces,
Err(e) => {
Err(_) => {
error!(
"failed to replay and trace transaction '{}' . does your RPC provider support it?",
&transaction_hash
);
error!("error: '{}' .", e);

return Err(backoff::Error::Transient { err: (), retry_after: None })
}
};
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/ether/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub fn to_type(string: &str) -> ParamType {
string = string.replacen(&format!("[{}]", &size), "", 1);
}

let arg_type = match string.as_str() {
let arg_type = match string.as_str().replace("memory", "").trim() {
"address" => ParamType::Address,
"bool" => ParamType::Bool,
"string" => ParamType::String,
Expand Down
186 changes: 0 additions & 186 deletions crates/core/src/heimdall.rs

This file was deleted.

4 changes: 2 additions & 2 deletions crates/core/tests/test_decompile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ mod integration_tests {
.expect("failed to decompile");

// assert that the output is correct
for line in &["function Unresolved_2fa61cd8(address arg0) public payable returns (uint16) {",
for line in &["function Unresolved_2fa61cd8(address arg0) public view returns (uint16) {",
"function Unresolved_41161b10(uint240 arg0, address arg1) public payable returns (bool) {",
"function Unresolved_06fdde03() public payable returns (bytes memory) {"] {
"constant Unresolved_06fdde03"] {
println!("{line}");
assert!(result.source.as_ref().expect("decompile source is empty").contains(line));
}
Expand Down
1 change: 1 addition & 0 deletions crates/decompile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exclude.workspace = true
heimdall-config = { workspace = true }
heimdall-common = { workspace = true }
heimdall-cache = { workspace = true }
heimdall-decoder = { workspace = true }
thiserror = "1.0.50"
clap = { workspace = true, features = ["derive"] }
derive_builder = "0.12.0"
Expand Down
32 changes: 29 additions & 3 deletions crates/decompile/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ pub(crate) mod postprocess;
pub(crate) mod resolve;

use alloy_json_abi::JsonAbi;
use ethers::types::H160;
use ethers::{
abi::{decode as abi_decode, ParamType, Token},
types::H160,
};
use eyre::eyre;
use heimdall_common::{
ether::{
bytecode::get_bytecode_from_target,
compiler::detect_compiler,
signatures::{score_signature, ResolvedError, ResolvedFunction, ResolvedLog},
types::to_type,
},
utils::strings::{encode_hex, encode_hex_reduced, StringExt},
utils::strings::{decode_hex, encode_hex, encode_hex_reduced, StringExt},
};
use heimdall_disassembler::{disassemble, DisassemblerArgsBuilder};
use heimdall_vm::{
Expand Down Expand Up @@ -159,7 +163,29 @@ pub async fn decompile(args: DecompilerArgs) -> Result<DecompileResult, Error> {
);

// analyze the symbolic execution trace
let analyzed_function = analyzer.analyze(trace_root)?;
let mut analyzed_function = analyzer.analyze(trace_root)?;

// if the function is constant, we can get the exact val
if analyzed_function.is_constant() {
evm.reset();
let x = evm.call(&decode_hex(&selector).expect("invalid selector"), 0)?;

let returns_param_type = analyzed_function
.returns
.as_ref()
.map(|ret_type| to_type(ret_type.replace("memory", "").trim()))
.unwrap_or(ParamType::Bytes);

let decoded = abi_decode(&[returns_param_type], &x.returndata)
.map(|decoded| match decoded[0].clone() {
Token::String(s) => format!("\"{}\"", s),
Token::Uint(x) | Token::Int(x) => x.to_string(),
token => format!("0x{}", token),
})
.unwrap_or_else(|_| encode_hex(&x.returndata));

analyzed_function.constant_value = Some(decoded);
}

Ok::<_, Error>(analyzed_function)
})
Expand Down
12 changes: 6 additions & 6 deletions crates/decompile/src/core/out/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ pub fn build_abi(
// add functions
functions.iter().for_each(|f| {
// determine the state mutability of the function
let state_mutability = match f.payable {
true => StateMutability::Payable,
false => match f.pure {
true => StateMutability::Pure,
false => match f.view {
true => StateMutability::View,
let state_mutability = match f.pure {
true => StateMutability::Pure,
false => match f.view {
true => StateMutability::View,
false => match f.payable {
true => StateMutability::Payable,
false => StateMutability::NonPayable,
},
},
Expand Down
Loading

0 comments on commit b76687b

Please sign in to comment.