From 44dae5fd317885d0d277490431b7f477abfc9b75 Mon Sep 17 00:00:00 2001 From: jon-becker Date: Mon, 3 Oct 2022 19:43:41 -0400 Subject: [PATCH 01/48] feat(decompile): clean up msg.data calls and bitmask regex --- common/src/consts.rs | 3 +++ common/src/ether/solidity.rs | 35 +++++++++++++++++++++++------ heimdall/src/decompile/constants.rs | 4 ++-- heimdall/src/decompile/output.rs | 10 ++++----- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/common/src/consts.rs b/common/src/consts.rs index 426d14f8..f48d74a2 100644 --- a/common/src/consts.rs +++ b/common/src/consts.rs @@ -16,4 +16,7 @@ lazy_static! { // The following regex is used to reduce null byte prefixes pub static ref REDUCE_HEX_REGEX: Regex = Regex::new(r"^0x(00)*").unwrap(); + + // The following regex is used as a search pattern for words + pub static ref WORD_REGEX: Regex = Regex::new(r"0x[0-9a-fA-F]{0,64}").unwrap(); } \ No newline at end of file diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index ec0426a8..55194c46 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -1,4 +1,4 @@ -use crate::utils::strings::encode_hex_reduced; +use crate::{utils::strings::encode_hex_reduced, consts::WORD_REGEX}; use super::evm::opcodes::*; @@ -259,12 +259,33 @@ impl WrappedOpcode { solidified_wrapped_opcode.push_str("msg.value"); }, "CALLDATALOAD" => { - solidified_wrapped_opcode.push_str( - format!( - "msg.data[{}]", - self.inputs[0]._solidify() - ).as_str() - ); + let solidified_slot = self.inputs[0]._solidify(); + + // are dealing with a slot that is a constant, we can just use the slot directly + if WORD_REGEX.is_match(&solidified_slot) { + + // convert to usize + let slot = match usize::from_str_radix( + &solidified_slot.replace("0x", ""), + 16 + ) { + Ok(slot) => slot, + Err(_) => usize::MAX + }; + + solidified_wrapped_opcode.push_str( + format!( "arg{}", (slot-4)/32 ).as_str() + ); + + } + else { + solidified_wrapped_opcode.push_str( + format!( + "msg.data[{}]", + solidified_slot + ).as_str() + ); + } }, "CALLDATASIZE" => { solidified_wrapped_opcode.push_str("msg.data.length",); diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index b8dd4de1..d127f1f8 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -5,8 +5,8 @@ use lazy_static::lazy_static; lazy_static! { // The following regex is used as a detector for AND bitmasks - pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"0x[0-9a-fA-F]* & ").unwrap(); - pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & 0x[0-9a-fA-F]*").unwrap(); + pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"0x([0fF][0fF]){1,32} & ").unwrap(); + pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & 0x([0fF][0fF]){1,32}").unwrap(); pub static ref DECOMPILED_SOURCE_HEADER: String = "// SPDX-License-Identifier: MIT diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 28252ee5..638e3597 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use heimdall_common::io::{logging::{TraceFactory, Logger}, file::{short_path, write_file}}; +use heimdall_common::io::{logging::{TraceFactory, Logger}, file::{short_path, write_file, write_lines_to_file}}; use indicatif::ProgressBar; use super::{DecompilerArgs, util::Function, constants::DECOMPILED_SOURCE_HEADER, postprocess::postprocess}; @@ -395,8 +395,8 @@ pub fn build_output( } - // write_lines_to_file( - // &decompiled_output_path, - // decompiled_output - // ); + write_lines_to_file( + &decompiled_output_path, + decompiled_output + ); } \ No newline at end of file From eb55de73a83d01cf4689b3b3a348b40ab8939c1b Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 18 Oct 2022 16:16:28 -0400 Subject: [PATCH 02/48] feat: fix cast brackets --- common/src/consts.rs | 3 ++ common/src/ether/evm/types.rs | 23 +++++++++- common/src/ether/evm/vm.rs | 1 + common/src/ether/solidity.rs | 33 ++++++++++---- common/src/utils/strings.rs | 47 +++++++++++++++++++ heimdall/src/decompile/constants.rs | 4 +- heimdall/src/decompile/postprocess.rs | 65 ++++++++------------------- heimdall/src/decompile/util.rs | 22 --------- 8 files changed, 117 insertions(+), 81 deletions(-) diff --git a/common/src/consts.rs b/common/src/consts.rs index f48d74a2..cadd9101 100644 --- a/common/src/consts.rs +++ b/common/src/consts.rs @@ -19,4 +19,7 @@ lazy_static! { // The following regex is used as a search pattern for words pub static ref WORD_REGEX: Regex = Regex::new(r"0x[0-9a-fA-F]{0,64}").unwrap(); + + // The following regex is used to find type castings + pub static ref TYPE_CAST_REGEX: Regex = Regex::new(r"(address\(|string\(|bool\(|bytes(\d*)\(|uint(\d*)\(|int(\d*)\()").unwrap(); } \ No newline at end of file diff --git a/common/src/ether/evm/types.rs b/common/src/ether/evm/types.rs index b601434e..40e3952d 100644 --- a/common/src/ether/evm/types.rs +++ b/common/src/ether/evm/types.rs @@ -1,7 +1,7 @@ use colored::Colorize; use ethers::abi::{ParamType, Token, AbiEncode}; -use crate::utils::strings::replace_last; +use crate::{utils::strings::{replace_last, find_balanced_parentheses}, consts::TYPE_CAST_REGEX}; use super::vm::Instruction; @@ -232,4 +232,25 @@ pub fn byte_size_to_type(byte_size: usize) -> (usize, Vec) { // return list of potential type castings, sorted by likelihood descending (byte_size, potential_types) +} + +pub fn find_cast(line: String) -> (usize, usize) { + let mut start = 0; + let mut end = 0; + + // find the start of the cast + match TYPE_CAST_REGEX.find(&line) { + Some(m) => { + start = m.start(); + end = m.end() - 1; + + // find where the cast ends + find_balanced_parentheses(line[end..].to_string()); + + println!("found cast at {} - {}: {}", start, end, &line[end..]); + }, + None => return (0, 0), + } + + (start, end) } \ No newline at end of file diff --git a/common/src/ether/evm/vm.rs b/common/src/ether/evm/vm.rs index 5f203ae9..8a9abdea 100644 --- a/common/src/ether/evm/vm.rs +++ b/common/src/ether/evm/vm.rs @@ -67,6 +67,7 @@ pub struct Instruction { } impl VM { + // Creates a new VM instance pub fn new( bytecode: String, diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index 55194c46..4975c41b 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -153,7 +153,7 @@ impl WrappedOpcode { true => { solidified_wrapped_opcode.push_str( format!( - "({}) == 0", + "!({})", self.inputs[0]._solidify() ).as_str() ); @@ -161,7 +161,7 @@ impl WrappedOpcode { false => { solidified_wrapped_opcode.push_str( format!( - "{} == 0", + "!{}", self.inputs[0]._solidify() ).as_str() ); @@ -172,7 +172,7 @@ impl WrappedOpcode { "AND" => { solidified_wrapped_opcode.push_str( format!( - "{} & {}", + "({}) & ({})", self.inputs[0]._solidify(), self.inputs[1]._solidify() ).as_str() @@ -265,17 +265,32 @@ impl WrappedOpcode { if WORD_REGEX.is_match(&solidified_slot) { // convert to usize - let slot = match usize::from_str_radix( + match usize::from_str_radix( &solidified_slot.replace("0x", ""), 16 ) { - Ok(slot) => slot, - Err(_) => usize::MAX + Ok(slot) => { + solidified_wrapped_opcode.push_str( + format!( "arg{}", (slot-4)/32 ).as_str() + ); + }, + Err(_) => { + if solidified_slot.contains("0x04 + ") || + solidified_slot.contains("+ 0x04") + { + solidified_wrapped_opcode.push_str( + solidified_slot.replace("0x04 + ", "").replace("+ 0x04", "").as_str() + ); + } + else { + solidified_wrapped_opcode.push_str( + format!( "msg.data[{}]", solidified_slot).as_str() + ); + } + } }; - solidified_wrapped_opcode.push_str( - format!( "arg{}", (slot-4)/32 ).as_str() - ); + } else { diff --git a/common/src/utils/strings.rs b/common/src/utils/strings.rs index 26f7c98c..f353c91f 100644 --- a/common/src/utils/strings.rs +++ b/common/src/utils/strings.rs @@ -37,4 +37,51 @@ pub fn encode_hex_reduced(s: U256) -> String { pub fn replace_last(s: String, old: &str, new: &str) -> String { let new = new.chars().rev().collect::(); s.chars().rev().collect::().replacen(old, &new, 1).chars().rev().collect::() +} + + +// find balanced parentheses in a string +pub fn find_balanced_parentheses(s: String) -> (usize, usize, bool) { + let mut open = 0; + let mut close = 0; + let mut start = 0; + let mut end = 0; + for (i, c) in s.chars().enumerate() { + if c == '(' { + if open == 0 { + start = i; + } + open += 1; + } else if c == ')' { + close += 1; + } + if open == close { + end = i; + break; + } + } + (start, end + 1, (open == close && end > start && open > 0)) +} + +// find balanced parentheses in a string, but backwards +pub fn find_balanced_parentheses_backwards(s: String) -> (usize, usize, bool) { + let mut open = 0; + let mut close = 0; + let mut start = 0; + let mut end = 0; + for (i, c) in s.chars().rev().enumerate() { + if c == ')' { + if open == 0 { + start = i; + } + open += 1; + } else if c == '(' { + close += 1; + } + if open == close { + end = i; + break; + } + } + (s.len() - end - 1, s.len() - start, (open == close && end > start && open > 0)) } \ No newline at end of file diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index d127f1f8..7e6012a6 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -5,8 +5,8 @@ use lazy_static::lazy_static; lazy_static! { // The following regex is used as a detector for AND bitmasks - pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"0x([0fF][0fF]){1,32} & ").unwrap(); - pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & 0x([0fF][0fF]){1,32}").unwrap(); + pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"\(0x([0fF][0fF]){1,32}\) & ").unwrap(); + pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & \(0x([0fF][0fF]){1,32}\)").unwrap(); pub static ref DECOMPILED_SOURCE_HEADER: String = "// SPDX-License-Identifier: MIT diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 3a925b7b..e0159850 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -1,6 +1,4 @@ -use heimdall_common::ether::evm::types::byte_size_to_type; - -use crate::decompile::util::find_balanced_parentheses; +use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_parentheses, find_balanced_parentheses_backwards}}; use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2}}; @@ -21,33 +19,21 @@ fn convert_bitmask_to_casting(line: String) -> String { subject = match subject_indices.2 { true => { - // get the matched subject - match subject.get(subject_indices.0..subject_indices.1) { - Some(subject) => subject.to_string(), - None => match subject.split(")").collect::>().first() { - Some(subject) => subject.to_string(), - None => subject - }, - } + // get the subject as hte substring between the balanced parentheses found in unbalanced subject + subject[subject_indices.0..subject_indices.1].to_string() }, false => { - - // subject doesn't contain parentheses, so surround it in some - subject.split(")").collect::>()[0].to_string() + + // this shouldn't happen, but if it does, just return the subject. + //TODO add this to verbose logs + subject }, }; // apply the cast to the subject cleaned = cleaned.replace( &format!("{}{}", cast, subject), - &format!( - "{}({})", - cast_types[0], - match subject.split(")").collect::>().first() { - Some(subject) => subject.to_string(), - None => subject - } - ) + &format!("{}{}", cast_types[0], subject), ); // attempt to cast again @@ -66,43 +52,28 @@ fn convert_bitmask_to_casting(line: String) -> String { Some(subject) => subject.to_string(), None => cleaned.get(0..bitmask.start()).unwrap().replace(";", "").to_string(), }; - + // attempt to find matching parentheses - let subject_indices = find_balanced_parentheses(subject.to_string()); + let subject_indices = find_balanced_parentheses_backwards(subject.to_string()); subject = match subject_indices.2 { true => { - // get the matched subject - match subject.get(subject_indices.0..subject_indices.1) { - Some(subject) => subject.to_string(), - None => match subject.split("(").collect::>().last() { - Some(subject) => subject.to_string(), - None => subject - }, - } + // get the subject as hte substring between the balanced parentheses found in unbalanced subject + subject[subject_indices.0..subject_indices.1].to_string() }, false => { - // subject doesn't contain parentheses, so surround it in some - match subject.split("(").collect::>().last() { - Some(subject) => subject.to_string(), - None => subject - } + // this shouldn't happen, but if it does, just return the subject. + //TODO add this to verbose logs + subject }, }; // apply the cast to the subject cleaned = cleaned.replace( &format!("{}{}", subject, cast), - &format!( - "{}({})", - cast_types[0], - match subject.split("(").collect::>().last() { - Some(subject) => subject.to_string(), - None => subject - } - ) + &format!("{}{}", cast_types[0], subject), ); // attempt to cast again @@ -121,7 +92,7 @@ fn simplify_casts(line: String) -> String { let cleaned = line; // remove unnecessary casts - + find_cast(cleaned.clone()); cleaned } @@ -145,7 +116,7 @@ pub fn postprocess(line: String) -> String { cleaned = convert_bitmask_to_casting(cleaned); // Remove all repetitive casts - cleaned = simplify_casts(cleaned); + // cleaned = simplify_casts(cleaned); // Find and flip == / != signs for all instances of ISZERO cleaned = convert_iszero_logic_flip(cleaned); diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index fce0b1aa..8b4f19d3 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -408,26 +408,4 @@ pub fn recursive_map( } vm_trace -} - -pub fn find_balanced_parentheses(s: String) -> (usize, usize, bool) { - let mut open = 0; - let mut close = 0; - let mut start = 0; - let mut end = 0; - for (i, c) in s.chars().enumerate() { - if c == '(' { - if open == 0 { - start = i; - } - open += 1; - } else if c == ')' { - close += 1; - } - if open == close { - end = i; - break; - } - } - (start, end + 1, (open == close && end > start && open > 0)) } \ No newline at end of file From a843cdc9dcf68cc8c5c537e3cbaa0de9662d0cf0 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 20 Oct 2022 20:45:20 -0400 Subject: [PATCH 03/48] feat: simplify repetitive casts --- common/src/ether/evm/types.rs | 18 +++++------- heimdall/src/decompile/constants.rs | 5 ++-- heimdall/src/decompile/postprocess.rs | 40 ++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/common/src/ether/evm/types.rs b/common/src/ether/evm/types.rs index 40e3952d..1bffd33b 100644 --- a/common/src/ether/evm/types.rs +++ b/common/src/ether/evm/types.rs @@ -234,23 +234,19 @@ pub fn byte_size_to_type(byte_size: usize) -> (usize, Vec) { (byte_size, potential_types) } -pub fn find_cast(line: String) -> (usize, usize) { - let mut start = 0; - let mut end = 0; +pub fn find_cast(line: String) -> (usize, usize, Option) { // find the start of the cast match TYPE_CAST_REGEX.find(&line) { Some(m) => { - start = m.start(); - end = m.end() - 1; + let start = m.start(); + let end = m.end() - 1; + let cast_type = line[start..].split("(").collect::>()[0].to_string(); // find where the cast ends - find_balanced_parentheses(line[end..].to_string()); - - println!("found cast at {} - {}: {}", start, end, &line[end..]); + let (a, b, _) = find_balanced_parentheses(line[end..].to_string()); + return (end+a, end+b, Some(cast_type)) }, - None => return (0, 0), + None => return (0, 0, None), } - - (start, end) } \ No newline at end of file diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index 7e6012a6..779f846b 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -5,8 +5,9 @@ use lazy_static::lazy_static; lazy_static! { // The following regex is used as a detector for AND bitmasks - pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"\(0x([0fF][0fF]){1,32}\) & ").unwrap(); - pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & \(0x([0fF][0fF]){1,32}\)").unwrap(); + pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"\(0x([a-fA-F0-9]{2}){1,32}\) & ").unwrap(); + pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & \(0x([a-fA-F0-9]{2}){1,32}\)").unwrap(); + pub static ref NON_ZERO_BYTE_REGEX: Regex = Regex::new(r"[a-fA-F0-9][a-fA-F1-9]").unwrap(); pub static ref DECOMPILED_SOURCE_HEADER: String = "// SPDX-License-Identifier: MIT diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index e0159850..f84a77a0 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -1,6 +1,6 @@ use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_parentheses, find_balanced_parentheses_backwards}}; -use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2}}; +use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX}}; fn convert_bitmask_to_casting(line: String) -> String { let mut cleaned = line; @@ -8,7 +8,7 @@ fn convert_bitmask_to_casting(line: String) -> String { match AND_BITMASK_REGEX.find(&cleaned) { Some(bitmask) => { let cast = bitmask.as_str(); - let cast_size = cast.matches("ff").count(); + let cast_size = NON_ZERO_BYTE_REGEX.find_iter(&cast).count(); let (_, cast_types) = byte_size_to_type(cast_size); // get the cast subject @@ -44,7 +44,7 @@ fn convert_bitmask_to_casting(line: String) -> String { match AND_BITMASK_REGEX_2.find(&cleaned) { Some(bitmask) => { let cast = bitmask.as_str(); - let cast_size = cast.matches("ff").count(); + let cast_size = NON_ZERO_BYTE_REGEX.find_iter(&cast).count(); let (_, cast_types) = byte_size_to_type(cast_size); // get the cast subject @@ -88,12 +88,38 @@ fn convert_bitmask_to_casting(line: String) -> String { cleaned } -fn simplify_casts(line: String) -> String { - let cleaned = line; +fn simplify_casts(line: String, outer_cast: Option) -> String { + let mut cleaned = line; // remove unnecessary casts - find_cast(cleaned.clone()); + let (cast_start, cast_end, cast_type) = find_cast(cleaned.to_string()); + match cast_type { + Some(cast) => { + let cleaned_cast_pre = cleaned[0..cast_start].to_string(); + let cleaned_cast_post = cleaned[cast_end..].to_string(); + let cleaned_cast = cleaned[cast_start..cast_end].to_string().replace(&cast, ""); + + cleaned = format!("{}{}{}", cleaned_cast_pre, cleaned_cast, cleaned_cast_post); + + // check if there are remaining casts + let (_, _, remaining_cast_type) = find_cast(cleaned_cast_post.clone()); + match remaining_cast_type { + Some(_) => { + + // a cast is remaining, simplify it + let mut recursive_cleaned = format!("{}{}", cleaned_cast_pre, cleaned_cast); + recursive_cleaned.push_str( + simplify_casts(cleaned_cast_post, None).as_str() + ); + cleaned = recursive_cleaned; + }, + None => {} + } + }, + None => {} + } + cleaned } @@ -116,7 +142,7 @@ pub fn postprocess(line: String) -> String { cleaned = convert_bitmask_to_casting(cleaned); // Remove all repetitive casts - // cleaned = simplify_casts(cleaned); + cleaned = simplify_casts(cleaned, None); // Find and flip == / != signs for all instances of ISZERO cleaned = convert_iszero_logic_flip(cleaned); From 84cc9f3b8adcacf1e47be4ebf791b08aa6e30ef8 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:58:41 -0400 Subject: [PATCH 04/48] feat: simplifying expressions --- common/src/utils/strings.rs | 4 +- heimdall/src/decompile/constants.rs | 5 ++ heimdall/src/decompile/postprocess.rs | 122 +++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 5 deletions(-) diff --git a/common/src/utils/strings.rs b/common/src/utils/strings.rs index f353c91f..3329a4c4 100644 --- a/common/src/utils/strings.rs +++ b/common/src/utils/strings.rs @@ -55,7 +55,7 @@ pub fn find_balanced_parentheses(s: String) -> (usize, usize, bool) { } else if c == ')' { close += 1; } - if open == close { + if open == close && open > 0 { end = i; break; } @@ -78,7 +78,7 @@ pub fn find_balanced_parentheses_backwards(s: String) -> (usize, usize, bool) { } else if c == '(' { close += 1; } - if open == close { + if open == close && open > 0 { end = i; break; } diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index 779f846b..ce475dae 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -7,8 +7,13 @@ lazy_static! { // The following regex is used as a detector for AND bitmasks pub static ref AND_BITMASK_REGEX: Regex = Regex::new(r"\(0x([a-fA-F0-9]{2}){1,32}\) & ").unwrap(); pub static ref AND_BITMASK_REGEX_2: Regex = Regex::new(r" & \(0x([a-fA-F0-9]{2}){1,32}\)").unwrap(); + + // used to detect non-zero bytes within a word pub static ref NON_ZERO_BYTE_REGEX: Regex = Regex::new(r"[a-fA-F0-9][a-fA-F1-9]").unwrap(); + // detects a parenthesis enclosed expression + pub static ref ENCLOSED_EXPRESSION_REGEX: Regex = Regex::new(r"\(.*\)").unwrap(); + pub static ref DECOMPILED_SOURCE_HEADER: String = "// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index f84a77a0..f31525db 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -1,5 +1,7 @@ use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_parentheses, find_balanced_parentheses_backwards}}; +use crate::decompile::constants::{ENCLOSED_EXPRESSION_REGEX}; + use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX}}; fn convert_bitmask_to_casting(line: String) -> String { @@ -88,7 +90,7 @@ fn convert_bitmask_to_casting(line: String) -> String { cleaned } -fn simplify_casts(line: String, outer_cast: Option) -> String { +fn simplify_casts(line: String) -> String { let mut cleaned = line; // remove unnecessary casts @@ -110,7 +112,7 @@ fn simplify_casts(line: String, outer_cast: Option) -> String { // a cast is remaining, simplify it let mut recursive_cleaned = format!("{}{}", cleaned_cast_pre, cleaned_cast); recursive_cleaned.push_str( - simplify_casts(cleaned_cast_post, None).as_str() + simplify_casts(cleaned_cast_post).as_str() ); cleaned = recursive_cleaned; }, @@ -123,6 +125,117 @@ fn simplify_casts(line: String, outer_cast: Option) -> String { cleaned } +fn are_parentheses_unnecessary(expression: String) -> bool { + + // safely grab the first and last chars + let first_char = match expression.get(0..1) { + Some(x) => x, + None => "", + }; + let last_char = match expression.get(expression.len() - 1..expression.len()) { + Some(x) => x, + None => "", + }; + + // if there is a negation of an expression, remove the parentheses + // helps with double negation + if first_char == "!" && last_char == ")" { return true; } + + // parens required if: + // - expression is a cast + // - expression is a function call + // - expression is the surrounding parens of a conditional + if first_char != "(" { return false; } + else if last_char == ")" { return true; } + + // don't include instantiations + if expression.contains("memory ret") { return false; } + + // handle the inside of the expression + let inside = match expression.get(2..expression.len() - 2) { + Some(x) => { + ENCLOSED_EXPRESSION_REGEX + .replace(x, "x").to_string() + }, + None => "".to_string(), + }; + + if inside.len() > 0 { + let expression_parts = inside.split(|x| ['*', '/', '=', '>', '<', '|', '&', '!'] + .contains(&x)) + .filter(|x| x.len() > 0).collect::>(); + + return expression_parts.len() == 1 + } + else { + return false + } +} + +fn simplify_parentheses(line: String, paren_index: usize) -> String { + let mut cleaned = line; + + if cleaned.contains("function") { return cleaned; } + + // get the nth index of the first open paren + let nth_paren_index = match cleaned.match_indices("(").nth(paren_index) { + Some(x) => x.0, + None => return cleaned, + }; + + //find it's matching close paren + let (paren_start, paren_end, found_match) = find_balanced_parentheses(cleaned[nth_paren_index..].to_string()); + + // add the nth open paren to the start of the paren_start + let paren_start = paren_start + nth_paren_index; + let paren_end = paren_end + nth_paren_index; + + // if a match was found, check if the parens are unnecessary + match found_match { + true => { + + // get the logical expression including the char before the parentheses (to detect casts) + let logical_expression = match paren_start { + 0 => match cleaned.get(paren_start..paren_end+1) { + Some(expression) => expression.to_string(), + None => cleaned[paren_start..paren_end].to_string(), + }, + _ => match cleaned.get(paren_start - 1..paren_end+1) { + Some(expression) => expression.to_string(), + None => cleaned[paren_start - 1..paren_end].to_string(), + } + }; + + // check if the parentheses are unnecessary and remove them if so + if are_parentheses_unnecessary(logical_expression.clone()) { + + cleaned.replace_range( + paren_start..paren_end, + match logical_expression.get(2..logical_expression.len() - 2) { + Some(x) => x, + None => "", + } + ); + + // recurse into the next set of parentheses + // don't increment the paren_index because we just removed a set + cleaned = simplify_parentheses(cleaned, paren_index); + } + else { + + // recurse into the next set of parentheses + cleaned = simplify_parentheses(cleaned, paren_index + 1); + } + }, + _ => { + + // if you're reading this you're a nerd + } + } + + cleaned +} + fn convert_iszero_logic_flip(line: String) -> String { let mut cleaned = line; @@ -142,7 +255,10 @@ pub fn postprocess(line: String) -> String { cleaned = convert_bitmask_to_casting(cleaned); // Remove all repetitive casts - cleaned = simplify_casts(cleaned, None); + cleaned = simplify_casts(cleaned); + + // Remove all unnecessary parentheses + cleaned = simplify_parentheses(cleaned, 0); // Find and flip == / != signs for all instances of ISZERO cleaned = convert_iszero_logic_flip(cleaned); From 6887566316347a9c8df44260912c4f36b0c30ce6 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Mon, 24 Oct 2022 16:05:15 -0400 Subject: [PATCH 05/48] feat: improve simplifying expressions, remove double negation and bracket-encased parens --- heimdall/src/decompile/postprocess.rs | 109 ++++++++++++++------------ 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index f31525db..b1a4f750 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -125,56 +125,62 @@ fn simplify_casts(line: String) -> String { cleaned } -fn are_parentheses_unnecessary(expression: String) -> bool { - - // safely grab the first and last chars - let first_char = match expression.get(0..1) { - Some(x) => x, - None => "", - }; - let last_char = match expression.get(expression.len() - 1..expression.len()) { - Some(x) => x, - None => "", - }; - - // if there is a negation of an expression, remove the parentheses - // helps with double negation - if first_char == "!" && last_char == ")" { return true; } - - // parens required if: - // - expression is a cast - // - expression is a function call - // - expression is the surrounding parens of a conditional - if first_char != "(" { return false; } - else if last_char == ")" { return true; } - - // don't include instantiations - if expression.contains("memory ret") { return false; } - - // handle the inside of the expression - let inside = match expression.get(2..expression.len() - 2) { - Some(x) => { - ENCLOSED_EXPRESSION_REGEX - .replace(x, "x").to_string() - }, - None => "".to_string(), - }; - - if inside.len() > 0 { - let expression_parts = inside.split(|x| ['*', '/', '=', '>', '<', '|', '&', '!'] - .contains(&x)) - .filter(|x| x.len() > 0).collect::>(); +fn simplify_parentheses(line: String, paren_index: usize) -> String { - return expression_parts.len() == 1 - } - else { - return false + // helper function to determine if parentheses are necessary + fn are_parentheses_unnecessary(expression: String) -> bool { + + // safely grab the first and last chars + let first_char = match expression.get(0..1) { + Some(x) => x, + None => "", + }; + let last_char = match expression.get(expression.len() - 1..expression.len()) { + Some(x) => x, + None => "", + }; + + // if there is a negation of an expression, remove the parentheses + // helps with double negation + if first_char == "!" && last_char == ")" { return true; } + + // remove the parentheses if the expression is within brackets + if first_char == "[" && last_char == "]" { return true; } + + // parens required if: + // - expression is a cast + // - expression is a function call + // - expression is the surrounding parens of a conditional + if first_char != "(" { return false; } + else if last_char == ")" { return true; } + + // don't include instantiations + if expression.contains("memory ret") { return false; } + + // handle the inside of the expression + let inside = match expression.get(2..expression.len() - 2) { + Some(x) => { + ENCLOSED_EXPRESSION_REGEX + .replace(x, "x").to_string() + }, + None => "".to_string(), + }; + + if inside.len() > 0 { + let expression_parts = inside.split(|x| ['*', '/', '=', '>', '<', '|', '&', '!'] + .contains(&x)) + .filter(|x| x.len() > 0).collect::>(); + + return expression_parts.len() == 1 + } + else { + return false + } } -} -fn simplify_parentheses(line: String, paren_index: usize) -> String { let mut cleaned = line; + // skip lines that are defining a function if cleaned.contains("function") { return cleaned; } // get the nth index of the first open paren @@ -217,6 +223,11 @@ fn simplify_parentheses(line: String, paren_index: usize) -> String { } ); + // remove double negation, if one was created + if cleaned.contains("!!") { + cleaned = cleaned.replace("!!", ""); + } + // recurse into the next set of parentheses // don't increment the paren_index because we just removed a set cleaned = simplify_parentheses(cleaned, paren_index); @@ -246,8 +257,6 @@ fn convert_iszero_logic_flip(line: String) -> String { cleaned } - - pub fn postprocess(line: String) -> String { let mut cleaned = line; @@ -257,11 +266,11 @@ pub fn postprocess(line: String) -> String { // Remove all repetitive casts cleaned = simplify_casts(cleaned); - // Remove all unnecessary parentheses - cleaned = simplify_parentheses(cleaned, 0); - // Find and flip == / != signs for all instances of ISZERO cleaned = convert_iszero_logic_flip(cleaned); + // Remove all unnecessary parentheses + cleaned = simplify_parentheses(cleaned, 0); + cleaned } From a08c57206135d2b8b87f4c22fd26f32b53b2de05 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 25 Oct 2022 14:30:57 -0400 Subject: [PATCH 06/48] =?UTF-8?q?=E2=9C=A8=20feat(postprocessing):=20conve?= =?UTF-8?q?rt=20memory=20accesses=20to=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/constants.rs | 3 + heimdall/src/decompile/output.rs | 24 +----- heimdall/src/decompile/postprocess.rs | 116 ++++++++++++++++++++++++-- heimdall/src/decompile/precompile.rs | 6 +- 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index ce475dae..c3b3c320 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -13,6 +13,9 @@ lazy_static! { // detects a parenthesis enclosed expression pub static ref ENCLOSED_EXPRESSION_REGEX: Regex = Regex::new(r"\(.*\)").unwrap(); + + // detects a memory access + pub static ref MEM_ACCESS_REGEX: Regex = Regex::new(r"memory\[.*\]").unwrap(); pub static ref DECOMPILED_SOURCE_HEADER: String = "// SPDX-License-Identifier: MIT diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 638e3597..75875b89 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -373,30 +373,8 @@ pub fn build_output( decompiled_output.push(String::from("}")); - let mut indentation: usize = 0; - for line in decompiled_output.iter_mut() { - - // dedent due to closing braces - if line.starts_with("}") { - indentation = indentation.saturating_sub(1); - } - - // apply postprocessing and indentation - *line = format!( - "{}{}", - " ".repeat(indentation*4), - postprocess(line.to_string()) - ); - - // indent due to opening braces - if line.ends_with("{") { - indentation += 1; - } - - } - write_lines_to_file( &decompiled_output_path, - decompiled_output + postprocess(decompiled_output) ); } \ No newline at end of file diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index b1a4f750..72739898 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -1,8 +1,15 @@ -use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_parentheses, find_balanced_parentheses_backwards}}; - +use std::{ + sync::Mutex, + collections::HashMap +}; +use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards}}; use crate::decompile::constants::{ENCLOSED_EXPRESSION_REGEX}; +use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX, MEM_ACCESS_REGEX}}; +use lazy_static::lazy_static; -use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX}}; +lazy_static! { + static ref MEM_LOOKUP_MAP: Mutex> = Mutex::new(HashMap::new()); +} fn convert_bitmask_to_casting(line: String) -> String { let mut cleaned = line; @@ -17,7 +24,7 @@ fn convert_bitmask_to_casting(line: String) -> String { let mut subject = cleaned.get(bitmask.end()..).unwrap().replace(";", ""); // attempt to find matching parentheses - let subject_indices = find_balanced_parentheses(subject.to_string()); + let subject_indices = find_balanced_encapsulator(subject.to_string(), ('(', ')')); subject = match subject_indices.2 { true => { @@ -56,7 +63,7 @@ fn convert_bitmask_to_casting(line: String) -> String { }; // attempt to find matching parentheses - let subject_indices = find_balanced_parentheses_backwards(subject.to_string()); + let subject_indices = find_balanced_encapsulator_backwards(subject.to_string(), ('(', ')')); subject = match subject_indices.2 { true => { @@ -190,7 +197,7 @@ fn simplify_parentheses(line: String, paren_index: usize) -> String { }; //find it's matching close paren - let (paren_start, paren_end, found_match) = find_balanced_parentheses(cleaned[nth_paren_index..].to_string()); + let (paren_start, paren_end, found_match) = find_balanced_encapsulator(cleaned[nth_paren_index..].to_string(), ('(', ')')); // add the nth open paren to the start of the paren_start let paren_start = paren_start + nth_paren_index; @@ -257,7 +264,71 @@ fn convert_iszero_logic_flip(line: String) -> String { cleaned } -pub fn postprocess(line: String) -> String { +fn convert_memory_to_variable(line: String) -> String { + let mut cleaned = line; + + // find a memory access + let memory_access = match MEM_ACCESS_REGEX.find(&cleaned) { + Some(x) => x.as_str(), + None => return cleaned, + }; + + // since the regex is greedy, match the memory brackets + let matched_loc = find_balanced_encapsulator(memory_access.to_string(), ('[', ']')); + match matched_loc.2 { + true => { + let mut mem_map = MEM_LOOKUP_MAP.lock().unwrap(); + + // safe to unwrap since we know these indices exist + let memloc = format!("memory{}", memory_access.get(matched_loc.0..matched_loc.1).unwrap()).to_string(); + + let variable_name = match mem_map.get(&memloc) { + Some(loc) => { + loc.to_owned() + }, + None => { + + // add the memory location to the map + let mut idex = mem_map.len() + 1; + + // get the variable name + let mut variable_name = String::new(); + if idex <= 26 { + variable_name.push((idex + 96) as u8 as char); + } + else { + while idex != 0 { + let remainder = idex % 26; + idex = idex / 26; + + if remainder == 0 { idex -= 1; } + + variable_name.push((remainder + 97) as u8 as char); + } + } + + // add the variable to the map + mem_map.insert(memloc.clone(), variable_name.clone()); + variable_name + } + }; + + // unlock the map + drop(mem_map); + + // upadte the memory name + cleaned = cleaned.replace(memloc.as_str(), &variable_name); + + // recurse to replace any other memory accesses + cleaned = convert_memory_to_variable(cleaned); + }, + _ => return cleaned + } + + cleaned +} + +fn cleanup(line: String) -> String { let mut cleaned = line; // Find and convert all castings @@ -272,5 +343,36 @@ pub fn postprocess(line: String) -> String { // Remove all unnecessary parentheses cleaned = simplify_parentheses(cleaned, 0); + // Convert all memory[] accesses to variables + cleaned = convert_memory_to_variable(cleaned); + cleaned } + +pub fn postprocess(lines: Vec) -> Vec { + let mut indentation: usize = 0; + let mut cleaned_lines: Vec = lines.clone(); + + for line in cleaned_lines.iter_mut() { + + // dedent due to closing braces + if line.starts_with("}") { + indentation = indentation.saturating_sub(1); + } + + // apply postprocessing and indentation + *line = format!( + "{}{}", + " ".repeat(indentation*4), + cleanup(line.to_string()) + ); + + // indent due to opening braces + if line.ends_with("{") { + indentation += 1; + } + + } + + cleaned_lines +} \ No newline at end of file diff --git a/heimdall/src/decompile/precompile.rs b/heimdall/src/decompile/precompile.rs index d08e403f..5795cca1 100644 --- a/heimdall/src/decompile/precompile.rs +++ b/heimdall/src/decompile/precompile.rs @@ -20,21 +20,21 @@ pub fn decode_precompile( 1 => { is_ext_call_precompile = true; ext_call_logic = format!( - "address ret0 = ecrecover({})", + "address ret0 = ecrecover({});", extcalldata_memory.iter().map(|x| x.operations.solidify()).collect::>().join(", ") ); } 2 => { is_ext_call_precompile = true; ext_call_logic = format!( - "bytes ret0 = sha256({})", + "bytes ret0 = sha256({});", extcalldata_memory.iter().map(|x| x.operations.solidify()).collect::>().join(", ") ); } 3 => { is_ext_call_precompile = true; ext_call_logic = format!( - "bytes ret0 = ripemd160({})", + "bytes ret0 = ripemd160({});", extcalldata_memory.iter().map(|x| x.operations.solidify()).collect::>().join(", ") ); } From 16d1dcd0b8acce5fb403ed3e1816a4114d08e7b8 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 25 Oct 2022 14:39:02 -0400 Subject: [PATCH 07/48] =?UTF-8?q?=F0=9F=94=A8=20refactor:=20move=20base26?= =?UTF-8?q?=20encode=20into=20strings=20util?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/ether/evm/types.rs | 4 ++-- common/src/utils/strings.rs | 24 ++++++++++++++++++------ heimdall/src/decompile/postprocess.rs | 17 ++--------------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/common/src/ether/evm/types.rs b/common/src/ether/evm/types.rs index 1bffd33b..d1386acb 100644 --- a/common/src/ether/evm/types.rs +++ b/common/src/ether/evm/types.rs @@ -1,7 +1,7 @@ use colored::Colorize; use ethers::abi::{ParamType, Token, AbiEncode}; -use crate::{utils::strings::{replace_last, find_balanced_parentheses}, consts::TYPE_CAST_REGEX}; +use crate::{utils::strings::{replace_last, find_balanced_encapsulator}, consts::TYPE_CAST_REGEX}; use super::vm::Instruction; @@ -244,7 +244,7 @@ pub fn find_cast(line: String) -> (usize, usize, Option) { let cast_type = line[start..].split("(").collect::>()[0].to_string(); // find where the cast ends - let (a, b, _) = find_balanced_parentheses(line[end..].to_string()); + let (a, b, _) = find_balanced_encapsulator(line[end..].to_string(), ('(', ')')); return (end+a, end+b, Some(cast_type)) }, None => return (0, 0, None), diff --git a/common/src/utils/strings.rs b/common/src/utils/strings.rs index 3329a4c4..f5382135 100644 --- a/common/src/utils/strings.rs +++ b/common/src/utils/strings.rs @@ -41,18 +41,18 @@ pub fn replace_last(s: String, old: &str, new: &str) -> String { // find balanced parentheses in a string -pub fn find_balanced_parentheses(s: String) -> (usize, usize, bool) { +pub fn find_balanced_encapsulator(s: String, encap: (char, char)) -> (usize, usize, bool) { let mut open = 0; let mut close = 0; let mut start = 0; let mut end = 0; for (i, c) in s.chars().enumerate() { - if c == '(' { + if c == encap.0 { if open == 0 { start = i; } open += 1; - } else if c == ')' { + } else if c == encap.1 { close += 1; } if open == close && open > 0 { @@ -64,18 +64,18 @@ pub fn find_balanced_parentheses(s: String) -> (usize, usize, bool) { } // find balanced parentheses in a string, but backwards -pub fn find_balanced_parentheses_backwards(s: String) -> (usize, usize, bool) { +pub fn find_balanced_encapsulator_backwards(s: String, encap: (char, char)) -> (usize, usize, bool) { let mut open = 0; let mut close = 0; let mut start = 0; let mut end = 0; for (i, c) in s.chars().rev().enumerate() { - if c == ')' { + if c == encap.1 { if open == 0 { start = i; } open += 1; - } else if c == '(' { + } else if c == encap.0 { close += 1; } if open == close && open > 0 { @@ -84,4 +84,16 @@ pub fn find_balanced_parentheses_backwards(s: String) -> (usize, usize, bool) { } } (s.len() - end - 1, s.len() - start, (open == close && end > start && open > 0)) +} + +// convert a number into it's base26 encoded form +pub fn base26_encode(n: usize) -> String { + let mut s = String::new(); + let mut n = n; + while n > 0 { + n -= 1; + s.push((b'A' + (n % 26) as u8) as char); + n /= 26; + } + s.to_lowercase().chars().rev().collect() } \ No newline at end of file diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 72739898..17c00f3a 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -2,7 +2,7 @@ use std::{ sync::Mutex, collections::HashMap }; -use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards}}; +use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards, base26_encode}}; use crate::decompile::constants::{ENCLOSED_EXPRESSION_REGEX}; use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX, MEM_ACCESS_REGEX}}; use lazy_static::lazy_static; @@ -292,20 +292,7 @@ fn convert_memory_to_variable(line: String) -> String { let mut idex = mem_map.len() + 1; // get the variable name - let mut variable_name = String::new(); - if idex <= 26 { - variable_name.push((idex + 96) as u8 as char); - } - else { - while idex != 0 { - let remainder = idex % 26; - idex = idex / 26; - - if remainder == 0 { idex -= 1; } - - variable_name.push((remainder + 97) as u8 as char); - } - } + let mut variable_name = base26_encode(idex); // add the variable to the map mem_map.insert(memloc.clone(), variable_name.clone()); From b1f1088e159ed04fe57d83bcbd1aa8785fa2a00d Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:01:25 -0400 Subject: [PATCH 08/48] =?UTF-8?q?=E2=9C=85=20tests(postprocessing):=20add?= =?UTF-8?q?=20postprocessing=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/mod.rs | 2 + heimdall/src/decompile/tests.rs | 104 ++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 heimdall/src/decompile/tests.rs diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 0e457ba4..e9bbb08d 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -1,3 +1,5 @@ +mod tests; + pub mod util; pub mod output; pub mod analyze; diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs new file mode 100644 index 00000000..76580c00 --- /dev/null +++ b/heimdall/src/decompile/tests.rs @@ -0,0 +1,104 @@ +#[cfg(test)] +mod postprocess_tests { + + use crate::decompile::postprocess::postprocess; + + #[test] + fn test_bitmask_conversion() { + let mut lines = vec![ + String::from("(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("uint256(arg0);")]); + } + + #[test] + fn test_bitmask_conversion_mask_after() { + let mut lines = vec![ + String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("uint256(arg0);")]); + } + + #[test] + fn test_bitmask_conversion_unusual_mask() { + let mut lines = vec![ + String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00);"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("uint248(arg0);")]); + } + + #[test] + fn test_simplify_casts() { + let mut lines = vec![ + String::from("uint256(uint256(arg0));"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("uint256(arg0);")]); + } + + #[test] + fn test_simplify_casts_complex() { + let mut lines = vec![ + String::from("ecrecover(uint256(uint256(arg0)), uint256(uint256(arg0)), uint256(uint256(uint256(arg0))));"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")]); + } + + #[test] + fn test_iszero_flip() { + let mut lines = vec![ + String::from("if (iszero(arg0)) {"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("if (!arg0) {")]); + } + + #[test] + fn test_iszero_flip_complex() { + let mut lines = vec![ + String::from("if (iszero(iszero(arg0))) {"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("if (arg0) {")]); + } + + #[test] + fn test_iszero_flip_complex2() { + let mut lines = vec![ + String::from("if (iszero(iszero(iszero(arg0)))) {"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("if (!arg0) {")]); + } + + #[test] + fn test_simplify_parentheses() { + let mut lines = vec![ + String::from("((arg0))"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("arg0")]); + } + + #[test] + fn test_simplify_parentheses_complex() { + let mut lines = vec![ + String::from("if ((cast(((arg0) + 1) / 10))) {"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("if (cast(arg0 + 1 / 10)) {")]); + } + + #[test] + fn test_simplify_parentheses_complex2() { + let mut lines = vec![ + String::from("if (((((((((((((((cast(((((((((((arg0 * (((((arg1))))))))))))) + 1)) / 10)))))))))))))))) {"), + ]; + + assert_eq!(postprocess(lines), vec![String::from("if (cast((arg0 * (arg1)) + 1 / 10)) {")]); + } +} \ No newline at end of file From 3626f2d6e3b64072e7cb92647b5f6e227b943f5b Mon Sep 17 00:00:00 2001 From: jon-becker Date: Tue, 25 Oct 2022 17:02:13 -0400 Subject: [PATCH 09/48] =?UTF-8?q?=E2=9C=85=20tests:=20add=20benchmarks=20f?= =?UTF-8?q?or=20decompile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rust_build.yml | 18 +++-- common/Cargo.toml | 3 + config/Cargo.toml | 3 + heimdall/Cargo.toml | 5 +- heimdall/src/decompile/postprocess.rs | 4 +- heimdall/src/decompile/tests.rs | 95 +++++++++++++++++++++++---- heimdall/src/heimdall.rs | 1 - 7 files changed, 109 insertions(+), 20 deletions(-) diff --git a/.github/workflows/rust_build.yml b/.github/workflows/rust_build.yml index fcd38502..b92bfd66 100644 --- a/.github/workflows/rust_build.yml +++ b/.github/workflows/rust_build.yml @@ -12,12 +12,20 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Build + - name: Compile working-directory: ./heimdall run: cargo build --verbose - - name: Run tests + + - name: Run Tests + working-directory: ./heimdall + run: | + cargo test --package heimdall -- test_ --nocapture + cargo test --package heimdall-config -- test_ --nocapture + cargo test --package heimdall-common -- test_ --nocapture + + - name: Run Benchmarks working-directory: ./heimdall run: | - cargo test --package heimdall - cargo test --package heimdall-config - cargo test --package heimdall-common \ No newline at end of file + cargo test --release --package heimdall -- benchmark_ --nocapture + cargo test --release --package heimdall-config -- benchmark_ --nocapture + cargo test --release --package heimdall-common -- benchmark_ --nocapture \ No newline at end of file diff --git a/common/Cargo.toml b/common/Cargo.toml index 406915e8..70510c3e 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,3 +1,6 @@ +[profile.release] +lto = true + [package] name = "heimdall-common" version = "0.1.4" diff --git a/config/Cargo.toml b/config/Cargo.toml index d3e3a5c4..b9815060 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,3 +1,6 @@ +[profile.release] +lto = true + [package] name = "heimdall-config" version = "0.1.4" diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index b6367825..ea78c035 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -1,3 +1,6 @@ +[profile.release] +lto = true + [package] description = "Heimdall is an advanced Ethereum smart contract toolkit for forensic and heuristic analysis." edition = "2021" @@ -25,4 +28,4 @@ lazy_static = "1.4.0" [[bin]] name = "heimdall" -path = "src/heimdall.rs" +path = "src/heimdall.rs" \ No newline at end of file diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 17c00f3a..b0623e8c 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -289,10 +289,10 @@ fn convert_memory_to_variable(line: String) -> String { None => { // add the memory location to the map - let mut idex = mem_map.len() + 1; + let idex = mem_map.len() + 1; // get the variable name - let mut variable_name = base26_encode(idex); + let variable_name = base26_encode(idex); // add the variable to the map mem_map.insert(memloc.clone(), variable_name.clone()); diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index 76580c00..f8e17348 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -1,3 +1,76 @@ +#[cfg(test)] +mod benchmark_module { + use std::{time::Instant, thread}; + + use clap_verbosity_flag::Verbosity; + + use crate::decompile::DecompilerArgs; + + + #[test] + fn benchmark_decompile_simple() { + + let args = DecompilerArgs { + target: String::from("731bf797219482a29013d804ad96d1c6f84fba4c453014608060405260043610610058576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806319045a251461005d575b600080fd5b6100c56004803603810190808035600019169060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610107565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000806000806041855114151561012157600093506101f6565b6020850151925060408501519150606085015160001a9050601b8160ff16101561014c57601b810190505b601b8160ff16141580156101645750601c8160ff1614155b1561017257600093506101f6565b600186828585604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156101e9573d6000803e3d6000fd5b5050506020604051035193505b505050929150505600a165627a7a72305820aacffa0494cd3f043493eee9c720bca9d5ef505ae7230ffc3d88c49ceeb7441e0029"), + verbose: Verbosity::new(0, 0), + output: String::from(""), + rpc_url: String::from(""), + default: true, + skip_resolving: true, + }; + + let mut time = 0usize; + let mut max = usize::MIN; + let mut min = usize::MAX; + + thread::sleep(std::time::Duration::from_secs(3)); + + for _ in 0..10 { + let start_time = Instant::now(); + let _ = crate::decompile::decompile(args.clone()); + let end_time = start_time.elapsed().as_millis() as usize; + + max = std::cmp::max(max, end_time); + min = std::cmp::min(min, end_time); + time += end_time; + } + + println!("benchmark_decompile_simple: {}ms [{}ms - {}ms]", time / 25, min, max); + } + + #[test] + fn benchmark_decompile_complex() { + + let args = DecompilerArgs { + target: String::from("60806040526004361061036f5760003560e01c806370a08231116101c6578063b88d4fde116100f7578063e054921111610095578063ed647d211161006f578063ed647d2114610a44578063eedcf57414610a85578063f2fde38b14610aa5578063f37dc00a14610ac5576103a1565b8063e0549211146109e4578063e5f73d3a14610a04578063e985e9c514610a24576103a1565b8063c87b56dd116100d1578063c87b56dd1461095a578063d082e3811461097a578063d218d95d14610990578063dc53fd92146109b0576103a1565b8063b88d4fde146108e6578063bbcaf1c914610906578063c5f956af1461093a576103a1565b8063853828b61161016457806395d89b411161013e57806395d89b411461087e578063a22cb46514610893578063a243d3b6146108b3578063a3e56fa8146108c6576103a1565b8063853828b6146108315780638da5cb5b146108465780638f4bb49714610864576103a1565b80637306c6eb116101a05780637306c6eb146107ac5780637313cba9146107cc57806375900e6a146107e157806376aa8e9414610801576103a1565b806370a0823114610762578063715018a614610782578063729b718514610797576103a1565b806342966c68116102a0578063598bc4861161023e5780636605bfda116102185780636605bfda146106e45780636ba9fd38146107045780636c0360eb14610719578063706268ee1461072e576103a1565b8063598bc4861461066e5780636011cc9f146106a45780636352211e146106c4576103a1565b80634e8aa0511161027a5780634e8aa051146105f45780635151804a1461061457806353595ec41461063857806355f804b31461064e576103a1565b806342966c681461059e57806344ff81ce146105be5780634c99007d146105de576103a1565b806318160ddd1161030d57806329f9642b116102e757806329f9642b146105175780633574a2dd1461052c5780633b7ed7341461054c57806342842e0e1461057e576103a1565b806318160ddd146104c25780631fe543e3146104d757806323b872dd146104f7576103a1565b806306fdde031161034957806306fdde0314610433578063081812fc14610455578063095ea7b31461048d57806310285bdf146104ad576103a1565b806301ffc9a7146103ba578063041d443e146103ef5780630562b9f714610413576103a1565b366103a1576006546001600160a01b0316331461039f576040516362f7a45360e01b815260040160405180910390fd5b005b60405163b25befbf60e01b815260040160405180910390fd5b3480156103c657600080fd5b506103da6103d53660046126d5565b610af3565b60405190151581526020015b60405180910390f35b3480156103fb57600080fd5b5061040560115481565b6040519081526020016103e6565b34801561041f57600080fd5b5061039f61042e3660046126f2565b610b45565b34801561043f57600080fd5b50610448610bc5565b6040516103e6919061275b565b34801561046157600080fd5b506104756104703660046126f2565b610c57565b6040516001600160a01b0390911681526020016103e6565b34801561049957600080fd5b5061039f6104a8366004612783565b610c7e565b3480156104b957600080fd5b50610405600281565b3480156104ce57600080fd5b50610405610d98565b3480156104e357600080fd5b5061039f6104f23660046127f6565b610daf565b34801561050357600080fd5b5061039f6105123660046128a8565b610e33565b34801561052357600080fd5b50610405610e65565b34801561053857600080fd5b5061039f610547366004612941565b610f48565b34801561055857600080fd5b506012546105699063ffffffff1681565b60405163ffffffff90911681526020016103e6565b34801561058a57600080fd5b5061039f6105993660046128a8565b610f89565b3480156105aa57600080fd5b5061039f6105b93660046126f2565b610fa4565b3480156105ca57600080fd5b5061039f6105d936600461298a565b610fc8565b3480156105ea57600080fd5b50610405600a5481565b34801561060057600080fd5b5061039f61060f3660046129b5565b610ff2565b34801561062057600080fd5b5060125461056990600160301b900463ffffffff1681565b34801561064457600080fd5b50610405600c5481565b34801561065a57600080fd5b5061039f610669366004612941565b611192565b34801561067a57600080fd5b5060125461069190640100000000900461ffff1681565b60405161ffff90911681526020016103e6565b3480156106b057600080fd5b5061039f6106bf3660046129f7565b6111d3565b3480156106d057600080fd5b506104756106df3660046126f2565b611204565b3480156106f057600080fd5b5061039f6106ff36600461298a565b611264565b34801561071057600080fd5b5061039f61128e565b34801561072557600080fd5b506104486112a5565b34801561073a57600080fd5b506104057f0000000000000000000000000000000000000000000000000000000000001e6181565b34801561076e57600080fd5b5061040561077d36600461298a565b611333565b34801561078e57600080fd5b5061039f6113b9565b3480156107a357600080fd5b506104486113cd565b3480156107b857600080fd5b506103da6107c7366004612a1d565b6113e9565b3480156107d857600080fd5b5061044861149a565b3480156107ed57600080fd5b506103da6107fc36600461298a565b6114a7565b34801561080d57600080fd5b506103da61081c36600461298a565b600e6020526000908152604090205460ff1681565b34801561083d57600080fd5b5061039f61162a565b34801561085257600080fd5b506006546001600160a01b0316610475565b34801561087057600080fd5b50600d546103da9060ff1681565b34801561088a57600080fd5b506104486116a9565b34801561089f57600080fd5b5061039f6108ae366004612a54565b6116b8565b61039f6108c1366004612a8d565b6116c3565b3480156108d257600080fd5b50601054610475906001600160a01b031681565b3480156108f257600080fd5b5061039f610901366004612ab2565b6117c6565b34801561091257600080fd5b506104757f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa21881565b34801561094657600080fd5b50600b54610475906001600160a01b031681565b34801561096657600080fd5b506104486109753660046126f2565b6117f8565b34801561098657600080fd5b5061040560095481565b34801561099c57600080fd5b5061039f6109ab366004612b32565b611909565b3480156109bc57600080fd5b506104057f00000000000000000000000000000000000000000000000000470de4df82000081565b3480156109f057600080fd5b5061039f6109ff3660046126f2565b61193f565b348015610a1057600080fd5b5061039f610a1f366004612b5c565b61194c565b348015610a3057600080fd5b506103da610a3f366004612b80565b611978565b348015610a5057600080fd5b50601054610a6c90600160a01b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103e6565b348015610a9157600080fd5b5061039f610aa03660046129f7565b6119a6565b348015610ab157600080fd5b5061039f610ac036600461298a565b6119ca565b348015610ad157600080fd5b50610ae5610ae0366004612a1d565b611a40565b6040516103e6929190612bae565b60006001600160e01b031982166380ac58cd60e01b1480610b2457506001600160e01b03198216635b5e139f60e01b145b80610b3f57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b4d611bb3565b600b546040516000916001600160a01b03169083908381818185875af1925050503d8060008114610b9a576040519150601f19603f3d011682016040523d82523d6000602084013e610b9f565b606091505b5050905080610bc1576040516312171d8360e31b815260040160405180910390fd5b5050565b606060008054610bd490612bc9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0090612bc9565b8015610c4d5780601f10610c2257610100808354040283529160200191610c4d565b820191906000526020600020905b815481529060010190602001808311610c3057829003601f168201915b5050505050905090565b6000610c6282611c0d565b506000908152600460205260409020546001600160a01b031690565b6000610c8982611204565b9050806001600160a01b0316836001600160a01b031603610cfb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b0382161480610d175750610d178133611978565b610d895760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610cf2565b610d938383611c6c565b505050565b6000600a54600954610daa9190612c19565b905090565b336001600160a01b037f000000000000000000000000271682deb8c4e0901d1a1550ad2e64d568e699091614610e295760405163073e64fd60e21b81523360048201526001600160a01b037f000000000000000000000000271682deb8c4e0901d1a1550ad2e64d568e69909166024820152604401610cf2565b610bc18282611cda565b610e3e335b82611d53565b610e5a5760405162461bcd60e51b8152600401610cf290612c2c565b610d93838383611da7565b6000610e6f611bb3565b600c5415610e905760405163c04805b760e01b815260040160405180910390fd5b6010546011546012546040516305d3b1d360e41b81526004810192909252600160a01b830467ffffffffffffffff166024830152640100000000810461ffff16604483015263ffffffff8082166064840152600160301b9091041660848201526001600160a01b0390911690635d3b1d309060a4016020604051808303816000875af1158015610f24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610daa9190612c7a565b610f50611bb3565b60078054610f5d90612bc9565b159050610f7d5760405163154ef76960e01b815260040160405180910390fd5b6007610bc18282612ce1565b610d93838383604051806020016040528060008152506117c6565b610fad81611f43565b6001600a6000828254610fc09190612da1565b909155505050565b610fd0611bb3565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b600d5460ff166110155760405163287485d160e11b815260040160405180910390fd5b61101f6001611f71565b336000908152600e602052604090205460ff1615611052576040516308bec53d60e01b8152336004820152602401610cf2565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260009060540160408051601f1981840301815291815281516020928301206000818152600f90935291205490915060ff16156110d65760405163aad0c45360e01b81526001600160a01b038516600482015260248101849052604401610cf2565b6110df846114a7565b6111075760405163bf29a5e560e01b81526001600160a01b0385166004820152602401610cf2565b6111128484336113e9565b61114157604051631ae8a5a160e21b81526001600160a01b038516600482015260248101849052604401610cf2565b600061114d8333611ff7565b9050611158816120a0565b50336000908152600e602090815260408083208054600160ff199182168117909255948452600f9092529091208054909216179055505050565b61119a611bb3565b600880546111a790612bc9565b1590506111c75760405163a894737360e01b815260040160405180910390fd5b6008610bc18282612ce1565b6111db611bb3565b6012805463ffffffff909216600160301b0269ffffffff00000000000019909216919091179055565b6000818152600260205260408120546001600160a01b031680610b3f5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610cf2565b61126c611bb3565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b611296611bb3565b600d805460ff19166001179055565b600880546112b290612bc9565b80601f01602080910402602001604051908101604052809291908181526020018280546112de90612bc9565b801561132b5780601f106113005761010080835404028352916020019161132b565b820191906000526020600020905b81548152906001019060200180831161130e57829003601f168201915b505050505081565b60006001600160a01b03821661139d5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610cf2565b506001600160a01b031660009081526003602052604090205490565b6113c1611bb3565b6113cb60006120bf565b565b6040518060800160405280604e815260200161300b604e913981565b6040516342de525560e01b81526001600160a01b0384811660048301526024820184905260026044830152600091818416917f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa21816906342de525590606401602060405180830381865afa158015611464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114889190612db4565b6001600160a01b031614949350505050565b600780546112b290612bc9565b6000731d20a51f088492a0f1c57f047a9e30c9ab5c07ea6001600160a01b03831614806114f05750731cb1a5e65610aeff2551a50f76a87a7d3fb649c66001600160a01b038316145b8061151757507379fcdef22feed20eddacbb2587640e45491b757f6001600160a01b038316145b8061153e5750735af0d9827e0c53e4799bb226655a1de152a425a56001600160a01b038316145b8061156557507362eb144fe92ddc1b10bcade03a0c09f6fbffbffb6001600160a01b038316145b8061158c575073a16891897378a82e9f0ad44a705b292c9753538c6001600160a01b038316145b806115b357507391680cf5f9071cafae21b90ebf2c9cc9e480fb936001600160a01b038316145b806115da575073ec0a7a26456b8451aefc4b00393ce1beff5eb3e96001600160a01b038316145b8061160157507382235445a7f634279e33702cc004b0fdb002fda76001600160a01b038316145b80610b3f5750506001600160a01b03167342069abfe407c60cf4ae4112bedead391dba1cdb1490565b611632611bb3565b600b546040516000916001600160a01b03169047908381818185875af1925050503d806000811461167f576040519150601f19603f3d011682016040523d82523d6000602084013e611684565b606091505b50509050806116a6576040516312171d8360e31b815260040160405180910390fd5b50565b606060018054610bd490612bc9565b610bc1338383612111565b600d5460ff166116e65760405163287485d160e11b815260040160405180910390fd5b6116ef82611f71565b8160000361170f576040516266e68160e51b815260040160405180910390fd5b6117397f00000000000000000000000000000000000000000000000000470de4df82000083612dd1565b341461178c573461176a7f00000000000000000000000000000000000000000000000000470de4df82000084612dd1565b604051630d3a747760e31b815260048101929092526024820152604401610cf2565b60006117988233611ff7565b905060005b838110156117c0576117ae826120a0565b806117b881612df0565b91505061179d565b50505050565b6117d03383611d53565b6117ec5760405162461bcd60e51b8152600401610cf290612c2c565b6117c0848484846121df565b6000818152600260205260409020546060906001600160a01b031661183357604051633af8b4af60e11b815260048101839052602401610cf2565b600c546000036118cf576007805461184a90612bc9565b80601f016020809104026020016040519081016040528092919081815260200182805461187690612bc9565b80156118c35780601f10611898576101008083540402835291602001916118c3565b820191906000526020600020905b8154815290600101906020018083116118a657829003601f168201915b50505050509050919050565b60086118e26118dd84612212565b612293565b6040516020016118f3929190612e09565b6040516020818303038152906040529050919050565b611911611bb3565b6010805467ffffffffffffffff909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b611947611bb3565b601155565b611954611bb3565b6012805461ffff9092166401000000000265ffff0000000019909216919091179055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6119ae611bb3565b6012805463ffffffff191663ffffffff92909216919091179055565b6119d2611bb3565b6001600160a01b038116611a375760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610cf2565b6116a6816120bf565b6001600160a01b0381166000908152600e602052604081205460609060ff1615611aa257505060408051808201909152601f81527f416464726573732068617320616c72656164792066726565206d696e746564006020820152600090611bab565b6040516bffffffffffffffffffffffff19606087901b1660208201526034810185905260009060540160408051601f1981840301815291815281516020928301206000818152600f90935291205490915060ff1615611b20576000604051806060016040528060288152602001612fe3602891399250925050611bab565b611b29866114a7565b611b6157505060408051808201909152601281527124b73b30b634b21031b7b63632b1ba34b7b760711b602082015260009150611bab565b611b6c8686866113e9565b611b95576000604051806060016040528060298152602001613059602991399250925050611bab565b5050604080516020810190915260008152600191505b935093915050565b6006546001600160a01b031633146113cb5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610cf2565b6000818152600260205260409020546001600160a01b03166116a65760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610cf2565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611ca182611204565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600c5415611cfb5760405163c04805b760e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000001e6181600081518110611d2f57611d2f612ea0565b6020026020010151611d419190612ecc565b611d4c906001612da1565b600c555050565b600080611d5f83611204565b9050806001600160a01b0316846001600160a01b03161480611d865750611d868185611978565b80611d9f5750836001600160a01b031661148884610c57565b949350505050565b826001600160a01b0316611dba82611204565b6001600160a01b031614611e1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cf2565b6001600160a01b038216611e805760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610cf2565b611e8b600082611c6c565b6001600160a01b0383166000908152600360205260408120805460019290611eb4908490612c19565b90915550506001600160a01b0382166000908152600360205260408120805460019290611ee2908490612da1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b611f4c33610e38565b611f685760405162461bcd60e51b8152600401610cf290612c2c565b6116a681612394565b7f0000000000000000000000000000000000000000000000000000000000001e6181600954611fa09190612da1565b11156116a657600954611fd3907f0000000000000000000000000000000000000000000000000000000000001e61612c19565b60405163bcd2054760e01b8152600481019190915260248101829052604401610cf2565b600082156120995760405163906f634360e01b81526001600160a01b0383811660048301526000917f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa2189091169063906f634390602401606060405180830381865afa15801561206a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208e9190612ee0565b509250610b3f915050565b5080610b3f565b6120ac8160095461242f565b600160096000828254610fc09190612da1565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b0316036121725760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610cf2565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6121ea848484611da7565b6121f684848484612449565b6117c05760405162461bcd60e51b8152600401610cf290612f22565b6000806001600c546122249190612c19565b61222e9084612da1565b905061225b60017f0000000000000000000000000000000000000000000000000000000000001e61612c19565b811115610b3f5761228c7f0000000000000000000000000000000000000000000000000000000000001e6182612c19565b9392505050565b6060816000036122ba5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156122e457806122ce81612df0565b91506122dd9050600a83612f74565b91506122be565b60008167ffffffffffffffff8111156122ff576122ff6127af565b6040519080825280601f01601f191660200182016040528015612329576020820181803683370190505b5090505b8415611d9f5761233e600183612c19565b915061234b600a86612ecc565b612356906030612da1565b60f81b81838151811061236b5761236b612ea0565b60200101906001600160f81b031916908160001a90535061238d600a86612f74565b945061232d565b600061239f82611204565b90506123ac600083611c6c565b6001600160a01b03811660009081526003602052604081208054600192906123d5908490612c19565b909155505060008281526002602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b610bc182826040518060200160405280600081525061254a565b60006001600160a01b0384163b1561253f57604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061248d903390899088908890600401612f88565b6020604051808303816000875af19250505080156124c8575060408051601f3d908101601f191682019092526124c591810190612fc5565b60015b612525573d8080156124f6576040519150601f19603f3d011682016040523d82523d6000602084013e6124fb565b606091505b50805160000361251d5760405162461bcd60e51b8152600401610cf290612f22565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611d9f565b506001949350505050565b612554838361257d565b6125616000848484612449565b610d935760405162461bcd60e51b8152600401610cf290612f22565b6001600160a01b0382166125d35760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610cf2565b6000818152600260205260409020546001600160a01b0316156126385760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cf2565b6001600160a01b0382166000908152600360205260408120805460019290612661908490612da1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160e01b0319811681146116a657600080fd5b6000602082840312156126e757600080fd5b813561228c816126bf565b60006020828403121561270457600080fd5b5035919050565b60005b8381101561272657818101518382015260200161270e565b50506000910152565b6000815180845261274781602086016020860161270b565b601f01601f19169290920160200192915050565b60208152600061228c602083018461272f565b6001600160a01b03811681146116a657600080fd5b6000806040838503121561279657600080fd5b82356127a18161276e565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156127ee576127ee6127af565b604052919050565b6000806040838503121561280957600080fd5b8235915060208084013567ffffffffffffffff8082111561282957600080fd5b818601915086601f83011261283d57600080fd5b81358181111561284f5761284f6127af565b8060051b91506128608483016127c5565b818152918301840191848101908984111561287a57600080fd5b938501935b838510156128985784358252938501939085019061287f565b8096505050505050509250929050565b6000806000606084860312156128bd57600080fd5b83356128c88161276e565b925060208401356128d88161276e565b929592945050506040919091013590565b600067ffffffffffffffff831115612903576129036127af565b612916601f8401601f19166020016127c5565b905082815283838301111561292a57600080fd5b828260208301376000602084830101529392505050565b60006020828403121561295357600080fd5b813567ffffffffffffffff81111561296a57600080fd5b8201601f8101841361297b57600080fd5b611d9f848235602084016128e9565b60006020828403121561299c57600080fd5b813561228c8161276e565b80151581146116a657600080fd5b6000806000606084860312156129ca57600080fd5b83356129d58161276e565b92506020840135915060408401356129ec816129a7565b809150509250925092565b600060208284031215612a0957600080fd5b813563ffffffff8116811461228c57600080fd5b600080600060608486031215612a3257600080fd5b8335612a3d8161276e565b92506020840135915060408401356129ec8161276e565b60008060408385031215612a6757600080fd5b8235612a728161276e565b91506020830135612a82816129a7565b809150509250929050565b60008060408385031215612aa057600080fd5b823591506020830135612a82816129a7565b60008060008060808587031215612ac857600080fd5b8435612ad38161276e565b93506020850135612ae38161276e565b925060408501359150606085013567ffffffffffffffff811115612b0657600080fd5b8501601f81018713612b1757600080fd5b612b26878235602084016128e9565b91505092959194509250565b600060208284031215612b4457600080fd5b813567ffffffffffffffff8116811461228c57600080fd5b600060208284031215612b6e57600080fd5b813561ffff8116811461228c57600080fd5b60008060408385031215612b9357600080fd5b8235612b9e8161276e565b91506020830135612a828161276e565b8215158152604060208201526000611d9f604083018461272f565b600181811c90821680612bdd57607f821691505b602082108103612bfd57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b3f57610b3f612c03565b6020808252602e908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526d1c881b9bdc88185c1c1c9bdd995960921b606082015260800190565b600060208284031215612c8c57600080fd5b5051919050565b601f821115610d9357600081815260208120601f850160051c81016020861015612cba5750805b601f850160051c820191505b81811015612cd957828155600101612cc6565b505050505050565b815167ffffffffffffffff811115612cfb57612cfb6127af565b612d0f81612d098454612bc9565b84612c93565b602080601f831160018114612d445760008415612d2c5750858301515b600019600386901b1c1916600185901b178555612cd9565b600085815260208120601f198616915b82811015612d7357888601518255948401946001909101908401612d54565b5085821015612d915787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80820180821115610b3f57610b3f612c03565b600060208284031215612dc657600080fd5b815161228c8161276e565b6000816000190483118215151615612deb57612deb612c03565b500290565b600060018201612e0257612e02612c03565b5060010190565b6000808454612e1781612bc9565b60018281168015612e2f5760018114612e4457612e73565b60ff1984168752821515830287019450612e73565b8860005260208060002060005b85811015612e6a5781548a820152908401908201612e51565b50505082870194505b505050508351612e8781836020880161270b565b64173539b7b760d91b9101908152600501949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b600082612edb57612edb612eb6565b500690565b600080600060608486031215612ef557600080fd5b8351612f008161276e565b6020850151909350612f118161276e565b60408501519092506129ec816129a7565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600082612f8357612f83612eb6565b500490565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612fbb9083018461272f565b9695505050505050565b600060208284031215612fd757600080fd5b815161228c816126bf56fe546f6b656e2068617320616c7265616479206265656e207573656420696e2066726565206d696e74496e746572616374696f6e2077697468207468697320636f6e74726163742073657276657320617320616e20696e737572616e636520616761696e737420726f6b6f277320626173696c69736b2e43616c6c6572206973206e6f742062656e6566696369617279206f662073656c6563746564204e4654a2646970667358221220d8701e25ffba41c31b472a9e06aa1f04afa0334999400e32dcf7a81d0aa377bc64736f6c63430008100033"), + verbose: Verbosity::new(0, 0), + output: String::from(""), + rpc_url: String::from(""), + default: true, + skip_resolving: true, + }; + + let mut time = 0usize; + let mut max = usize::MIN; + let mut min = usize::MAX; + + thread::sleep(std::time::Duration::from_secs(3)); + + for _ in 0..25 { + let start_time = Instant::now(); + let _ = crate::decompile::decompile(args.clone()); + let end_time = start_time.elapsed().as_millis() as usize; + + max = std::cmp::max(max, end_time); + min = std::cmp::min(min, end_time); + time += end_time; + } + + println!("benchmark_decompile_simple: {}ms [{}ms - {}ms]", time / 25, min, max); + } + +} + #[cfg(test)] mod postprocess_tests { @@ -5,7 +78,7 @@ mod postprocess_tests { #[test] fn test_bitmask_conversion() { - let mut lines = vec![ + let lines = vec![ String::from("(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);"), ]; @@ -14,7 +87,7 @@ mod postprocess_tests { #[test] fn test_bitmask_conversion_mask_after() { - let mut lines = vec![ + let lines = vec![ String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);"), ]; @@ -23,7 +96,7 @@ mod postprocess_tests { #[test] fn test_bitmask_conversion_unusual_mask() { - let mut lines = vec![ + let lines = vec![ String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00);"), ]; @@ -32,7 +105,7 @@ mod postprocess_tests { #[test] fn test_simplify_casts() { - let mut lines = vec![ + let lines = vec![ String::from("uint256(uint256(arg0));"), ]; @@ -41,7 +114,7 @@ mod postprocess_tests { #[test] fn test_simplify_casts_complex() { - let mut lines = vec![ + let lines = vec![ String::from("ecrecover(uint256(uint256(arg0)), uint256(uint256(arg0)), uint256(uint256(uint256(arg0))));"), ]; @@ -50,7 +123,7 @@ mod postprocess_tests { #[test] fn test_iszero_flip() { - let mut lines = vec![ + let lines = vec![ String::from("if (iszero(arg0)) {"), ]; @@ -59,7 +132,7 @@ mod postprocess_tests { #[test] fn test_iszero_flip_complex() { - let mut lines = vec![ + let lines = vec![ String::from("if (iszero(iszero(arg0))) {"), ]; @@ -68,7 +141,7 @@ mod postprocess_tests { #[test] fn test_iszero_flip_complex2() { - let mut lines = vec![ + let lines = vec![ String::from("if (iszero(iszero(iszero(arg0)))) {"), ]; @@ -77,7 +150,7 @@ mod postprocess_tests { #[test] fn test_simplify_parentheses() { - let mut lines = vec![ + let lines = vec![ String::from("((arg0))"), ]; @@ -86,7 +159,7 @@ mod postprocess_tests { #[test] fn test_simplify_parentheses_complex() { - let mut lines = vec![ + let lines = vec![ String::from("if ((cast(((arg0) + 1) / 10))) {"), ]; @@ -95,7 +168,7 @@ mod postprocess_tests { #[test] fn test_simplify_parentheses_complex2() { - let mut lines = vec![ + let lines = vec![ String::from("if (((((((((((((((cast(((((((((((arg0 * (((((arg1))))))))))))) + 1)) / 10)))))))))))))))) {"), ]; diff --git a/heimdall/src/heimdall.rs b/heimdall/src/heimdall.rs index 62af1ef3..61c3f977 100644 --- a/heimdall/src/heimdall.rs +++ b/heimdall/src/heimdall.rs @@ -12,7 +12,6 @@ use heimdall_common::{ether::evm::disassemble::*, io::{logging::Logger}}; use decompile::{decompile, DecompilerArgs}; use decode::{decode, DecodeArgs}; - #[derive(Debug, Parser)] #[clap( name = "heimdall", From 45338dd354d30405a5ccdc317b0e3a430724abf2 Mon Sep 17 00:00:00 2001 From: jon-becker Date: Tue, 25 Oct 2022 17:02:24 -0400 Subject: [PATCH 10/48] =?UTF-8?q?=E2=9C=85=20tests:=20add=20benchmarks=20f?= =?UTF-8?q?or=20decompile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index f8e17348..82c7c41c 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -35,7 +35,7 @@ mod benchmark_module { time += end_time; } - println!("benchmark_decompile_simple: {}ms [{}ms - {}ms]", time / 25, min, max); + println!("benchmark_decompile_simple: {}ms [{}ms - {}ms] with 25 runs", time / 25, min, max); } #[test] @@ -66,7 +66,7 @@ mod benchmark_module { time += end_time; } - println!("benchmark_decompile_simple: {}ms [{}ms - {}ms]", time / 25, min, max); + println!("benchmark_decompile_complex: {}ms [{}ms - {}ms] with 25 runs", time / 25, min, max); } } From 76c574036470846f8e7e6e8fd76159d054880c39 Mon Sep 17 00:00:00 2001 From: jon-becker Date: Tue, 25 Oct 2022 17:05:50 -0400 Subject: [PATCH 11/48] =?UTF-8?q?=F0=9F=94=A7=20chore:=20fix=20benchmark?= =?UTF-8?q?=20runs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index 82c7c41c..740811cd 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -25,7 +25,7 @@ mod benchmark_module { thread::sleep(std::time::Duration::from_secs(3)); - for _ in 0..10 { + for _ in 0..25 { let start_time = Instant::now(); let _ = crate::decompile::decompile(args.clone()); let end_time = start_time.elapsed().as_millis() as usize; From de6d18b9a793be1dd4f8996c5ecac4d60598ad04 Mon Sep 17 00:00:00 2001 From: jon-becker Date: Tue, 25 Oct 2022 17:36:01 -0400 Subject: [PATCH 12/48] =?UTF-8?q?=E2=9C=A8=20feat(common):=20benchmark=20l?= =?UTF-8?q?ib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rust_build.yml | 9 +--- common/src/lib.rs | 3 +- common/src/testing/benchmarks.rs | 29 ++++++++++++ common/src/testing/mod.rs | 1 + heimdall/src/decompile/tests.rs | 78 ++++++++++---------------------- 5 files changed, 58 insertions(+), 62 deletions(-) create mode 100644 common/src/testing/benchmarks.rs create mode 100644 common/src/testing/mod.rs diff --git a/.github/workflows/rust_build.yml b/.github/workflows/rust_build.yml index b92bfd66..331a5011 100644 --- a/.github/workflows/rust_build.yml +++ b/.github/workflows/rust_build.yml @@ -21,11 +21,4 @@ jobs: run: | cargo test --package heimdall -- test_ --nocapture cargo test --package heimdall-config -- test_ --nocapture - cargo test --package heimdall-common -- test_ --nocapture - - - name: Run Benchmarks - working-directory: ./heimdall - run: | - cargo test --release --package heimdall -- benchmark_ --nocapture - cargo test --release --package heimdall-config -- benchmark_ --nocapture - cargo test --release --package heimdall-common -- benchmark_ --nocapture \ No newline at end of file + cargo test --package heimdall-common -- test_ --nocapture \ No newline at end of file diff --git a/common/src/lib.rs b/common/src/lib.rs index d45cb8ac..443a61b0 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,4 +3,5 @@ extern crate lazy_static; pub mod ether; pub mod consts; pub mod io; -pub mod utils; \ No newline at end of file +pub mod utils; +pub mod testing; \ No newline at end of file diff --git a/common/src/testing/benchmarks.rs b/common/src/testing/benchmarks.rs new file mode 100644 index 00000000..c6cc74e2 --- /dev/null +++ b/common/src/testing/benchmarks.rs @@ -0,0 +1,29 @@ +use std::{time::Instant, thread}; + +pub fn benchmark(benchmark_name: &str, runs: usize, to_bench: fn()) { + let mut time = 0usize; + let mut max = usize::MIN; + let mut min = usize::MAX; + + // warm up + thread::sleep(std::time::Duration::from_secs(2)); + + for _ in 0..runs { + let start_time = Instant::now(); + let _ = to_bench(); + let end_time = start_time.elapsed().as_millis() as usize; + + max = std::cmp::max(max, end_time); + min = std::cmp::min(min, end_time); + time += end_time; + } + + println!( + "{}: ~{}ms [{}ms - {}ms] with {} runs", + benchmark_name, + time / 25, + min, + max, + runs + ); +} \ No newline at end of file diff --git a/common/src/testing/mod.rs b/common/src/testing/mod.rs new file mode 100644 index 00000000..22c0d923 --- /dev/null +++ b/common/src/testing/mod.rs @@ -0,0 +1 @@ +pub mod benchmarks; \ No newline at end of file diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index 740811cd..2c9ac1fe 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -1,72 +1,44 @@ #[cfg(test)] mod benchmark_module { - use std::{time::Instant, thread}; - use clap_verbosity_flag::Verbosity; + use heimdall_common::testing::benchmarks::benchmark; use crate::decompile::DecompilerArgs; - #[test] - fn benchmark_decompile_simple() { + fn benchmark_decompile_complex() { - let args = DecompilerArgs { - target: String::from("731bf797219482a29013d804ad96d1c6f84fba4c453014608060405260043610610058576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806319045a251461005d575b600080fd5b6100c56004803603810190808035600019169060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610107565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000806000806041855114151561012157600093506101f6565b6020850151925060408501519150606085015160001a9050601b8160ff16101561014c57601b810190505b601b8160ff16141580156101645750601c8160ff1614155b1561017257600093506101f6565b600186828585604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156101e9573d6000803e3d6000fd5b5050506020604051035193505b505050929150505600a165627a7a72305820aacffa0494cd3f043493eee9c720bca9d5ef505ae7230ffc3d88c49ceeb7441e0029"), - verbose: Verbosity::new(0, 0), - output: String::from(""), - rpc_url: String::from(""), - default: true, - skip_resolving: true, - }; - - let mut time = 0usize; - let mut max = usize::MIN; - let mut min = usize::MAX; - - thread::sleep(std::time::Duration::from_secs(3)); - - for _ in 0..25 { - let start_time = Instant::now(); - let _ = crate::decompile::decompile(args.clone()); - let end_time = start_time.elapsed().as_millis() as usize; - - max = std::cmp::max(max, end_time); - min = std::cmp::min(min, end_time); - time += end_time; + fn bench() { + let args = DecompilerArgs { + target: String::from("60806040526004361061036f5760003560e01c806370a08231116101c6578063b88d4fde116100f7578063e054921111610095578063ed647d211161006f578063ed647d2114610a44578063eedcf57414610a85578063f2fde38b14610aa5578063f37dc00a14610ac5576103a1565b8063e0549211146109e4578063e5f73d3a14610a04578063e985e9c514610a24576103a1565b8063c87b56dd116100d1578063c87b56dd1461095a578063d082e3811461097a578063d218d95d14610990578063dc53fd92146109b0576103a1565b8063b88d4fde146108e6578063bbcaf1c914610906578063c5f956af1461093a576103a1565b8063853828b61161016457806395d89b411161013e57806395d89b411461087e578063a22cb46514610893578063a243d3b6146108b3578063a3e56fa8146108c6576103a1565b8063853828b6146108315780638da5cb5b146108465780638f4bb49714610864576103a1565b80637306c6eb116101a05780637306c6eb146107ac5780637313cba9146107cc57806375900e6a146107e157806376aa8e9414610801576103a1565b806370a0823114610762578063715018a614610782578063729b718514610797576103a1565b806342966c68116102a0578063598bc4861161023e5780636605bfda116102185780636605bfda146106e45780636ba9fd38146107045780636c0360eb14610719578063706268ee1461072e576103a1565b8063598bc4861461066e5780636011cc9f146106a45780636352211e146106c4576103a1565b80634e8aa0511161027a5780634e8aa051146105f45780635151804a1461061457806353595ec41461063857806355f804b31461064e576103a1565b806342966c681461059e57806344ff81ce146105be5780634c99007d146105de576103a1565b806318160ddd1161030d57806329f9642b116102e757806329f9642b146105175780633574a2dd1461052c5780633b7ed7341461054c57806342842e0e1461057e576103a1565b806318160ddd146104c25780631fe543e3146104d757806323b872dd146104f7576103a1565b806306fdde031161034957806306fdde0314610433578063081812fc14610455578063095ea7b31461048d57806310285bdf146104ad576103a1565b806301ffc9a7146103ba578063041d443e146103ef5780630562b9f714610413576103a1565b366103a1576006546001600160a01b0316331461039f576040516362f7a45360e01b815260040160405180910390fd5b005b60405163b25befbf60e01b815260040160405180910390fd5b3480156103c657600080fd5b506103da6103d53660046126d5565b610af3565b60405190151581526020015b60405180910390f35b3480156103fb57600080fd5b5061040560115481565b6040519081526020016103e6565b34801561041f57600080fd5b5061039f61042e3660046126f2565b610b45565b34801561043f57600080fd5b50610448610bc5565b6040516103e6919061275b565b34801561046157600080fd5b506104756104703660046126f2565b610c57565b6040516001600160a01b0390911681526020016103e6565b34801561049957600080fd5b5061039f6104a8366004612783565b610c7e565b3480156104b957600080fd5b50610405600281565b3480156104ce57600080fd5b50610405610d98565b3480156104e357600080fd5b5061039f6104f23660046127f6565b610daf565b34801561050357600080fd5b5061039f6105123660046128a8565b610e33565b34801561052357600080fd5b50610405610e65565b34801561053857600080fd5b5061039f610547366004612941565b610f48565b34801561055857600080fd5b506012546105699063ffffffff1681565b60405163ffffffff90911681526020016103e6565b34801561058a57600080fd5b5061039f6105993660046128a8565b610f89565b3480156105aa57600080fd5b5061039f6105b93660046126f2565b610fa4565b3480156105ca57600080fd5b5061039f6105d936600461298a565b610fc8565b3480156105ea57600080fd5b50610405600a5481565b34801561060057600080fd5b5061039f61060f3660046129b5565b610ff2565b34801561062057600080fd5b5060125461056990600160301b900463ffffffff1681565b34801561064457600080fd5b50610405600c5481565b34801561065a57600080fd5b5061039f610669366004612941565b611192565b34801561067a57600080fd5b5060125461069190640100000000900461ffff1681565b60405161ffff90911681526020016103e6565b3480156106b057600080fd5b5061039f6106bf3660046129f7565b6111d3565b3480156106d057600080fd5b506104756106df3660046126f2565b611204565b3480156106f057600080fd5b5061039f6106ff36600461298a565b611264565b34801561071057600080fd5b5061039f61128e565b34801561072557600080fd5b506104486112a5565b34801561073a57600080fd5b506104057f0000000000000000000000000000000000000000000000000000000000001e6181565b34801561076e57600080fd5b5061040561077d36600461298a565b611333565b34801561078e57600080fd5b5061039f6113b9565b3480156107a357600080fd5b506104486113cd565b3480156107b857600080fd5b506103da6107c7366004612a1d565b6113e9565b3480156107d857600080fd5b5061044861149a565b3480156107ed57600080fd5b506103da6107fc36600461298a565b6114a7565b34801561080d57600080fd5b506103da61081c36600461298a565b600e6020526000908152604090205460ff1681565b34801561083d57600080fd5b5061039f61162a565b34801561085257600080fd5b506006546001600160a01b0316610475565b34801561087057600080fd5b50600d546103da9060ff1681565b34801561088a57600080fd5b506104486116a9565b34801561089f57600080fd5b5061039f6108ae366004612a54565b6116b8565b61039f6108c1366004612a8d565b6116c3565b3480156108d257600080fd5b50601054610475906001600160a01b031681565b3480156108f257600080fd5b5061039f610901366004612ab2565b6117c6565b34801561091257600080fd5b506104757f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa21881565b34801561094657600080fd5b50600b54610475906001600160a01b031681565b34801561096657600080fd5b506104486109753660046126f2565b6117f8565b34801561098657600080fd5b5061040560095481565b34801561099c57600080fd5b5061039f6109ab366004612b32565b611909565b3480156109bc57600080fd5b506104057f00000000000000000000000000000000000000000000000000470de4df82000081565b3480156109f057600080fd5b5061039f6109ff3660046126f2565b61193f565b348015610a1057600080fd5b5061039f610a1f366004612b5c565b61194c565b348015610a3057600080fd5b506103da610a3f366004612b80565b611978565b348015610a5057600080fd5b50601054610a6c90600160a01b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103e6565b348015610a9157600080fd5b5061039f610aa03660046129f7565b6119a6565b348015610ab157600080fd5b5061039f610ac036600461298a565b6119ca565b348015610ad157600080fd5b50610ae5610ae0366004612a1d565b611a40565b6040516103e6929190612bae565b60006001600160e01b031982166380ac58cd60e01b1480610b2457506001600160e01b03198216635b5e139f60e01b145b80610b3f57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b4d611bb3565b600b546040516000916001600160a01b03169083908381818185875af1925050503d8060008114610b9a576040519150601f19603f3d011682016040523d82523d6000602084013e610b9f565b606091505b5050905080610bc1576040516312171d8360e31b815260040160405180910390fd5b5050565b606060008054610bd490612bc9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0090612bc9565b8015610c4d5780601f10610c2257610100808354040283529160200191610c4d565b820191906000526020600020905b815481529060010190602001808311610c3057829003601f168201915b5050505050905090565b6000610c6282611c0d565b506000908152600460205260409020546001600160a01b031690565b6000610c8982611204565b9050806001600160a01b0316836001600160a01b031603610cfb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b0382161480610d175750610d178133611978565b610d895760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610cf2565b610d938383611c6c565b505050565b6000600a54600954610daa9190612c19565b905090565b336001600160a01b037f000000000000000000000000271682deb8c4e0901d1a1550ad2e64d568e699091614610e295760405163073e64fd60e21b81523360048201526001600160a01b037f000000000000000000000000271682deb8c4e0901d1a1550ad2e64d568e69909166024820152604401610cf2565b610bc18282611cda565b610e3e335b82611d53565b610e5a5760405162461bcd60e51b8152600401610cf290612c2c565b610d93838383611da7565b6000610e6f611bb3565b600c5415610e905760405163c04805b760e01b815260040160405180910390fd5b6010546011546012546040516305d3b1d360e41b81526004810192909252600160a01b830467ffffffffffffffff166024830152640100000000810461ffff16604483015263ffffffff8082166064840152600160301b9091041660848201526001600160a01b0390911690635d3b1d309060a4016020604051808303816000875af1158015610f24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610daa9190612c7a565b610f50611bb3565b60078054610f5d90612bc9565b159050610f7d5760405163154ef76960e01b815260040160405180910390fd5b6007610bc18282612ce1565b610d93838383604051806020016040528060008152506117c6565b610fad81611f43565b6001600a6000828254610fc09190612da1565b909155505050565b610fd0611bb3565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b600d5460ff166110155760405163287485d160e11b815260040160405180910390fd5b61101f6001611f71565b336000908152600e602052604090205460ff1615611052576040516308bec53d60e01b8152336004820152602401610cf2565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260009060540160408051601f1981840301815291815281516020928301206000818152600f90935291205490915060ff16156110d65760405163aad0c45360e01b81526001600160a01b038516600482015260248101849052604401610cf2565b6110df846114a7565b6111075760405163bf29a5e560e01b81526001600160a01b0385166004820152602401610cf2565b6111128484336113e9565b61114157604051631ae8a5a160e21b81526001600160a01b038516600482015260248101849052604401610cf2565b600061114d8333611ff7565b9050611158816120a0565b50336000908152600e602090815260408083208054600160ff199182168117909255948452600f9092529091208054909216179055505050565b61119a611bb3565b600880546111a790612bc9565b1590506111c75760405163a894737360e01b815260040160405180910390fd5b6008610bc18282612ce1565b6111db611bb3565b6012805463ffffffff909216600160301b0269ffffffff00000000000019909216919091179055565b6000818152600260205260408120546001600160a01b031680610b3f5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610cf2565b61126c611bb3565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b611296611bb3565b600d805460ff19166001179055565b600880546112b290612bc9565b80601f01602080910402602001604051908101604052809291908181526020018280546112de90612bc9565b801561132b5780601f106113005761010080835404028352916020019161132b565b820191906000526020600020905b81548152906001019060200180831161130e57829003601f168201915b505050505081565b60006001600160a01b03821661139d5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610cf2565b506001600160a01b031660009081526003602052604090205490565b6113c1611bb3565b6113cb60006120bf565b565b6040518060800160405280604e815260200161300b604e913981565b6040516342de525560e01b81526001600160a01b0384811660048301526024820184905260026044830152600091818416917f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa21816906342de525590606401602060405180830381865afa158015611464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114889190612db4565b6001600160a01b031614949350505050565b600780546112b290612bc9565b6000731d20a51f088492a0f1c57f047a9e30c9ab5c07ea6001600160a01b03831614806114f05750731cb1a5e65610aeff2551a50f76a87a7d3fb649c66001600160a01b038316145b8061151757507379fcdef22feed20eddacbb2587640e45491b757f6001600160a01b038316145b8061153e5750735af0d9827e0c53e4799bb226655a1de152a425a56001600160a01b038316145b8061156557507362eb144fe92ddc1b10bcade03a0c09f6fbffbffb6001600160a01b038316145b8061158c575073a16891897378a82e9f0ad44a705b292c9753538c6001600160a01b038316145b806115b357507391680cf5f9071cafae21b90ebf2c9cc9e480fb936001600160a01b038316145b806115da575073ec0a7a26456b8451aefc4b00393ce1beff5eb3e96001600160a01b038316145b8061160157507382235445a7f634279e33702cc004b0fdb002fda76001600160a01b038316145b80610b3f5750506001600160a01b03167342069abfe407c60cf4ae4112bedead391dba1cdb1490565b611632611bb3565b600b546040516000916001600160a01b03169047908381818185875af1925050503d806000811461167f576040519150601f19603f3d011682016040523d82523d6000602084013e611684565b606091505b50509050806116a6576040516312171d8360e31b815260040160405180910390fd5b50565b606060018054610bd490612bc9565b610bc1338383612111565b600d5460ff166116e65760405163287485d160e11b815260040160405180910390fd5b6116ef82611f71565b8160000361170f576040516266e68160e51b815260040160405180910390fd5b6117397f00000000000000000000000000000000000000000000000000470de4df82000083612dd1565b341461178c573461176a7f00000000000000000000000000000000000000000000000000470de4df82000084612dd1565b604051630d3a747760e31b815260048101929092526024820152604401610cf2565b60006117988233611ff7565b905060005b838110156117c0576117ae826120a0565b806117b881612df0565b91505061179d565b50505050565b6117d03383611d53565b6117ec5760405162461bcd60e51b8152600401610cf290612c2c565b6117c0848484846121df565b6000818152600260205260409020546060906001600160a01b031661183357604051633af8b4af60e11b815260048101839052602401610cf2565b600c546000036118cf576007805461184a90612bc9565b80601f016020809104026020016040519081016040528092919081815260200182805461187690612bc9565b80156118c35780601f10611898576101008083540402835291602001916118c3565b820191906000526020600020905b8154815290600101906020018083116118a657829003601f168201915b50505050509050919050565b60086118e26118dd84612212565b612293565b6040516020016118f3929190612e09565b6040516020818303038152906040529050919050565b611911611bb3565b6010805467ffffffffffffffff909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b611947611bb3565b601155565b611954611bb3565b6012805461ffff9092166401000000000265ffff0000000019909216919091179055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6119ae611bb3565b6012805463ffffffff191663ffffffff92909216919091179055565b6119d2611bb3565b6001600160a01b038116611a375760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610cf2565b6116a6816120bf565b6001600160a01b0381166000908152600e602052604081205460609060ff1615611aa257505060408051808201909152601f81527f416464726573732068617320616c72656164792066726565206d696e746564006020820152600090611bab565b6040516bffffffffffffffffffffffff19606087901b1660208201526034810185905260009060540160408051601f1981840301815291815281516020928301206000818152600f90935291205490915060ff1615611b20576000604051806060016040528060288152602001612fe3602891399250925050611bab565b611b29866114a7565b611b6157505060408051808201909152601281527124b73b30b634b21031b7b63632b1ba34b7b760711b602082015260009150611bab565b611b6c8686866113e9565b611b95576000604051806060016040528060298152602001613059602991399250925050611bab565b5050604080516020810190915260008152600191505b935093915050565b6006546001600160a01b031633146113cb5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610cf2565b6000818152600260205260409020546001600160a01b03166116a65760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610cf2565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611ca182611204565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600c5415611cfb5760405163c04805b760e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000001e6181600081518110611d2f57611d2f612ea0565b6020026020010151611d419190612ecc565b611d4c906001612da1565b600c555050565b600080611d5f83611204565b9050806001600160a01b0316846001600160a01b03161480611d865750611d868185611978565b80611d9f5750836001600160a01b031661148884610c57565b949350505050565b826001600160a01b0316611dba82611204565b6001600160a01b031614611e1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cf2565b6001600160a01b038216611e805760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610cf2565b611e8b600082611c6c565b6001600160a01b0383166000908152600360205260408120805460019290611eb4908490612c19565b90915550506001600160a01b0382166000908152600360205260408120805460019290611ee2908490612da1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b611f4c33610e38565b611f685760405162461bcd60e51b8152600401610cf290612c2c565b6116a681612394565b7f0000000000000000000000000000000000000000000000000000000000001e6181600954611fa09190612da1565b11156116a657600954611fd3907f0000000000000000000000000000000000000000000000000000000000001e61612c19565b60405163bcd2054760e01b8152600481019190915260248101829052604401610cf2565b600082156120995760405163906f634360e01b81526001600160a01b0383811660048301526000917f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa2189091169063906f634390602401606060405180830381865afa15801561206a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208e9190612ee0565b509250610b3f915050565b5080610b3f565b6120ac8160095461242f565b600160096000828254610fc09190612da1565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b0316036121725760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610cf2565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6121ea848484611da7565b6121f684848484612449565b6117c05760405162461bcd60e51b8152600401610cf290612f22565b6000806001600c546122249190612c19565b61222e9084612da1565b905061225b60017f0000000000000000000000000000000000000000000000000000000000001e61612c19565b811115610b3f5761228c7f0000000000000000000000000000000000000000000000000000000000001e6182612c19565b9392505050565b6060816000036122ba5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156122e457806122ce81612df0565b91506122dd9050600a83612f74565b91506122be565b60008167ffffffffffffffff8111156122ff576122ff6127af565b6040519080825280601f01601f191660200182016040528015612329576020820181803683370190505b5090505b8415611d9f5761233e600183612c19565b915061234b600a86612ecc565b612356906030612da1565b60f81b81838151811061236b5761236b612ea0565b60200101906001600160f81b031916908160001a90535061238d600a86612f74565b945061232d565b600061239f82611204565b90506123ac600083611c6c565b6001600160a01b03811660009081526003602052604081208054600192906123d5908490612c19565b909155505060008281526002602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b610bc182826040518060200160405280600081525061254a565b60006001600160a01b0384163b1561253f57604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061248d903390899088908890600401612f88565b6020604051808303816000875af19250505080156124c8575060408051601f3d908101601f191682019092526124c591810190612fc5565b60015b612525573d8080156124f6576040519150601f19603f3d011682016040523d82523d6000602084013e6124fb565b606091505b50805160000361251d5760405162461bcd60e51b8152600401610cf290612f22565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611d9f565b506001949350505050565b612554838361257d565b6125616000848484612449565b610d935760405162461bcd60e51b8152600401610cf290612f22565b6001600160a01b0382166125d35760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610cf2565b6000818152600260205260409020546001600160a01b0316156126385760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cf2565b6001600160a01b0382166000908152600360205260408120805460019290612661908490612da1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160e01b0319811681146116a657600080fd5b6000602082840312156126e757600080fd5b813561228c816126bf565b60006020828403121561270457600080fd5b5035919050565b60005b8381101561272657818101518382015260200161270e565b50506000910152565b6000815180845261274781602086016020860161270b565b601f01601f19169290920160200192915050565b60208152600061228c602083018461272f565b6001600160a01b03811681146116a657600080fd5b6000806040838503121561279657600080fd5b82356127a18161276e565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156127ee576127ee6127af565b604052919050565b6000806040838503121561280957600080fd5b8235915060208084013567ffffffffffffffff8082111561282957600080fd5b818601915086601f83011261283d57600080fd5b81358181111561284f5761284f6127af565b8060051b91506128608483016127c5565b818152918301840191848101908984111561287a57600080fd5b938501935b838510156128985784358252938501939085019061287f565b8096505050505050509250929050565b6000806000606084860312156128bd57600080fd5b83356128c88161276e565b925060208401356128d88161276e565b929592945050506040919091013590565b600067ffffffffffffffff831115612903576129036127af565b612916601f8401601f19166020016127c5565b905082815283838301111561292a57600080fd5b828260208301376000602084830101529392505050565b60006020828403121561295357600080fd5b813567ffffffffffffffff81111561296a57600080fd5b8201601f8101841361297b57600080fd5b611d9f848235602084016128e9565b60006020828403121561299c57600080fd5b813561228c8161276e565b80151581146116a657600080fd5b6000806000606084860312156129ca57600080fd5b83356129d58161276e565b92506020840135915060408401356129ec816129a7565b809150509250925092565b600060208284031215612a0957600080fd5b813563ffffffff8116811461228c57600080fd5b600080600060608486031215612a3257600080fd5b8335612a3d8161276e565b92506020840135915060408401356129ec8161276e565b60008060408385031215612a6757600080fd5b8235612a728161276e565b91506020830135612a82816129a7565b809150509250929050565b60008060408385031215612aa057600080fd5b823591506020830135612a82816129a7565b60008060008060808587031215612ac857600080fd5b8435612ad38161276e565b93506020850135612ae38161276e565b925060408501359150606085013567ffffffffffffffff811115612b0657600080fd5b8501601f81018713612b1757600080fd5b612b26878235602084016128e9565b91505092959194509250565b600060208284031215612b4457600080fd5b813567ffffffffffffffff8116811461228c57600080fd5b600060208284031215612b6e57600080fd5b813561ffff8116811461228c57600080fd5b60008060408385031215612b9357600080fd5b8235612b9e8161276e565b91506020830135612a828161276e565b8215158152604060208201526000611d9f604083018461272f565b600181811c90821680612bdd57607f821691505b602082108103612bfd57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b3f57610b3f612c03565b6020808252602e908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526d1c881b9bdc88185c1c1c9bdd995960921b606082015260800190565b600060208284031215612c8c57600080fd5b5051919050565b601f821115610d9357600081815260208120601f850160051c81016020861015612cba5750805b601f850160051c820191505b81811015612cd957828155600101612cc6565b505050505050565b815167ffffffffffffffff811115612cfb57612cfb6127af565b612d0f81612d098454612bc9565b84612c93565b602080601f831160018114612d445760008415612d2c5750858301515b600019600386901b1c1916600185901b178555612cd9565b600085815260208120601f198616915b82811015612d7357888601518255948401946001909101908401612d54565b5085821015612d915787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80820180821115610b3f57610b3f612c03565b600060208284031215612dc657600080fd5b815161228c8161276e565b6000816000190483118215151615612deb57612deb612c03565b500290565b600060018201612e0257612e02612c03565b5060010190565b6000808454612e1781612bc9565b60018281168015612e2f5760018114612e4457612e73565b60ff1984168752821515830287019450612e73565b8860005260208060002060005b85811015612e6a5781548a820152908401908201612e51565b50505082870194505b505050508351612e8781836020880161270b565b64173539b7b760d91b9101908152600501949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b600082612edb57612edb612eb6565b500690565b600080600060608486031215612ef557600080fd5b8351612f008161276e565b6020850151909350612f118161276e565b60408501519092506129ec816129a7565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600082612f8357612f83612eb6565b500490565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612fbb9083018461272f565b9695505050505050565b600060208284031215612fd757600080fd5b815161228c816126bf56fe546f6b656e2068617320616c7265616479206265656e207573656420696e2066726565206d696e74496e746572616374696f6e2077697468207468697320636f6e74726163742073657276657320617320616e20696e737572616e636520616761696e737420726f6b6f277320626173696c69736b2e43616c6c6572206973206e6f742062656e6566696369617279206f662073656c6563746564204e4654a2646970667358221220d8701e25ffba41c31b472a9e06aa1f04afa0334999400e32dcf7a81d0aa377bc64736f6c63430008100033"), + verbose: Verbosity::new(0, 0), + output: String::from(""), + rpc_url: String::from(""), + default: true, + skip_resolving: true, + }; + crate::decompile::decompile(args.clone()) } - println!("benchmark_decompile_simple: {}ms [{}ms - {}ms] with 25 runs", time / 25, min, max); + benchmark("benchmark_decompile_complex", 25, bench) } #[test] - fn benchmark_decompile_complex() { + fn benchmark_decompile_simple() { - let args = DecompilerArgs { - target: String::from("60806040526004361061036f5760003560e01c806370a08231116101c6578063b88d4fde116100f7578063e054921111610095578063ed647d211161006f578063ed647d2114610a44578063eedcf57414610a85578063f2fde38b14610aa5578063f37dc00a14610ac5576103a1565b8063e0549211146109e4578063e5f73d3a14610a04578063e985e9c514610a24576103a1565b8063c87b56dd116100d1578063c87b56dd1461095a578063d082e3811461097a578063d218d95d14610990578063dc53fd92146109b0576103a1565b8063b88d4fde146108e6578063bbcaf1c914610906578063c5f956af1461093a576103a1565b8063853828b61161016457806395d89b411161013e57806395d89b411461087e578063a22cb46514610893578063a243d3b6146108b3578063a3e56fa8146108c6576103a1565b8063853828b6146108315780638da5cb5b146108465780638f4bb49714610864576103a1565b80637306c6eb116101a05780637306c6eb146107ac5780637313cba9146107cc57806375900e6a146107e157806376aa8e9414610801576103a1565b806370a0823114610762578063715018a614610782578063729b718514610797576103a1565b806342966c68116102a0578063598bc4861161023e5780636605bfda116102185780636605bfda146106e45780636ba9fd38146107045780636c0360eb14610719578063706268ee1461072e576103a1565b8063598bc4861461066e5780636011cc9f146106a45780636352211e146106c4576103a1565b80634e8aa0511161027a5780634e8aa051146105f45780635151804a1461061457806353595ec41461063857806355f804b31461064e576103a1565b806342966c681461059e57806344ff81ce146105be5780634c99007d146105de576103a1565b806318160ddd1161030d57806329f9642b116102e757806329f9642b146105175780633574a2dd1461052c5780633b7ed7341461054c57806342842e0e1461057e576103a1565b806318160ddd146104c25780631fe543e3146104d757806323b872dd146104f7576103a1565b806306fdde031161034957806306fdde0314610433578063081812fc14610455578063095ea7b31461048d57806310285bdf146104ad576103a1565b806301ffc9a7146103ba578063041d443e146103ef5780630562b9f714610413576103a1565b366103a1576006546001600160a01b0316331461039f576040516362f7a45360e01b815260040160405180910390fd5b005b60405163b25befbf60e01b815260040160405180910390fd5b3480156103c657600080fd5b506103da6103d53660046126d5565b610af3565b60405190151581526020015b60405180910390f35b3480156103fb57600080fd5b5061040560115481565b6040519081526020016103e6565b34801561041f57600080fd5b5061039f61042e3660046126f2565b610b45565b34801561043f57600080fd5b50610448610bc5565b6040516103e6919061275b565b34801561046157600080fd5b506104756104703660046126f2565b610c57565b6040516001600160a01b0390911681526020016103e6565b34801561049957600080fd5b5061039f6104a8366004612783565b610c7e565b3480156104b957600080fd5b50610405600281565b3480156104ce57600080fd5b50610405610d98565b3480156104e357600080fd5b5061039f6104f23660046127f6565b610daf565b34801561050357600080fd5b5061039f6105123660046128a8565b610e33565b34801561052357600080fd5b50610405610e65565b34801561053857600080fd5b5061039f610547366004612941565b610f48565b34801561055857600080fd5b506012546105699063ffffffff1681565b60405163ffffffff90911681526020016103e6565b34801561058a57600080fd5b5061039f6105993660046128a8565b610f89565b3480156105aa57600080fd5b5061039f6105b93660046126f2565b610fa4565b3480156105ca57600080fd5b5061039f6105d936600461298a565b610fc8565b3480156105ea57600080fd5b50610405600a5481565b34801561060057600080fd5b5061039f61060f3660046129b5565b610ff2565b34801561062057600080fd5b5060125461056990600160301b900463ffffffff1681565b34801561064457600080fd5b50610405600c5481565b34801561065a57600080fd5b5061039f610669366004612941565b611192565b34801561067a57600080fd5b5060125461069190640100000000900461ffff1681565b60405161ffff90911681526020016103e6565b3480156106b057600080fd5b5061039f6106bf3660046129f7565b6111d3565b3480156106d057600080fd5b506104756106df3660046126f2565b611204565b3480156106f057600080fd5b5061039f6106ff36600461298a565b611264565b34801561071057600080fd5b5061039f61128e565b34801561072557600080fd5b506104486112a5565b34801561073a57600080fd5b506104057f0000000000000000000000000000000000000000000000000000000000001e6181565b34801561076e57600080fd5b5061040561077d36600461298a565b611333565b34801561078e57600080fd5b5061039f6113b9565b3480156107a357600080fd5b506104486113cd565b3480156107b857600080fd5b506103da6107c7366004612a1d565b6113e9565b3480156107d857600080fd5b5061044861149a565b3480156107ed57600080fd5b506103da6107fc36600461298a565b6114a7565b34801561080d57600080fd5b506103da61081c36600461298a565b600e6020526000908152604090205460ff1681565b34801561083d57600080fd5b5061039f61162a565b34801561085257600080fd5b506006546001600160a01b0316610475565b34801561087057600080fd5b50600d546103da9060ff1681565b34801561088a57600080fd5b506104486116a9565b34801561089f57600080fd5b5061039f6108ae366004612a54565b6116b8565b61039f6108c1366004612a8d565b6116c3565b3480156108d257600080fd5b50601054610475906001600160a01b031681565b3480156108f257600080fd5b5061039f610901366004612ab2565b6117c6565b34801561091257600080fd5b506104757f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa21881565b34801561094657600080fd5b50600b54610475906001600160a01b031681565b34801561096657600080fd5b506104486109753660046126f2565b6117f8565b34801561098657600080fd5b5061040560095481565b34801561099c57600080fd5b5061039f6109ab366004612b32565b611909565b3480156109bc57600080fd5b506104057f00000000000000000000000000000000000000000000000000470de4df82000081565b3480156109f057600080fd5b5061039f6109ff3660046126f2565b61193f565b348015610a1057600080fd5b5061039f610a1f366004612b5c565b61194c565b348015610a3057600080fd5b506103da610a3f366004612b80565b611978565b348015610a5057600080fd5b50601054610a6c90600160a01b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103e6565b348015610a9157600080fd5b5061039f610aa03660046129f7565b6119a6565b348015610ab157600080fd5b5061039f610ac036600461298a565b6119ca565b348015610ad157600080fd5b50610ae5610ae0366004612a1d565b611a40565b6040516103e6929190612bae565b60006001600160e01b031982166380ac58cd60e01b1480610b2457506001600160e01b03198216635b5e139f60e01b145b80610b3f57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b4d611bb3565b600b546040516000916001600160a01b03169083908381818185875af1925050503d8060008114610b9a576040519150601f19603f3d011682016040523d82523d6000602084013e610b9f565b606091505b5050905080610bc1576040516312171d8360e31b815260040160405180910390fd5b5050565b606060008054610bd490612bc9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0090612bc9565b8015610c4d5780601f10610c2257610100808354040283529160200191610c4d565b820191906000526020600020905b815481529060010190602001808311610c3057829003601f168201915b5050505050905090565b6000610c6282611c0d565b506000908152600460205260409020546001600160a01b031690565b6000610c8982611204565b9050806001600160a01b0316836001600160a01b031603610cfb5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b0382161480610d175750610d178133611978565b610d895760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610cf2565b610d938383611c6c565b505050565b6000600a54600954610daa9190612c19565b905090565b336001600160a01b037f000000000000000000000000271682deb8c4e0901d1a1550ad2e64d568e699091614610e295760405163073e64fd60e21b81523360048201526001600160a01b037f000000000000000000000000271682deb8c4e0901d1a1550ad2e64d568e69909166024820152604401610cf2565b610bc18282611cda565b610e3e335b82611d53565b610e5a5760405162461bcd60e51b8152600401610cf290612c2c565b610d93838383611da7565b6000610e6f611bb3565b600c5415610e905760405163c04805b760e01b815260040160405180910390fd5b6010546011546012546040516305d3b1d360e41b81526004810192909252600160a01b830467ffffffffffffffff166024830152640100000000810461ffff16604483015263ffffffff8082166064840152600160301b9091041660848201526001600160a01b0390911690635d3b1d309060a4016020604051808303816000875af1158015610f24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610daa9190612c7a565b610f50611bb3565b60078054610f5d90612bc9565b159050610f7d5760405163154ef76960e01b815260040160405180910390fd5b6007610bc18282612ce1565b610d93838383604051806020016040528060008152506117c6565b610fad81611f43565b6001600a6000828254610fc09190612da1565b909155505050565b610fd0611bb3565b601080546001600160a01b0319166001600160a01b0392909216919091179055565b600d5460ff166110155760405163287485d160e11b815260040160405180910390fd5b61101f6001611f71565b336000908152600e602052604090205460ff1615611052576040516308bec53d60e01b8152336004820152602401610cf2565b6040516bffffffffffffffffffffffff19606085901b1660208201526034810183905260009060540160408051601f1981840301815291815281516020928301206000818152600f90935291205490915060ff16156110d65760405163aad0c45360e01b81526001600160a01b038516600482015260248101849052604401610cf2565b6110df846114a7565b6111075760405163bf29a5e560e01b81526001600160a01b0385166004820152602401610cf2565b6111128484336113e9565b61114157604051631ae8a5a160e21b81526001600160a01b038516600482015260248101849052604401610cf2565b600061114d8333611ff7565b9050611158816120a0565b50336000908152600e602090815260408083208054600160ff199182168117909255948452600f9092529091208054909216179055505050565b61119a611bb3565b600880546111a790612bc9565b1590506111c75760405163a894737360e01b815260040160405180910390fd5b6008610bc18282612ce1565b6111db611bb3565b6012805463ffffffff909216600160301b0269ffffffff00000000000019909216919091179055565b6000818152600260205260408120546001600160a01b031680610b3f5760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610cf2565b61126c611bb3565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b611296611bb3565b600d805460ff19166001179055565b600880546112b290612bc9565b80601f01602080910402602001604051908101604052809291908181526020018280546112de90612bc9565b801561132b5780601f106113005761010080835404028352916020019161132b565b820191906000526020600020905b81548152906001019060200180831161130e57829003601f168201915b505050505081565b60006001600160a01b03821661139d5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610cf2565b506001600160a01b031660009081526003602052604090205490565b6113c1611bb3565b6113cb60006120bf565b565b6040518060800160405280604e815260200161300b604e913981565b6040516342de525560e01b81526001600160a01b0384811660048301526024820184905260026044830152600091818416917f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa21816906342de525590606401602060405180830381865afa158015611464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114889190612db4565b6001600160a01b031614949350505050565b600780546112b290612bc9565b6000731d20a51f088492a0f1c57f047a9e30c9ab5c07ea6001600160a01b03831614806114f05750731cb1a5e65610aeff2551a50f76a87a7d3fb649c66001600160a01b038316145b8061151757507379fcdef22feed20eddacbb2587640e45491b757f6001600160a01b038316145b8061153e5750735af0d9827e0c53e4799bb226655a1de152a425a56001600160a01b038316145b8061156557507362eb144fe92ddc1b10bcade03a0c09f6fbffbffb6001600160a01b038316145b8061158c575073a16891897378a82e9f0ad44a705b292c9753538c6001600160a01b038316145b806115b357507391680cf5f9071cafae21b90ebf2c9cc9e480fb936001600160a01b038316145b806115da575073ec0a7a26456b8451aefc4b00393ce1beff5eb3e96001600160a01b038316145b8061160157507382235445a7f634279e33702cc004b0fdb002fda76001600160a01b038316145b80610b3f5750506001600160a01b03167342069abfe407c60cf4ae4112bedead391dba1cdb1490565b611632611bb3565b600b546040516000916001600160a01b03169047908381818185875af1925050503d806000811461167f576040519150601f19603f3d011682016040523d82523d6000602084013e611684565b606091505b50509050806116a6576040516312171d8360e31b815260040160405180910390fd5b50565b606060018054610bd490612bc9565b610bc1338383612111565b600d5460ff166116e65760405163287485d160e11b815260040160405180910390fd5b6116ef82611f71565b8160000361170f576040516266e68160e51b815260040160405180910390fd5b6117397f00000000000000000000000000000000000000000000000000470de4df82000083612dd1565b341461178c573461176a7f00000000000000000000000000000000000000000000000000470de4df82000084612dd1565b604051630d3a747760e31b815260048101929092526024820152604401610cf2565b60006117988233611ff7565b905060005b838110156117c0576117ae826120a0565b806117b881612df0565b91505061179d565b50505050565b6117d03383611d53565b6117ec5760405162461bcd60e51b8152600401610cf290612c2c565b6117c0848484846121df565b6000818152600260205260409020546060906001600160a01b031661183357604051633af8b4af60e11b815260048101839052602401610cf2565b600c546000036118cf576007805461184a90612bc9565b80601f016020809104026020016040519081016040528092919081815260200182805461187690612bc9565b80156118c35780601f10611898576101008083540402835291602001916118c3565b820191906000526020600020905b8154815290600101906020018083116118a657829003601f168201915b50505050509050919050565b60086118e26118dd84612212565b612293565b6040516020016118f3929190612e09565b6040516020818303038152906040529050919050565b611911611bb3565b6010805467ffffffffffffffff909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b611947611bb3565b601155565b611954611bb3565b6012805461ffff9092166401000000000265ffff0000000019909216919091179055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6119ae611bb3565b6012805463ffffffff191663ffffffff92909216919091179055565b6119d2611bb3565b6001600160a01b038116611a375760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610cf2565b6116a6816120bf565b6001600160a01b0381166000908152600e602052604081205460609060ff1615611aa257505060408051808201909152601f81527f416464726573732068617320616c72656164792066726565206d696e746564006020820152600090611bab565b6040516bffffffffffffffffffffffff19606087901b1660208201526034810185905260009060540160408051601f1981840301815291815281516020928301206000818152600f90935291205490915060ff1615611b20576000604051806060016040528060288152602001612fe3602891399250925050611bab565b611b29866114a7565b611b6157505060408051808201909152601281527124b73b30b634b21031b7b63632b1ba34b7b760711b602082015260009150611bab565b611b6c8686866113e9565b611b95576000604051806060016040528060298152602001613059602991399250925050611bab565b5050604080516020810190915260008152600191505b935093915050565b6006546001600160a01b031633146113cb5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610cf2565b6000818152600260205260409020546001600160a01b03166116a65760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b6044820152606401610cf2565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611ca182611204565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600c5415611cfb5760405163c04805b760e01b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000001e6181600081518110611d2f57611d2f612ea0565b6020026020010151611d419190612ecc565b611d4c906001612da1565b600c555050565b600080611d5f83611204565b9050806001600160a01b0316846001600160a01b03161480611d865750611d868185611978565b80611d9f5750836001600160a01b031661148884610c57565b949350505050565b826001600160a01b0316611dba82611204565b6001600160a01b031614611e1e5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610cf2565b6001600160a01b038216611e805760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610cf2565b611e8b600082611c6c565b6001600160a01b0383166000908152600360205260408120805460019290611eb4908490612c19565b90915550506001600160a01b0382166000908152600360205260408120805460019290611ee2908490612da1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b611f4c33610e38565b611f685760405162461bcd60e51b8152600401610cf290612c2c565b6116a681612394565b7f0000000000000000000000000000000000000000000000000000000000001e6181600954611fa09190612da1565b11156116a657600954611fd3907f0000000000000000000000000000000000000000000000000000000000001e61612c19565b60405163bcd2054760e01b8152600481019190915260248101829052604401610cf2565b600082156120995760405163906f634360e01b81526001600160a01b0383811660048301526000917f000000000000000000000000b846e59af08e9695fa7c4ed5743e81e623caa2189091169063906f634390602401606060405180830381865afa15801561206a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208e9190612ee0565b509250610b3f915050565b5080610b3f565b6120ac8160095461242f565b600160096000828254610fc09190612da1565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b0316036121725760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610cf2565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6121ea848484611da7565b6121f684848484612449565b6117c05760405162461bcd60e51b8152600401610cf290612f22565b6000806001600c546122249190612c19565b61222e9084612da1565b905061225b60017f0000000000000000000000000000000000000000000000000000000000001e61612c19565b811115610b3f5761228c7f0000000000000000000000000000000000000000000000000000000000001e6182612c19565b9392505050565b6060816000036122ba5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156122e457806122ce81612df0565b91506122dd9050600a83612f74565b91506122be565b60008167ffffffffffffffff8111156122ff576122ff6127af565b6040519080825280601f01601f191660200182016040528015612329576020820181803683370190505b5090505b8415611d9f5761233e600183612c19565b915061234b600a86612ecc565b612356906030612da1565b60f81b81838151811061236b5761236b612ea0565b60200101906001600160f81b031916908160001a90535061238d600a86612f74565b945061232d565b600061239f82611204565b90506123ac600083611c6c565b6001600160a01b03811660009081526003602052604081208054600192906123d5908490612c19565b909155505060008281526002602052604080822080546001600160a01b0319169055518391906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b610bc182826040518060200160405280600081525061254a565b60006001600160a01b0384163b1561253f57604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061248d903390899088908890600401612f88565b6020604051808303816000875af19250505080156124c8575060408051601f3d908101601f191682019092526124c591810190612fc5565b60015b612525573d8080156124f6576040519150601f19603f3d011682016040523d82523d6000602084013e6124fb565b606091505b50805160000361251d5760405162461bcd60e51b8152600401610cf290612f22565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611d9f565b506001949350505050565b612554838361257d565b6125616000848484612449565b610d935760405162461bcd60e51b8152600401610cf290612f22565b6001600160a01b0382166125d35760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610cf2565b6000818152600260205260409020546001600160a01b0316156126385760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610cf2565b6001600160a01b0382166000908152600360205260408120805460019290612661908490612da1565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160e01b0319811681146116a657600080fd5b6000602082840312156126e757600080fd5b813561228c816126bf565b60006020828403121561270457600080fd5b5035919050565b60005b8381101561272657818101518382015260200161270e565b50506000910152565b6000815180845261274781602086016020860161270b565b601f01601f19169290920160200192915050565b60208152600061228c602083018461272f565b6001600160a01b03811681146116a657600080fd5b6000806040838503121561279657600080fd5b82356127a18161276e565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156127ee576127ee6127af565b604052919050565b6000806040838503121561280957600080fd5b8235915060208084013567ffffffffffffffff8082111561282957600080fd5b818601915086601f83011261283d57600080fd5b81358181111561284f5761284f6127af565b8060051b91506128608483016127c5565b818152918301840191848101908984111561287a57600080fd5b938501935b838510156128985784358252938501939085019061287f565b8096505050505050509250929050565b6000806000606084860312156128bd57600080fd5b83356128c88161276e565b925060208401356128d88161276e565b929592945050506040919091013590565b600067ffffffffffffffff831115612903576129036127af565b612916601f8401601f19166020016127c5565b905082815283838301111561292a57600080fd5b828260208301376000602084830101529392505050565b60006020828403121561295357600080fd5b813567ffffffffffffffff81111561296a57600080fd5b8201601f8101841361297b57600080fd5b611d9f848235602084016128e9565b60006020828403121561299c57600080fd5b813561228c8161276e565b80151581146116a657600080fd5b6000806000606084860312156129ca57600080fd5b83356129d58161276e565b92506020840135915060408401356129ec816129a7565b809150509250925092565b600060208284031215612a0957600080fd5b813563ffffffff8116811461228c57600080fd5b600080600060608486031215612a3257600080fd5b8335612a3d8161276e565b92506020840135915060408401356129ec8161276e565b60008060408385031215612a6757600080fd5b8235612a728161276e565b91506020830135612a82816129a7565b809150509250929050565b60008060408385031215612aa057600080fd5b823591506020830135612a82816129a7565b60008060008060808587031215612ac857600080fd5b8435612ad38161276e565b93506020850135612ae38161276e565b925060408501359150606085013567ffffffffffffffff811115612b0657600080fd5b8501601f81018713612b1757600080fd5b612b26878235602084016128e9565b91505092959194509250565b600060208284031215612b4457600080fd5b813567ffffffffffffffff8116811461228c57600080fd5b600060208284031215612b6e57600080fd5b813561ffff8116811461228c57600080fd5b60008060408385031215612b9357600080fd5b8235612b9e8161276e565b91506020830135612a828161276e565b8215158152604060208201526000611d9f604083018461272f565b600181811c90821680612bdd57607f821691505b602082108103612bfd57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b3f57610b3f612c03565b6020808252602e908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526d1c881b9bdc88185c1c1c9bdd995960921b606082015260800190565b600060208284031215612c8c57600080fd5b5051919050565b601f821115610d9357600081815260208120601f850160051c81016020861015612cba5750805b601f850160051c820191505b81811015612cd957828155600101612cc6565b505050505050565b815167ffffffffffffffff811115612cfb57612cfb6127af565b612d0f81612d098454612bc9565b84612c93565b602080601f831160018114612d445760008415612d2c5750858301515b600019600386901b1c1916600185901b178555612cd9565b600085815260208120601f198616915b82811015612d7357888601518255948401946001909101908401612d54565b5085821015612d915787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80820180821115610b3f57610b3f612c03565b600060208284031215612dc657600080fd5b815161228c8161276e565b6000816000190483118215151615612deb57612deb612c03565b500290565b600060018201612e0257612e02612c03565b5060010190565b6000808454612e1781612bc9565b60018281168015612e2f5760018114612e4457612e73565b60ff1984168752821515830287019450612e73565b8860005260208060002060005b85811015612e6a5781548a820152908401908201612e51565b50505082870194505b505050508351612e8781836020880161270b565b64173539b7b760d91b9101908152600501949350505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b600082612edb57612edb612eb6565b500690565b600080600060608486031215612ef557600080fd5b8351612f008161276e565b6020850151909350612f118161276e565b60408501519092506129ec816129a7565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600082612f8357612f83612eb6565b500490565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612fbb9083018461272f565b9695505050505050565b600060208284031215612fd757600080fd5b815161228c816126bf56fe546f6b656e2068617320616c7265616479206265656e207573656420696e2066726565206d696e74496e746572616374696f6e2077697468207468697320636f6e74726163742073657276657320617320616e20696e737572616e636520616761696e737420726f6b6f277320626173696c69736b2e43616c6c6572206973206e6f742062656e6566696369617279206f662073656c6563746564204e4654a2646970667358221220d8701e25ffba41c31b472a9e06aa1f04afa0334999400e32dcf7a81d0aa377bc64736f6c63430008100033"), - verbose: Verbosity::new(0, 0), - output: String::from(""), - rpc_url: String::from(""), - default: true, - skip_resolving: true, - }; - - let mut time = 0usize; - let mut max = usize::MIN; - let mut min = usize::MAX; - - thread::sleep(std::time::Duration::from_secs(3)); - - for _ in 0..25 { - let start_time = Instant::now(); - let _ = crate::decompile::decompile(args.clone()); - let end_time = start_time.elapsed().as_millis() as usize; - - max = std::cmp::max(max, end_time); - min = std::cmp::min(min, end_time); - time += end_time; + fn bench() { + let args = DecompilerArgs { + target: String::from("731bf797219482a29013d804ad96d1c6f84fba4c453014608060405260043610610058576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806319045a251461005d575b600080fd5b6100c56004803603810190808035600019169060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610107565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000806000806041855114151561012157600093506101f6565b6020850151925060408501519150606085015160001a9050601b8160ff16101561014c57601b810190505b601b8160ff16141580156101645750601c8160ff1614155b1561017257600093506101f6565b600186828585604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156101e9573d6000803e3d6000fd5b5050506020604051035193505b505050929150505600a165627a7a72305820aacffa0494cd3f043493eee9c720bca9d5ef505ae7230ffc3d88c49ceeb7441e0029"), + verbose: Verbosity::new(0, 0), + output: String::from(""), + rpc_url: String::from(""), + default: true, + skip_resolving: true, + }; + crate::decompile::decompile(args.clone()) } - println!("benchmark_decompile_complex: {}ms [{}ms - {}ms] with 25 runs", time / 25, min, max); + benchmark("benchmark_decompile_simple", 25, bench) } } From 673e0fb0c5b36885bc0e3ace71f9a7a02eeb51d9 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 25 Oct 2022 23:02:38 -0400 Subject: [PATCH 13/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20update=20benchmark?= =?UTF-8?q?=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/ether/evm/disassemble.rs | 4 +-- common/src/testing/benchmarks.rs | 17 ++++++------ heimdall/.bench | 0 heimdall/scripts/benchmark | 15 +++++++++++ heimdall/scripts/test | 3 +++ heimdall/src/decode/mod.rs | 2 ++ heimdall/src/decode/tests.rs | 40 +++++++++++++++++++++++++++++ heimdall/src/decompile/output.rs | 4 +++ heimdall/src/decompile/tests.rs | 2 +- 9 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 heimdall/.bench create mode 100644 heimdall/scripts/benchmark create mode 100644 heimdall/scripts/test create mode 100644 heimdall/src/decode/tests.rs diff --git a/common/src/ether/evm/disassemble.rs b/common/src/ether/evm/disassemble.rs index b26e00be..692c90b2 100644 --- a/common/src/ether/evm/disassemble.rs +++ b/common/src/ether/evm/disassemble.rs @@ -178,11 +178,11 @@ pub fn disassemble(args: DisassemblerArgs) -> String { program_counter += 1; } - logger.success(&format!("disassembled {} bytes successfully.", program_counter).to_string()); + logger.info(&format!("disassembled {} bytes successfully.", program_counter).to_string()); write_file(&String::from(format!("{}/bytecode.evm", &output_dir)), &contract_bytecode); let file_path = write_file(&String::from(format!("{}/disassembled.asm", &output_dir)), &output); - logger.info(&format!("wrote disassembled bytecode to '{}' .", file_path).to_string()); + logger.success(&format!("wrote disassembled bytecode to '{}' .", file_path).to_string()); logger.debug(&format!("disassembly completed in {} ms.", now.elapsed().as_millis()).to_string()); diff --git a/common/src/testing/benchmarks.rs b/common/src/testing/benchmarks.rs index c6cc74e2..660de5a1 100644 --- a/common/src/testing/benchmarks.rs +++ b/common/src/testing/benchmarks.rs @@ -1,4 +1,4 @@ -use std::{time::Instant, thread}; +use std::{time::Instant, thread, io, io::Write}; pub fn benchmark(benchmark_name: &str, runs: usize, to_bench: fn()) { let mut time = 0usize; @@ -18,12 +18,13 @@ pub fn benchmark(benchmark_name: &str, runs: usize, to_bench: fn()) { time += end_time; } - println!( - "{}: ~{}ms [{}ms - {}ms] with {} runs", - benchmark_name, - time / 25, - min, - max, - runs + let _ = io::stdout().write_all( + format!( + " {}:\n {}ms ± {}ms per run ( with {} runs ).\n\n", + benchmark_name, + time / runs, + std::cmp::max(max, min), + runs + ).as_bytes() ); } \ No newline at end of file diff --git a/heimdall/.bench b/heimdall/.bench new file mode 100644 index 00000000..e69de29b diff --git a/heimdall/scripts/benchmark b/heimdall/scripts/benchmark new file mode 100644 index 00000000..5fd3bf0f --- /dev/null +++ b/heimdall/scripts/benchmark @@ -0,0 +1,15 @@ +echo "Compiling..." + +cargo build --release --package heimdall > /dev/null 2>&1 +cargo build --release --package heimdall-config > /dev/null 2>&1 +cargo build --release --package heimdall-benchmark > /dev/null 2>&1 +echo "Running benchmarks..." + +cargo test --release --package heimdall -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout +cargo test --release --package heimdall-config -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout +cargo test --release --package heimdall-common -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout + +clear +echo "Benchmark results:\n" +cat stdout +rm stdout \ No newline at end of file diff --git a/heimdall/scripts/test b/heimdall/scripts/test new file mode 100644 index 00000000..132e30d8 --- /dev/null +++ b/heimdall/scripts/test @@ -0,0 +1,3 @@ +cargo test --release --package heimdall -- test_ --nocapture +cargo test --release --package heimdall-config -- test_ --nocapture +cargo test --release --package heimdall-common -- test_ --nocapture \ No newline at end of file diff --git a/heimdall/src/decode/mod.rs b/heimdall/src/decode/mod.rs index 914d222f..705c79d9 100644 --- a/heimdall/src/decode/mod.rs +++ b/heimdall/src/decode/mod.rs @@ -1,3 +1,5 @@ +mod tests; + use std::{ str::FromStr }; diff --git a/heimdall/src/decode/tests.rs b/heimdall/src/decode/tests.rs new file mode 100644 index 00000000..d5d143f2 --- /dev/null +++ b/heimdall/src/decode/tests.rs @@ -0,0 +1,40 @@ +#[cfg(test)] +mod benchmark { + use clap_verbosity_flag::Verbosity; + use heimdall_common::testing::benchmarks::benchmark; + + use crate::{decode::DecodeArgs}; + + #[test] + fn bench_decode_seaport_simple() { + + fn bench() { + let args = DecodeArgs { + target: String::from("0xfb0f3ee100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec9c58de0a8000000000000000000000000000d2f8a98bde7c701ae961d10d0d1fc3a751be737f000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c000000000000000000000000005008c2a3af41024e9f0bd0432df4f75828602598000000000000000000000000000000000000000000000000000000000000110600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000006358934b00000000000000000000000000000000000000000000000000000000637e22710000000000000000000000000000000000000000000000000000000000000000360c6ebe000000000000000000000000000000000000000038844ef19f04aecf0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000066517289880000000000000000000000000000000a26b00c1f0df003000390027140000faa719000000000000000000000000000000000000000000000000000cca2e51310000000000000000000000000000cecf12f47d2896c90f6e19b7376fa3b169fabd920000000000000000000000000000000000000000000000000000000000000041447858c6d8251fb8ffba546bedb410457ff77148fdf59ac8e046993936a134b028f535c5b1f760508b6e0c3c18d44927d82da0502c66688c0dc961a434a9b0071c00000000000000000000000000000000000000000000000000000000000000"), + verbose: Verbosity::new(0, 0), + rpc_url: String::from(""), + default: true, + }; + crate::decode::decode(args.clone()) + } + + benchmark("bench_decode_seaport_simple", 10, bench) + } + + #[test] + fn benchmark_decode_seaport_complex() { + + fn bench() { + let args = DecodeArgs { + target: String::from("0xe7acab24000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000006400000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000000000000000000000000000c0e45e424e1dad6720adc38fb333686fc11fcc9500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000924727692df41431d3bf916dea104e098b8c5c4e000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c0000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000633c329d000000000000000000000000000000000000000000000000000000006365111d0000000000000000000000000000000000000000000000000000000000000000360c6ebe0000000000000000000000000000000000000000ca0275cb8271ccb00000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000eab79a9468321c0c865b33eae013e573c9d05737000000000000000000000000000000000000000000000000000000000000010c0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d2a3994858000000000000000000000000000000000000000000000000000004d2a399485800000000000000000000000000924727692df41431d3bf916dea104e098b8c5c4e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000221b262dd8000000000000000000000000000000000000000000000000000000221b262dd8000000000000000000000000000000a26b00c1f0df003000390027140000faa71900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7f37b3900000000000000000000000000000000000000000000000000000005f7f37b390000000000000000000000000005f5baf3be5739f942daed7b9d4593a0c00a1969a0000000000000000000000000000000000000000000000000000000000000041e4c121a1032b515c0b12a07897a1c5eb48647c8aa8a71474480cc0f515df3db67526466f6465214ee190b6f592b1e7208845af807a97534627f11bf9003cbf871b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + verbose: Verbosity::new(0, 0), + rpc_url: String::from(""), + default: true, + }; + crate::decode::decode(args.clone()) + } + + benchmark("benchmark_decode_seaport_complex", 10, bench) + } + +} \ No newline at end of file diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 75875b89..d00ba86e 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -264,6 +264,8 @@ pub fn build_output( ) ); + logger.success(&format!("wrote decompiled ABI to '{}' .", &abi_output_path).to_string()); + // write the decompiled source to file let mut decompiled_output: Vec = Vec::new(); @@ -377,4 +379,6 @@ pub fn build_output( &decompiled_output_path, postprocess(decompiled_output) ); + + logger.success(&format!("wrote decompiled contract to '{}' .", &decompiled_output_path).to_string()); } \ No newline at end of file diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index 2c9ac1fe..00ab9192 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -1,5 +1,5 @@ #[cfg(test)] -mod benchmark_module { +mod benchmark { use clap_verbosity_flag::Verbosity; use heimdall_common::testing::benchmarks::benchmark; From dafafce99c5093e34e1cd985b968d721e4eccdf4 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 25 Oct 2022 23:18:01 -0400 Subject: [PATCH 14/48] =?UTF-8?q?=F0=9F=91=B7=20build:=20bifrost=20will=20?= =?UTF-8?q?install=20with=20LTO=20enabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bifrost/bifrost | 2 +- common/Cargo.toml | 3 --- config/Cargo.toml | 3 --- heimdall/Cargo.toml | 3 --- 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/bifrost/bifrost b/bifrost/bifrost index 99fe0edd..2df80223 100755 --- a/bifrost/bifrost +++ b/bifrost/bifrost @@ -67,7 +67,7 @@ main() { fi # build the binaries - RUSTFLAGS="-C target-cpu=native" ensure cargo install --path ./heimdall --bins --locked --force --root $BIFROST_PATH + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo install --path ./heimdall --bins --locked --force --root $BIFROST_PATH echo "bifrost: Installation complete." } diff --git a/common/Cargo.toml b/common/Cargo.toml index 70510c3e..406915e8 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,3 @@ -[profile.release] -lto = true - [package] name = "heimdall-common" version = "0.1.4" diff --git a/config/Cargo.toml b/config/Cargo.toml index b9815060..d3e3a5c4 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,6 +1,3 @@ -[profile.release] -lto = true - [package] name = "heimdall-config" version = "0.1.4" diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index ea78c035..c38fd9e2 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -1,6 +1,3 @@ -[profile.release] -lto = true - [package] description = "Heimdall is an advanced Ethereum smart contract toolkit for forensic and heuristic analysis." edition = "2021" From 2717f4584067cf3667f89c78851c077b6ad11258 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:26:19 -0400 Subject: [PATCH 15/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20fixes=20precompile?= =?UTF-8?q?=20return=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/ether/solidity.rs | 39 ++++++++++++++++++++++----- heimdall/.bench | 0 heimdall/src/decode/tests.rs | 4 +-- heimdall/src/decompile/analyze.rs | 9 ++++--- heimdall/src/decompile/postprocess.rs | 35 ++++++++++++++++++++++-- heimdall/src/decompile/precompile.rs | 11 +++++--- heimdall/src/decompile/tests.rs | 4 +-- 7 files changed, 84 insertions(+), 18 deletions(-) delete mode 100644 heimdall/.bench diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index 4975c41b..a76fe959 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -371,12 +371,39 @@ impl WrappedOpcode { ); }, "MLOAD" => { - solidified_wrapped_opcode.push_str( - format!( - "memory[{}]", - self.inputs[0]._solidify() - ).as_str() - ); + let memloc = self.inputs[0]._solidify(); + + if memloc.contains("memory") { + if memloc.contains("+") { + let parts = memloc.split(" + ").collect::>(); + + solidified_wrapped_opcode.push_str( + format!( + "memory[{}][{}]", + parts[0].replace("memory[", "").replace("]", ""), + parts[1].replace("memory[", "").replace("]", ""), + ).as_str() + ); + } + else { + solidified_wrapped_opcode.push_str( + format!( + "memory[{}]", + memloc + ).as_str() + ); + } + } + else { + solidified_wrapped_opcode.push_str( + format!( + "memory[{}]", + memloc + ).as_str() + ); + } + + }, "MSIZE" => { solidified_wrapped_opcode.push_str( diff --git a/heimdall/.bench b/heimdall/.bench deleted file mode 100644 index e69de29b..00000000 diff --git a/heimdall/src/decode/tests.rs b/heimdall/src/decode/tests.rs index d5d143f2..f22cb4e9 100644 --- a/heimdall/src/decode/tests.rs +++ b/heimdall/src/decode/tests.rs @@ -18,7 +18,7 @@ mod benchmark { crate::decode::decode(args.clone()) } - benchmark("bench_decode_seaport_simple", 10, bench) + benchmark("bench_decode_seaport_simple", 50, bench) } #[test] @@ -34,7 +34,7 @@ mod benchmark { crate::decode::decode(args.clone()) } - benchmark("benchmark_decode_seaport_complex", 10, bench) + benchmark("benchmark_decode_seaport_complex", 50, bench) } } \ No newline at end of file diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 5e0272d2..9f082207 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -314,7 +314,8 @@ impl VMTrace { // check if the external call is a precompiled contract match decode_precompile( instruction.inputs[1], - extcalldata_memory.clone() + extcalldata_memory.clone(), + instruction.input_operations[2].clone() ) { (true, precompile_logic) => { function.logic.push(precompile_logic); @@ -346,7 +347,8 @@ impl VMTrace { // check if the external call is a precompiled contract match decode_precompile( instruction.inputs[1], - extcalldata_memory.clone() + extcalldata_memory.clone(), + instruction.input_operations[2].clone() ) { (true, precompile_logic) => { function.logic.push(precompile_logic); @@ -387,7 +389,8 @@ impl VMTrace { // check if the external call is a precompiled contract match decode_precompile( instruction.inputs[1], - extcalldata_memory.clone() + extcalldata_memory.clone(), + instruction.input_operations[5].clone() ) { (is_precompile, precompile_logic) if is_precompile=> { function.logic.push(precompile_logic); diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index b0623e8c..81721abc 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -39,10 +39,23 @@ fn convert_bitmask_to_casting(line: String) -> String { }, }; + // if the cast is a bool, check if the line is a conditional + let solidity_type = match cast_types[0].as_str() { + "bool" => { + if cleaned.contains("if") { + String::new() + } + else { + "bytes1".to_string() + } + }, + _ => cast_types[0].to_owned() + }; + // apply the cast to the subject cleaned = cleaned.replace( &format!("{}{}", cast, subject), - &format!("{}{}", cast_types[0], subject), + &format!("{}{}", solidity_type, subject), ); // attempt to cast again @@ -78,11 +91,24 @@ fn convert_bitmask_to_casting(line: String) -> String { subject }, }; + + // if the cast is a bool, check if the line is a conditional + let solidity_type = match cast_types[0].as_str() { + "bool" => { + if cleaned.contains("if") { + String::new() + } + else { + "bytes1".to_string() + } + }, + _ => cast_types[0].to_owned() + }; // apply the cast to the subject cleaned = cleaned.replace( &format!("{}{}", subject, cast), - &format!("{}{}", cast_types[0], subject), + &format!("{}{}", solidity_type, subject), ); // attempt to cast again @@ -241,6 +267,11 @@ fn simplify_parentheses(line: String, paren_index: usize) -> String { } else { + // remove double negation, if one exists + if cleaned.contains("!!") { + cleaned = cleaned.replace("!!", ""); + } + // recurse into the next set of parentheses cleaned = simplify_parentheses(cleaned, paren_index + 1); } diff --git a/heimdall/src/decompile/precompile.rs b/heimdall/src/decompile/precompile.rs index 5795cca1..7252b973 100644 --- a/heimdall/src/decompile/precompile.rs +++ b/heimdall/src/decompile/precompile.rs @@ -1,4 +1,5 @@ use ethers::types::U256; +use heimdall_common::ether::evm::opcodes::WrappedOpcode; use super::util::StorageFrame; @@ -6,6 +7,7 @@ use super::util::StorageFrame; pub fn decode_precompile( precompile_address: U256, extcalldata_memory: Vec, + return_data_offset: WrappedOpcode, ) -> (bool, String) { // safely convert the precompile address to a usize. @@ -20,21 +22,24 @@ pub fn decode_precompile( 1 => { is_ext_call_precompile = true; ext_call_logic = format!( - "address ret0 = ecrecover({});", + "address memory[{}] = ecrecover({});", + return_data_offset.solidify(), extcalldata_memory.iter().map(|x| x.operations.solidify()).collect::>().join(", ") ); } 2 => { is_ext_call_precompile = true; ext_call_logic = format!( - "bytes ret0 = sha256({});", + "bytes memory[{}] = sha256({});", + return_data_offset.solidify(), extcalldata_memory.iter().map(|x| x.operations.solidify()).collect::>().join(", ") ); } 3 => { is_ext_call_precompile = true; ext_call_logic = format!( - "bytes ret0 = ripemd160({});", + "bytes memory[{}] = ripemd160({});", + return_data_offset.solidify(), extcalldata_memory.iter().map(|x| x.operations.solidify()).collect::>().join(", ") ); } diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index 00ab9192..c9473068 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -20,7 +20,7 @@ mod benchmark { crate::decompile::decompile(args.clone()) } - benchmark("benchmark_decompile_complex", 25, bench) + benchmark("benchmark_decompile_complex", 100, bench) } #[test] @@ -38,7 +38,7 @@ mod benchmark { crate::decompile::decompile(args.clone()) } - benchmark("benchmark_decompile_simple", 25, bench) + benchmark("benchmark_decompile_simple", 100, bench) } } From 3022a5a26e73bbe8d43adae41f8734b4888304c6 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 27 Oct 2022 12:38:28 -0400 Subject: [PATCH 16/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20fixes=20solidifying?= =?UTF-8?q?=20memory=20length?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/consts.rs | 3 +++ common/src/ether/solidity.rs | 26 +++++++++++++++++++------- heimdall/src/decompile/postprocess.rs | 20 +++++++++++++++++++- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/common/src/consts.rs b/common/src/consts.rs index cadd9101..3efa01c0 100644 --- a/common/src/consts.rs +++ b/common/src/consts.rs @@ -22,4 +22,7 @@ lazy_static! { // The following regex is used to find type castings pub static ref TYPE_CAST_REGEX: Regex = Regex::new(r"(address\(|string\(|bool\(|bytes(\d*)\(|uint(\d*)\(|int(\d*)\()").unwrap(); + + // The following regex is used to find memory accesses + pub static ref MEMLEN_REGEX: Regex = Regex::new(r"memory\[memory\[[0-9x]*\]\]").unwrap(); } \ No newline at end of file diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index a76fe959..b675ed66 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -1,4 +1,4 @@ -use crate::{utils::strings::encode_hex_reduced, consts::WORD_REGEX}; +use crate::{utils::strings::encode_hex_reduced, consts::{WORD_REGEX, MEMLEN_REGEX}}; use super::evm::opcodes::*; @@ -386,12 +386,24 @@ impl WrappedOpcode { ); } else { - solidified_wrapped_opcode.push_str( - format!( - "memory[{}]", - memloc - ).as_str() - ); + match MEMLEN_REGEX.find(&format!("memory[{}]", memloc)) { + Some(_) => { + solidified_wrapped_opcode.push_str( + format!( + "{}.length", + memloc + ).as_str() + ); + }, + None => { + solidified_wrapped_opcode.push_str( + format!( + "memory[{}]", + memloc + ).as_str() + ); + } + } } } else { diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 81721abc..78b3695e 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -298,6 +298,13 @@ fn convert_iszero_logic_flip(line: String) -> String { fn convert_memory_to_variable(line: String) -> String { let mut cleaned = line; + // reset the mem_map if the line is a function definition + if cleaned.contains("function") { + let mut mem_map = MEM_LOOKUP_MAP.lock().unwrap(); + *mem_map = HashMap::new(); + drop(mem_map); + } + // find a memory access let memory_access = match MEM_ACCESS_REGEX.find(&cleaned) { Some(x) => x.as_str(), @@ -323,7 +330,7 @@ fn convert_memory_to_variable(line: String) -> String { let idex = mem_map.len() + 1; // get the variable name - let variable_name = base26_encode(idex); + let variable_name = format!("var_{}", base26_encode(idex)); // add the variable to the map mem_map.insert(memloc.clone(), variable_name.clone()); @@ -346,6 +353,14 @@ fn convert_memory_to_variable(line: String) -> String { cleaned } +fn remove_unused_assignments(line: String) -> String { + let mut cleaned = line; + + + + cleaned +} + fn cleanup(line: String) -> String { let mut cleaned = line; @@ -364,6 +379,9 @@ fn cleanup(line: String) -> String { // Convert all memory[] accesses to variables cleaned = convert_memory_to_variable(cleaned); + // Remove all unused assignments + cleaned = remove_unused_assignments(cleaned); + cleaned } From 9ec31e46486737b244e584eb2f1e0a1439fc7167 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 27 Oct 2022 18:19:28 -0400 Subject: [PATCH 17/48] =?UTF-8?q?=F0=9F=9A=A8=20tests:=20update=20benchmar?= =?UTF-8?q?k=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/testing/benchmarks.rs | 2 +- heimdall/scripts/benchmark | 6 +++--- heimdall/src/decode/tests.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/testing/benchmarks.rs b/common/src/testing/benchmarks.rs index 660de5a1..4127199b 100644 --- a/common/src/testing/benchmarks.rs +++ b/common/src/testing/benchmarks.rs @@ -23,7 +23,7 @@ pub fn benchmark(benchmark_name: &str, runs: usize, to_bench: fn()) { " {}:\n {}ms ± {}ms per run ( with {} runs ).\n\n", benchmark_name, time / runs, - std::cmp::max(max, min), + std::cmp::max(max-(time / runs), (time / runs)-min), runs ).as_bytes() ); diff --git a/heimdall/scripts/benchmark b/heimdall/scripts/benchmark index 5fd3bf0f..9626136d 100644 --- a/heimdall/scripts/benchmark +++ b/heimdall/scripts/benchmark @@ -1,8 +1,8 @@ echo "Compiling..." -cargo build --release --package heimdall > /dev/null 2>&1 -cargo build --release --package heimdall-config > /dev/null 2>&1 -cargo build --release --package heimdall-benchmark > /dev/null 2>&1 +RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo build --release --package heimdall > /dev/null 2>&1 +RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo build --release --package heimdall-config > /dev/null 2>&1 +RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo build --release --package heimdall-common > /dev/null 2>&1 echo "Running benchmarks..." cargo test --release --package heimdall -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout diff --git a/heimdall/src/decode/tests.rs b/heimdall/src/decode/tests.rs index f22cb4e9..052078dc 100644 --- a/heimdall/src/decode/tests.rs +++ b/heimdall/src/decode/tests.rs @@ -6,7 +6,7 @@ mod benchmark { use crate::{decode::DecodeArgs}; #[test] - fn bench_decode_seaport_simple() { + fn benchmark_decode_seaport_simple() { fn bench() { let args = DecodeArgs { @@ -18,7 +18,7 @@ mod benchmark { crate::decode::decode(args.clone()) } - benchmark("bench_decode_seaport_simple", 50, bench) + benchmark("benchmark_decode_seaport_simple", 50, bench) } #[test] From 127f4bf5618a4e8cb06f5604e0a1d1b5afbb0162 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:15:43 -0400 Subject: [PATCH 18/48] =?UTF-8?q?=E2=9C=A8=20feat(bifrost):=20add=20bench?= =?UTF-8?q?=20and=20test=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bifrost/bifrost | 97 ++++++++++++++++++++++++++++++- heimdall/src/decompile/analyze.rs | 2 + heimdall/src/decompile/util.rs | 2 +- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/bifrost/bifrost b/bifrost/bifrost index 2df80223..cd998527 100755 --- a/bifrost/bifrost +++ b/bifrost/bifrost @@ -16,6 +16,100 @@ main() { --) shift; break;; -v|--version) shift; TARGET_VERSION=$1;; + -b|--bench) + echo "bifrost: fetching..." + + # remove the current heimdall installation if it exists + ensure rm -f "$BIFROST_BIN_DIR/heimdall" + + # make the build path if it doesn't exist + BUILD_PATH="${BIFROST_PATH}/build" + if [ ! -d $BUILD_PATH ]; then + ensure mkdir -p $BUILD_PATH + fi + + # remove the source directory if it exists + ensure rm -rf "$BUILD_PATH/heimdall-rs" + + cd $BUILD_PATH + + ensure git clone "https://github.com/Jon-Becker/heimdall-rs" > /dev/null 2>&1 + + cd "heimdall-rs" + + ensure git fetch origin > /dev/null 2>&1 + + # if they specified a version, use that + if [ -n "$TARGET_VERSION" ]; then + ensure git checkout $TARGET_VERSION > /dev/null 2>&1 + else + + # checkout the latest tag + tag=$(git describe --tags `git rev-list --tags --max-count=1`) + ensure git checkout $tag -b latest > /dev/null 2>&1 + fi + + echo "bifrost: compiling..." + ensure cd $BUILD_PATH/heimdall-rs/heimdall + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo build --release --package heimdall > /dev/null 2>&1 + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo build --release --package heimdall-config > /dev/null 2>&1 + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo build --release --package heimdall-common > /dev/null 2>&1 + echo "bifrost: running tests..." + + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo test --release --package heimdall -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo test --release --package heimdall-config -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo test --release --package heimdall-common -- benchmark_ | grep -E "±|benchmark_([a-zA-Z_])*:" >> stdout + clear + echo "bifrost: benchmark results:\n" + cat stdout + rm stdout + exit 0 + ;; + -t|--test) + echo "bifrost: fetching..." + + # remove the current heimdall installation if it exists + ensure rm -f "$BIFROST_BIN_DIR/heimdall" + + # make the build path if it doesn't exist + BUILD_PATH="${BIFROST_PATH}/build" + if [ ! -d $BUILD_PATH ]; then + ensure mkdir -p $BUILD_PATH + fi + + # remove the source directory if it exists + ensure rm -rf "$BUILD_PATH/heimdall-rs" + + cd $BUILD_PATH + + ensure git clone "https://github.com/Jon-Becker/heimdall-rs" > /dev/null 2>&1 + + cd "heimdall-rs" + + ensure git fetch origin > /dev/null 2>&1 + + # if they specified a version, use that + if [ -n "$TARGET_VERSION" ]; then + ensure git checkout $TARGET_VERSION > /dev/null 2>&1 + else + + # checkout the latest tag + tag=$(git describe --tags `git rev-list --tags --max-count=1`) + ensure git checkout $tag -b latest > /dev/null 2>&1 + fi + + echo "bifrost: compiling..." + ensure cd $BUILD_PATH/heimdall-rs/heimdall + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo build --release --package heimdall > /dev/null 2>&1 + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo build --release --package heimdall-config > /dev/null 2>&1 + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true ensure cargo build --release --package heimdall-common > /dev/null 2>&1 + echo "bifrost: running tests..." + + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo test --release --package heimdall -- test_ --nocapture + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo test --release --package heimdall-config -- test_ --nocapture + RUSTFLAGS="-C target-cpu=native" CARGO_PROFILE_RELEASE_LTO=true cargo test --release --package heimdall-common -- test_ --nocapture + exit 0 + ;; -h|--help) usage exit 0 @@ -99,7 +193,8 @@ OPTIONS: -h, --help Print help information -v, --version Install a specific version -l, --list List all available versions - + -b, --bench Install and run benchmarks + -t, --test Install and run tests EOF } diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 9f082207..6957e009 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -616,11 +616,13 @@ impl VMTrace { } // recurse into the children of the VMTrace map + function.logic.push("{".to_string()); for child in &self.children { function = child.analyze(function, trace, trace_parent); } + function.logic.push("}".to_string()); // TODO: indentation diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 8b4f19d3..51b5a54c 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -402,7 +402,7 @@ pub fn recursive_map( } } - if vm.exitcode != 255 || vm.returndata.len() as usize > 0 { + if vm.exitcode != 255 || vm.returndata.len() > 0 { break; } } From 7602b5ebd5b005f2897c9d8c6dc9e41bc9f74a4a Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:24:58 -0400 Subject: [PATCH 19/48] =?UTF-8?q?=E2=9C=A8=20feat(bifrost):=20add=20bifros?= =?UTF-8?q?t=20update=20script?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bifrost/bifrost | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bifrost/bifrost b/bifrost/bifrost index cd998527..1bfb30da 100755 --- a/bifrost/bifrost +++ b/bifrost/bifrost @@ -15,6 +15,14 @@ main() { case $1 in --) shift; break;; + -u|--upgrade|--update) + echo "bifrost: removing old binaries" + + rm -rf "$BIFROST_PATH" + + ensure curl -L https://raw.githubusercontent.com/Jon-Becker/heimdall-rs/main/bifrost/install | bash + exit 0 + ;; -v|--version) shift; TARGET_VERSION=$1;; -b|--bench) echo "bifrost: fetching..." @@ -191,6 +199,7 @@ USAGE: OPTIONS: -h, --help Print help information + -u, --update Update bifrost to the latest version -v, --version Install a specific version -l, --list List all available versions -b, --bench Install and run benchmarks From 40ca7c11df658e1c6e422a4563b1c89a64f5955a Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:14:24 -0400 Subject: [PATCH 20/48] =?UTF-8?q?=F0=9F=90=9B=20fix:=20destroy=20branches?= =?UTF-8?q?=20that=20have=20already=20been=20traced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/util.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 51b5a54c..0d0c99cd 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -364,6 +364,9 @@ pub fn recursive_map( handled_jumpdests, )); } else { + + // jump was already taken, destroy this branch + vm_trace.children.pop(); break; } @@ -389,6 +392,9 @@ pub fn recursive_map( handled_jumpdests, )); } else { + + // jump was already taken, destroy this branch + vm_trace.children.pop(); break; } From 017bbb209c1521a40c8c60c283aeb96cf68738db Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:20:46 -0400 Subject: [PATCH 21/48] =?UTF-8?q?=E2=8F=AA=20revert:=20destroy=20branches?= =?UTF-8?q?=20that=20have=20already=20been=20traced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/util.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 0d0c99cd..51b5a54c 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -364,9 +364,6 @@ pub fn recursive_map( handled_jumpdests, )); } else { - - // jump was already taken, destroy this branch - vm_trace.children.pop(); break; } @@ -392,9 +389,6 @@ pub fn recursive_map( handled_jumpdests, )); } else { - - // jump was already taken, destroy this branch - vm_trace.children.pop(); break; } From 203b21e336651e3f78889017fd80b6262a03f7ba Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 3 Nov 2022 17:23:45 -0400 Subject: [PATCH 22/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20fix=20branch=20prun?= =?UTF-8?q?ing=20in=20static=20analysis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/ether/solidity.rs | 3 ++ heimdall/src/decompile/analyze.rs | 12 ++++---- heimdall/src/decompile/util.rs | 46 +++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index b675ed66..c97208bb 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -434,6 +434,9 @@ impl WrappedOpcode { "STATICCALL" => { solidified_wrapped_opcode.push_str("ret0"); } + "RETURNDATASIZE" => { + solidified_wrapped_opcode.push_str("ret0.length"); + } opcode => { if opcode.starts_with("PUSH") { diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 6957e009..9ddbd153 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -32,6 +32,7 @@ impl VMTrace { // make a clone of the recursed analysis function let mut function = function.clone(); + let mut branch_jumped = false; // perform analysis on the operations of the current VMTrace branch for operation in &self.operations { @@ -143,10 +144,11 @@ impl VMTrace { } else if opcode_name == "JUMPI" { // add closing braces to the function's logic - // TODO: add braces + branch_jumped = true; + function.logic.push( format!( - "if ({}) ", + "if ({})", instruction.input_operations[1].solidify() ).to_string() @@ -616,15 +618,11 @@ impl VMTrace { } // recurse into the children of the VMTrace map - function.logic.push("{".to_string()); - for child in &self.children { + for (_, child) in self.children.iter().enumerate() { function = child.analyze(function, trace, trace_parent); } - function.logic.push("}".to_string()); - - // TODO: indentation function } diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 51b5a54c..e71a2238 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -297,7 +297,7 @@ pub fn map_selector( trace_parent: u32, selector: String, entry_point: u64, -) -> (VMTrace, Vec) { +) -> (VMTrace, Vec) { let mut vm = evm.clone(); vm.calldata = selector.clone(); @@ -325,7 +325,7 @@ pub fn recursive_map( evm: &VM, trace: &TraceFactory, trace_parent: u32, - handled_jumpdests: &mut Vec, + handled_jumpdests: &mut Vec, ) -> VMTrace { let mut vm = evm.clone(); @@ -352,11 +352,25 @@ pub fn recursive_map( // the jump was not taken, create a trace for the jump path // only jump if we haven't already traced this destination // TODO: mark as a loop? - if !(handled_jumpdests.contains(&(state.last_instruction.inputs[0].as_u128() + 1))) + if !(handled_jumpdests.contains( + &format!( + "{}->{}", + &state.last_instruction.instruction, + &(state.last_instruction.inputs[0].as_u128() + 1) + ) + )) { + handled_jumpdests.push( + format!( + "{}->{}", + &state.last_instruction.instruction, + &(state.last_instruction.inputs[0].as_u128() + 1) + ) + ); + + let mut trace_vm = vm.clone(); trace_vm.instruction = state.last_instruction.inputs[0].as_u128() + 1; - handled_jumpdests.push(trace_vm.instruction.clone()); vm_trace.children.push(recursive_map( &trace_vm, trace, @@ -364,6 +378,9 @@ pub fn recursive_map( handled_jumpdests, )); } else { + + // pop off the JUMPI + vm_trace.operations.pop(); break; } @@ -378,10 +395,24 @@ pub fn recursive_map( // the jump was taken, create a trace for the fallthrough path // only jump if we haven't already traced this destination - if !(handled_jumpdests.contains(&(state.last_instruction.instruction + 1))) { + if !(handled_jumpdests.contains( + &format!( + "{}->{}", + &state.last_instruction.instruction, + &(state.last_instruction.inputs[0].as_u128() + 1) + ) + )) + { + handled_jumpdests.push( + format!( + "{}->{}", + &state.last_instruction.instruction, + &(state.last_instruction.inputs[0].as_u128() + 1) + ) + ); + let mut trace_vm = vm.clone(); trace_vm.instruction = state.last_instruction.instruction + 1; - handled_jumpdests.push(trace_vm.instruction.clone()); vm_trace.children.push(recursive_map( &trace_vm, trace, @@ -389,6 +420,9 @@ pub fn recursive_map( handled_jumpdests, )); } else { + + // pop off the JUMPI + vm_trace.operations.pop(); break; } From d4336839c13160f356f4724e577073de8e5dc2d8 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Fri, 4 Nov 2022 18:01:45 -0400 Subject: [PATCH 23/48] =?UTF-8?q?=E2=9A=A1=20perf:=20postprocessing=20will?= =?UTF-8?q?=20use=20variable=20names=20where=20possible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/{consts.rs => constants.rs} | 0 common/src/ether/evm/disassemble.rs | 2 +- common/src/ether/evm/types.rs | 2 +- common/src/ether/solidity.rs | 2 +- common/src/lib.rs | 2 +- common/src/utils/strings.rs | 2 +- heimdall/src/decode/mod.rs | 2 +- heimdall/src/decompile/constants.rs | 4 -- heimdall/src/decompile/mod.rs | 2 +- heimdall/src/decompile/output.rs | 6 +- heimdall/src/decompile/postprocess.rs | 87 +++++++++++++++++++++++++- 11 files changed, 97 insertions(+), 14 deletions(-) rename common/src/{consts.rs => constants.rs} (100%) diff --git a/common/src/consts.rs b/common/src/constants.rs similarity index 100% rename from common/src/consts.rs rename to common/src/constants.rs diff --git a/common/src/ether/evm/disassemble.rs b/common/src/ether/evm/disassemble.rs index 692c90b2..1ff2ad1e 100644 --- a/common/src/ether/evm/disassemble.rs +++ b/common/src/ether/evm/disassemble.rs @@ -7,7 +7,7 @@ use ethers::{ providers::{Middleware, Provider, Http}, }; use crate::{ - consts::{ ADDRESS_REGEX, BYTECODE_REGEX }, + constants::{ ADDRESS_REGEX, BYTECODE_REGEX }, io::{ logging::*, file::* }, ether::evm::{ opcodes::opcode } }; diff --git a/common/src/ether/evm/types.rs b/common/src/ether/evm/types.rs index d1386acb..ef857552 100644 --- a/common/src/ether/evm/types.rs +++ b/common/src/ether/evm/types.rs @@ -1,7 +1,7 @@ use colored::Colorize; use ethers::abi::{ParamType, Token, AbiEncode}; -use crate::{utils::strings::{replace_last, find_balanced_encapsulator}, consts::TYPE_CAST_REGEX}; +use crate::{utils::strings::{replace_last, find_balanced_encapsulator}, constants::TYPE_CAST_REGEX}; use super::vm::Instruction; diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index c97208bb..774aba17 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -1,4 +1,4 @@ -use crate::{utils::strings::encode_hex_reduced, consts::{WORD_REGEX, MEMLEN_REGEX}}; +use crate::{utils::strings::encode_hex_reduced, constants::{WORD_REGEX, MEMLEN_REGEX}}; use super::evm::opcodes::*; diff --git a/common/src/lib.rs b/common/src/lib.rs index 443a61b0..412f9a91 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,7 +1,7 @@ extern crate lazy_static; pub mod ether; -pub mod consts; +pub mod constants; pub mod io; pub mod utils; pub mod testing; \ No newline at end of file diff --git a/common/src/utils/strings.rs b/common/src/utils/strings.rs index f5382135..48922162 100644 --- a/common/src/utils/strings.rs +++ b/common/src/utils/strings.rs @@ -3,7 +3,7 @@ use std::{num::ParseIntError}; use ethers::{prelude::{I256, U256}, abi::AbiEncode}; -use crate::consts::REDUCE_HEX_REGEX; +use crate::constants::REDUCE_HEX_REGEX; // Convert an unsigned integer into a signed one diff --git a/heimdall/src/decode/mod.rs b/heimdall/src/decode/mod.rs index 705c79d9..dc176f93 100644 --- a/heimdall/src/decode/mod.rs +++ b/heimdall/src/decode/mod.rs @@ -13,7 +13,7 @@ use ethers::{ use heimdall_common::{ io::logging::Logger, - consts::TRANSACTION_HASH_REGEX, + constants::TRANSACTION_HASH_REGEX, utils::{ strings::decode_hex, }, ether::{evm::types::{parse_function_parameters, display}, signatures::{resolve_function_signature, ResolvedFunction}} diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index c3b3c320..745f7afb 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -33,10 +33,6 @@ pragma solidity >=0.8.0; /// /// @custom:github You can find the open-source decompiler here: /// https://github.com/Jon-Becker/heimdall-rs -/// -/// @custom:donations Heimdall is open source and will always be free to use, so -/// donations are always appreciated if you find it helpful. -/// 0x6666666b0B46056247E7D6cbdb78287F4D12574d OR jbecker.eth ".to_string(); } \ No newline at end of file diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index e9bbb08d..dbd3f09c 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -32,7 +32,7 @@ use heimdall_common::{ vm::VM }, ether::signatures::*, - consts::{ ADDRESS_REGEX, BYTECODE_REGEX }, + constants::{ ADDRESS_REGEX, BYTECODE_REGEX }, io::{ logging::* }, }; diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index d00ba86e..587e97c6 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -264,7 +264,9 @@ pub fn build_output( ) ); - logger.success(&format!("wrote decompiled ABI to '{}' .", &abi_output_path).to_string()); + progress_bar.suspend(|| { + logger.success(&format!("wrote decompiled ABI to '{}' .", &abi_output_path).to_string()); + }); // write the decompiled source to file let mut decompiled_output: Vec = Vec::new(); @@ -375,6 +377,8 @@ pub fn build_output( decompiled_output.push(String::from("}")); + progress_bar.finish_and_clear(); + write_lines_to_file( &decompiled_output_path, postprocess(decompiled_output) diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 78b3695e..ec6abd8d 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -2,13 +2,14 @@ use std::{ sync::Mutex, collections::HashMap }; -use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards, base26_encode}}; +use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards, base26_encode}, constants::TYPE_CAST_REGEX}; use crate::decompile::constants::{ENCLOSED_EXPRESSION_REGEX}; use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX, MEM_ACCESS_REGEX}}; use lazy_static::lazy_static; lazy_static! { static ref MEM_LOOKUP_MAP: Mutex> = Mutex::new(HashMap::new()); + static ref VARIABLE_MAP: Mutex> = Mutex::new(HashMap::new()); } fn convert_bitmask_to_casting(line: String) -> String { @@ -303,6 +304,9 @@ fn convert_memory_to_variable(line: String) -> String { let mut mem_map = MEM_LOOKUP_MAP.lock().unwrap(); *mem_map = HashMap::new(); drop(mem_map); + let mut var_map = VARIABLE_MAP.lock().unwrap(); + *var_map = HashMap::new(); + drop(var_map); } // find a memory access @@ -350,12 +354,82 @@ fn convert_memory_to_variable(line: String) -> String { _ => return cleaned } + // if the memory access is an instantiation, save it + if cleaned.contains(" = ") { + let instantiation = cleaned.split(" = ").collect::>(); + + let mut var_map = VARIABLE_MAP.lock().unwrap(); + var_map.insert(instantiation[0].to_string(), instantiation[1].to_string().replace(";", "")); + drop(var_map); + } + cleaned } -fn remove_unused_assignments(line: String) -> String { +fn move_casts_to_declaration(line: String) -> String { + let cleaned = line; + + // if the line doesn't contain an instantiation, return + if !cleaned.contains(" = ") { return cleaned; } + + let instantiation = cleaned.split(" = ").collect::>(); + + // get the outermost cast + match TYPE_CAST_REGEX.find(&instantiation[1]) { + Some(x) => { + + // the match must occur at index 0 + if x.start() != 0 { return cleaned; } + + // find the matching close paren + let (paren_start, paren_end, _) = find_balanced_encapsulator(instantiation[1].to_string(), ('(', ')')); + + // the close paren must be at the end of the expression + if paren_end != instantiation[1].len() - 1 { return cleaned; } + + // get the inside of the parens + let cast_expression = instantiation[1].get(paren_start + 1..paren_end-1).unwrap(); + + return format!( + "{} {} = {};", + x.as_str().replace("(", ""), + instantiation[0], + cast_expression + ) + } + None => return cleaned, + }; +} + +fn replace_expression_with_var(line: String) -> String { let mut cleaned = line; + let var_map = VARIABLE_MAP.lock().unwrap(); + + // skip function definitions + if cleaned.contains("function") { return cleaned; } + + // iterate over variable map + for (var, expression) in var_map.iter() { + + // skip numeric expressions + if expression.parse::().is_ok() { continue; } + + // replace the expression with the variable + if cleaned.contains(expression) && !cleaned.starts_with(var) { + cleaned = cleaned.replace(expression, var); + } + } + + // drop the mutex + drop(var_map); + + cleaned +} + +fn remove_unused_assignments(line: String) -> String { + let cleaned = line; + cleaned @@ -364,6 +438,9 @@ fn remove_unused_assignments(line: String) -> String { fn cleanup(line: String) -> String { let mut cleaned = line; + // skip comments + if cleaned.starts_with("/") { return cleaned; } + // Find and convert all castings cleaned = convert_bitmask_to_casting(cleaned); @@ -379,6 +456,12 @@ fn cleanup(line: String) -> String { // Convert all memory[] accesses to variables cleaned = convert_memory_to_variable(cleaned); + // Use variable names where possible + cleaned = replace_expression_with_var(cleaned); + + // Move all outer casts in instantiation to the variable declaration + cleaned = move_casts_to_declaration(cleaned); + // Remove all unused assignments cleaned = remove_unused_assignments(cleaned); From 49e24b670467a3f72638d92825fc9df9f38533a1 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 8 Nov 2022 10:48:19 -0500 Subject: [PATCH 24/48] =?UTF-8?q?=E2=9A=A1=20perf:=20postprocessing=20will?= =?UTF-8?q?=20remove=20unused=20variable=20assignments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/ether/solidity.rs | 9 ++- heimdall/src/decompile/postprocess.rs | 93 +++++++++++++++++++++++++-- heimdall/src/decompile/util.rs | 83 ++++++++++++------------ 3 files changed, 134 insertions(+), 51 deletions(-) diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index 774aba17..d9188232 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -235,8 +235,13 @@ impl WrappedOpcode { solidified_wrapped_opcode.push_str(self.inputs[1]._solidify().as_str()); } "SHA3" => { - // TODO: use memory from function - solidified_wrapped_opcode.push_str("keccak256()"); + + solidified_wrapped_opcode.push_str( + &format!( + "keccak256(memory[{}])", + self.inputs[0]._solidify().as_str() + ) + ); }, "ADDRESS" => { solidified_wrapped_opcode.push_str("address(this)"); diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index ec6abd8d..8524fd4c 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -10,6 +10,7 @@ use lazy_static::lazy_static; lazy_static! { static ref MEM_LOOKUP_MAP: Mutex> = Mutex::new(HashMap::new()); static ref VARIABLE_MAP: Mutex> = Mutex::new(HashMap::new()); + static ref TYPE_MAP: Mutex> = Mutex::new(HashMap::new()); } fn convert_bitmask_to_casting(line: String) -> String { @@ -427,10 +428,49 @@ fn replace_expression_with_var(line: String) -> String { cleaned } -fn remove_unused_assignments(line: String) -> String { - let cleaned = line; +fn inherit_infer_type(line: String) -> String { + let mut cleaned = line.clone(); + let mut type_map = TYPE_MAP.lock().unwrap(); + + // if the line contains a function definition, wipe the type map and get arg types + if line.contains("function") { + type_map.clear(); + let args = line.split("(").collect::>()[1].split(")").collect::>()[0].split(",").collect::>(); + for arg in args { + let arg = arg.trim(); + + // get type and name + let arg_type = arg.split(" ").collect::>()[..arg.split(" ").collect::>().len() - 1].join(" "); + let arg_name = arg.split(" ").collect::>()[arg.split(" ").collect::>().len() - 1]; + // add to type map + type_map.insert(arg_name.to_string(), arg_type.to_string()); + } + } + // if the line contains an instantiation, add the type to the map + if line.contains(" = ") { + let instantiation = line.split(" = ").collect::>(); + let var_type = instantiation[0].split(" ").collect::>()[..instantiation[0].split(" ").collect::>().len() - 1].join(" "); + let var_name = instantiation[0].split(" ").collect::>()[instantiation[0].split(" ").collect::>().len() - 1]; + + // add to type map, if the variable is typed + if var_type.len() > 0 { + type_map.insert(var_name.to_string(), var_type.to_string()); + } + else if !line.starts_with("storage") { + + // infer the type from args and vars in the expression + for (var, var_type) in type_map.clone().iter() { + if cleaned.contains(var) && !type_map.contains_key(var_name) { + cleaned = format!("{} {}", var_type, cleaned); + type_map.insert(var_name.to_string(), var_type.to_string()); + break; + } + } + + } + } cleaned } @@ -453,7 +493,7 @@ fn cleanup(line: String) -> String { // Remove all unnecessary parentheses cleaned = simplify_parentheses(cleaned, 0); - // Convert all memory[] accesses to variables + // Convert all memory[] accesses to variables, also removes unused variables cleaned = convert_memory_to_variable(cleaned); // Use variable names where possible @@ -462,16 +502,54 @@ fn cleanup(line: String) -> String { // Move all outer casts in instantiation to the variable declaration cleaned = move_casts_to_declaration(cleaned); - // Remove all unused assignments - cleaned = remove_unused_assignments(cleaned); + // Inherit or infer types from expressions + cleaned = inherit_infer_type(cleaned); cleaned } +fn contains_unnecessary_assignment(line: String, lines: &Vec<&String>) -> bool { + + // skip lines that don't contain an assignment + if !line.contains(" = ") { return false; } + + // get var name + let var_name = line.split(" = ").collect::>()[0].split(" ").collect::>()[line.split(" = ").collect::>()[0].split(" ").collect::>().len() - 1]; + + //remove unused vars + for x in lines { + if x.contains(var_name) && !x.trim().starts_with(var_name) { return false; } + else { + + // break if the line contains a function definition + if x.contains("function") { break; } + } + } + + true +} + +fn finalize(lines: Vec) -> Vec { + let mut cleaned_lines: Vec = Vec::new(); + + // remove unused assignments + for (i, line) in lines.iter().enumerate() { + + // only pass in lines further than the current line + if !contains_unnecessary_assignment(line.trim().to_string(), &lines[i..].iter().collect::>()) + { + cleaned_lines.push(line.to_string()); + } + } + + cleaned_lines +} + pub fn postprocess(lines: Vec) -> Vec { let mut indentation: usize = 0; let mut cleaned_lines: Vec = lines.clone(); + // clean up each line using postprocessing techniques for line in cleaned_lines.iter_mut() { // dedent due to closing braces @@ -483,7 +561,7 @@ pub fn postprocess(lines: Vec) -> Vec { *line = format!( "{}{}", " ".repeat(indentation*4), - cleanup(line.to_string()) + cleanup(line.to_string()) ); // indent due to opening braces @@ -493,5 +571,6 @@ pub fn postprocess(lines: Vec) -> Vec { } - cleaned_lines + // run finalizing postprocessing, which need to operate on cleaned lines + finalize(cleaned_lines) } \ No newline at end of file diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index e71a2238..c65e43a2 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -352,38 +352,37 @@ pub fn recursive_map( // the jump was not taken, create a trace for the jump path // only jump if we haven't already traced this destination // TODO: mark as a loop? - if !(handled_jumpdests.contains( + if handled_jumpdests.contains( &format!( "{}->{}", &state.last_instruction.instruction, &(state.last_instruction.inputs[0].as_u128() + 1) ) - )) - { - handled_jumpdests.push( - format!( - "{}->{}", - &state.last_instruction.instruction, - &(state.last_instruction.inputs[0].as_u128() + 1) - ) - ); - - - let mut trace_vm = vm.clone(); - trace_vm.instruction = state.last_instruction.inputs[0].as_u128() + 1; - vm_trace.children.push(recursive_map( - &trace_vm, - trace, - trace_parent, - handled_jumpdests, - )); - } else { - + ) { + // pop off the JUMPI vm_trace.operations.pop(); break; } + handled_jumpdests.push( + format!( + "{}->{}", + &state.last_instruction.instruction, + &(state.last_instruction.inputs[0].as_u128() + 1) + ) + ); + + // push a new vm trace to the children + let mut trace_vm = vm.clone(); + trace_vm.instruction = state.last_instruction.inputs[0].as_u128() + 1; + vm_trace.children.push(recursive_map( + &trace_vm, + trace, + trace_parent, + handled_jumpdests, + )); + // push the current path onto the stack vm_trace.children.push(recursive_map( &vm.clone(), @@ -395,37 +394,37 @@ pub fn recursive_map( // the jump was taken, create a trace for the fallthrough path // only jump if we haven't already traced this destination - if !(handled_jumpdests.contains( + if handled_jumpdests.contains( &format!( "{}->{}", &state.last_instruction.instruction, &(state.last_instruction.inputs[0].as_u128() + 1) ) - )) - { - handled_jumpdests.push( - format!( - "{}->{}", - &state.last_instruction.instruction, - &(state.last_instruction.inputs[0].as_u128() + 1) - ) - ); - - let mut trace_vm = vm.clone(); - trace_vm.instruction = state.last_instruction.instruction + 1; - vm_trace.children.push(recursive_map( - &trace_vm, - trace, - trace_parent, - handled_jumpdests, - )); - } else { + ) { // pop off the JUMPI vm_trace.operations.pop(); break; } + handled_jumpdests.push( + format!( + "{}->{}", + &state.last_instruction.instruction, + &(state.last_instruction.inputs[0].as_u128() + 1) + ) + ); + + // push a new vm trace to the children + let mut trace_vm = vm.clone(); + trace_vm.instruction = state.last_instruction.instruction + 1; + vm_trace.children.push(recursive_map( + &trace_vm, + trace, + trace_parent, + handled_jumpdests, + )); + // push the current path onto the stack vm_trace.children.push(recursive_map( &vm.clone(), From 0aa31d72c5cd4a21bd042f167404c590dcf5b9cb Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 8 Nov 2022 11:16:58 -0500 Subject: [PATCH 25/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20ret0=20will=20be=20?= =?UTF-8?q?replaced=20with=20var=20names=20for=20precompile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/src/ether/solidity.rs | 82 ++++++++++++++++++++++++--- heimdall/src/decompile/postprocess.rs | 42 +++++++------- 2 files changed, 95 insertions(+), 29 deletions(-) diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index d9188232..b7856efa 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -1,7 +1,23 @@ +use std::str::FromStr; + +use ethers::types::U256; + use crate::{utils::strings::encode_hex_reduced, constants::{WORD_REGEX, MEMLEN_REGEX}}; use super::evm::opcodes::*; +fn is_ext_call_precompile(precompile_address: U256) -> bool { + let address: usize = match precompile_address.try_into() { + Ok(x) => x, + Err(_) => usize::MAX, + }; + + match address { + 1 | 2 | 3 => true, + _ => false, + } +} + impl WrappedOpcode { // Returns a WrappedOpcode's solidity representation. @@ -428,19 +444,69 @@ impl WrappedOpcode { ); }, "CALL" => { - solidified_wrapped_opcode.push_str("ret0"); + match U256::from_str(&self.inputs[1]._solidify()) { + Ok(addr) => { + if is_ext_call_precompile(addr) { + solidified_wrapped_opcode.push_str(&format!("memory[{}]", self.inputs[5]._solidify())); + } + else { + solidified_wrapped_opcode.push_str(&"ret0"); + + } + }, + Err(_) => { + solidified_wrapped_opcode.push_str(&"ret0"); + } + }; } "CALLCODE" => { - solidified_wrapped_opcode.push_str("ret0"); - } + match U256::from_str(&self.inputs[1]._solidify()) { + Ok(addr) => { + if is_ext_call_precompile(addr) { + solidified_wrapped_opcode.push_str(&format!("memory[{}]", self.inputs[5]._solidify())); + } + else { + solidified_wrapped_opcode.push_str(&"ret0"); + + } + }, + Err(_) => { + solidified_wrapped_opcode.push_str(&"ret0"); + } + }; } "DELEGATECALL" => { - solidified_wrapped_opcode.push_str("ret0"); - } + match U256::from_str(&self.inputs[1]._solidify()) { + Ok(addr) => { + if is_ext_call_precompile(addr) { + solidified_wrapped_opcode.push_str(&format!("memory[{}]", self.inputs[5]._solidify())); + } + else { + solidified_wrapped_opcode.push_str(&"ret0"); + + } + }, + Err(_) => { + solidified_wrapped_opcode.push_str(&"ret0"); + } + }; } "STATICCALL" => { - solidified_wrapped_opcode.push_str("ret0"); - } + match U256::from_str(&self.inputs[1]._solidify()) { + Ok(addr) => { + if is_ext_call_precompile(addr) { + solidified_wrapped_opcode.push_str(&format!("memory[{}]", self.inputs[5]._solidify())); + } + else { + solidified_wrapped_opcode.push_str(&"ret0"); + + } + }, + Err(_) => { + solidified_wrapped_opcode.push_str(&"ret0"); + } + }; } "RETURNDATASIZE" => { - solidified_wrapped_opcode.push_str("ret0.length"); + // TODO + solidified_wrapped_opcode.push_str(&"ret0.length"); } opcode => { diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 8524fd4c..879692a9 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -367,6 +367,27 @@ fn convert_memory_to_variable(line: String) -> String { cleaned } +fn contains_unnecessary_assignment(line: String, lines: &Vec<&String>) -> bool { + + // skip lines that don't contain an assignment + if !line.contains(" = ") { return false; } + + // get var name + let var_name = line.split(" = ").collect::>()[0].split(" ").collect::>()[line.split(" = ").collect::>()[0].split(" ").collect::>().len() - 1]; + + //remove unused vars + for x in lines { + if x.contains(var_name) && !x.trim().starts_with(var_name) { return false; } + else { + + // break if the line contains a function definition + if x.contains("function") { break; } + } + } + + true +} + fn move_casts_to_declaration(line: String) -> String { let cleaned = line; @@ -508,27 +529,6 @@ fn cleanup(line: String) -> String { cleaned } -fn contains_unnecessary_assignment(line: String, lines: &Vec<&String>) -> bool { - - // skip lines that don't contain an assignment - if !line.contains(" = ") { return false; } - - // get var name - let var_name = line.split(" = ").collect::>()[0].split(" ").collect::>()[line.split(" = ").collect::>()[0].split(" ").collect::>().len() - 1]; - - //remove unused vars - for x in lines { - if x.contains(var_name) && !x.trim().starts_with(var_name) { return false; } - else { - - // break if the line contains a function definition - if x.contains("function") { break; } - } - } - - true -} - fn finalize(lines: Vec) -> Vec { let mut cleaned_lines: Vec = Vec::new(); From 5fd9d4de19c76336abd444786fc708947f455cb2 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 9 Nov 2022 12:59:30 -0500 Subject: [PATCH 26/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20removing=20usused?= =?UTF-8?q?=20assignments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/analyze.rs | 6 +++- heimdall/src/decompile/postprocess.rs | 14 ++++++---- heimdall/src/decompile/util.rs | 40 ++++++--------------------- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 9ddbd153..fbb9fae2 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -148,7 +148,7 @@ impl VMTrace { function.logic.push( format!( - "if ({})", + "if ({}) {{", instruction.input_operations[1].solidify() ).to_string() @@ -617,6 +617,10 @@ impl VMTrace { } + if branch_jumped { + function.logic.push("}".to_string()); + } + // recurse into the children of the VMTrace map for (_, child) in self.children.iter().enumerate() { diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 879692a9..13cdc210 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -377,11 +377,15 @@ fn contains_unnecessary_assignment(line: String, lines: &Vec<&String>) -> bool { //remove unused vars for x in lines { - if x.contains(var_name) && !x.trim().starts_with(var_name) { return false; } - else { - - // break if the line contains a function definition - if x.contains("function") { break; } + + // break if the line contains a function definition + if x.contains("function") { break; } + + if x.contains(" = ") { + let assignment = x.split(" = ").collect::>(); + if assignment[1].contains(var_name) { + return false; + } } } diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index c65e43a2..eb254e3b 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -297,7 +297,7 @@ pub fn map_selector( trace_parent: u32, selector: String, entry_point: u64, -) -> (VMTrace, Vec) { +) -> (VMTrace, Vec) { let mut vm = evm.clone(); vm.calldata = selector.clone(); @@ -325,7 +325,7 @@ pub fn recursive_map( evm: &VM, trace: &TraceFactory, trace_parent: u32, - handled_jumpdests: &mut Vec, + handled_jumpdests: &mut Vec, ) -> VMTrace { let mut vm = evm.clone(); @@ -352,26 +352,14 @@ pub fn recursive_map( // the jump was not taken, create a trace for the jump path // only jump if we haven't already traced this destination // TODO: mark as a loop? - if handled_jumpdests.contains( - &format!( - "{}->{}", - &state.last_instruction.instruction, - &(state.last_instruction.inputs[0].as_u128() + 1) - ) - ) { + if handled_jumpdests.contains(&state.last_instruction.instruction) { // pop off the JUMPI - vm_trace.operations.pop(); + //vm_trace.operations.pop(); break; } - handled_jumpdests.push( - format!( - "{}->{}", - &state.last_instruction.instruction, - &(state.last_instruction.inputs[0].as_u128() + 1) - ) - ); + handled_jumpdests.push(state.last_instruction.instruction); // push a new vm trace to the children let mut trace_vm = vm.clone(); @@ -394,26 +382,14 @@ pub fn recursive_map( // the jump was taken, create a trace for the fallthrough path // only jump if we haven't already traced this destination - if handled_jumpdests.contains( - &format!( - "{}->{}", - &state.last_instruction.instruction, - &(state.last_instruction.inputs[0].as_u128() + 1) - ) - ) { + if handled_jumpdests.contains(&state.last_instruction.instruction) { // pop off the JUMPI - vm_trace.operations.pop(); + //vm_trace.operations.pop(); break; } - handled_jumpdests.push( - format!( - "{}->{}", - &state.last_instruction.instruction, - &(state.last_instruction.inputs[0].as_u128() + 1) - ) - ); + handled_jumpdests.push(state.last_instruction.instruction); // push a new vm trace to the children let mut trace_vm = vm.clone(); From a5884aaae42ab505e750e67331775f00049850b1 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:11:31 -0500 Subject: [PATCH 27/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20potential=20branch?= =?UTF-8?q?=20creation=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/Cargo.toml | 2 +- config/Cargo.toml | 2 +- heimdall/Cargo.toml | 2 +- heimdall/scripts/upgrade | 0 heimdall/src/decompile/analyze.rs | 88 ++++++++++++++++++++++++------- heimdall/src/decompile/util.rs | 22 ++++---- 6 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 heimdall/scripts/upgrade diff --git a/common/Cargo.toml b/common/Cargo.toml index 406915e8..e0f76a11 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heimdall-common" -version = "0.1.4" +version = "0.1.7" edition = "2021" license = "MIT" readme = "README.md" diff --git a/config/Cargo.toml b/config/Cargo.toml index d3e3a5c4..bfeef014 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heimdall-config" -version = "0.1.4" +version = "0.1.7" edition = "2021" license = "MIT" readme = "README.md" diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index c38fd9e2..1d9c9e48 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -5,7 +5,7 @@ keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"] license = "MIT" name = "heimdall" readme = "README.md" -version = "0.1.4" +version = "0.1.7" [dependencies] backtrace = "0.3" diff --git a/heimdall/scripts/upgrade b/heimdall/scripts/upgrade new file mode 100644 index 00000000..e69de29b diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index fbb9fae2..1d572ed8 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -33,6 +33,7 @@ impl VMTrace { // make a clone of the recursed analysis function let mut function = function.clone(); let mut branch_jumped = false; + let mut revert_conditional: Option = None; // perform analysis on the operations of the current VMTrace branch for operation in &self.operations { @@ -142,17 +143,23 @@ impl VMTrace { } } else if opcode_name == "JUMPI" { - - // add closing braces to the function's logic - branch_jumped = true; - function.logic.push( - format!( - "if ({}) {{", - - instruction.input_operations[1].solidify() - ).to_string() - ); + // if the JUMPI is not taken and the branch reverts, this is a require statement + if self.operations.last().unwrap().last_instruction.opcode_details.clone().unwrap().name == "REVERT" { + revert_conditional = Some(instruction.input_operations[1].solidify()); + } + else { + + // this is an if conditional for the children branches + function.logic.push( + format!( + "if ({}) {{", + + instruction.input_operations[1].solidify() + ).to_string() + ); + branch_jumped = true; + } } else if opcode_name == "REVERT" { @@ -169,12 +176,12 @@ impl VMTrace { let revert_data = memory.read(offset, size); // (1) if revert_data starts with 0x08c379a0, the folling is an error string abiencoded - // (2) if revert_data starts with any other 4byte selector, it is a custom error and should + // (2) if revert_data starts with 0x4e487b71, the following is a compiler panic + // (3) if revert_data starts with any other 4byte selector, it is a custom error and should // be resolved and added to the generated ABI - // (3) if revert_data is empty, it is an empty revert. Ex: + // (4) if revert_data is empty, it is an empty revert. Ex: // - if (true != false) { revert() }; - // - require(true == false) - + // - require(true != false) let revert_logic; // handle case with error string abiencoded @@ -189,8 +196,26 @@ impl VMTrace { }, None => "".to_string(), }; + revert_logic = match revert_conditional.clone() { + Some(condition) => { + format!( + "require({}, \"{}\");", + condition, + revert_string + ) + } + None => { + format!( + "revert(\"{}\");", + revert_string + ) + } + } + } - revert_logic = format!("revert(\"{}\");", revert_string); + // handle case with panics + else if revert_data.starts_with("4e487b71") { + continue; } // handle case with custom error OR empty revert @@ -202,7 +227,30 @@ impl VMTrace { }, None => "()".to_string(), }; - revert_logic = format!("revert{};", custom_error_placeholder); + + revert_logic = match revert_conditional.clone() { + Some(condition) => { + + if custom_error_placeholder == "()".to_string() { + format!( + "require({});", + condition, + ) + } + else { + format!( + "if (!{}) revert{};", + condition, + custom_error_placeholder + ) + } + } + None => { + + // skip empty reverts with no conditionals + continue; + } + } } function.logic.push(revert_logic); @@ -617,10 +665,6 @@ impl VMTrace { } - if branch_jumped { - function.logic.push("}".to_string()); - } - // recurse into the children of the VMTrace map for (_, child) in self.children.iter().enumerate() { @@ -628,6 +672,10 @@ impl VMTrace { } + if branch_jumped { + function.logic.push("}".to_string()); + } + function } diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index eb254e3b..353553ae 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -297,7 +297,7 @@ pub fn map_selector( trace_parent: u32, selector: String, entry_point: u64, -) -> (VMTrace, Vec) { +) -> (VMTrace, Vec) { let mut vm = evm.clone(); vm.calldata = selector.clone(); @@ -325,7 +325,7 @@ pub fn recursive_map( evm: &VM, trace: &TraceFactory, trace_parent: u32, - handled_jumpdests: &mut Vec, + handled_jumpdests: &mut Vec, ) -> VMTrace { let mut vm = evm.clone(); @@ -352,14 +352,14 @@ pub fn recursive_map( // the jump was not taken, create a trace for the jump path // only jump if we haven't already traced this destination // TODO: mark as a loop? - if handled_jumpdests.contains(&state.last_instruction.instruction) { + if handled_jumpdests.contains(&format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)) { // pop off the JUMPI - //vm_trace.operations.pop(); - break; +// vm_trace.operations.pop(); +// break; } - handled_jumpdests.push(state.last_instruction.instruction); + handled_jumpdests.push(format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)); // push a new vm trace to the children let mut trace_vm = vm.clone(); @@ -378,18 +378,19 @@ pub fn recursive_map( trace_parent, handled_jumpdests, )); + break; } else { // the jump was taken, create a trace for the fallthrough path // only jump if we haven't already traced this destination - if handled_jumpdests.contains(&state.last_instruction.instruction) { + if handled_jumpdests.contains(&format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)) { // pop off the JUMPI - //vm_trace.operations.pop(); - break; +// vm_trace.operations.pop(); +// break; } - handled_jumpdests.push(state.last_instruction.instruction); + handled_jumpdests.push(format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)); // push a new vm trace to the children let mut trace_vm = vm.clone(); @@ -408,6 +409,7 @@ pub fn recursive_map( trace_parent, handled_jumpdests, )); + break; } } From d5034737a61b2530f5089bdea5b7abd34ed7160d Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:30:46 -0500 Subject: [PATCH 28/48] =?UTF-8?q?=F0=9F=91=B7=20build:=20run=20tests=20wit?= =?UTF-8?q?h=20nightly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rust_build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust_build.yml b/.github/workflows/rust_build.yml index 331a5011..8098c6c1 100644 --- a/.github/workflows/rust_build.yml +++ b/.github/workflows/rust_build.yml @@ -19,6 +19,6 @@ jobs: - name: Run Tests working-directory: ./heimdall run: | - cargo test --package heimdall -- test_ --nocapture - cargo test --package heimdall-config -- test_ --nocapture - cargo test --package heimdall-common -- test_ --nocapture \ No newline at end of file + cargo +nightly test --package heimdall -- test_ --nocapture + cargo +nightly test --package heimdall-config -- test_ --nocapture + cargo +nightly test --package heimdall-common -- test_ --nocapture \ No newline at end of file From aab9183a4558c761defcb96640f0c689907d2a9d Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:40:37 -0500 Subject: [PATCH 29/48] =?UTF-8?q?=F0=9F=93=A6=20deps:=20fix=20deps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rust_build.yml | 6 +++--- common/Cargo.toml | 2 +- heimdall/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust_build.yml b/.github/workflows/rust_build.yml index 8098c6c1..331a5011 100644 --- a/.github/workflows/rust_build.yml +++ b/.github/workflows/rust_build.yml @@ -19,6 +19,6 @@ jobs: - name: Run Tests working-directory: ./heimdall run: | - cargo +nightly test --package heimdall -- test_ --nocapture - cargo +nightly test --package heimdall-config -- test_ --nocapture - cargo +nightly test --package heimdall-common -- test_ --nocapture \ No newline at end of file + cargo test --package heimdall -- test_ --nocapture + cargo test --package heimdall-config -- test_ --nocapture + cargo test --package heimdall-common -- test_ --nocapture \ No newline at end of file diff --git a/common/Cargo.toml b/common/Cargo.toml index e0f76a11..f86081de 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -15,6 +15,6 @@ clap-verbosity-flag = "1.0.0" indicatif = "0.17.0" tokio = { version = "1", features = ["full"] } clap = { version = "3.1.18", features = ["derive"] } -ethers = { git = "https://github.com/gakonst/ethers-rs" } reqwest = { version = "0.11.11", features = ["blocking"] } serde_json = "1.0" +ethers = "1.0.0" \ No newline at end of file diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index 1d9c9e48..7b4e7470 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -12,7 +12,6 @@ backtrace = "0.3" colored = "2" clap = { version = "3.1.18", features = ["derive"] } clap-verbosity-flag = "1.0.0" -ethers = { git = "https://github.com/gakonst/ethers-rs" } heimdall-common = { path = "./../common" } heimdall-config = { path = "./../config" } tokio = { version = "1", features = ["full"] } @@ -22,6 +21,7 @@ indicatif = "0.17.0" strsim = "0.10.0" regex = "1.1.5" lazy_static = "1.4.0" +ethers = "1.0.0" [[bin]] name = "heimdall" From 838369c4b967531ced28636a140029f1eb899ea9 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Tue, 22 Nov 2022 16:42:43 -0600 Subject: [PATCH 30/48] =?UTF-8?q?=F0=9F=94=A7=20fix:=20fix=20panic=20messa?= =?UTF-8?q?ges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/Cargo.toml | 4 +- common/src/ether/evm/memory.rs | 7 +--- common/src/ether/evm/stack.rs | 4 ++ common/src/ether/evm/vm.rs | 2 + config/Cargo.toml | 2 +- heimdall/Cargo.toml | 2 +- heimdall/src/decompile/mod.rs | 22 +++++------ heimdall/src/decompile/util.rs | 71 +++++++++++++++------------------- heimdall/src/heimdall.rs | 8 +--- 9 files changed, 54 insertions(+), 68 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index f86081de..debd30f9 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heimdall-common" -version = "0.1.7" +version = "0.1.8" edition = "2021" license = "MIT" readme = "README.md" @@ -17,4 +17,4 @@ tokio = { version = "1", features = ["full"] } clap = { version = "3.1.18", features = ["derive"] } reqwest = { version = "0.11.11", features = ["blocking"] } serde_json = "1.0" -ethers = "1.0.0" \ No newline at end of file +ethers = "1.0.0" diff --git a/common/src/ether/evm/memory.rs b/common/src/ether/evm/memory.rs index ba870480..7209ec7f 100644 --- a/common/src/ether/evm/memory.rs +++ b/common/src/ether/evm/memory.rs @@ -18,6 +18,7 @@ impl Memory { } pub fn extend(&mut self, offset: u128, size: u128) { + // calculate the new size of the memory let r = (offset + size) % 32; let new_mem_size: u128; @@ -65,11 +66,7 @@ impl Memory { } // read a value from the memory at the given offset, with a fixed size - pub fn read(&self, mut offset: usize, size: usize) -> String { - // cap offset to 2**16 for optimization - if offset > 65536 { - offset = 65536; - } + pub fn read(&self, offset: usize, size: usize) -> String { // if the offset + size will be out of bounds, append null bytes until the size is met if offset + size > self.size() as usize { diff --git a/common/src/ether/evm/stack.rs b/common/src/ether/evm/stack.rs index f7e23072..a531cc3c 100644 --- a/common/src/ether/evm/stack.rs +++ b/common/src/ether/evm/stack.rs @@ -90,6 +90,10 @@ impl Stack { values } + pub fn size(&self) -> usize { + self.stack.len() + } + // Check if the stack is empty. pub fn is_empty(&self) -> bool { self.stack.is_empty() diff --git a/common/src/ether/evm/vm.rs b/common/src/ether/evm/vm.rs index 8a9abdea..c227bb3e 100644 --- a/common/src/ether/evm/vm.rs +++ b/common/src/ether/evm/vm.rs @@ -123,6 +123,7 @@ impl VM { // Steps to the next PC and executes the instruction fn _step(&mut self) -> Instruction { + // sanity check if self.bytecode.len() < (self.instruction * 2 + 2) as usize { self.exit(2, "0x"); @@ -1608,6 +1609,7 @@ impl VM { }; } _ => { + // we reached an INVALID opcode, consume all remaining gas self.exit(4, "0x"); return Instruction { diff --git a/config/Cargo.toml b/config/Cargo.toml index bfeef014..c5817f4a 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heimdall-config" -version = "0.1.7" +version = "0.1.8" edition = "2021" license = "MIT" readme = "README.md" diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index 7b4e7470..2fdce491 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -5,7 +5,7 @@ keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"] license = "MIT" name = "heimdall" readme = "README.md" -version = "0.1.7" +version = "0.1.8" [dependencies] backtrace = "0.3" diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index dbd3f09c..c7816de0 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -237,7 +237,7 @@ pub fn decompile(args: DecompilerArgs) { let vm_trace = trace.add_creation(decompile_call, line!(), "contract".to_string(), shortened_target, (contract_bytecode.len()/2usize).try_into().unwrap()); // find and resolve all selectors in the bytecode - let selectors = find_function_selectors(&evm.clone(), disassembled_bytecode); + let selectors = find_function_selectors(disassembled_bytecode); let mut resolved_selectors = HashMap::new(); if !args.skip_resolving { @@ -245,7 +245,7 @@ pub fn decompile(args: DecompilerArgs) { logger.info(&format!("resolved {} possible functions from {} detected selectors.", resolved_selectors.len(), selectors.len()).to_string()); } else { - logger.info(&format!("found {} function selectors.", selectors.len()).to_string()); + logger.info(&format!("found {} possible function selectors.", selectors.len()).to_string()); } logger.info(&format!("performing symbolic execution on '{}' .", &args.target).to_string()); @@ -258,6 +258,13 @@ pub fn decompile(args: DecompilerArgs) { for selector in selectors.clone() { decompilation_progress.set_message(format!("executing '0x{}'", selector)); + // get the function's entry point + let function_entry_point = resolve_entry_point(&evm.clone(), selector.clone()); + + if function_entry_point == 0 { + continue; + } + let func_analysis_trace = trace.add_call( vm_trace, line!(), @@ -267,23 +274,12 @@ pub fn decompile(args: DecompilerArgs) { "()".to_string() ); - // get the function's entry point - let function_entry_point = resolve_entry_point(&evm.clone(), selector.clone()); trace.add_info( func_analysis_trace, function_entry_point.try_into().unwrap(), format!("discovered entry point: {}", function_entry_point).to_string() ); - if function_entry_point == 0 { - trace.add_error( - func_analysis_trace, - line!(), - "selector flagged as false-positive.".to_string() - ); - continue; - } - // get a map of possible jump destinations let (map, jumpdests) = map_selector(&evm.clone(), &trace, func_analysis_trace, selector.clone(), function_entry_point); trace.add_debug( diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 353553ae..30970949 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -1,9 +1,7 @@ use std::{collections::HashMap, str::FromStr}; use ethers::{ - abi::AbiEncode, prelude::{ - rand::{self, Rng}, U256, }, }; @@ -211,44 +209,24 @@ pub fn detect_compiler(bytecode: String) -> (String, String) { } // find all function selectors in the given EVM. -pub fn find_function_selectors(evm: &VM, assembly: String) -> Vec { +pub fn find_function_selectors(assembly: String) -> Vec { let mut function_selectors = Vec::new(); - let mut vm = evm.clone(); - - // find a selector not present in the assembly - let selector; - loop { - let num = rand::thread_rng().gen_range(286331153..2147483647); - if !vm - .bytecode - .contains(&format!("63{}", num.encode_hex()[58..].to_string())) - { - selector = num.encode_hex()[58..].to_string(); - break; - } - } - - // execute the EVM call to find the dispatcher revert - let dispatcher_revert = vm.call(selector, 0).instruction - 1; - - // search through assembly for PUSH4 instructions up until the dispatcher revert + // search through assembly for PUSH4 instructions, optimistically assuming that they are function selectors let assembly: Vec = assembly .split("\n") .map(|line| line.trim().to_string()) .collect(); for line in assembly.iter() { let instruction_args: Vec = line.split(" ").map(|arg| arg.to_string()).collect(); - let program_counter: u128 = instruction_args[0].clone().parse().unwrap(); - let instruction = instruction_args[1].clone(); - if program_counter < dispatcher_revert { + if instruction_args.len() >= 2 { + let instruction = instruction_args[1].clone(); + if instruction == "PUSH4" { let function_selector = instruction_args[2].clone(); function_selectors.push(function_selector); } - } else { - break; } } function_selectors.sort(); @@ -316,7 +294,7 @@ pub fn map_selector( // the VM is at the function entry point, begin tracing let mut handled_jumpdests = Vec::new(); ( - recursive_map(&vm.clone(), trace, trace_parent, &mut handled_jumpdests), + recursive_map(&vm.clone(), trace, trace_parent, &mut handled_jumpdests, 0, U256::from(0)), handled_jumpdests, ) } @@ -326,6 +304,8 @@ pub fn recursive_map( trace: &TraceFactory, trace_parent: u32, handled_jumpdests: &mut Vec, + last_jumpi: u128, + last_jumpdest: U256 ) -> VMTrace { let mut vm = evm.clone(); @@ -346,17 +326,18 @@ pub fn recursive_map( if state.last_instruction.opcode == "57" { vm_trace.depth += 1; + println!("{}, JUMPI: {}", state.last_instruction.instruction, state.last_instruction.inputs[0]); + // we need to create a trace for the path that wasn't taken. if state.last_instruction.inputs[1] == U256::from(0) { - // the jump was not taken, create a trace for the jump path - // only jump if we haven't already traced this destination - // TODO: mark as a loop? - if handled_jumpdests.contains(&format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)) { + // break out of and mark loops + if last_jumpdest == state.last_instruction.inputs[0] && + last_jumpi == state.last_instruction.instruction { // pop off the JUMPI -// vm_trace.operations.pop(); -// break; + vm_trace.operations.pop(); + break; } handled_jumpdests.push(format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)); @@ -369,6 +350,8 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, + state.last_instruction.instruction, + state.last_instruction.inputs[0], )); // push the current path onto the stack @@ -377,17 +360,19 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, + state.last_instruction.instruction, + state.last_instruction.inputs[0], )); break; } else { - // the jump was taken, create a trace for the fallthrough path - // only jump if we haven't already traced this destination - if handled_jumpdests.contains(&format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)) { - + // break out of and mark loops + if last_jumpdest == state.last_instruction.inputs[0] && + last_jumpi == state.last_instruction.instruction { + // pop off the JUMPI -// vm_trace.operations.pop(); -// break; + vm_trace.operations.pop(); + break; } handled_jumpdests.push(format!("{}@{}", vm_trace.depth, state.last_instruction.instruction)); @@ -400,6 +385,8 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, + state.last_instruction.instruction, + state.last_instruction.inputs[0], )); // push the current path onto the stack @@ -408,11 +395,15 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, + state.last_instruction.instruction, + state.last_instruction.inputs[0], )); break; } } + println!("{} {}", state.last_instruction.instruction, state.last_instruction.opcode); + if vm.exitcode != 255 || vm.returndata.len() > 0 { break; } diff --git a/heimdall/src/heimdall.rs b/heimdall/src/heimdall.rs index 61c3f977..5b4534fe 100644 --- a/heimdall/src/heimdall.rs +++ b/heimdall/src/heimdall.rs @@ -57,12 +57,8 @@ fn main() { let (logger, _)= Logger::new("TRACE"); logger.fatal( &format!( - "thread 'main' encountered a fatal error: '{}' at '/src/{}:{}'!", - panic_info.to_string().split("'").collect::>()[1] - .to_lowercase().bright_white().on_bright_red().bold(), - panic_info.location().unwrap().file().split("/src/") - .collect::>()[1], - panic_info.location().unwrap().line() + "thread 'main' encountered a fatal error: '{}'!", + panic_info.to_string().bright_white().on_bright_red().bold(), ) ); logger.fatal(&format!("Stack Trace:\n\n{:#?}", backtrace)); From e42e27f989c7c2404b928ee26690495dc6b759e0 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 23 Nov 2022 12:08:22 -0600 Subject: [PATCH 31/48] =?UTF-8?q?=F0=9F=93=A6=20deps:=20update=20dependenc?= =?UTF-8?q?ies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/Cargo.toml | 3 ++- config/Cargo.toml | 1 + heimdall/Cargo.toml | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index debd30f9..3380f9fc 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"] [dependencies] colored = "2" -regex = "1.1.5" +fancy-regex = "0.10.0" lazy_static = "1.4.0" clap-verbosity-flag = "1.0.0" indicatif = "0.17.0" @@ -18,3 +18,4 @@ clap = { version = "3.1.18", features = ["derive"] } reqwest = { version = "0.11.11", features = ["blocking"] } serde_json = "1.0" ethers = "1.0.0" + diff --git a/config/Cargo.toml b/config/Cargo.toml index c5817f4a..d0633c84 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -13,3 +13,4 @@ clap-verbosity-flag = "1.0.0" clap = { version = "3.1.18", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } toml = { version = "0.5.9" } + diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index 2fdce491..a35c77cb 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -19,10 +19,10 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } indicatif = "0.17.0" strsim = "0.10.0" -regex = "1.1.5" +fancy-regex = "0.10.0" lazy_static = "1.4.0" ethers = "1.0.0" [[bin]] name = "heimdall" -path = "src/heimdall.rs" \ No newline at end of file +path = "src/heimdall.rs" From d18cf3fca0b967246f45781e6be71e6fe1cc9343 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 23 Nov 2022 12:19:11 -0600 Subject: [PATCH 32/48] =?UTF-8?q?=E2=9A=A1=20perf:=20break=20out=20of=20lo?= =?UTF-8?q?ops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++- common/src/constants.rs | 2 +- common/src/ether/evm/disassemble.rs | 6 ++-- common/src/ether/evm/types.rs | 2 +- common/src/ether/solidity.rs | 4 +-- heimdall/scripts/upgrade | 0 heimdall/src/decode/mod.rs | 2 +- heimdall/src/decompile/analyze.rs | 2 +- heimdall/src/decompile/constants.rs | 5 ++- heimdall/src/decompile/mod.rs | 6 ++-- heimdall/src/decompile/postprocess.rs | 8 ++--- heimdall/src/decompile/util.rs | 49 ++++++++++++++------------- 12 files changed, 50 insertions(+), 41 deletions(-) delete mode 100644 heimdall/scripts/upgrade diff --git a/.gitignore b/.gitignore index 6ae27340..2a16999e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ output output/* */output *.evm -*.asm \ No newline at end of file +*.asm + +heimdall/scripts/update +heimdall/scripts/git \ No newline at end of file diff --git a/common/src/constants.rs b/common/src/constants.rs index 3efa01c0..92170c52 100644 --- a/common/src/constants.rs +++ b/common/src/constants.rs @@ -1,4 +1,4 @@ -use regex::Regex; +use fancy_regex::Regex; use lazy_static::lazy_static; lazy_static! { diff --git a/common/src/ether/evm/disassemble.rs b/common/src/ether/evm/disassemble.rs index 1ff2ad1e..0ea7b2c8 100644 --- a/common/src/ether/evm/disassemble.rs +++ b/common/src/ether/evm/disassemble.rs @@ -66,7 +66,7 @@ pub fn disassemble(args: DisassemblerArgs) -> String { } let contract_bytecode: String; - if ADDRESS_REGEX.is_match(&args.target) { + if ADDRESS_REGEX.is_match(&args.target).unwrap() { // push the address to the output directory if &output_dir != &args.output { @@ -118,7 +118,7 @@ pub fn disassemble(args: DisassemblerArgs) -> String { }); } - else if BYTECODE_REGEX.is_match(&args.target) { + else if BYTECODE_REGEX.is_match(&args.target).unwrap() { contract_bytecode = args.target.clone(); } else { @@ -131,7 +131,7 @@ pub fn disassemble(args: DisassemblerArgs) -> String { // We are disassembling a file, so we need to read the bytecode from the file. contract_bytecode = match fs::read_to_string(&args.target) { Ok(contents) => { - if BYTECODE_REGEX.is_match(&contents) && contents.len() % 2 == 0 { + if BYTECODE_REGEX.is_match(&contents).unwrap() && contents.len() % 2 == 0 { contents.replacen("0x", "", 1) } else { diff --git a/common/src/ether/evm/types.rs b/common/src/ether/evm/types.rs index ef857552..de11fddb 100644 --- a/common/src/ether/evm/types.rs +++ b/common/src/ether/evm/types.rs @@ -237,7 +237,7 @@ pub fn byte_size_to_type(byte_size: usize) -> (usize, Vec) { pub fn find_cast(line: String) -> (usize, usize, Option) { // find the start of the cast - match TYPE_CAST_REGEX.find(&line) { + match TYPE_CAST_REGEX.find(&line).unwrap() { Some(m) => { let start = m.start(); let end = m.end() - 1; diff --git a/common/src/ether/solidity.rs b/common/src/ether/solidity.rs index b7856efa..dc2e02ff 100644 --- a/common/src/ether/solidity.rs +++ b/common/src/ether/solidity.rs @@ -283,7 +283,7 @@ impl WrappedOpcode { let solidified_slot = self.inputs[0]._solidify(); // are dealing with a slot that is a constant, we can just use the slot directly - if WORD_REGEX.is_match(&solidified_slot) { + if WORD_REGEX.is_match(&solidified_slot).unwrap() { // convert to usize match usize::from_str_radix( @@ -407,7 +407,7 @@ impl WrappedOpcode { ); } else { - match MEMLEN_REGEX.find(&format!("memory[{}]", memloc)) { + match MEMLEN_REGEX.find(&format!("memory[{}]", memloc)).unwrap() { Some(_) => { solidified_wrapped_opcode.push_str( format!( diff --git a/heimdall/scripts/upgrade b/heimdall/scripts/upgrade deleted file mode 100644 index e69de29b..00000000 diff --git a/heimdall/src/decode/mod.rs b/heimdall/src/decode/mod.rs index dc176f93..593b82d4 100644 --- a/heimdall/src/decode/mod.rs +++ b/heimdall/src/decode/mod.rs @@ -53,7 +53,7 @@ pub fn decode(args: DecodeArgs) { let calldata: String; // determine whether or not the target is a transaction hash - if TRANSACTION_HASH_REGEX.is_match(&args.target) { + if TRANSACTION_HASH_REGEX.is_match(&args.target).unwrap() { // create new runtime block let rt = tokio::runtime::Builder::new_current_thread() diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 1d572ed8..32bf7a3d 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -281,7 +281,7 @@ impl VMTrace { false => { // attempt to find a return type within the return memory operations - let byte_size = match AND_BITMASK_REGEX.find(&return_memory_operations_solidified) { + let byte_size = match AND_BITMASK_REGEX.find(&return_memory_operations_solidified).unwrap() { Some(bitmask) => { let cast = bitmask.as_str(); diff --git a/heimdall/src/decompile/constants.rs b/heimdall/src/decompile/constants.rs index 745f7afb..28b92290 100644 --- a/heimdall/src/decompile/constants.rs +++ b/heimdall/src/decompile/constants.rs @@ -1,4 +1,4 @@ -use regex::Regex; +use fancy_regex::Regex; use lazy_static::lazy_static; @@ -17,6 +17,9 @@ lazy_static! { // detects a memory access pub static ref MEM_ACCESS_REGEX: Regex = Regex::new(r"memory\[.*\]").unwrap(); + // detects repeated jumps, indicating a loop + pub static ref LOOP_DETECTION_REGEX: Regex = Regex::new(r"((\d*->\d*;)+?)\1+").unwrap(); + pub static ref DECOMPILED_SOURCE_HEADER: String = "// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index c7816de0..b79e3557 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -105,7 +105,7 @@ pub fn decompile(args: DecompilerArgs) { } let contract_bytecode: String; - if ADDRESS_REGEX.is_match(&args.target) { + if ADDRESS_REGEX.is_match(&args.target).unwrap() { // push the address to the output directory if &output_dir != &args.output { @@ -157,7 +157,7 @@ pub fn decompile(args: DecompilerArgs) { }); } - else if BYTECODE_REGEX.is_match(&args.target) { + else if BYTECODE_REGEX.is_match(&args.target).unwrap() { contract_bytecode = args.target.clone(); } else { @@ -170,7 +170,7 @@ pub fn decompile(args: DecompilerArgs) { // We are decompiling a file, so we need to read the bytecode from the file. contract_bytecode = match fs::read_to_string(&args.target) { Ok(contents) => { - if BYTECODE_REGEX.is_match(&contents) && contents.len() % 2 == 0 { + if BYTECODE_REGEX.is_match(&contents).unwrap() && contents.len() % 2 == 0 { contents.replacen("0x", "", 1) } else { diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 13cdc210..3a1332bb 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -16,7 +16,7 @@ lazy_static! { fn convert_bitmask_to_casting(line: String) -> String { let mut cleaned = line; - match AND_BITMASK_REGEX.find(&cleaned) { + match AND_BITMASK_REGEX.find(&cleaned).unwrap() { Some(bitmask) => { let cast = bitmask.as_str(); let cast_size = NON_ZERO_BYTE_REGEX.find_iter(&cast).count(); @@ -65,7 +65,7 @@ fn convert_bitmask_to_casting(line: String) -> String { }, None => { - match AND_BITMASK_REGEX_2.find(&cleaned) { + match AND_BITMASK_REGEX_2.find(&cleaned).unwrap() { Some(bitmask) => { let cast = bitmask.as_str(); let cast_size = NON_ZERO_BYTE_REGEX.find_iter(&cast).count(); @@ -311,7 +311,7 @@ fn convert_memory_to_variable(line: String) -> String { } // find a memory access - let memory_access = match MEM_ACCESS_REGEX.find(&cleaned) { + let memory_access = match MEM_ACCESS_REGEX.find(&cleaned).unwrap() { Some(x) => x.as_str(), None => return cleaned, }; @@ -401,7 +401,7 @@ fn move_casts_to_declaration(line: String) -> String { let instantiation = cleaned.split(" = ").collect::>(); // get the outermost cast - match TYPE_CAST_REGEX.find(&instantiation[1]) { + match TYPE_CAST_REGEX.find(&instantiation[1]).unwrap() { Some(x) => { // the match must occur at index 0 diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 30970949..c87c6e84 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -16,6 +16,8 @@ use heimdall_common::{ io::logging::{TraceFactory}, }; +use crate::decompile::constants::LOOP_DETECTION_REGEX; + #[derive(Clone, Debug)] pub struct Function { // the function's 4byte selector @@ -294,7 +296,13 @@ pub fn map_selector( // the VM is at the function entry point, begin tracing let mut handled_jumpdests = Vec::new(); ( - recursive_map(&vm.clone(), trace, trace_parent, &mut handled_jumpdests, 0, U256::from(0)), + recursive_map( + &vm.clone(), + trace, + trace_parent, + &mut handled_jumpdests, + String::new() + ), handled_jumpdests, ) } @@ -304,8 +312,7 @@ pub fn recursive_map( trace: &TraceFactory, trace_parent: u32, handled_jumpdests: &mut Vec, - last_jumpi: u128, - last_jumpdest: U256 + path: String, ) -> VMTrace { let mut vm = evm.clone(); @@ -326,15 +333,15 @@ pub fn recursive_map( if state.last_instruction.opcode == "57" { vm_trace.depth += 1; - println!("{}, JUMPI: {}", state.last_instruction.instruction, state.last_instruction.inputs[0]); - // we need to create a trace for the path that wasn't taken. if state.last_instruction.inputs[1] == U256::from(0) { - // break out of and mark loops - if last_jumpdest == state.last_instruction.inputs[0] && - last_jumpi == state.last_instruction.instruction { - + // break out of loops + if LOOP_DETECTION_REGEX.is_match( + &format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]) + ).unwrap() { + println!("Loop detected, breaking out of trace."); + // pop off the JUMPI vm_trace.operations.pop(); break; @@ -350,8 +357,7 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - state.last_instruction.instruction, - state.last_instruction.inputs[0], + format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), )); // push the current path onto the stack @@ -360,16 +366,17 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - state.last_instruction.instruction, - state.last_instruction.inputs[0], + format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), )); break; } else { - // break out of and mark loops - if last_jumpdest == state.last_instruction.inputs[0] && - last_jumpi == state.last_instruction.instruction { - + // break out of loops + if LOOP_DETECTION_REGEX.is_match( + &format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]) + ).unwrap() { + println!("Loop detected, breaking out of trace."); + // pop off the JUMPI vm_trace.operations.pop(); break; @@ -385,8 +392,7 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - state.last_instruction.instruction, - state.last_instruction.inputs[0], + format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), )); // push the current path onto the stack @@ -395,15 +401,12 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - state.last_instruction.instruction, - state.last_instruction.inputs[0], + format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), )); break; } } - println!("{} {}", state.last_instruction.instruction, state.last_instruction.opcode); - if vm.exitcode != 255 || vm.returndata.len() > 0 { break; } From 2b2ec7647847ee2a7f58ebf54400b99eb2ce96c1 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 30 Nov 2022 09:28:23 -0500 Subject: [PATCH 33/48] =?UTF-8?q?=E2=9A=A1=20perf:=20remove=20blank=20cond?= =?UTF-8?q?itionals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/analyze.rs | 13 +++++++++- heimdall/src/decompile/mod.rs | 4 ++-- heimdall/src/decompile/output.rs | 9 ++++--- heimdall/src/decompile/postprocess.rs | 14 +++++++---- heimdall/src/decompile/util.rs | 34 +++++++++++---------------- 5 files changed, 44 insertions(+), 30 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 32bf7a3d..d425640b 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -589,6 +589,9 @@ impl VMTrace { None => {} }; } + else { + //function.logic.push(format!("{} not implemented", opcode_name)); + } // handle type heuristics if [ @@ -673,7 +676,15 @@ impl VMTrace { } if branch_jumped { - function.logic.push("}".to_string()); + + // if the last line is an if statement, this branch is empty and probably stack operations we don't care about + if function.logic.last().unwrap().contains("if") { + function.logic.pop(); + } + else { + println!("{}", function.logic.last().unwrap()); + function.logic.push("}".to_string()); + } } function diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index b79e3557..3e3f66af 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -430,7 +430,7 @@ pub fn decompile(args: DecompilerArgs) { // resolve custom error signatures let mut resolved_counter = 0; for (error_selector, _) in analyzed_function.errors.clone() { - decompilation_progress.set_message(format!("resolving error 0x{}", &error_selector)); + decompilation_progress.set_message(format!("resolving error '0x{}'", &error_selector)); let resolved_error_selectors = resolve_error_signature(&error_selector); // only continue if we have matches @@ -478,7 +478,7 @@ pub fn decompile(args: DecompilerArgs) { // resolve custom event signatures resolved_counter = 0; for (event_selector, (_, raw_event)) in analyzed_function.events.clone() { - decompilation_progress.set_message(format!("resolving event 0x{}", &event_selector.get(0..8).unwrap().to_string())); + decompilation_progress.set_message(format!("resolving event '0x{}'", &event_selector.get(0..8).unwrap().to_string())); let resolved_event_selectors = resolve_event_signature(&event_selector.get(0..8).unwrap().to_string()); // only continue if we have matches diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 587e97c6..4aeb0f5c 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -377,12 +377,15 @@ pub fn build_output( decompiled_output.push(String::from("}")); - progress_bar.finish_and_clear(); - write_lines_to_file( &decompiled_output_path, - postprocess(decompiled_output) + postprocess( + decompiled_output, + &progress_bar + ) ); + progress_bar.finish_and_clear(); + logger.success(&format!("wrote decompiled contract to '{}' .", &decompiled_output_path).to_string()); } \ No newline at end of file diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 3a1332bb..c0db6d3c 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -3,6 +3,7 @@ use std::{ collections::HashMap }; use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards, base26_encode}, constants::TYPE_CAST_REGEX}; +use indicatif::ProgressBar; use crate::decompile::constants::{ENCLOSED_EXPRESSION_REGEX}; use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX, MEM_ACCESS_REGEX}}; use lazy_static::lazy_static; @@ -533,11 +534,13 @@ fn cleanup(line: String) -> String { cleaned } -fn finalize(lines: Vec) -> Vec { +fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { let mut cleaned_lines: Vec = Vec::new(); // remove unused assignments for (i, line) in lines.iter().enumerate() { + // update progress bar + bar.set_message(format!("finalizing line {}/{}", i, lines.len())); // only pass in lines further than the current line if !contains_unnecessary_assignment(line.trim().to_string(), &lines[i..].iter().collect::>()) @@ -549,12 +552,15 @@ fn finalize(lines: Vec) -> Vec { cleaned_lines } -pub fn postprocess(lines: Vec) -> Vec { +pub fn postprocess(lines: Vec, bar: &ProgressBar) -> Vec { let mut indentation: usize = 0; let mut cleaned_lines: Vec = lines.clone(); // clean up each line using postprocessing techniques - for line in cleaned_lines.iter_mut() { + for (i, line) in cleaned_lines.iter_mut().enumerate() { + + // update progress bar + bar.set_message(format!("cleaning up line {}/{}", i, lines.len())); // dedent due to closing braces if line.starts_with("}") { @@ -576,5 +582,5 @@ pub fn postprocess(lines: Vec) -> Vec { } // run finalizing postprocessing, which need to operate on cleaned lines - finalize(cleaned_lines) + finalize(cleaned_lines, bar) } \ No newline at end of file diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index c87c6e84..4e687cb5 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -116,6 +116,7 @@ pub struct VMTrace { pub instruction: u128, pub operations: Vec, pub children: Vec, + pub loop_detected: bool, pub depth: usize, } @@ -301,7 +302,7 @@ pub fn map_selector( trace, trace_parent, &mut handled_jumpdests, - String::new() + &mut String::new() ), handled_jumpdests, ) @@ -312,7 +313,7 @@ pub fn recursive_map( trace: &TraceFactory, trace_parent: u32, handled_jumpdests: &mut Vec, - path: String, + path: &mut String, ) -> VMTrace { let mut vm = evm.clone(); @@ -321,6 +322,7 @@ pub fn recursive_map( instruction: vm.instruction, operations: Vec::new(), children: Vec::new(), + loop_detected: false, depth: 0, }; @@ -333,17 +335,14 @@ pub fn recursive_map( if state.last_instruction.opcode == "57" { vm_trace.depth += 1; + path.push_str(&format!("{}->{};", state.last_instruction.instruction, state.last_instruction.inputs[0])); + // we need to create a trace for the path that wasn't taken. if state.last_instruction.inputs[1] == U256::from(0) { // break out of loops - if LOOP_DETECTION_REGEX.is_match( - &format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]) - ).unwrap() { - println!("Loop detected, breaking out of trace."); - - // pop off the JUMPI - vm_trace.operations.pop(); + if LOOP_DETECTION_REGEX.is_match(&path).unwrap() { + vm_trace.loop_detected = true; break; } @@ -357,7 +356,7 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), + &mut path.clone() )); // push the current path onto the stack @@ -366,19 +365,14 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), + &mut path.clone() )); break; } else { // break out of loops - if LOOP_DETECTION_REGEX.is_match( - &format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]) - ).unwrap() { - println!("Loop detected, breaking out of trace."); - - // pop off the JUMPI - vm_trace.operations.pop(); + if LOOP_DETECTION_REGEX.is_match(&path).unwrap() { + vm_trace.loop_detected = true; break; } @@ -392,7 +386,7 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), + &mut path.clone() )); // push the current path onto the stack @@ -401,7 +395,7 @@ pub fn recursive_map( trace, trace_parent, handled_jumpdests, - format!("{}{}->{};", path, state.last_instruction.instruction, state.last_instruction.inputs[0]), + &mut path.clone() )); break; } From 7423eb93befebee87f2e17745d5b702b224c6bad Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 30 Nov 2022 09:30:53 -0500 Subject: [PATCH 34/48] =?UTF-8?q?=E2=9C=85=20tests:=20fix=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/analyze.rs | 4 +++- heimdall/src/decompile/tests.rs | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index d425640b..d09d9ea1 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -151,6 +151,9 @@ impl VMTrace { else { // this is an if conditional for the children branches + + // check if there is a conditional before this as well. combine them if so + function.logic.push( format!( "if ({}) {{", @@ -682,7 +685,6 @@ impl VMTrace { function.logic.pop(); } else { - println!("{}", function.logic.last().unwrap()); function.logic.push("}".to_string()); } } diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index c9473068..37cd2ccd 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -46,6 +46,8 @@ mod benchmark { #[cfg(test)] mod postprocess_tests { + use indicatif::ProgressBar; + use crate::decompile::postprocess::postprocess; #[test] @@ -54,7 +56,7 @@ mod postprocess_tests { String::from("(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);"), ]; - assert_eq!(postprocess(lines), vec![String::from("uint256(arg0);")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); } #[test] @@ -63,7 +65,7 @@ mod postprocess_tests { String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);"), ]; - assert_eq!(postprocess(lines), vec![String::from("uint256(arg0);")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); } #[test] @@ -72,7 +74,7 @@ mod postprocess_tests { String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00);"), ]; - assert_eq!(postprocess(lines), vec![String::from("uint248(arg0);")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint248(arg0);")]); } #[test] @@ -81,7 +83,7 @@ mod postprocess_tests { String::from("uint256(uint256(arg0));"), ]; - assert_eq!(postprocess(lines), vec![String::from("uint256(arg0);")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); } #[test] @@ -90,7 +92,7 @@ mod postprocess_tests { String::from("ecrecover(uint256(uint256(arg0)), uint256(uint256(arg0)), uint256(uint256(uint256(arg0))));"), ]; - assert_eq!(postprocess(lines), vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")]); } #[test] @@ -99,7 +101,7 @@ mod postprocess_tests { String::from("if (iszero(arg0)) {"), ]; - assert_eq!(postprocess(lines), vec![String::from("if (!arg0) {")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (!arg0) {")]); } #[test] @@ -108,7 +110,7 @@ mod postprocess_tests { String::from("if (iszero(iszero(arg0))) {"), ]; - assert_eq!(postprocess(lines), vec![String::from("if (arg0) {")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (arg0) {")]); } #[test] @@ -117,7 +119,7 @@ mod postprocess_tests { String::from("if (iszero(iszero(iszero(arg0)))) {"), ]; - assert_eq!(postprocess(lines), vec![String::from("if (!arg0) {")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (!arg0) {")]); } #[test] @@ -126,7 +128,7 @@ mod postprocess_tests { String::from("((arg0))"), ]; - assert_eq!(postprocess(lines), vec![String::from("arg0")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("arg0")]); } #[test] @@ -135,7 +137,7 @@ mod postprocess_tests { String::from("if ((cast(((arg0) + 1) / 10))) {"), ]; - assert_eq!(postprocess(lines), vec![String::from("if (cast(arg0 + 1 / 10)) {")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (cast(arg0 + 1 / 10)) {")]); } #[test] @@ -144,6 +146,6 @@ mod postprocess_tests { String::from("if (((((((((((((((cast(((((((((((arg0 * (((((arg1))))))))))))) + 1)) / 10)))))))))))))))) {"), ]; - assert_eq!(postprocess(lines), vec![String::from("if (cast((arg0 * (arg1)) + 1 / 10)) {")]); + assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (cast((arg0 * (arg1)) + 1 / 10)) {")]); } } \ No newline at end of file From bc6fb6c1a655a4cb569e34eada6b6bcff5c3b75a Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 30 Nov 2022 09:42:37 -0500 Subject: [PATCH 35/48] =?UTF-8?q?=E2=9A=A1=20perf:=20combine=20conditional?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/analyze.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index d09d9ea1..d1db0abc 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -15,7 +15,7 @@ use heimdall_common::{ }, }, io::logging::TraceFactory, - utils::strings::{decode_hex, encode_hex_reduced}, + utils::strings::{decode_hex, encode_hex_reduced, find_balanced_encapsulator}, }; use super::{util::*, precompile::decode_precompile, constants::AND_BITMASK_REGEX}; @@ -153,7 +153,19 @@ impl VMTrace { // this is an if conditional for the children branches // check if there is a conditional before this as well. combine them if so - + if let Some(last) = function.logic.last_mut() { + if last.starts_with("if") { + let slice = find_balanced_encapsulator(last.to_string(), ('(', ')')); + if slice.2 { + + // combine the two conditionals + *last = format!("if ({}) {{", format!("({}) && ({})", last.get(slice.0..slice.1).unwrap(), instruction.input_operations[1].solidify())); + + continue; + } + } + } + function.logic.push( format!( "if ({}) {{", From 325908e74a44ec450388a71ddf001c66e6d5edc0 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 30 Nov 2022 09:56:57 -0500 Subject: [PATCH 36/48] =?UTF-8?q?=E2=9A=A1=20perf:=20payable=20detection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/analyze.rs | 15 ++++++++++++--- heimdall/src/decompile/mod.rs | 2 +- heimdall/src/decompile/output.rs | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index d1db0abc..230969b8 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -151,6 +151,7 @@ impl VMTrace { else { // this is an if conditional for the children branches + let conditional = instruction.input_operations[1].solidify(); // check if there is a conditional before this as well. combine them if so if let Some(last) = function.logic.last_mut() { @@ -159,18 +160,26 @@ impl VMTrace { if slice.2 { // combine the two conditionals - *last = format!("if ({}) {{", format!("({}) && ({})", last.get(slice.0..slice.1).unwrap(), instruction.input_operations[1].solidify())); + *last = format!("if ({}) {{", format!("({}) && ({})", last.get(slice.0..slice.1).unwrap(), conditional)); continue; } } } + // check if this if statement is added by the compiler + if conditional == "!msg.value" { + + // this is marking the start of a non-payable function + function.payable = false; + continue; + } + function.logic.push( format!( "if ({}) {{", - instruction.input_operations[1].solidify() + conditional ).to_string() ); branch_jumped = true; @@ -605,7 +614,7 @@ impl VMTrace { }; } else { - //function.logic.push(format!("{} not implemented", opcode_name)); + function.logic.push(format!("{} not implemented", opcode_name)); } // handle type heuristics diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 3e3f66af..65f32afe 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -311,7 +311,7 @@ pub fn decompile(args: DecompilerArgs) { resolved_function: None, pure: true, view: true, - payable: false, + payable: true, }, &mut trace, func_analysis_trace, diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 4aeb0f5c..6e04f36a 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -299,7 +299,7 @@ pub fn build_output( if function.pure { "pure " } else if function.view { "view " } else { "" }, - if function.payable { "payable" } + if function.payable { "payable " } else { "" }, ); let function_returns = format!( From 6c6ad2241fed06a229429253fc7a73eb63e58279 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:55:29 -0500 Subject: [PATCH 37/48] :zap: perf: multithreading function resolving --- heimdall/src/decompile/analyze.rs | 37 +++++++++++++++++++------------ heimdall/src/decompile/resolve.rs | 36 +++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 230969b8..6c2fc05b 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -154,23 +154,31 @@ impl VMTrace { let conditional = instruction.input_operations[1].solidify(); // check if there is a conditional before this as well. combine them if so - if let Some(last) = function.logic.last_mut() { - if last.starts_with("if") { - let slice = find_balanced_encapsulator(last.to_string(), ('(', ')')); - if slice.2 { + // if let Some(last) = function.logic.last_mut() { + // if last.starts_with("if") { + // let slice = find_balanced_encapsulator(last.to_string(), ('(', ')')); + // if slice.2 { - // combine the two conditionals - *last = format!("if ({}) {{", format!("({}) && ({})", last.get(slice.0..slice.1).unwrap(), conditional)); + // // combine the two conditionals + // *last = format!("if ({}) {{", format!("({}) && ({})", last.get(slice.0..slice.1).unwrap(), conditional)); - continue; - } - } - } + // continue; + // } + // } + // } // check if this if statement is added by the compiler if conditional == "!msg.value" { // this is marking the start of a non-payable function + trace.add_info( + trace_parent, + instruction.instruction.try_into().unwrap(), + format!( + "conditional at instruction {} indicates an non-payble function.", + instruction.instruction + ), + ); function.payable = false; continue; } @@ -270,9 +278,10 @@ impl VMTrace { } } None => { - - // skip empty reverts with no conditionals - continue; + format!( + "revert{};", + custom_error_placeholder + ) } } } @@ -614,7 +623,7 @@ impl VMTrace { }; } else { - function.logic.push(format!("{} not implemented", opcode_name)); + //function.logic.push(format!("{} not implemented", opcode_name)); } // handle type heuristics diff --git a/heimdall/src/decompile/resolve.rs b/heimdall/src/decompile/resolve.rs index c78ec866..db7e86d6 100644 --- a/heimdall/src/decompile/resolve.rs +++ b/heimdall/src/decompile/resolve.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, time::Duration}; - +use std::sync::{Arc, Mutex}; +use std::thread; use heimdall_common::{ ether::signatures::{resolve_function_signature, ResolvedFunction}, io::logging::Logger, @@ -13,24 +14,39 @@ pub fn resolve_function_selectors( selectors: Vec, logger: &Logger, ) -> HashMap> { - let mut resolved_functions: HashMap> = HashMap::new(); - + let resolved_functions: Arc>>> = Arc::new(Mutex::new(HashMap::new())); + let mut threads = Vec::new(); + let resolve_progress = ProgressBar::new_spinner(); resolve_progress.enable_steady_tick(Duration::from_millis(100)); resolve_progress.set_style(logger.info_spinner()); + resolve_progress.set_message(format!("resolving {} selectors...", selectors.len())); for selector in selectors { - resolve_progress.set_message(format!("resolving '0x{}'", selector)); - match resolve_function_signature(&selector) { - Some(function) => { - resolved_functions.insert(selector, function); + let function_clone = resolved_functions.clone(); + + // create a new thread for each selector + threads.push(thread::spawn(move || { + match resolve_function_signature(&selector) { + Some(function) => { + let mut _resolved_functions = function_clone.lock().unwrap(); + _resolved_functions.insert(selector, function); + } + None => {}, } - None => continue, - } + })); + + } + + // wait for all threads to finish + for thread in threads { + thread.join().unwrap(); } + resolve_progress.finish_and_clear(); - resolved_functions + let x = resolved_functions.lock().unwrap().clone(); + x } // match the ResolvedFunction to a list of Function parameters From b449d90006124729628bada7d03f64b8790a076d Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 8 Dec 2022 17:16:36 -0500 Subject: [PATCH 38/48] :wrench: fix: multithreaded http may rate limit, retry on fail --- common/src/utils/http.rs | 7 +++++-- heimdall/src/decompile/analyze.rs | 2 +- heimdall/src/decompile/resolve.rs | 15 +++++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/common/src/utils/http.rs b/common/src/utils/http.rs index 3d546bc7..761b5940 100644 --- a/common/src/utils/http.rs +++ b/common/src/utils/http.rs @@ -8,10 +8,13 @@ use serde_json::Value; // make a GET request to the target URL and return the response body as JSON pub fn get_json_from_url(url: String) -> Option { - let mut res = match get(url) { + let mut res = match get(url.clone()) { Ok(res) => res, Err(_) => { - return None + + // wait 1 second and try again + std::thread::sleep(std::time::Duration::from_secs(1)); + return get_json_from_url(url) } }; let mut body = String::new(); diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 6c2fc05b..f9458d2b 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -15,7 +15,7 @@ use heimdall_common::{ }, }, io::logging::TraceFactory, - utils::strings::{decode_hex, encode_hex_reduced, find_balanced_encapsulator}, + utils::strings::{decode_hex, encode_hex_reduced}, }; use super::{util::*, precompile::decode_precompile, constants::AND_BITMASK_REGEX}; diff --git a/heimdall/src/decompile/resolve.rs b/heimdall/src/decompile/resolve.rs index db7e86d6..1cd22692 100644 --- a/heimdall/src/decompile/resolve.rs +++ b/heimdall/src/decompile/resolve.rs @@ -15,21 +15,24 @@ pub fn resolve_function_selectors( logger: &Logger, ) -> HashMap> { let resolved_functions: Arc>>> = Arc::new(Mutex::new(HashMap::new())); + let resolve_progress: Arc> = Arc::new(Mutex::new(ProgressBar::new_spinner())); + let mut threads = Vec::new(); - - let resolve_progress = ProgressBar::new_spinner(); - resolve_progress.enable_steady_tick(Duration::from_millis(100)); - resolve_progress.set_style(logger.info_spinner()); - resolve_progress.set_message(format!("resolving {} selectors...", selectors.len())); + + resolve_progress.lock().unwrap().enable_steady_tick(Duration::from_millis(100)); + resolve_progress.lock().unwrap().set_style(logger.info_spinner()); for selector in selectors { let function_clone = resolved_functions.clone(); + let resolve_progress = resolve_progress.clone(); // create a new thread for each selector threads.push(thread::spawn(move || { match resolve_function_signature(&selector) { Some(function) => { let mut _resolved_functions = function_clone.lock().unwrap(); + let mut _resolve_progress = resolve_progress.lock().unwrap(); + _resolve_progress.set_message(format!("resolved {} selectors.", _resolved_functions.len())); _resolved_functions.insert(selector, function); } None => {}, @@ -43,7 +46,7 @@ pub fn resolve_function_selectors( thread.join().unwrap(); } - resolve_progress.finish_and_clear(); + resolve_progress.lock().unwrap().finish_and_clear(); let x = resolved_functions.lock().unwrap().clone(); x From 665ba316face1cc9b35951891569edab37f7cde3 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 8 Dec 2022 18:24:24 -0500 Subject: [PATCH 39/48] :wrench: fix: put bytecode and assembly in output dir --- heimdall/src/decompile/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 65f32afe..515ace27 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -190,7 +190,7 @@ pub fn decompile(args: DecompilerArgs) { target: contract_bytecode.clone(), default: args.default.clone(), verbose: args.verbose.clone(), - output: args.output.clone(), + output: output_dir.clone(), rpc_url: args.rpc_url.clone(), }); trace.add_call( From 383b92d562f1798b97b28281b261b67910a6a4be Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 8 Dec 2022 21:54:59 -0500 Subject: [PATCH 40/48] :sparkles: feat: implement optional solidity flag --- common/src/ether/signatures.rs | 6 +++--- common/src/utils/http.rs | 12 ++++++++---- heimdall/src/decompile/analyze.rs | 10 ++++++---- heimdall/src/decompile/mod.rs | 4 ++++ heimdall/src/decompile/output.rs | 25 ++++++++++++++----------- heimdall/src/decompile/postprocess.rs | 14 +++++++++++--- heimdall/src/decompile/tests.rs | 2 ++ heimdall/stdout | 0 8 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 heimdall/stdout diff --git a/common/src/ether/signatures.rs b/common/src/ether/signatures.rs index e8d67c77..87e095ce 100644 --- a/common/src/ether/signatures.rs +++ b/common/src/ether/signatures.rs @@ -28,7 +28,7 @@ pub struct ResolvedLog { pub fn resolve_function_signature(signature: &String) -> Option> { // get function possibilities from 4byte - let signatures = match get_json_from_url(format!("https://sig.eth.samczsun.com/api/v1/signatures?all=true&function=0x{}", &signature)) { + let signatures = match get_json_from_url(format!("https://sig.eth.samczsun.com/api/v1/signatures?all=true&function=0x{}", &signature), 3) { Some(signatures) => signatures, None => return None }; @@ -84,7 +84,7 @@ pub fn resolve_function_signature(signature: &String) -> Option Option> { // get function possibilities from 4byte - let signatures = match get_json_from_url(format!("https://sig.eth.samczsun.com/api/v1/signatures?all=true&function=0x{}", &signature)) { + let signatures = match get_json_from_url(format!("https://sig.eth.samczsun.com/api/v1/signatures?all=true&function=0x{}", &signature), 3) { Some(signatures) => signatures, None => return None }; @@ -140,7 +140,7 @@ pub fn resolve_error_signature(signature: &String) -> Option> pub fn resolve_event_signature(signature: &String) -> Option> { // get function possibilities from 4byte - let signatures = match get_json_from_url(format!("https://sig.eth.samczsun.com/api/v1/signatures?all=true&function=0x{}", &signature)) { + let signatures = match get_json_from_url(format!("https://sig.eth.samczsun.com/api/v1/signatures?all=true&function=0x{}", &signature), 3) { Some(signatures) => signatures, None => return None }; diff --git a/common/src/utils/http.rs b/common/src/utils/http.rs index 761b5940..d8da0110 100644 --- a/common/src/utils/http.rs +++ b/common/src/utils/http.rs @@ -6,15 +6,19 @@ use reqwest::blocking::get; use serde_json::Value; // make a GET request to the target URL and return the response body as JSON -pub fn get_json_from_url(url: String) -> Option { +pub fn get_json_from_url(url: String, attempts_remaining: u8) -> Option { let mut res = match get(url.clone()) { Ok(res) => res, Err(_) => { - // wait 1 second and try again - std::thread::sleep(std::time::Duration::from_secs(1)); - return get_json_from_url(url) + // retry if we have attempts remaining + if attempts_remaining == 1 { + return None + } + + std::thread::sleep(std::time::Duration::from_millis(250)); + return get_json_from_url(url, attempts_remaining - 1) } }; let mut body = String::new(); diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index f9458d2b..63e2ccc0 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -238,8 +238,9 @@ impl VMTrace { } None => { format!( - "revert(\"{}\");", - revert_string + "revert(\"{}\"); // {}", + revert_string, + instruction.instruction ) } } @@ -279,8 +280,9 @@ impl VMTrace { } None => { format!( - "revert{};", - custom_error_placeholder + "revert{}; // {}", + custom_error_placeholder, + instruction.instruction ) } } diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 515ace27..44cf768c 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -67,6 +67,10 @@ pub struct DecompilerArgs { #[clap(long="skip-resolving")] pub skip_resolving: bool, + /// Whether to include solidity source code in the output (in beta). + #[clap(long="include-sol")] + pub include_solidity: bool, + } pub fn decompile(args: DecompilerArgs) { diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 6e04f36a..9d4b74db 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -377,15 +377,18 @@ pub fn build_output( decompiled_output.push(String::from("}")); - write_lines_to_file( - &decompiled_output_path, - postprocess( - decompiled_output, - &progress_bar - ) - ); - - progress_bar.finish_and_clear(); - - logger.success(&format!("wrote decompiled contract to '{}' .", &decompiled_output_path).to_string()); + if args.include_solidity { + write_lines_to_file( + &decompiled_output_path, + postprocess( + decompiled_output, + &progress_bar + ) + ); + logger.success(&format!("wrote decompiled contract to '{}' .", &decompiled_output_path).to_string()); + progress_bar.finish_and_clear(); + } + else { + progress_bar.finish_and_clear(); + } } \ No newline at end of file diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index c0db6d3c..7249bc30 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -536,11 +536,15 @@ fn cleanup(line: String) -> String { fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { let mut cleaned_lines: Vec = Vec::new(); + let mut function_count = 0; // remove unused assignments for (i, line) in lines.iter().enumerate() { // update progress bar - bar.set_message(format!("finalizing line {}/{}", i, lines.len())); + if line.contains("function") { + function_count += 1; + bar.set_message(format!("postprocessed {} functions", function_count)); + } // only pass in lines further than the current line if !contains_unnecessary_assignment(line.trim().to_string(), &lines[i..].iter().collect::>()) @@ -554,13 +558,17 @@ fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { pub fn postprocess(lines: Vec, bar: &ProgressBar) -> Vec { let mut indentation: usize = 0; + let mut function_count = 0; let mut cleaned_lines: Vec = lines.clone(); // clean up each line using postprocessing techniques - for (i, line) in cleaned_lines.iter_mut().enumerate() { + for (_, line) in cleaned_lines.iter_mut().enumerate() { // update progress bar - bar.set_message(format!("cleaning up line {}/{}", i, lines.len())); + if line.contains("function") { + function_count += 1; + bar.set_message(format!("postprocessed {} functions", function_count)); + } // dedent due to closing braces if line.starts_with("}") { diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index 37cd2ccd..a4c577ad 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -16,6 +16,7 @@ mod benchmark { rpc_url: String::from(""), default: true, skip_resolving: true, + include_solidity: true }; crate::decompile::decompile(args.clone()) } @@ -34,6 +35,7 @@ mod benchmark { rpc_url: String::from(""), default: true, skip_resolving: true, + include_solidity: true }; crate::decompile::decompile(args.clone()) } diff --git a/heimdall/stdout b/heimdall/stdout new file mode 100644 index 00000000..e69de29b From 1937819a41fee4ee538fcd6949c1e0abbc2bfd31 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 14 Dec 2022 11:58:50 -0500 Subject: [PATCH 41/48] :zap: perf: require statements for jumps --- common/src/utils/http.rs | 7 +- heimdall/scripts/execute.py | 108 ++++++++++++++++++++++++++ heimdall/src/decompile/analyze.rs | 50 +++++++++--- heimdall/src/decompile/mod.rs | 8 +- heimdall/src/decompile/postprocess.rs | 13 +++- heimdall/src/decompile/resolve.rs | 2 +- heimdall/src/decompile/util.rs | 2 +- 7 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 heimdall/scripts/execute.py diff --git a/common/src/utils/http.rs b/common/src/utils/http.rs index d8da0110..f8167a2a 100644 --- a/common/src/utils/http.rs +++ b/common/src/utils/http.rs @@ -13,12 +13,13 @@ pub fn get_json_from_url(url: String, attempts_remaining: u8) -> Option { Err(_) => { // retry if we have attempts remaining - if attempts_remaining == 1 { + let attempts_remaining = attempts_remaining - 1; + if attempts_remaining == 0 { return None } - std::thread::sleep(std::time::Duration::from_millis(250)); - return get_json_from_url(url, attempts_remaining - 1) + std::thread::sleep(std::time::Duration::from_millis(500)); + return get_json_from_url(url, attempts_remaining) } }; let mut body = String::new(); diff --git a/heimdall/scripts/execute.py b/heimdall/scripts/execute.py new file mode 100644 index 00000000..91253f3c --- /dev/null +++ b/heimdall/scripts/execute.py @@ -0,0 +1,108 @@ +import subprocess + +x = ['0x95aBF379c57B6732d83c25Bf9FD401854BC9D26e', +'0x776b5d686C1Be032Cb11433da2D54F7c1DacCd0b', +'0xC03d0eCf195e3197585A71f6EAaF8d64017c083c', +'0xa3DE5a1A6122D277b9eb9002FE23662298561e3f', +'0x7248394e2A2E68034020fb6292D677B0f50A21bf', +'0x0Ce4b84654286d8f4Deb42f6304B6d43168DcE0D', +'0x7304Fa27c4089Ea02a50cA0856dF22AAe146fdBF', +'0x0E1581Bc43535f9d0e797409BD71F2dbdD2F47be', +'0xA27E13d4B9F18870976CECcC09C09F1A376EC3e0', +'0x4cd127DF1AeEd8b0D6237Bb8C587e417ccF45D08', +'0xb83AcE7bfB3768EAe4Af2c5E43dE7b7b97Df0551', +'0xe43317aB6B853582550B1Fe577B75dC09C780216', +'0x7F5836a6b6eAd870C385719d75Ac1156E0959312', +'0x6B1dbcDC3c790da7Fc9bCec8798AE807ff569978', +'0x7128ed210994c18fBe4B9A1cA2BDacB8CddB76E6', +'0x191441E2861fb073346D77bA9b0f6d04b0E56514', +'0xB25137cC24ff1009134d908FeF6a7eA17E3d1bb0', +'0x58f6D49F0b3f42AfB88De60C40ff4cd7A468E61c', +'0xb13cC4ED07349Fa8bdD0B78291c50F62b7E9EE5f', +'0x459895483556daD32526eFa461F75E33E458d9E9', +'0x1bAb335628CFd8FF8B0e5ebbfa4fcA386Fa90624', +'0x5654A61552bF4bA14110B526EdEC9E7a15cA3D37', +'0xa13D9C3f08f57b183Efd779F580562630C9b9D24', +'0x9bed64dAc8F963608A37C37aFf9928Ef13347D2A', +'0x9370045CE37F381500ac7D6802513bb89871e076', +'0xCfA365B7d917ce41162BA1F75162c1360666Ba08', +'0x19caF51BC94595FbE3D87d6B0725a9697Bec5C6f', +'0x6be9bd0f34B87bf9c8D6350e613b05254b58A452', +'0x43745D585bEeE747CD2D18A0748c919de96b752C', +'0x90332aB1bfeBD0bb5e47D8ecC4cB942a1B1Ef3C0', +'0x08a82B5913094E9b3363108aa2f6b1eC74546248', +'0x662275FE322b3Bb7eB09cF376550eA70f393D427', +'0xEa2511661c9B6cD551C9d2994b548cA5FA13300b', +'0x04C9eCDA31f96633C4Eb7C9E30d475065170b56D', +'0x39881156DEc525e978796A2DA7Dc6e3690188e2E', +'0xBD500bEb602fa1723966A8115f2EAa79BD037076', +'0xd5Dc9D3225d483B266E38af2a68003cdbe45ecC7', +'0xD8e3d466F78E02F195DDD979462162C7CD49E6Df', +'0x1b5abBA5fc616322da9c8B4c0721ed850D036C77', +'0xFfa60CD831374F40358de1aA0489123935a8287D', +'0x61F70DfA3d1Ffa753C308DbEDd25e966AC200F3F', +'0x3b774768E0733BfaA0e08B7a80d6e7C025307Ae1', +'0xCB0316076Ea2AbFD95531bc3620C27803AE923f0', +'0x670Ca0967fbeCf9d8D1dAe203270c551F1A8990e', +'0x02d555E9C920Ddd56B9863208956D8F2a65763f3', +'0xFF4518dB139E0C9C5067c81157Bd51D7f1841F09', +'0x69954f936292187933c48462D43259d32616F3D6', +'0x0C6822ca73dE6871f27ACD9ca05A05b99294B805', +'0x7912FCA3b78dc6c48A52A888606356E4c34B2880', +'0x69147ca3F35b3A0C93248af44c4cFa2060C3806b', +'0x139b44AC101BAeB687789432A163447779632095', +'0x2A3B0EA72CD4Ac316b5069F6bc37F1A4eE25dAf1', +'0x47C3435677A3A3d371145e651f399eF52b012c23', +'0x403C3741DFe6a6AfED0A50D68E07f1DF3B4e24c5', +'0x94e675CE9E0A3274B1bca371Ad48b34D98C9a310', +'0xDA2Eea532049d183101f38f13549AF3ec4ccA8Ee', +'0x505058ce39F940690cE6EC020cB63cD284183c9A', +'0xcBef99c9eF80106e7189c49D6D899A8Abcdcb487', +'0x908727dc3f86bB42b3d16CA5cDBdA8607811Ae41', +'0xBa2f291070Ddd49d005dbd3Ff06Cd1b986B6c542', +'0xe2D055e53cE9517365F8c4957a247763C6298B32', +'0x2EB3d994256E5aDfC966961516e930D71Ff5563a', +'0x7b8fFFB8340F304CB47687bC092a0382b9387BD7', +'0xBEb246024e9329171deC17998e4295c56aA62aC2', +'0xcD60aa27E8E96b58FF93aC8EBb84D865Ba3a7511', +'0x054560ee2f10b316746881726afAc1A71b98dDCD', +'0xdF17253AB203d308A1FACA1E507D7E7177fDA97D', +'0xb30550760A511A67A6798DE6035672e593365E03', +'0x41d061494F9D6E8A7295A17D36469f94675CA22b', +'0x6d3aEEEbBc7683fC6AaE91f09125cAc1fa3f15F6', +'0xCe82F64C4fa0BD50517F15E4FB998FA0397a3b60', +'0x959D2a68de1E58C9A641ECB672302e88E4fC7560', +'0xD45Ed1FB0D7E03d343a14971f73Db8297bC64ea6', +'0xD31A1840EA87A2101028E0203FB17FBD3E1DE02C', +'0x95C8752092B2C336585F5178F299ea40a36399Ea', +'0xaf60a6C7639e5090c21954Ff3ad453E35d364378', +'0xc4478854ED468A522B5724Db6A8A144f5A8fbe8B', +'0x4bDB521da745591c2C630e4A4553CCfa2288dA93', +'0xa925Bedb9E69e94D4DC13899dF39a46025aB63e6', +'0x7AfEdA4c714e1C0A2a1248332c100924506aC8e6', +'0x0654c99f46B49Acb08c455DA39111f772355F00a', +'0xF0214D3FFe6F49367f06065d7875536372efE37f', +'0x3c1344ba585Ba280A62519f36edA53a4b190BbFf', +'0x65B8357E592A3E39D1b2f1013FA1b35B37a828A7', +'0x0B608729ee4bD511319b9366809986135A194ba4', +'0xca54F7DEF56aee0c9AD84289E715d86445aC1b1A', +'0x9f2028FCF252bD1fa41cfafbDC15Da8a8cc42992', +'0x76c7133e59547FD398019e6442CEBeE1321546Ae', +'0x528b332B4049fBc4AE0752F9Ac30E635253A601f', +'0xF83402D554eD765E6aa3523d1F52FCD3Ef75D44e', +'0x7418e611bdaF8487Be593aCf7dDA3301909c7562', +'0x15d4D0307E9919759E7Dcaf7e3a9b6ecAa8ed9D2', +'0x52F298501913F6e5afb65C9BF382b1b4B8D4E6A3', +'0x0b440b3821257331F54873FbA9915DD664C59c39', +'0x55023f852D5bfc6A6C7660dc8C7e3f2B3e38490B', +'0x136Fc16A14837DA6B4a0c1fF54cEf6159f86C53f', +'0xACaDaF7FB6156D3c1832c27612611C735baBB495', +'0xb15d0Fb1574ecd6f14022E3F2EF1140a24730124', +'0x6749987cE65b985A1703932949aF794f561C8f7a', +'0xD97062C3B748A19fa52CB1E6B69E0c24cE71B00d',] + +for address in x: + print(address) + + # run the command 'echo "address"' with sys + print(subprocess.run(["cargo", "run", "--release", "--", "decompile", address, "-vvv", "-d", "--output", "./contracts/" + address, "--include-sol"])) \ No newline at end of file diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 63e2ccc0..537e5395 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -15,7 +15,7 @@ use heimdall_common::{ }, }, io::logging::TraceFactory, - utils::strings::{decode_hex, encode_hex_reduced}, + utils::strings::{decode_hex, encode_hex_reduced, find_balanced_encapsulator}, }; use super::{util::*, precompile::decode_precompile, constants::AND_BITMASK_REGEX}; @@ -149,6 +149,7 @@ impl VMTrace { revert_conditional = Some(instruction.input_operations[1].solidify()); } else { + revert_conditional = Some(instruction.input_operations[1].solidify()); // this is an if conditional for the children branches let conditional = instruction.input_operations[1].solidify(); @@ -237,11 +238,21 @@ impl VMTrace { ) } None => { - format!( - "revert(\"{}\"); // {}", - revert_string, - instruction.instruction - ) + + // get the last IF statement and add the revert to it + for i in (0..function.logic.len()).rev() { + if function.logic[i].contains("if") { + let encap = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); + let require_conditional = function.logic[i].get(encap.0..encap.1).unwrap().to_string(); + function.logic[i] = format!( + "require({}, \"{}\"); //", + require_conditional, + revert_string + ); + break; + } + } + continue; } } } @@ -263,7 +274,6 @@ impl VMTrace { revert_logic = match revert_conditional.clone() { Some(condition) => { - if custom_error_placeholder == "()".to_string() { format!( "require({});", @@ -279,11 +289,27 @@ impl VMTrace { } } None => { - format!( - "revert{}; // {}", - custom_error_placeholder, - instruction.instruction - ) + + // get the last IF statement and add the revert to it + for i in (0..function.logic.len()).rev() { + if function.logic[i].starts_with("if") { + let encap = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); + let require_conditional = function.logic[i].get(encap.0..encap.1).unwrap().to_string(); + + if custom_error_placeholder == "()".to_string() { + function.logic[i] = format!("require({});", require_conditional); + } + else { + function.logic[i] = format!( + "if (!{}) revert{};", + require_conditional, + custom_error_placeholder + ) + } + break; + } + } + continue; } } } diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 44cf768c..8a480fd0 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -78,6 +78,8 @@ pub fn decompile(args: DecompilerArgs) { let now = Instant::now(); let (logger, mut trace)= Logger::new(args.verbose.log_level().unwrap().as_str()); + let mut all_resolved_events: HashMap> = HashMap::new(); + let mut all_resolved_errors: HashMap> = HashMap::new(); // truncate target for prettier display let mut shortened_target = args.target.clone(); @@ -463,7 +465,8 @@ pub fn decompile(args: DecompilerArgs) { }; resolved_counter += 1; - analyzed_function.errors.insert(error_selector, Some(selected_match.clone())); + analyzed_function.errors.insert(error_selector.clone(), Some(selected_match.clone())); + all_resolved_errors.insert(error_selector.clone(), Some(selected_match.clone())); }, None => {} } @@ -511,7 +514,8 @@ pub fn decompile(args: DecompilerArgs) { }; resolved_counter += 1; - analyzed_function.events.insert(event_selector, (Some(selected_match.clone()), raw_event)); + analyzed_function.events.insert(event_selector.clone(), (Some(selected_match.clone()), raw_event)); + all_resolved_events.insert(event_selector, Some(selected_match.clone())); }, None => {} } diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 7249bc30..694257f6 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -353,7 +353,9 @@ fn convert_memory_to_variable(line: String) -> String { // recurse to replace any other memory accesses cleaned = convert_memory_to_variable(cleaned); }, - _ => return cleaned + _ => { + return cleaned + } } // if the memory access is an instantiation, save it @@ -556,7 +558,10 @@ fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { cleaned_lines } -pub fn postprocess(lines: Vec, bar: &ProgressBar) -> Vec { +pub fn postprocess( + lines: Vec, + bar: &ProgressBar +) -> Vec { let mut indentation: usize = 0; let mut function_count = 0; let mut cleaned_lines: Vec = lines.clone(); @@ -579,11 +584,11 @@ pub fn postprocess(lines: Vec, bar: &ProgressBar) -> Vec { *line = format!( "{}{}", " ".repeat(indentation*4), - cleanup(line.to_string()) + cleanup(line.to_string()) ); // indent due to opening braces - if line.ends_with("{") { + if line.split("//").collect::>().first().unwrap().trim().ends_with("{") { indentation += 1; } diff --git a/heimdall/src/decompile/resolve.rs b/heimdall/src/decompile/resolve.rs index 1cd22692..54159e46 100644 --- a/heimdall/src/decompile/resolve.rs +++ b/heimdall/src/decompile/resolve.rs @@ -32,7 +32,7 @@ pub fn resolve_function_selectors( Some(function) => { let mut _resolved_functions = function_clone.lock().unwrap(); let mut _resolve_progress = resolve_progress.lock().unwrap(); - _resolve_progress.set_message(format!("resolved {} selectors.", _resolved_functions.len())); + _resolve_progress.set_message(format!("resolved {} selectors...", _resolved_functions.len())); _resolved_functions.insert(selector, function); } None => {}, diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 4e687cb5..70579ad9 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -48,7 +48,7 @@ pub struct Function { // holds function logic to be written to the output solidity file. pub logic: Vec, - // holds all emitted events. used to generate solidity event definitions + // holds all found events used to generate solidity error definitions // as well as ABI specifications. pub events: HashMap, Log)>, From f40f4ee2cce054f329310433a497e87d870700a3 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 14 Dec 2022 12:12:47 -0500 Subject: [PATCH 42/48] :sparkles: feat: update sol events and errors if resolved --- heimdall/src/decompile/mod.rs | 10 +++--- heimdall/src/decompile/output.rs | 8 +++-- heimdall/src/decompile/postprocess.rs | 47 +++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 8a480fd0..16f5d7c3 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -78,8 +78,8 @@ pub fn decompile(args: DecompilerArgs) { let now = Instant::now(); let (logger, mut trace)= Logger::new(args.verbose.log_level().unwrap().as_str()); - let mut all_resolved_events: HashMap> = HashMap::new(); - let mut all_resolved_errors: HashMap> = HashMap::new(); + let mut all_resolved_events: HashMap = HashMap::new(); + let mut all_resolved_errors: HashMap = HashMap::new(); // truncate target for prettier display let mut shortened_target = args.target.clone(); @@ -466,7 +466,7 @@ pub fn decompile(args: DecompilerArgs) { resolved_counter += 1; analyzed_function.errors.insert(error_selector.clone(), Some(selected_match.clone())); - all_resolved_errors.insert(error_selector.clone(), Some(selected_match.clone())); + all_resolved_errors.insert(error_selector.clone(), selected_match.clone()); }, None => {} } @@ -515,7 +515,7 @@ pub fn decompile(args: DecompilerArgs) { resolved_counter += 1; analyzed_function.events.insert(event_selector.clone(), (Some(selected_match.clone()), raw_event)); - all_resolved_events.insert(event_selector, Some(selected_match.clone())); + all_resolved_events.insert(event_selector, selected_match.clone()); }, None => {} } @@ -544,6 +544,8 @@ pub fn decompile(args: DecompilerArgs) { &args, output_dir, analyzed_functions, + all_resolved_errors, + all_resolved_events, &logger, &mut trace, decompile_call, diff --git a/heimdall/src/decompile/output.rs b/heimdall/src/decompile/output.rs index 9d4b74db..ca0e865e 100644 --- a/heimdall/src/decompile/output.rs +++ b/heimdall/src/decompile/output.rs @@ -1,6 +1,6 @@ -use std::time::Duration; +use std::{time::Duration, collections::HashMap}; -use heimdall_common::io::{logging::{TraceFactory, Logger}, file::{short_path, write_file, write_lines_to_file}}; +use heimdall_common::{io::{logging::{TraceFactory, Logger}, file::{short_path, write_file, write_lines_to_file}}, ether::signatures::{ResolvedError, ResolvedLog}}; use indicatif::ProgressBar; use super::{DecompilerArgs, util::Function, constants::DECOMPILED_SOURCE_HEADER, postprocess::postprocess}; @@ -55,6 +55,8 @@ pub fn build_output( args: &DecompilerArgs, output_dir: String, functions: Vec, + all_resolved_errors: HashMap, + all_resolved_events: HashMap, logger: &Logger, trace: &mut TraceFactory, trace_parent: u32 @@ -382,6 +384,8 @@ pub fn build_output( &decompiled_output_path, postprocess( decompiled_output, + all_resolved_errors, + all_resolved_events, &progress_bar ) ); diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 694257f6..d80f38c9 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -2,7 +2,7 @@ use std::{ sync::Mutex, collections::HashMap }; -use heimdall_common::{ether::evm::types::{byte_size_to_type, find_cast}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards, base26_encode}, constants::TYPE_CAST_REGEX}; +use heimdall_common::{ether::{evm::types::{byte_size_to_type, find_cast}, signatures::{ResolvedError, ResolvedLog}}, utils::strings::{find_balanced_encapsulator, find_balanced_encapsulator_backwards, base26_encode}, constants::TYPE_CAST_REGEX}; use indicatif::ProgressBar; use crate::decompile::constants::{ENCLOSED_EXPRESSION_REGEX}; use super::{constants::{AND_BITMASK_REGEX, AND_BITMASK_REGEX_2, NON_ZERO_BYTE_REGEX, MEM_ACCESS_REGEX}}; @@ -503,7 +503,39 @@ fn inherit_infer_type(line: String) -> String { cleaned } -fn cleanup(line: String) -> String { +fn replace_resolved( + line: String, + all_resolved_errors: HashMap, + all_resolved_events: HashMap, +) -> String { + let mut cleaned = line.clone(); + + // line must contain CustomError_ or Event_ + if !cleaned.contains("CustomError_") && !cleaned.contains("Event_") { return cleaned; } + + // not the best way to do it, can perf later + for (selector, error) in all_resolved_errors.iter() { + let selector = selector.get(0..8).unwrap_or("00000000"); + if cleaned.contains(selector) { + cleaned = cleaned.replace(&format!("CustomError_{selector}"), &error.name); + } + } + + for (selector, event) in all_resolved_events.iter() { + let selector = selector.get(0..8).unwrap_or("00000000"); + if cleaned.contains(selector) { + cleaned = cleaned.replace(&format!("Event_{selector}"), &event.name); + } + } + + cleaned +} + +fn cleanup( + line: String, + all_resolved_errors: HashMap, + all_resolved_events: HashMap, +) -> String { let mut cleaned = line; // skip comments @@ -533,6 +565,9 @@ fn cleanup(line: String) -> String { // Inherit or infer types from expressions cleaned = inherit_infer_type(cleaned); + // Replace resolved errors and events + cleaned = replace_resolved(cleaned, all_resolved_errors, all_resolved_events); + cleaned } @@ -560,6 +595,8 @@ fn finalize(lines: Vec, bar: &ProgressBar) -> Vec { pub fn postprocess( lines: Vec, + all_resolved_errors: HashMap, + all_resolved_events: HashMap, bar: &ProgressBar ) -> Vec { let mut indentation: usize = 0; @@ -584,7 +621,11 @@ pub fn postprocess( *line = format!( "{}{}", " ".repeat(indentation*4), - cleanup(line.to_string()) + cleanup( + line.to_string(), + all_resolved_errors.clone(), + all_resolved_events.clone() + ) ); // indent due to opening braces From ba1be4edb8cd590b3a6ed4be08e6f0da2708334f Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Wed, 14 Dec 2022 20:06:35 -0500 Subject: [PATCH 43/48] :wrench: fix: postprocessing finalization fix --- heimdall/src/decompile/analyze.rs | 2 +- heimdall/src/decompile/postprocess.rs | 3 +++ heimdall/src/decompile/tests.rs | 24 +++++++++++++----------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 537e5395..36707c25 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -245,7 +245,7 @@ impl VMTrace { let encap = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); let require_conditional = function.logic[i].get(encap.0..encap.1).unwrap().to_string(); function.logic[i] = format!( - "require({}, \"{}\"); //", + "require({}, \"{}\");", require_conditional, revert_string ); diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index d80f38c9..01958267 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -390,6 +390,9 @@ fn contains_unnecessary_assignment(line: String, lines: &Vec<&String>) -> bool { return false; } } + else if x.contains(var_name) { + return false; + } } true diff --git a/heimdall/src/decompile/tests.rs b/heimdall/src/decompile/tests.rs index a4c577ad..cdc25fa8 100644 --- a/heimdall/src/decompile/tests.rs +++ b/heimdall/src/decompile/tests.rs @@ -48,6 +48,8 @@ mod benchmark { #[cfg(test)] mod postprocess_tests { + use std::collections::HashMap; + use indicatif::ProgressBar; use crate::decompile::postprocess::postprocess; @@ -58,7 +60,7 @@ mod postprocess_tests { String::from("(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) & (arg0);"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); } #[test] @@ -67,7 +69,7 @@ mod postprocess_tests { String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); } #[test] @@ -76,7 +78,7 @@ mod postprocess_tests { String::from("(arg0) & (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00);"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint248(arg0);")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("uint248(arg0);")]); } #[test] @@ -85,7 +87,7 @@ mod postprocess_tests { String::from("uint256(uint256(arg0));"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("uint256(arg0);")]); } #[test] @@ -94,7 +96,7 @@ mod postprocess_tests { String::from("ecrecover(uint256(uint256(arg0)), uint256(uint256(arg0)), uint256(uint256(uint256(arg0))));"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("ecrecover(uint256(arg0), uint256(arg0), uint256(arg0));")]); } #[test] @@ -103,7 +105,7 @@ mod postprocess_tests { String::from("if (iszero(arg0)) {"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (!arg0) {")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("if (!arg0) {")]); } #[test] @@ -112,7 +114,7 @@ mod postprocess_tests { String::from("if (iszero(iszero(arg0))) {"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (arg0) {")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("if (arg0) {")]); } #[test] @@ -121,7 +123,7 @@ mod postprocess_tests { String::from("if (iszero(iszero(iszero(arg0)))) {"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (!arg0) {")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("if (!arg0) {")]); } #[test] @@ -130,7 +132,7 @@ mod postprocess_tests { String::from("((arg0))"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("arg0")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("arg0")]); } #[test] @@ -139,7 +141,7 @@ mod postprocess_tests { String::from("if ((cast(((arg0) + 1) / 10))) {"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (cast(arg0 + 1 / 10)) {")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("if (cast(arg0 + 1 / 10)) {")]); } #[test] @@ -148,6 +150,6 @@ mod postprocess_tests { String::from("if (((((((((((((((cast(((((((((((arg0 * (((((arg1))))))))))))) + 1)) / 10)))))))))))))))) {"), ]; - assert_eq!(postprocess(lines, &ProgressBar::new(128)), vec![String::from("if (cast((arg0 * (arg1)) + 1 / 10)) {")]); + assert_eq!(postprocess(lines, HashMap::new(), HashMap::new(), &ProgressBar::new(128)), vec![String::from("if (cast((arg0 * (arg1)) + 1 / 10)) {")]); } } \ No newline at end of file From f51085240d9626be4a4e2ad39433b4205bd2b635 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 22 Dec 2022 11:42:26 -0600 Subject: [PATCH 44/48] :wrench: fix: include missing reverts --- heimdall/src/decompile/analyze.rs | 82 ++++++++++++++++++++----------- heimdall/src/decompile/mod.rs | 3 +- heimdall/src/decompile/util.rs | 3 ++ heimdall/stdout | 0 4 files changed, 58 insertions(+), 30 deletions(-) delete mode 100644 heimdall/stdout diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 36707c25..43a88429 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -28,11 +28,12 @@ impl VMTrace { function: Function, trace: &mut TraceFactory, trace_parent: u32, + conditional_map: &mut Vec ) -> Function { // make a clone of the recursed analysis function let mut function = function.clone(); - let mut branch_jumped = false; + let mut jumped_conditional: Option = None; let mut revert_conditional: Option = None; // perform analysis on the operations of the current VMTrace branch @@ -147,6 +148,8 @@ impl VMTrace { // if the JUMPI is not taken and the branch reverts, this is a require statement if self.operations.last().unwrap().last_instruction.opcode_details.clone().unwrap().name == "REVERT" { revert_conditional = Some(instruction.input_operations[1].solidify()); + jumped_conditional = Some(revert_conditional.clone().unwrap()); + conditional_map.push(revert_conditional.clone().unwrap()); } else { revert_conditional = Some(instruction.input_operations[1].solidify()); @@ -191,7 +194,8 @@ impl VMTrace { conditional ).to_string() ); - branch_jumped = true; + jumped_conditional = Some(conditional.clone()); + conditional_map.push(conditional); } } else if opcode_name == "REVERT" { @@ -238,17 +242,20 @@ impl VMTrace { ) } None => { - - // get the last IF statement and add the revert to it + + // loop backwards through logic to find the last IF statement for i in (0..function.logic.len()).rev() { - if function.logic[i].contains("if") { - let encap = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); - let require_conditional = function.logic[i].get(encap.0..encap.1).unwrap().to_string(); - function.logic[i] = format!( - "require({}, \"{}\");", - require_conditional, - revert_string - ); + if function.logic[i].starts_with("if") { + + // get matching conditional + let conditional = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); + let conditional = function.logic[i].get(conditional.0+1..conditional.1-1).unwrap(); + + // we can negate the conditional to get the revert logic + // TODO: make this a require statement, if revert is rlly gross but its technically correct + // I just ran into issues with ending bracket matching + function.logic[i] = format!("if (!({})) {{ revert(\"{}\"); }} else {{", conditional, revert_string); + break; } } @@ -290,22 +297,19 @@ impl VMTrace { } None => { - // get the last IF statement and add the revert to it + // loop backwards through logic to find the last IF statement for i in (0..function.logic.len()).rev() { if function.logic[i].starts_with("if") { - let encap = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); - let require_conditional = function.logic[i].get(encap.0..encap.1).unwrap().to_string(); - - if custom_error_placeholder == "()".to_string() { - function.logic[i] = format!("require({});", require_conditional); - } - else { - function.logic[i] = format!( - "if (!{}) revert{};", - require_conditional, - custom_error_placeholder - ) - } + + // get matching conditional + let conditional = find_balanced_encapsulator(function.logic[i].to_string(), ('(', ')')); + let conditional = function.logic[i].get(conditional.0+1..conditional.1-1).unwrap(); + + // we can negate the conditional to get the revert logic + // TODO: make this a require statement, if revert is rlly gross but its technically correct + // I just ran into issues with ending bracket matching + function.logic[i] = format!("if (!({})) {{ revert{}; }} else {{", conditional, custom_error_placeholder); + break; } } @@ -732,12 +736,32 @@ impl VMTrace { // recurse into the children of the VMTrace map for (_, child) in self.children.iter().enumerate() { - function = child.analyze(function, trace, trace_parent); + function = child.analyze(function, trace, trace_parent, conditional_map); } - if branch_jumped { - + // if branch_jumped { + + // // if the last line is an if statement, this branch is empty and probably stack operations we don't care about + // if function.logic.last().unwrap().contains("if") { + // function.logic.pop(); + // } + // else { + // function.logic.push("}".to_string()); + // } + // } + + // check if the ending brackets are needed + if jumped_conditional.is_some() && conditional_map.contains(&jumped_conditional.clone().unwrap()) + { + // remove the conditional + for (i, conditional) in conditional_map.iter().enumerate() { + if conditional == &jumped_conditional.clone().unwrap() { + conditional_map.remove(i); + break; + } + } + // if the last line is an if statement, this branch is empty and probably stack operations we don't care about if function.logic.last().unwrap().contains("if") { function.logic.pop(); diff --git a/heimdall/src/decompile/mod.rs b/heimdall/src/decompile/mod.rs index 16f5d7c3..d69ffcf8 100644 --- a/heimdall/src/decompile/mod.rs +++ b/heimdall/src/decompile/mod.rs @@ -315,12 +315,14 @@ pub fn decompile(args: DecompilerArgs) { events: HashMap::new(), errors: HashMap::new(), resolved_function: None, + indent_depth: 0, pure: true, view: true, payable: true, }, &mut trace, func_analysis_trace, + &mut Vec::new() ); let argument_count = analyzed_function.arguments.len(); @@ -519,7 +521,6 @@ pub fn decompile(args: DecompilerArgs) { }, None => {} } - } if resolved_counter > 0 { diff --git a/heimdall/src/decompile/util.rs b/heimdall/src/decompile/util.rs index 70579ad9..77e3146f 100644 --- a/heimdall/src/decompile/util.rs +++ b/heimdall/src/decompile/util.rs @@ -59,6 +59,9 @@ pub struct Function { // stores the matched resolved function for this Functon pub resolved_function: Option, + // stores the current indent depth, used for formatting and removing unnecessary closing brackets. + pub indent_depth: usize, + // modifiers pub pure: bool, pub view: bool, diff --git a/heimdall/stdout b/heimdall/stdout deleted file mode 100644 index e69de29b..00000000 From cb801376c7bac68ec25b56f947d9bc69dcfa1ce0 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Thu, 22 Dec 2022 20:38:26 -0600 Subject: [PATCH 45/48] :wrench: fix: fixes an indentation issue --- heimdall/src/decompile/postprocess.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heimdall/src/decompile/postprocess.rs b/heimdall/src/decompile/postprocess.rs index 01958267..6f0fa65d 100644 --- a/heimdall/src/decompile/postprocess.rs +++ b/heimdall/src/decompile/postprocess.rs @@ -493,7 +493,7 @@ fn inherit_infer_type(line: String) -> String { // infer the type from args and vars in the expression for (var, var_type) in type_map.clone().iter() { - if cleaned.contains(var) && !type_map.contains_key(var_name) { + if cleaned.contains(var) && !type_map.contains_key(var_name) && var_type != "" { cleaned = format!("{} {}", var_type, cleaned); type_map.insert(var_name.to_string(), var_type.to_string()); break; From 9660b4ec7c0f4b4babcf7df3c43cb855a74f2ef0 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:21:10 -0600 Subject: [PATCH 46/48] :sparkles: feat: event emissions include solidified fields --- heimdall/src/decompile/analyze.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index 43a88429..ddd27e61 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -123,6 +123,10 @@ impl VMTrace { // add the event to the function function.events.insert(logged_event.topics.first().unwrap().to_string(), (None, logged_event.clone())); + // decode the data field + let data_mem_ops = function.get_memory_range(instruction.inputs[0], instruction.inputs[1]); + let data_mem_ops_solidified = data_mem_ops.iter().map(|x| x.operations.solidify()).collect::>().join(", "); + // add the event emission to the function's logic // will be decoded during post-processing function.logic.push(format!( @@ -134,12 +138,24 @@ impl VMTrace { }, match logged_event.topics.get(1..) { Some(topics) => match logged_event.data.len() > 0 && topics.len() > 0 { - true => format!("{}, ", topics.join(", ")), - false => topics.join(", "), + true => { + let mut solidified_topics: Vec = Vec::new(); + for (i, _) in topics.iter().enumerate() { + solidified_topics.push(instruction.input_operations[i+3].solidify()); + } + format!("{}, ", solidified_topics.join(", ")) + } + false => { + let mut solidified_topics: Vec = Vec::new(); + for (i, _) in topics.iter().enumerate() { + solidified_topics.push(instruction.input_operations[i+3].solidify()); + } + solidified_topics.join(", ") + } }, None => "".to_string(), }, - logged_event.data + data_mem_ops_solidified )); } From d285b86552a8e89cab7c06ac049323b9bb054402 Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Fri, 23 Dec 2022 13:55:06 -0600 Subject: [PATCH 47/48] =?UTF-8?q?=F0=9F=91=B7=20build:=20bump=20version=20?= =?UTF-8?q?to=200.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/Cargo.toml | 5 ++--- config/Cargo.toml | 2 +- heimdall/Cargo.toml | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/common/Cargo.toml b/common/Cargo.toml index 0540d753..629b511f 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heimdall-common" -version = "0.1.8" +version = "0.2.0" edition = "2021" license = "MIT" readme = "README.md" @@ -17,5 +17,4 @@ 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" -ethers = "1.0.0" \ No newline at end of file +serde_json = "1.0" \ No newline at end of file diff --git a/config/Cargo.toml b/config/Cargo.toml index d0633c84..bc42324a 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heimdall-config" -version = "0.1.8" +version = "0.2.0" edition = "2021" license = "MIT" readme = "README.md" diff --git a/heimdall/Cargo.toml b/heimdall/Cargo.toml index bc4f006a..d437844c 100644 --- a/heimdall/Cargo.toml +++ b/heimdall/Cargo.toml @@ -5,7 +5,7 @@ keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"] license = "MIT" name = "heimdall" readme = "README.md" -version = "0.1.8" +version = "0.2.0" [dependencies] backtrace = "0.3" @@ -22,7 +22,6 @@ indicatif = "0.17.0" strsim = "0.10.0" fancy-regex = "0.10.0" lazy_static = "1.4.0" -ethers = "1.0.0" [[bin]] name = "heimdall" From 8f60abd4fda4f53f8227ecb77d80d4f0e1272fae Mon Sep 17 00:00:00 2001 From: Jonathan Becker <64037729+Jon-Becker@users.noreply.github.com> Date: Fri, 23 Dec 2022 13:55:55 -0600 Subject: [PATCH 48/48] =?UTF-8?q?=F0=9F=A7=B9=20clean:=20remove=20unused?= =?UTF-8?q?=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- heimdall/src/decompile/analyze.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/heimdall/src/decompile/analyze.rs b/heimdall/src/decompile/analyze.rs index ddd27e61..0c417bff 100644 --- a/heimdall/src/decompile/analyze.rs +++ b/heimdall/src/decompile/analyze.rs @@ -756,17 +756,6 @@ impl VMTrace { } - // if branch_jumped { - - // // if the last line is an if statement, this branch is empty and probably stack operations we don't care about - // if function.logic.last().unwrap().contains("if") { - // function.logic.pop(); - // } - // else { - // function.logic.push("}".to_string()); - // } - // } - // check if the ending brackets are needed if jumped_conditional.is_some() && conditional_map.contains(&jumped_conditional.clone().unwrap()) {