Skip to content

Commit 1842108

Browse files
committed
feat: Enable sibling discovery and offset handling in cross-function analysis
This commit significantly enhances the robustness of structure reconstruction by improving how the analyzer traverses the call graph, specifically handling pointer arithmetic and identifying related "sibling" functions. - include/structor/cross_function_analyzer.hpp, src/cross_function_analyzer.cpp: - Implemented sibling discovery: when tracing backward to a caller, the analyzer now also traces forward to find other callees receiving the same variable. This ensures that analyzing `init_obj(ptr)` also discovers `process_obj(ptr)` via their common caller. - Updated `CallerFinder` to extract pointer offsets (deltas) from call arguments (e.g., `func((char*)ptr + 0x10)`). - Modified `trace_backward` to normalize field access offsets based on these deltas, allowing correct reconstruction of substructures. - include/structor/type_propagator.hpp: - Added forward propagation logic to ensure synthesized types are applied to sibling functions. - Enhanced `find_base_var` to robustly identify base variables through complex expressions (casts, refs, pointer arithmetic). - src/z3/layout_constraints.cpp, src/z3/field_candidates.cpp: - Added `add_type_preference_constraints` to favor typed fields over raw bytes when overlapping candidates exist. - Implemented `fill_gaps_with_padding` to insert explicit padding fields in the final structure. - Added comprehensive logging to the Z3 constraint building and solving phases for better debugging. - include/structor/access_collector.hpp: - Added `is_zero_initialization` to detect and flag writes that set fields to zero/NULL. - integration_tests/: - Added comprehensive test suite (Python scripts and C source) verifying propagation across linked-list operations and substructure passing. Impact: Users will see more complete structure definitions when analyzing code where pointers are passed between multiple functions. The tool can now correctly infer full structures even when functions only access specific substructures (offsets), and types are propagated more reliably across the entire usage graph.
1 parent fa17365 commit 1842108

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3346
-224
lines changed

.github/workflows/build.yml

Lines changed: 144 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,69 @@ jobs:
1717
fail-fast: false
1818
matrix:
1919
include:
20+
# IDA stable builds
2021
- os: macos-latest
2122
name: macos-x86_64
2223
cmake_arch: x86_64
24+
ida_branch: ""
25+
ida_suffix: ""
2326
- os: macos-latest
2427
name: macos-arm64
2528
cmake_arch: arm64
29+
ida_branch: ""
30+
ida_suffix: ""
2631
- os: ubuntu-latest
2732
name: linux-x86_64
33+
ida_branch: ""
34+
ida_suffix: ""
2835
- os: windows-latest
2936
name: windows-x86_64
3037
cmake_arch: x64
38+
ida_branch: ""
39+
ida_suffix: ""
40+
# IDA 9.3 beta builds
41+
- os: macos-latest
42+
name: macos-x86_64
43+
cmake_arch: x86_64
44+
ida_branch: releases/9.3.0-beta
45+
ida_suffix: "-ida93beta"
46+
- os: macos-latest
47+
name: macos-arm64
48+
cmake_arch: arm64
49+
ida_branch: releases/9.3.0-beta
50+
ida_suffix: "-ida93beta"
51+
- os: ubuntu-latest
52+
name: linux-x86_64
53+
ida_branch: releases/9.3.0-beta
54+
ida_suffix: "-ida93beta"
55+
- os: windows-latest
56+
name: windows-x86_64
57+
cmake_arch: x64
58+
ida_branch: releases/9.3.0-beta
59+
ida_suffix: "-ida93beta"
3160

3261
runs-on: ${{ matrix.os }}
33-
name: Build ${{ matrix.name }}
62+
name: Build ${{ matrix.name }}${{ matrix.ida_suffix }}
3463

3564
steps:
3665
- name: Checkout repository
3766
uses: actions/checkout@v4
3867
with:
3968
submodules: recursive
4069

41-
- name: Checkout IDA SDK
70+
- name: Checkout IDA SDK (stable)
71+
if: matrix.ida_branch == ''
72+
uses: actions/checkout@v4
73+
with:
74+
repository: ${{ env.IDA_SDK_REPO }}
75+
path: ida-sdk
76+
77+
- name: Checkout IDA SDK (beta)
78+
if: matrix.ida_branch != ''
4279
uses: actions/checkout@v4
4380
with:
4481
repository: ${{ env.IDA_SDK_REPO }}
82+
ref: ${{ matrix.ida_branch }}
4583
path: ida-sdk
4684

4785
- name: Setup MSVC (Windows)
@@ -56,6 +94,75 @@ jobs:
5694
sudo apt-get update
5795
sudo apt-get install -y build-essential cmake
5896
97+
- name: Cache Z3
98+
id: cache-z3
99+
uses: actions/cache@v4
100+
with:
101+
path: ${{ github.workspace }}/z3-install
102+
key: z3-4.15.4-${{ matrix.name }}
103+
104+
- name: Download Z3 (macOS)
105+
if: runner.os == 'macOS' && steps.cache-z3.outputs.cache-hit != 'true'
106+
run: |
107+
Z3_VERSION="4.15.4"
108+
if [ "${{ matrix.cmake_arch }}" = "arm64" ]; then
109+
Z3_ARCHIVE="z3-${Z3_VERSION}-arm64-osx-13.7.6.zip"
110+
else
111+
Z3_ARCHIVE="z3-${Z3_VERSION}-x64-osx-13.7.6.zip"
112+
fi
113+
Z3_URL="https://github.com/Z3Prover/z3/releases/download/z3-${Z3_VERSION}/${Z3_ARCHIVE}"
114+
115+
curl -L -o z3.zip "$Z3_URL"
116+
unzip z3.zip
117+
mv z3-${Z3_VERSION}-* z3-install
118+
119+
echo "Z3 installed to: $(pwd)/z3-install"
120+
ls -la z3-install/
121+
122+
- name: Download Z3 (Linux)
123+
if: runner.os == 'Linux' && steps.cache-z3.outputs.cache-hit != 'true'
124+
run: |
125+
Z3_VERSION="4.15.4"
126+
Z3_ARCHIVE="z3-${Z3_VERSION}-x64-glibc-2.39.zip"
127+
Z3_URL="https://github.com/Z3Prover/z3/releases/download/z3-${Z3_VERSION}/${Z3_ARCHIVE}"
128+
129+
curl -L -o z3.zip "$Z3_URL"
130+
unzip z3.zip
131+
mv z3-${Z3_VERSION}-* z3-install
132+
133+
echo "Z3 installed to: $(pwd)/z3-install"
134+
ls -la z3-install/
135+
136+
- name: Download Z3 (Windows)
137+
if: runner.os == 'Windows' && steps.cache-z3.outputs.cache-hit != 'true'
138+
shell: pwsh
139+
run: |
140+
$z3Version = "4.15.4"
141+
$z3Archive = "z3-$z3Version-x64-win.zip"
142+
$z3Url = "https://github.com/Z3Prover/z3/releases/download/z3-$z3Version/$z3Archive"
143+
144+
Invoke-WebRequest -Uri $z3Url -OutFile z3.zip
145+
Expand-Archive -Path z3.zip -DestinationPath . -Force
146+
$extractedDir = Get-ChildItem -Directory -Filter "z3-$z3Version-*" | Select-Object -First 1
147+
Rename-Item $extractedDir.Name "z3-install"
148+
149+
Write-Host "Z3 installed to: ${{ github.workspace }}/z3-install"
150+
Write-Host "Directory structure:"
151+
Get-ChildItem z3-install/ -Recurse | Where-Object { $_.Extension -in ".dll", ".lib", ".h" } | ForEach-Object { $_.FullName }
152+
153+
- name: Set Z3 environment (Unix)
154+
if: runner.os != 'Windows'
155+
run: |
156+
echo "Z3_ROOT=${{ github.workspace }}/z3-install" >> $GITHUB_ENV
157+
echo "CMAKE_PREFIX_PATH=${{ github.workspace }}/z3-install" >> $GITHUB_ENV
158+
159+
- name: Set Z3 environment (Windows)
160+
if: runner.os == 'Windows'
161+
shell: pwsh
162+
run: |
163+
echo "Z3_ROOT=${{ github.workspace }}/z3-install" >> $env:GITHUB_ENV
164+
echo "CMAKE_PREFIX_PATH=${{ github.workspace }}/z3-install" >> $env:GITHUB_ENV
165+
59166
- name: Configure CMake (macOS)
60167
if: runner.os == 'macOS'
61168
run: |
@@ -64,7 +171,10 @@ jobs:
64171
cmake .. \
65172
-DCMAKE_BUILD_TYPE=Release \
66173
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.cmake_arch }} \
67-
-DIDA_SDK_DIR=${{ github.workspace }}/ida-sdk/src
174+
-DIDA_SDK_DIR=${{ github.workspace }}/ida-sdk/src \
175+
-DZ3_USE_CUSTOM=ON \
176+
-DZ3_CUSTOM_INCLUDE_DIR=${{ github.workspace }}/z3-install/include \
177+
-DZ3_CUSTOM_LIBRARY=${{ github.workspace }}/z3-install/bin/libz3.dylib
68178
69179
- name: Configure CMake (Linux)
70180
if: runner.os == 'Linux'
@@ -73,14 +183,22 @@ jobs:
73183
cd build
74184
cmake .. \
75185
-DCMAKE_BUILD_TYPE=Release \
76-
-DIDA_SDK_DIR=${{ github.workspace }}/ida-sdk/src
186+
-DIDA_SDK_DIR=${{ github.workspace }}/ida-sdk/src \
187+
-DZ3_USE_CUSTOM=ON \
188+
-DZ3_CUSTOM_INCLUDE_DIR=${{ github.workspace }}/z3-install/include \
189+
-DZ3_CUSTOM_LIBRARY=${{ github.workspace }}/z3-install/bin/libz3.so
77190
78191
- name: Configure CMake (Windows)
79192
if: runner.os == 'Windows'
193+
shell: pwsh
80194
run: |
81195
mkdir build -Force
82196
cd build
83-
cmake .. -G "Visual Studio 17 2022" -A ${{ matrix.cmake_arch }} -DIDA_SDK_DIR=${{ github.workspace }}/ida-sdk/src
197+
# Find the import library - could be in bin/ or lib/
198+
$z3Root = "${{ github.workspace }}/z3-install"
199+
$implib = if (Test-Path "$z3Root/lib/libz3.lib") { "$z3Root/lib/libz3.lib" } else { "$z3Root/bin/libz3.lib" }
200+
Write-Host "Using Z3 import library: $implib"
201+
cmake .. -G "Visual Studio 17 2022" -A ${{ matrix.cmake_arch }} "-DIDA_SDK_DIR=${{ github.workspace }}/ida-sdk/src" -DZ3_USE_CUSTOM=ON "-DZ3_CUSTOM_INCLUDE_DIR=$z3Root/include" "-DZ3_CUSTOM_LIBRARY=$z3Root/bin/libz3.dll" "-DZ3_CUSTOM_IMPLIB=$implib"
84202
85203
- name: Build (Unix)
86204
if: runner.os != 'Windows'
@@ -99,10 +217,10 @@ jobs:
99217
id: find_plugin_unix
100218
run: |
101219
if [ "${{ runner.os }}" = "macOS" ]; then
102-
PLUGIN_FILE=$(find build -name "structor64.dylib" -type f | head -1)
220+
PLUGIN_FILE=$(find build -name "structor.dylib" -type f | head -1)
103221
EXT="dylib"
104222
else
105-
PLUGIN_FILE=$(find build -name "structor64.so" -type f | head -1)
223+
PLUGIN_FILE=$(find build -name "structor.so" -type f | head -1)
106224
EXT="so"
107225
fi
108226
@@ -121,7 +239,7 @@ jobs:
121239
id: find_plugin_windows
122240
shell: pwsh
123241
run: |
124-
$plugin = Get-ChildItem -Path build -Recurse -Filter "structor64.dll" | Select-Object -First 1
242+
$plugin = Get-ChildItem -Path build -Recurse -Filter "structor.dll" | Select-Object -First 1
125243
if ($null -eq $plugin) {
126244
Write-Error "Could not find built plugin"
127245
Get-ChildItem -Path build -Recurse -Filter "structor*"
@@ -131,23 +249,35 @@ jobs:
131249
echo "extension=dll" >> $env:GITHUB_OUTPUT
132250
Write-Host "Found plugin: $($plugin.FullName)"
133251
134-
- name: Prepare artifact (Unix)
135-
if: runner.os != 'Windows'
252+
- name: Prepare artifact (macOS)
253+
if: runner.os == 'macOS'
254+
run: |
255+
mkdir -p artifacts
256+
cp "${{ steps.find_plugin_unix.outputs.plugin_file }}" "artifacts/structor${{ matrix.ida_suffix }}_${{ matrix.name }}.${{ steps.find_plugin_unix.outputs.extension }}"
257+
# Copy Z3 library
258+
cp "${{ github.workspace }}/z3-install/bin/libz3.dylib" "artifacts/libz3_${{ matrix.name }}.dylib"
259+
260+
- name: Prepare artifact (Linux)
261+
if: runner.os == 'Linux'
136262
run: |
137263
mkdir -p artifacts
138-
cp "${{ steps.find_plugin_unix.outputs.plugin_file }}" "artifacts/structor64_${{ matrix.name }}.${{ steps.find_plugin_unix.outputs.extension }}"
264+
cp "${{ steps.find_plugin_unix.outputs.plugin_file }}" "artifacts/structor${{ matrix.ida_suffix }}_${{ matrix.name }}.${{ steps.find_plugin_unix.outputs.extension }}"
265+
# Copy Z3 library
266+
cp "${{ github.workspace }}/z3-install/bin/libz3.so" "artifacts/libz3_${{ matrix.name }}.so"
139267
140268
- name: Prepare artifact (Windows)
141269
if: runner.os == 'Windows'
142270
shell: pwsh
143271
run: |
144272
New-Item -ItemType Directory -Force -Path artifacts
145-
Copy-Item "${{ steps.find_plugin_windows.outputs.plugin_file }}" "artifacts/structor64_${{ matrix.name }}.${{ steps.find_plugin_windows.outputs.extension }}"
273+
Copy-Item "${{ steps.find_plugin_windows.outputs.plugin_file }}" "artifacts/structor${{ matrix.ida_suffix }}_${{ matrix.name }}.${{ steps.find_plugin_windows.outputs.extension }}"
274+
# Copy Z3 library
275+
Copy-Item "${{ github.workspace }}/z3-install/bin/libz3.dll" "artifacts/libz3_${{ matrix.name }}.dll"
146276
147277
- name: Upload artifact
148278
uses: actions/upload-artifact@v4
149279
with:
150-
name: structor-${{ matrix.name }}
280+
name: structor${{ matrix.ida_suffix }}-${{ matrix.name }}
151281
path: artifacts/
152282
retention-days: 30
153283

@@ -167,6 +297,7 @@ jobs:
167297
- name: Prepare release files
168298
run: |
169299
mkdir -p release
300+
# Copy all plugin and Z3 library files
170301
find artifacts -type f \( -name "*.dylib" -o -name "*.so" -o -name "*.dll" \) -exec cp {} release/ \;
171302
ls -la release/
172303

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,5 @@ tests/string_obfuscation/test_strings
9494

9595
.cache
9696
.claude
97+
98+
__pycache__

CMakeLists.txt

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,62 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
1010
# Z3 Theorem Prover Integration
1111
# ============================================================================
1212

13-
# Try to find system Z3 first (much faster than building from source)
14-
find_package(Z3 CONFIG QUIET)
15-
16-
if(Z3_FOUND)
17-
message(STATUS "Found system Z3: ${Z3_VERSION}")
13+
# Allow specifying custom Z3 location
14+
option(Z3_USE_CUSTOM "Use custom Z3 library instead of system" OFF)
15+
set(Z3_CUSTOM_INCLUDE_DIR "" CACHE PATH "Custom Z3 include directory")
16+
set(Z3_CUSTOM_LIBRARY "" CACHE FILEPATH "Custom Z3 library path")
17+
set(Z3_CUSTOM_IMPLIB "" CACHE FILEPATH "Custom Z3 import library path (Windows only)")
18+
19+
if(Z3_USE_CUSTOM AND Z3_CUSTOM_INCLUDE_DIR AND Z3_CUSTOM_LIBRARY)
20+
message(STATUS "Using custom Z3: ${Z3_CUSTOM_LIBRARY}")
1821
set(Z3_USE_SYSTEM TRUE)
22+
set(Z3_CUSTOM TRUE)
23+
24+
# Create imported target for custom Z3
25+
add_library(z3_custom SHARED IMPORTED)
26+
if(WIN32)
27+
# On Windows, we need the import library (.lib) for linking
28+
if(Z3_CUSTOM_IMPLIB)
29+
set_target_properties(z3_custom PROPERTIES
30+
IMPORTED_LOCATION "${Z3_CUSTOM_LIBRARY}"
31+
IMPORTED_IMPLIB "${Z3_CUSTOM_IMPLIB}"
32+
INTERFACE_INCLUDE_DIRECTORIES "${Z3_CUSTOM_INCLUDE_DIR}"
33+
)
34+
else()
35+
# Try to find .lib next to .dll or in lib/ directory
36+
get_filename_component(Z3_LIB_DIR "${Z3_CUSTOM_LIBRARY}" DIRECTORY)
37+
get_filename_component(Z3_LIB_DIR_PARENT "${Z3_LIB_DIR}" DIRECTORY)
38+
find_file(Z3_IMPLIB_FOUND
39+
NAMES libz3.lib z3.lib
40+
PATHS "${Z3_LIB_DIR}" "${Z3_LIB_DIR_PARENT}/lib"
41+
NO_DEFAULT_PATH
42+
)
43+
if(Z3_IMPLIB_FOUND)
44+
message(STATUS "Found Z3 import library: ${Z3_IMPLIB_FOUND}")
45+
set_target_properties(z3_custom PROPERTIES
46+
IMPORTED_LOCATION "${Z3_CUSTOM_LIBRARY}"
47+
IMPORTED_IMPLIB "${Z3_IMPLIB_FOUND}"
48+
INTERFACE_INCLUDE_DIRECTORIES "${Z3_CUSTOM_INCLUDE_DIR}"
49+
)
50+
else()
51+
message(FATAL_ERROR "Z3_CUSTOM_IMPLIB not set and could not find import library (.lib) for Z3")
52+
endif()
53+
endif()
54+
else()
55+
set_target_properties(z3_custom PROPERTIES
56+
IMPORTED_LOCATION "${Z3_CUSTOM_LIBRARY}"
57+
INTERFACE_INCLUDE_DIRECTORIES "${Z3_CUSTOM_INCLUDE_DIR}"
58+
)
59+
endif()
1960
else()
61+
# Try to find system Z3 first (much faster than building from source)
62+
find_package(Z3 CONFIG QUIET)
63+
64+
if(Z3_FOUND)
65+
message(STATUS "Found system Z3: ${Z3_VERSION}")
66+
set(Z3_USE_SYSTEM TRUE)
67+
set(Z3_CUSTOM FALSE)
68+
else()
2069
message(STATUS "System Z3 not found, building from source (this takes 15+ minutes)...")
2170
include(FetchContent)
2271

@@ -38,8 +87,10 @@ else()
3887
set(Z3_BUILD_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE)
3988
set(Z3_BUILD_DOTNET_BINDINGS OFF CACHE BOOL "" FORCE)
4089

41-
FetchContent_MakeAvailable(z3)
42-
set(Z3_USE_SYSTEM FALSE)
90+
FetchContent_MakeAvailable(z3)
91+
set(Z3_USE_SYSTEM FALSE)
92+
set(Z3_CUSTOM FALSE)
93+
endif()
4394
endif()
4495

4596
# Verify exceptions are enabled (required for Z3 C++ API)
@@ -240,7 +291,9 @@ if(HEXRAYS_LIB)
240291
endif()
241292

242293
# Link Z3 to structor
243-
if(Z3_USE_SYSTEM)
294+
if(Z3_CUSTOM)
295+
target_link_libraries(structor${IDA_SUFFIX} PRIVATE z3_custom)
296+
elseif(Z3_USE_SYSTEM)
244297
target_link_libraries(structor${IDA_SUFFIX} PRIVATE z3::libz3)
245298
# System Z3 includes are handled by the imported target
246299
else()
@@ -254,7 +307,7 @@ endif()
254307

255308
# Set plugin output name and properties
256309
set_target_properties(structor${IDA_SUFFIX} PROPERTIES
257-
OUTPUT_NAME "structor${IDA_SUFFIX}"
310+
OUTPUT_NAME "structor"
258311
SUFFIX ${PLUGIN_EXT}
259312
PREFIX ""
260313
)

0 commit comments

Comments
 (0)