BlogPublished 2026-06-29 ยท Smart Contract Security

How does concolic execution compare to ABI recovery tools?

By Meridion Risk

ABI recovery tools are commonly used to derive the entry points from bytecode if the source code is unavailable. Functionally, they perform a bytecode check for PUSH4 instructions that look like selector comparisons and return a list of function selectors. This method works for a straightforward Solidity contract without obfuscation.

If our threat model includes a tampered compiler, a supply-chain compromise, or a deliberately obfuscated dispatcher, we need a more sophisticated method to exhaustively detect all entry points.

We performed a comparison of WhatsABI with concolic execution: Seven obfuscated selectors. WhatsABI recovered zero. Concolic execution recovered all seven.

The Setup

We started from a simple baseline: a dispatcher that compares calldata against a selector via a single PUSH4 followed by an EQ. This is the case every tool handles. It is the surface syntax of selector dispatch, and any scanner that looks for PUSH4 will catch it.

Then we introduced one obfuscated test case at a time, each representing a class of transformation an attacker could apply to hide a callable entry point.

The seven obfuscation classes

Each test case takes a real injected selector and routes it through a different kind of algebra before the final comparison:

  1. Shifted constant. The selector is stored shifted and rebuilt with SHL or SHR at dispatch time. No PUSH4 of the original four bytes ever appears.
  2. XOR-masked selector. The selector is XOR'd with a constant at compile time. The dispatcher XORs the incoming calldata with the same constant before comparing. The stored value does not resemble any known selector.
  3. Split-bytes assembly. The four selector bytes are pushed individually and reassembled with shifts and ORs, avoiding any use of PUSH4.
  4. Calldata-side XOR transform. Instead of transforming the stored constant, the dispatcher transforms calldata before comparing it against a plaintext constant. The four-byte value in the bytecode does not match the selector a caller would send.
  5. Calldata-side ADD transform. Same structure as the XOR case, but using ADD. Scanning for the selector in bytecode still produces nothing, because the constant being compared against calldata+k is not the selector itself.
  6. Storage-backed selector written at construction. The constructor writes the selector to a storage slot. The dispatcher reads that slot and compares it against calldata. Neither the ABI nor a runtime bytecode scan will reveal the value as it lives in storage, set at deploy time.
  7. Storage XOR-mask combo. The constructor writes a masked selector to storage. The dispatcher reads it, unmasks it, then compares. Two levels of indirection: one through storage, one through arithmetic. No static scan of the runtime bytecode can resolve it.

What WhatsABI recovered

WhatsABI is one of the best-known tools for recovering selectors from deployed bytecode. It uses heuristics tuned to common compiler output. Its detection model is built around recognizing patterns in the bytecode surface without semantic analysis of the dispatch logic.

Across all seven obfuscated test cases, WhatsABI recovered the injected selector in zero of them. The PUSH4-based patterns it looks for are not present. The heuristics that work well on normal Solidity output cannot handle obfuscation.

Why concolic execution works

Concolic execution does not rely on static analysis; it runs the bytecode. A concrete input drives real execution along a real path, while a symbolic shadow tracks the constraints that every branch imposes on calldata. When the dispatcher branches on whether a computed value equals some threshold, the symbolic state captures that condition as an equation and can reason about it.

That means it does not matter how the dispatcher assembles its comparison value. XOR, ADD, shift, storage read, any combination: the engine records the branch predicate and solves for the calldata that satisfies it.

The takeaway for bytecode assurance

Exhaustive entrypoint recovery needs concolic execution.

We conclude that concolic execution is essential for two use cases:

(1) assuring that all entry points in a given bytecode correspond exactly to the intended entry points defined in the Solidity codebase; and

(2) uncovering all entry points in an unknown piece of bytecode when the source code is unavailable.

Read the full case study about detecting supply-chain attacks on solc to understand the motivation for our tool.