From 599078efd5af6b0ae7563caf5e76c424ffb562d9 Mon Sep 17 00:00:00 2001 From: shuo-young <2857043555@qq.com> Date: Tue, 23 Jan 2024 16:37:38 +0800 Subject: [PATCH] add flow analysis and dockerfile --- .dockerignore | 13 + .gitattributes | 1 + .gitignore | 4 +- Cargo.lock | 1 + Cargo.toml | 1 + Dockerfile | 57 +++++ README.md | 3 + gigahorse-toolchain/clients/leslie.dl | 23 ++ src/contract/contract.rs | 4 +- src/contract/data_structure.rs | 59 +++++ src/flow/flow_analysis.rs | 352 ++++++++++++++++++++++++++ src/flow/mod.rs | 1 + src/graph/call_graph.rs | 21 +- src/main.rs | 92 ++++++- 14 files changed, 611 insertions(+), 21 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitattributes create mode 100644 Dockerfile create mode 100644 src/flow/mod.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a9bc0dd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +env/ +.temp +cache +.git +Dockerfile +output +.vscode +target \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f123180 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +gigahorse-toolchain/* linguist-vendored \ No newline at end of file diff --git a/.gitignore b/.gitignore index ccb5166..b2b82f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -.vscode \ No newline at end of file +.vscode + +output/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index afe9b82..ea58991 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -810,6 +810,7 @@ dependencies = [ "log", "reqwest", "serde", + "serde_json", "tokio", "tracing", "web3", diff --git a/Cargo.toml b/Cargo.toml index d81dd65..5d7decd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ serde = { version = "1.0", features = ["derive"] } log = "0.4" env_logger = "0.10.1" tracing = "0.1" +serde_json = "1.0" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b5219d6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,57 @@ +FROM ubuntu:22.04 + +USER root + +ENV DEBIAN_FRONTEND=noninteractive + +# Install some essentials +RUN apt-get update && apt-get upgrade -y && apt-get install -y \ + build-essential \ + libboost-all-dev \ + wget + +# Install python3 +RUN apt-get install python3-dev python3-pip -y + +# Install souffle +RUN wget https://souffle-lang.github.io/ppa/souffle-key.public -O /usr/share/keyrings/souffle-archive-keyring.gpg +RUN echo "deb [signed-by=/usr/share/keyrings/souffle-archive-keyring.gpg] https://souffle-lang.github.io/ppa/ubuntu/ stable main" | tee /etc/apt/sources.list.d/souffle.list +RUN apt-get update && apt-get install souffle -y + +# Dependencies for Gigahorse output viz +RUN apt-get update && apt-get install -y graphviz +RUN apt-get update && apt-get install -y libssl-dev + +RUN python3 -m pip install --upgrade pip +RUN python3 -m pip install pydot + +# Install Rust and Cargo using rustup +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +# Set the environment path to include Cargo binaries +ENV PATH="/root/.cargo/bin:${PATH}" + +# Set up a non-root 'lydia' user +RUN groupadd -r lydia && useradd -ms /bin/bash -g lydia lydia + +RUN mkdir -p /opt/lydia + +# Copy gigahorse project root +COPY . /opt/lydia/ + +RUN chown -R lydia:lydia /opt/lydia +RUN chmod -R o+rwx /opt/lydia + +# Switch to new 'gigahorse' user context +USER lydia + +# Souffle-addon bare-minimum make +RUN cd /opt/lydia/gigahorse-toolchain/souffle-addon && make libsoufflenum.so +# RUN cd /opt/lydia && pip3 install -r requirements.txt + +WORKDIR /opt/lydia + +# RUN cargo build --release + +CMD ["-h"] +ENTRYPOINT ["cargo", "run"] \ No newline at end of file diff --git a/README.md b/README.md index b03585a..9ae363d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Lydia + ![Static Badge](https://img.shields.io/badge/license-apache-blue) ![Static Badge](https://img.shields.io/badge/language-rust-red) An Attacker Contract Identification Tool Implemented in Rust based on BlockWatchdog. + +RUST_LOG=info cargo run -- ETH 0x10C509AA9ab291C76c45414e7CdBd375e1D5AcE8 diff --git a/gigahorse-toolchain/clients/leslie.dl b/gigahorse-toolchain/clients/leslie.dl index 1e0d49f..644909e 100644 --- a/gigahorse-toolchain/clients/leslie.dl +++ b/gigahorse-toolchain/clients/leslie.dl @@ -99,6 +99,8 @@ Leslie_ExternalCallInHook(callStmt, funcSign) :- Leslie_ExternalCallInfo(func,callStmt,_,_,_,_), (sigText = "tokensReceived(bytes4,bytes32,address,address,address,uint256,bytes,bytes)"; sigText = "transferFrom(address,address,uint256)"; + // sigText = "transfer(address,address,uint256)"; + sigText = "depositLp(uint256,uint256)"; sigText = "tokensToSend(address,address,address,uint256,bytes,bytes)"; sigText = "onERC721Received(address,address,uint256,bytes)"; sigText = "onERC1155Received(address,address,uint256,uint256,bytes)"; @@ -108,6 +110,27 @@ Leslie_ExternalCallInHook(callStmt, funcSign) :- sigText = "onFlashLoan(address,address,uint256,uint256,bytes)"; sigText = "uniswapV2Call(address,uint256,uint256,bytes)"). +Leslie_ExternalCallInHook(callStmt, funcSign) :- + funcSign = "0xeeeeeeee", + Leslie_FunctionSelector(func, funcSign), + Leslie_ExternalCallInfo(func,callStmt,_,_,_,_). + +.decl Leslie_DoubleCallToSameContract(funcSign:symbol, callee:Value) +.output Leslie_DoubleCallToSameContract +Leslie_DoubleCallToSameContract(funcSign, callee) :- + Leslie_ExternalCall_Callee_ConstType(func1, _, callee), + Leslie_ExternalCallInHook(callStmt2, funcSign), + Leslie_ExternalCall_Callee_ConstType(func2, callStmt2, callee), + func1 != func2. + +.decl Leslie_DoubleCallToSameContractByStorage(funcSign:symbol, storageSlot:symbol, byteLow:number, byteHigh:number) +.output Leslie_DoubleCallToSameContractByStorage +Leslie_DoubleCallToSameContractByStorage(funcSign, storageSlot, byteLow, byteHigh) :- + Leslie_ExternalCall_Callee_StorageType(func1, _, storageSlot, byteLow, byteHigh), + Leslie_ExternalCallInHook(callStmt2, funcSign), + Leslie_ExternalCall_Callee_StorageType(func2, callStmt2, storageSlot, byteLow, byteHigh), + func1 != func2. + .decl Leslie_ExternalCallInFallback(callStmt:Statement, funcSign:symbol) .output Leslie_ExternalCallInFallback Leslie_ExternalCallInFallback(callStmt, funcSign) :- diff --git a/src/contract/contract.rs b/src/contract/contract.rs index 7f697f2..dd4d151 100644 --- a/src/contract/contract.rs +++ b/src/contract/contract.rs @@ -37,7 +37,7 @@ pub struct Contract { block_number: u64, pub(crate) caller: String, pub(crate) call_site: String, - pub(crate) level: u32, + pub(crate) level: i32, origin: bool, func: String, func_sign_dict: HashMap, @@ -67,7 +67,7 @@ impl Contract { block_number: u64, caller: String, call_site: String, - level: u32, + level: i32, ) -> Contract { // Initialize a Contract instance let formatted_logic_addr = Self::format_addr(&logic_addr); diff --git a/src/contract/data_structure.rs b/src/contract/data_structure.rs index cc39535..1af7816 100644 --- a/src/contract/data_structure.rs +++ b/src/contract/data_structure.rs @@ -140,3 +140,62 @@ pub(crate) struct ExternalCall { pub(crate) caller_func_sign: String, pub(crate) caller_addr: String, } + +#[derive(Debug)] +pub struct SensitiveOpOfBadRandomnessAfterExternalCall { + pub func_sign: String, + pub call_stmt: String, + pub sensitive_var: String, + pub source_op: String, +} + +impl From for SensitiveOpOfBadRandomnessAfterExternalCall { + fn from(record: StringRecord) -> Self { + // Parse and create SensitiveOpOfBadRandomnessAfterExternalCall + SensitiveOpOfBadRandomnessAfterExternalCall { + func_sign: record[0].to_string(), + call_stmt: record[1].to_string(), + sensitive_var: record[2].to_string(), + source_op: record[3].to_string(), + } + } +} + +pub struct SensitiveOpOfDoSAfterExternalCall { + pub func_sign: String, + pub call_stm: String, + pub call_ret_var: String, + pub call_ret_index: String, + pub sensitive_var: String, +} + +impl From for SensitiveOpOfDoSAfterExternalCall { + fn from(record: StringRecord) -> Self { + // Parse and create SensitiveOpOfDoSAfterExternalCall + SensitiveOpOfDoSAfterExternalCall { + func_sign: record[0].to_string(), + call_stm: record[1].to_string(), + call_ret_var: record[2].to_string(), + call_ret_index: record[3].to_string(), + sensitive_var: record[4].to_string(), + } + } +} + +#[derive(Debug)] +pub struct TaintedCallArg { + pub func_sign: String, + pub call_stmt: String, + pub call_arg_index: String, +} + +impl From for TaintedCallArg { + fn from(record: StringRecord) -> Self { + // Parse and create TaintedCallArg + TaintedCallArg { + func_sign: record[0].to_string(), + call_stmt: record[1].to_string(), + call_arg_index: record[2].to_string(), + } + } +} diff --git a/src/flow/flow_analysis.rs b/src/flow/flow_analysis.rs index 8b13789..27bd0dd 100644 --- a/src/flow/flow_analysis.rs +++ b/src/flow/flow_analysis.rs @@ -1 +1,353 @@ +use std::cell::RefCell; +use std::collections::HashSet; +use std::rc::Rc; +use std::{collections::HashMap, error::Error, path::Path}; +use csv::{ReaderBuilder, StringRecord}; + +use crate::contract::contract::Contract; +use crate::contract::data_structure::{self, ExternalCall, TaintedCallArg}; +use log::info; +const OUTPUT_PATH: &str = "./gigahorse-toolchain/"; +const TEMP_PATH: &str = "./gigahorse-toolchain/.temp/"; + +#[derive(Debug)] +struct ProgramPoint { + caller_addr: String, + call_site: String, + caller_func_sign: String, + target_contract_addr: String, + target_func_sign: String, + index: i32, + program_point_type: String, +} + +pub struct FlowAnalysis<'a> { + contracts: &'a HashMap, + main_contract_sign_list: Vec, + external_call_in_func_sigature: HashSet, + visited_contracts: Vec, + visited_funcs: Vec, + intra_callsigs: Vec, + sensitive_callsigs: Vec, + attack_matrix: HashMap, + victim_callback_info: HashMap>, + attack_reenter_info: HashMap>, +} + +impl<'a> FlowAnalysis<'a> { + pub fn new( + contracts: &'a HashMap, + main_contract_sign_list: Vec, + external_call_in_func_sigature: HashSet, + visited_contracts: Vec, + visited_funcs: Vec, + ) -> Self { + FlowAnalysis { + contracts: contracts, + main_contract_sign_list: main_contract_sign_list, + external_call_in_func_sigature: external_call_in_func_sigature, + visited_contracts: visited_contracts, + visited_funcs: visited_funcs, + intra_callsigs: Vec::new(), + sensitive_callsigs: Vec::new(), + attack_matrix: HashMap::new(), + victim_callback_info: HashMap::new(), + attack_reenter_info: HashMap::new(), + } + } + + fn read_csv>( + &self, + file_path: &str, + data: &mut Vec, + ) -> Result<(), Box> { + if Path::new(&file_path).exists() { + let mut rdr = ReaderBuilder::new() + .delimiter(b'\t') + .from_path(&file_path)?; + for result in rdr.records() { + let record = result?; + data.push(T::from(record)); + } + } + Ok(()) + } + + fn intraprocedural_br_analysis(&mut self) -> bool { + for key in self.contracts.keys() { + if self.contracts[key].level == 0 { + let temp_address = key.split("_").collect::>()[2]; + let temp_func_sign = key.split("_").collect::>()[3]; + let mut br_analysis_df = Vec::new(); + self.read_csv::( + &format!( + "{}{}/out/Leslie_SensitiveOpOfBadRandomnessAfterExternalCall.csv", + TEMP_PATH, temp_address + ), + &mut br_analysis_df, + ); + for br_analysis in br_analysis_df { + if br_analysis.func_sign == temp_func_sign { + return true; + } + } + return false; + } + } + return false; + } + + fn intraprocedural_dos_analysis(&mut self) -> bool { + for key in self.contracts.keys() { + if self.contracts[key].level == 0 { + let temp_address = key.split("_").collect::>()[2]; + let temp_func_sign = key.split("_").collect::>()[3]; + let mut dos_analysis_df = Vec::new(); + self.read_csv::( + &format!( + "{}{}/out/Leslie_SensitiveOpOfDoSAfterExternalCall.csv", + TEMP_PATH, temp_address + ), + &mut dos_analysis_df, + ); + for dos_analysis in dos_analysis_df { + if dos_analysis.func_sign == temp_func_sign { + return true; + } + } + return false; + } + } + return false; + } + + fn find_executed_pp( + &self, + caller: &str, + call_site: &str, + contract_addr: &str, + func_sign: &str, + ) -> String { + let mut addr = String::new(); + let mut level: i32 = -1; + for key in self.contracts.keys() { + let temp: Vec<&str> = key.split('_').collect(); + if temp.len() >= 4 && temp[0] == caller && temp[1] == call_site && temp[3] == func_sign + { + match self.contracts.get(key) { + Some(contract) => { + if addr.is_empty() || contract.level > level { + addr = temp[2].to_string(); + level = contract.level; + } + } + None => continue, + } + } + } + addr + } + + fn get_new_program_point( + &self, + caller: &str, + call_site: &str, + target_contract_addr: &str, + target_func_sign: &str, + index: i32, + caller_func_sign: &str, + program_point_type: &str, + ) -> ProgramPoint { + let addr = self.find_executed_pp(caller, call_site, target_contract_addr, target_func_sign); + ProgramPoint { + caller_addr: caller.to_string(), + call_site: call_site.to_string(), + caller_func_sign: caller_func_sign.to_string(), + target_contract_addr: addr, + target_func_sign: target_func_sign.to_string(), + index: index, + program_point_type: program_point_type.to_string(), + } + } + + fn get_external_call_info( + &self, + call_site: &str, + external_calls: &[ExternalCall], + ) -> Option<(String, String, String)> { + for external_call in external_calls { + if external_call.call_site == call_site { + return Some(( + external_call.caller_addr.clone(), + external_call.target_logic_addr.clone(), + external_call.target_func_sign.clone(), + )); + } + } + None + } + + fn get_call_args_flow_from_sources( + &self, + contract_addr: &str, + func_sign: &str, + ) -> Vec { + let mut call_args = Vec::new(); + let mut tainted_call_arg_df = Vec::new(); + self.read_csv::( + &format!( + "{}{}/out/Leslie_TaintedCallArg.csv", + TEMP_PATH, contract_addr + ), + &mut tainted_call_arg_df, + ); + + for result in tainted_call_arg_df { + let call_arg = TaintedCallArg { + call_stmt: result.call_stmt.clone(), + func_sign: result.func_sign.clone(), + call_arg_index: result.call_arg_index.clone(), + }; + call_args.push(call_arg); + } + // info!("call args of {}: {:?}", contract_addr, call_args); + call_args + } + + fn get_pps_near_source(&self) -> Vec { + let mut pps_near_source = Vec::new(); + + for (key, contract) in self.contracts.iter() { + // info!("contract key: {}", key); + let parts: Vec<&str> = key.split('_').collect(); + let (temp_caller, temp_callsite, temp_address, temp_func_sign) = + (parts[0], parts[1], parts[2], parts[3]); + + if contract.level == 0 { + // info!("original contract"); + let temp_caller_func_sign = &contract.func_sign; + let temp_call_args = + self.get_call_args_flow_from_sources(temp_address, temp_caller_func_sign); + + if !temp_call_args.is_empty() { + for temp_call_arg in temp_call_args { + if let Some(( + temp_external_call_caller, + temp_external_call_logic_addr, + temp_external_call_func_sign, + )) = self.get_external_call_info( + &temp_call_arg.call_stmt, + &contract.external_calls, + ) { + pps_near_source.push(self.get_new_program_point( + &temp_external_call_caller, + &temp_call_arg.call_stmt, + &temp_external_call_logic_addr, + &temp_external_call_func_sign, + temp_call_arg.call_arg_index.parse::().unwrap(), + temp_caller_func_sign, + "call_arg", + )); + } + } + } + } + } + // info!("pps near source: {:?}", pps_near_source); + pps_near_source + } + + pub fn detect(&mut self) -> (bool, HashMap) { + let mut cross_contract = false; + for (key, contract) in self.contracts.iter() { + if contract.level != 0 { + cross_contract = true; + break; + } + } + + if !cross_contract { + return (false, self.attack_matrix.clone()); + } + + let mut result = false; + + // Assuming intraprocedural_br_analysis and intraprocedural_dos_analysis are methods returning bool + if self.intraprocedural_br_analysis() { + self.attack_matrix.insert("br".to_string(), true); + } else { + self.attack_matrix.insert("br".to_string(), false); + } + + if self.intraprocedural_dos_analysis() { + self.attack_matrix.insert("dos".to_string(), true); + } else { + self.attack_matrix.insert("dos".to_string(), false); + } + + let source = self.get_pps_near_source(); + // info!("pp near source: {:?}", source); + + // Assuming get_pps_near_source and get_pps_near_sink return appropriate data structures + let mut reachable_site: HashMap = HashMap::new(); + + // Assuming is_same and is_reachable are methods + // for pp1 in &pps_near_source { + // for pp2 in &pps_near_sink { + // if self.is_same(pp1, pp2) || self.is_reachable(pp1, pp2) { + // reachable = true; + // let caller = pp2.caller.clone(); + // let caller_func_sign = pp2.caller_func_sign.clone(); + // reachable_site.insert(pp2.func_sign.clone(), (caller, caller_func_sign)); + // } + // } + // } + + // let mut victim_callback_info = HashMap::new(); + // let mut attack_reenter_info = HashMap::new(); + + // if reachable { + // let overlap: HashSet<_> = sensitive_callsigs + // .intersection(&self.external_call_in_func_signature) + // .collect(); + // if !overlap.is_empty() { + // for i in &overlap { + // victim_callback_info + // .entry(i.clone()) + // .or_insert_with(Vec::new); + // attack_reenter_info + // .entry(i.clone()) + // .or_insert_with(Vec::new); + + // if let Some(site) = reachable_site.get(i) { + // if !victim_callback_info.get_mut(i).unwrap().contains(site) { + // victim_callback_info.get_mut(i).unwrap().push(site.clone()); + // } + // } + // for (key, contract) in &self.contracts { + // if contract.func_sign == *i && contract.level == 0 { + // for ec in &contract.external_calls { + // let temp_target_address = ec.logic_addr.clone(); + // let temp_func_sign = ec.func_sign.clone(); + // let res = (temp_target_address.clone(), temp_func_sign.clone()); + // if !attack_reenter_info.get_mut(i).unwrap().contains(&res) + // && self.visited_contracts.contains(&temp_target_address) + // && self.visited_funcs.contains(&temp_func_sign) + // { + // attack_reenter_info.get_mut(i).unwrap().push(res); + // } + // } + // result = true; + // self.attack_matrix.insert("reentrancy".to_string(), true); + // } + // } + // } + // } + // } + + // self.victim_callback_info = victim_callback_info; + // self.attack_reenter_info = attack_reenter_info; + (result, self.attack_matrix.clone()) + } +} diff --git a/src/flow/mod.rs b/src/flow/mod.rs new file mode 100644 index 0000000..ac524c6 --- /dev/null +++ b/src/flow/mod.rs @@ -0,0 +1 @@ +pub mod flow_analysis; diff --git a/src/graph/call_graph.rs b/src/graph/call_graph.rs index 1681f69..05bc7e7 100644 --- a/src/graph/call_graph.rs +++ b/src/graph/call_graph.rs @@ -1,24 +1,25 @@ use crate::{contract::contract::Contract, Source}; +use std::cell::RefCell; use std::collections::{HashMap, HashSet}; - +use std::rc::Rc; #[allow(dead_code)] -pub struct CallGraph { +pub struct CallGraph<'a> { output: String, visited_contracts: HashSet, visited_funcs: HashSet, - max_level: u32, + max_level: i32, platform: String, - contracts: HashMap, + contracts: &'a mut HashMap, } -impl CallGraph { - pub fn new(platform: String) -> CallGraph { +impl<'a> CallGraph<'a> { + pub fn new(platform: String, contracts: &'a mut HashMap) -> CallGraph { CallGraph { output: String::new(), visited_contracts: HashSet::new(), visited_funcs: HashSet::new(), max_level: 0, platform, - contracts: HashMap::new(), + contracts: contracts, } } @@ -26,6 +27,10 @@ impl CallGraph { &self.output } + pub fn get_contracts(&self) -> &HashMap { + &self.contracts + } + pub async fn construct_cross_contract_call_graph( &mut self, source: Source, @@ -68,8 +73,6 @@ impl CallGraph { if self.contracts.contains_key(&temp_key) { continue; } - - self.visited_contracts.insert(temp.logic_addr.clone()); self.visited_funcs.insert(temp.func_sign.clone()); let mut new_contract = Contract::new( diff --git a/src/main.rs b/src/main.rs index ae57d75..0294b6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,26 @@ mod contract; +mod flow; use crate::contract::contract::Contract; +use crate::flow::flow_analysis::FlowAnalysis; mod graph; +mod output; use crate::graph::call_graph::CallGraph; +use crate::output::result_structure::{ + ExternalCall, OpCreation, Overlap, Result, SemanticFeatures, +}; #[allow(unused_imports)] use log::{debug, error, info, log_enabled, Level}; +use serde_json; +use std::borrow::BorrowMut; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use std::env; +use std::fs::File; +use std::io::Write; +use std::rc::Rc; use std::time::Instant; +const JSON_PATH: &str = "./output/"; #[derive(Debug)] struct Source { platform: String, @@ -17,7 +31,7 @@ struct Source { caller: String, caller_func_sign: String, call_site: String, - level: u32, + level: i32, } #[allow(unused_mut)] @@ -65,18 +79,19 @@ async fn main() { let external_call_in_func_signature = external_call_in_func_signature.clone(); - // let visited_contracts: Vec = Vec::new(); - // let visited_funcs: Vec = Vec::new(); + let visited_contracts: Vec = Vec::new(); + let visited_funcs: Vec = Vec::new(); + + let mut call_path: Vec = Vec::new(); // let m_call_depth: u32 = 0; // let call_graph_str: String = String::new(); - // let mut contracts: Vec> = Vec::new(); - - // let mut external_call_in_func_signature = mem::take(external_call_in_func_signature); + let mut contracts = HashMap::new(); if input_contract.is_createbin().clone() { } else { let mut max_call_depth: u32 = 0; - for func_sign in external_call_in_func_signature.into_iter() { + for func_sign in external_call_in_func_signature.clone().into_iter() { + // let mut contracts_mut = contracts.borrow_mut(); println!("{}", func_sign); let source = Source { platform: platform.to_string(), @@ -89,7 +104,8 @@ async fn main() { call_site: "".to_string(), level: 0, }; - let mut cross_contract_call_graph = CallGraph::new(platform.to_string()); + let mut cross_contract_call_graph = + CallGraph::new(platform.to_string(), &mut contracts); if let Err(e) = cross_contract_call_graph .construct_cross_contract_call_graph(source) @@ -97,11 +113,69 @@ async fn main() { { eprintln!("An error occurred during call graph construction: {}", e); }; - // cross_contract_call_graph.construct_cross_contract_call_graph(source); let call_graph_str = cross_contract_call_graph.get_output(); + call_path.push(call_graph_str.to_string()); println!("{}", call_graph_str); } + info!("length of contracts: {}", contracts.len()); } + let mut result = Result { + is_attack: false, + warning: String::from("medium"), + attack_matrix: HashMap::new(), + analysis_loc: String::new(), + platform: String::from("ETH"), + block_number: 16000000, // Assuming block_number is provided elsewhere in the code + time: None, + semantic_features: SemanticFeatures { + op_creation: OpCreation { + op_multicreate: false, + op_solecreate: false, + }, + op_selfdestruct: false, + op_env: false, + }, + external_call: ExternalCall { + externalcall_inhook: false, + externalcall_infallback: false, + hooks_focused: vec![ + "tokensReceived".to_string(), + // ... add other strings here + ], + }, + call_paths: Vec::new(), + visited_contracts: Vec::new(), + visited_contracts_num: 0, + visited_funcs: Vec::new(), + visited_funcs_num: 0, + max_call_depth: 0, + contract_funcsigs: Vec::new(), + contract_funcsigs_external_call: Vec::new(), + sensitive_callsigs: Vec::new(), + overlap: Overlap { + has_overlap: false, + overlap_external_call: Vec::new(), + }, + reentrancy_path_info: std::collections::HashMap::new(), + }; + + let mut detector = FlowAnalysis::new( + &contracts, + func_sign_list.clone(), + external_call_in_func_signature, + visited_contracts, + visited_funcs, + ); + + let (res_bool, res) = detector.detect(); + result.is_attack = res_bool; + result.attack_matrix = res; + result.call_paths = call_path; + + let serialized = serde_json::to_string_pretty(&result).unwrap(); + let mut file = File::create(format!("{}{}.json", JSON_PATH, logic_address)).unwrap(); + file.write(serialized.as_bytes()).unwrap(); + let duration = start.elapsed(); info!("analyze contract {} consumes {:?}", logic_address, duration); }