feat(analysis): add support for IDA Pro 6.6
This commit is contained in:
parent
a1c5a70429
commit
f2ba9b2019
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@ target/
|
|||||||
# Uploaded binaries
|
# Uploaded binaries
|
||||||
uploads/
|
uploads/
|
||||||
engines/ida5/ida5_files/**
|
engines/ida5/ida5_files/**
|
||||||
|
engines/ida66/ida66_files/**
|
||||||
|
|
||||||
### Intellij ###
|
### Intellij ###
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
|||||||
62
engines/ida66/Dockerfile.ida66
Normal file
62
engines/ida66/Dockerfile.ida66
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install 32-bit Wine, Xvfb, and utilities
|
||||||
|
RUN dpkg --add-architecture i386 && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
wine \
|
||||||
|
wine32 \
|
||||||
|
xvfb \
|
||||||
|
xauth \
|
||||||
|
fonts-dejavu-core \
|
||||||
|
fonts-liberation && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Configure Wine environment variables
|
||||||
|
ENV WINEPREFIX=/wine/prefix
|
||||||
|
ENV WINEARCH=win32
|
||||||
|
ENV WINEDLLOVERRIDES="mscoree,mshtml="
|
||||||
|
|
||||||
|
RUN mkdir -p /wine/prefix
|
||||||
|
|
||||||
|
# Initialize the default Wine prefix
|
||||||
|
RUN xvfb-run -a wine wineboot -u && wineserver -w && ls -la /wine/prefix/*.reg
|
||||||
|
|
||||||
|
# Install Python 2.7 in Wine for IDAPython
|
||||||
|
COPY ./python-2.7.18.msi /tmp/python-2.7.18.msi
|
||||||
|
RUN xvfb-run -a wine msiexec /i /tmp/python-2.7.18.msi /qn && \
|
||||||
|
rm /tmp/python-2.7.18.msi
|
||||||
|
|
||||||
|
# Set up application directories
|
||||||
|
RUN mkdir -p /app/ida /input /output
|
||||||
|
COPY ./ida66_files /app/ida
|
||||||
|
|
||||||
|
# IDAPython local runtime: copy Python stdlib into IDA's python/ directory
|
||||||
|
RUN cp /wine/prefix/drive_c/Python27/python27.dll /app/ida/ && \
|
||||||
|
cp -r /wine/prefix/drive_c/Python27/Lib /app/ida/python/lib/ && \
|
||||||
|
cp -r /wine/prefix/drive_c/Python27/DLLs /app/ida/python/DLLs/ && \
|
||||||
|
sed -i 's/USE_LOCAL_PYTHON = 0/USE_LOCAL_PYTHON = 1/' /app/ida/cfg/python.cfg
|
||||||
|
|
||||||
|
# Import IDA 6.6 license configuration into Wine registry
|
||||||
|
COPY ida6_license.reg /tmp/ida6_license.reg
|
||||||
|
COPY ida-hkcu.sh /tmp/ida-hkcu.sh
|
||||||
|
RUN wine regedit /tmp/ida6_license.reg && \
|
||||||
|
rm /tmp/ida6_license.reg && \
|
||||||
|
bash /tmp/ida-hkcu.sh && \
|
||||||
|
rm /tmp/ida-hkcu.sh && \
|
||||||
|
wineserver -w && \
|
||||||
|
grep "Hex-Rays" /wine/prefix/user.reg
|
||||||
|
|
||||||
|
WORKDIR /app/ida
|
||||||
|
|
||||||
|
# Embed the analysis scripts into the image
|
||||||
|
COPY extract.py /app/ida/extract.py
|
||||||
|
|
||||||
|
# Configure the entrypoint script
|
||||||
|
COPY entrypoint.sh /app/ida/entrypoint.sh
|
||||||
|
RUN chmod +x /app/ida/entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/ida/entrypoint.sh"]
|
||||||
39
engines/ida66/entrypoint.sh
Normal file
39
engines/ida66/entrypoint.sh
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export WINEPREFIX="/wine/prefix"
|
||||||
|
export WINEARCH=win32
|
||||||
|
export WINEDEBUG=-all
|
||||||
|
export DISPLAY=:99
|
||||||
|
|
||||||
|
echo "[INFO] Starting virtual X11 display on :99 (1280x720)..."
|
||||||
|
Xvfb :99 -screen 0 1280x720x24 +extension RANDR &
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Accept target filename as first argument
|
||||||
|
TARGET_FILENAME="${1:-target.exe}"
|
||||||
|
|
||||||
|
if [ ! -f "/input/${TARGET_FILENAME}" ]; then
|
||||||
|
echo "[ERROR] Missing /input/${TARGET_FILENAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[INFO] Preparing internal analysis environment..."
|
||||||
|
cp "/input/${TARGET_FILENAME}" "/app/ida/target.exe"
|
||||||
|
|
||||||
|
echo "[INFO] Launching IDA Pro 6.6 (auto-script)..."
|
||||||
|
wine idaq.exe -A -S"extract.py" target.exe
|
||||||
|
|
||||||
|
echo "[INFO] Verifying generated artifacts..."
|
||||||
|
if [ -f "/app/ida/output.json" ]; then
|
||||||
|
cp /app/ida/output.json /output/output.json
|
||||||
|
echo "[SUCCESS] Static analysis completed. output.json is ready!"
|
||||||
|
RET_CODE=0
|
||||||
|
else
|
||||||
|
echo "[ERROR] IDAPython script failed to generate output.json"
|
||||||
|
RET_CODE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up temporary files inside the container before exiting
|
||||||
|
rm -f /app/ida/target.exe /app/ida/target.idb /app/ida/output.json
|
||||||
|
|
||||||
|
exit $RET_CODE
|
||||||
228
engines/ida66/extract.py
Normal file
228
engines/ida66/extract.py
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# IDAPython extraction script for IDA Pro 6.6
|
||||||
|
# Equivalent to extract.idc — same JSON output structure
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
import idc
|
||||||
|
import idaapi
|
||||||
|
import idautils
|
||||||
|
|
||||||
|
OUTPUT = "output.json"
|
||||||
|
|
||||||
|
# Cross-reference type constants (IDA SDK values)
|
||||||
|
FL_CF = 16 # Call Far
|
||||||
|
FL_CN = 17 # Call Near
|
||||||
|
FL_JF = 18 # Jump Far
|
||||||
|
FL_JN = 19 # Jump Near
|
||||||
|
|
||||||
|
|
||||||
|
def msg(text):
|
||||||
|
idc.Message(text)
|
||||||
|
sys.stderr.write(text)
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def _json_string(s):
|
||||||
|
"""Escape a string for JSON — returns the quoted representation.
|
||||||
|
Handles raw bytes in disassembly that aren't valid UTF-8."""
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
return json.dumps(s)
|
||||||
|
try:
|
||||||
|
return json.dumps(s)
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return json.dumps(s.decode('latin-1'))
|
||||||
|
|
||||||
|
|
||||||
|
def _json_hex(addr):
|
||||||
|
"""Format an address as 0x-prefixed hex string (uppercase, 8 digits)."""
|
||||||
|
return "0x%08X" % addr
|
||||||
|
|
||||||
|
|
||||||
|
def extract_func(f, ea):
|
||||||
|
"""Extract a single function. Returns True on success, False on failure."""
|
||||||
|
func_name = idc.GetFunctionName(ea)
|
||||||
|
func_end = idc.FindFuncEnd(ea)
|
||||||
|
func_flags = idc.GetFunctionFlags(ea)
|
||||||
|
|
||||||
|
f.write(" {\n")
|
||||||
|
f.write(" \"function\": %s,\n" % _json_string(func_name))
|
||||||
|
f.write(" \"address\": \"%s\",\n" % _json_hex(ea))
|
||||||
|
f.write(" \"size\": %d,\n" % (func_end - ea))
|
||||||
|
|
||||||
|
# ---- flags ----
|
||||||
|
flags_parts = []
|
||||||
|
if func_flags & idc.FUNC_LIB:
|
||||||
|
flags_parts.append("LIBRARY")
|
||||||
|
if func_flags & idc.FUNC_THUNK:
|
||||||
|
flags_parts.append("THUNK")
|
||||||
|
if func_flags & idc.FUNC_FAR:
|
||||||
|
flags_parts.append("FAR")
|
||||||
|
if func_flags & idc.FUNC_FRAME:
|
||||||
|
flags_parts.append("FRAME")
|
||||||
|
f.write(" \"flags\": %s,\n" % _json_string("|".join(flags_parts)))
|
||||||
|
|
||||||
|
# ---- xrefs (who calls/jumps to this function) ----
|
||||||
|
xref_parts = []
|
||||||
|
for xref in idautils.XrefsTo(ea):
|
||||||
|
if xref.type in (FL_CF, FL_CN, FL_JF, FL_JN):
|
||||||
|
caller_name = idc.GetFunctionName(xref.frm)
|
||||||
|
if caller_name != "":
|
||||||
|
if xref.type == FL_CN or xref.type == FL_CF:
|
||||||
|
type_str = "CALL"
|
||||||
|
elif xref.type == FL_JN or xref.type == FL_JF:
|
||||||
|
type_str = "JUMP"
|
||||||
|
else:
|
||||||
|
type_str = "UNKNOWN"
|
||||||
|
xref_parts.append("%s:%s" % (caller_name, type_str))
|
||||||
|
f.write(" \"xrefs\": %s,\n" % _json_string(" ".join(xref_parts)))
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
# ---- assembly ----
|
||||||
|
asm_lines = []
|
||||||
|
curr_inst = ea
|
||||||
|
last_inst = 0
|
||||||
|
while curr_inst < func_end:
|
||||||
|
if curr_inst == last_inst:
|
||||||
|
break
|
||||||
|
last_inst = curr_inst
|
||||||
|
disasm = idc.GetDisasm(curr_inst)
|
||||||
|
if disasm:
|
||||||
|
asm_lines.append("%s %s" % (_json_hex(curr_inst), disasm))
|
||||||
|
curr_inst = curr_inst + idc.ItemSize(curr_inst)
|
||||||
|
f.write(" \"assembly\": %s,\n" % _json_string("\n".join(asm_lines)))
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
# ---- labels (named data/code references from this function) ----
|
||||||
|
label_parts = []
|
||||||
|
curr_inst = ea
|
||||||
|
last_inst = 0
|
||||||
|
while curr_inst < func_end:
|
||||||
|
if curr_inst == last_inst:
|
||||||
|
break
|
||||||
|
last_inst = curr_inst
|
||||||
|
for xref in idautils.XrefsFrom(curr_inst):
|
||||||
|
if xref.type in (FL_CF, FL_CN, FL_JF, FL_JN):
|
||||||
|
label_name = idc.Name(xref.to)
|
||||||
|
if label_name != "":
|
||||||
|
if xref.type == FL_CN or xref.type == FL_CF:
|
||||||
|
type_str = "CALL"
|
||||||
|
elif xref.type == FL_JN or xref.type == FL_JF:
|
||||||
|
type_str = "JUMP"
|
||||||
|
else:
|
||||||
|
type_str = "UNKNOWN"
|
||||||
|
label_parts.append(
|
||||||
|
"%s:%s:%s" % (label_name, _json_hex(xref.to), type_str)
|
||||||
|
)
|
||||||
|
curr_inst = curr_inst + idc.ItemSize(curr_inst)
|
||||||
|
f.write(" \"labels\": %s\n" % _json_string(" ".join(label_parts)))
|
||||||
|
f.write(" }")
|
||||||
|
f.flush()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def extract(f):
|
||||||
|
entry_ea = idc.MinEA()
|
||||||
|
entry_name = idc.Name(entry_ea)
|
||||||
|
if entry_name == "":
|
||||||
|
entry_name = "entry_point"
|
||||||
|
f.write(
|
||||||
|
"{\n \"entry_point\": \"%s:%s\",\n" % (_json_hex(entry_ea), entry_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- segments ----
|
||||||
|
f.write(" \"segments\": [\n")
|
||||||
|
segs = []
|
||||||
|
seg = idc.FirstSeg()
|
||||||
|
while seg != idc.BADADDR:
|
||||||
|
segs.append(
|
||||||
|
" {\"name\": %s, \"start\": \"%s\", \"end\": \"%s\"}"
|
||||||
|
% (
|
||||||
|
_json_string(idc.SegName(seg)),
|
||||||
|
_json_hex(idc.SegStart(seg)),
|
||||||
|
_json_hex(idc.SegEnd(seg)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
seg = idc.NextSeg(seg)
|
||||||
|
f.write(",\n".join(segs))
|
||||||
|
f.write("\n ],\n")
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
# ---- functions ----
|
||||||
|
f.write(" \"functions\": [\n")
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
ea = idc.NextFunction(0)
|
||||||
|
func_count = 0
|
||||||
|
error_count = 0
|
||||||
|
|
||||||
|
while ea != idc.BADADDR:
|
||||||
|
if func_count > 0:
|
||||||
|
f.write(",\n")
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if extract_func(f, ea):
|
||||||
|
func_count += 1
|
||||||
|
else:
|
||||||
|
error_count += 1
|
||||||
|
msg("[extract.py] Aviso: falha ao extrair funcao em %s\n"
|
||||||
|
% _json_hex(ea))
|
||||||
|
except Exception as e:
|
||||||
|
error_count += 1
|
||||||
|
tb_str = traceback.format_exc().strip().replace("\n", "\\n")
|
||||||
|
msg("[extract.py] Erro na funcao %s: %s\n"
|
||||||
|
% (_json_hex(ea), str(e)))
|
||||||
|
# Write a placeholder with the error info so JSON remains valid
|
||||||
|
f.write(
|
||||||
|
" {\"function\": \"__ERROR__\","
|
||||||
|
" \"address\": \"%s\","
|
||||||
|
" \"error\": %s}"
|
||||||
|
% (_json_hex(ea), _json_string(tb_str))
|
||||||
|
)
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
ea = idc.NextFunction(ea)
|
||||||
|
|
||||||
|
msg("[extract.py] Extraidas %d funcoes, %d erros.\n"
|
||||||
|
% (func_count, error_count))
|
||||||
|
|
||||||
|
f.write("\n ]\n}\n")
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
msg("[extract.py] Script iniciado. CWD=%s\n" % os.getcwd())
|
||||||
|
|
||||||
|
idaapi.autoWait()
|
||||||
|
msg("[extract.py] Analise concluida. Extraindo...\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Line-buffered — flushes after every newline, prevents data loss
|
||||||
|
f = open(OUTPUT, "w", 1)
|
||||||
|
except Exception as e:
|
||||||
|
msg("[extract.py] ERRO ao criar %s: %s\n" % (OUTPUT, str(e)))
|
||||||
|
idc.Exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
extract(f)
|
||||||
|
except Exception as e:
|
||||||
|
msg("[extract.py] ERRO na extracao: %s\n" % str(e))
|
||||||
|
msg(traceback.format_exc() + "\n")
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if os.path.exists(OUTPUT):
|
||||||
|
msg("[extract.py] %s gerado com sucesso (%d bytes)!\n" %
|
||||||
|
(OUTPUT, os.path.getsize(OUTPUT)))
|
||||||
|
else:
|
||||||
|
msg("[extract.py] FALHA: %s nao foi criado.\n" % OUTPUT)
|
||||||
|
|
||||||
|
idc.Exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
50
engines/ida66/ida-hkcu.sh
Normal file
50
engines/ida66/ida-hkcu.sh
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Import HKCU keys for IDA 6.6 — license + suppress nag dialogs
|
||||||
|
# These key names contain special chars that are tricky to escape in Dockerfile RUN
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "License Zhou Tao, Jiangsu Australia Sinuo Network Technology Co., Ltd." \
|
||||||
|
/t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "AutoCheckUpdates" /t REG_DWORD /d 0 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "AutoRequestUpdates" /t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "DisplayWelcome" /t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "InformedAboutUpdates2" /t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "InputDirectory" /t REG_SZ /d "C:/Program Files/IDA 6.6" /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "QuickFilterFlags" /t REG_DWORD /d 0 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "SplashWindow" /t REG_DWORD /d 0 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA" \
|
||||||
|
/v "48-3057-7374-2C6.6" /t REG_DWORD /d 0 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA\\Hidden Messages" \
|
||||||
|
/v "Congratulations the TIL validator is active " /t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA\\Hidden Messages" \
|
||||||
|
/v "GOOD NEWS THE BTREE VALIDATOR v66 IS ACTIVE The btree validator plugin will now verify the input d" \
|
||||||
|
/t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
wine reg add "HKEY_CURRENT_USER\\Software\\Hex-Rays\\IDA\\Hidden Messages" \
|
||||||
|
/v "The technical support period of the Hex Rays decompiler has expired You have 3 month grace period t" \
|
||||||
|
/t REG_DWORD /d 1 /f
|
||||||
|
|
||||||
|
echo "HKCU keys imported"
|
||||||
|
|
||||||
|
# Add Python27 to system PATH so python.plw finds python27.dll
|
||||||
|
echo "Updating system PATH..."
|
||||||
|
CUR=$(wine reg query "HKLM\\System\\CurrentControlSet\\Control\\Session Manager\\Environment" /v PATH 2>/dev/null | tr -d '\r' | awk '/PATH/{$1=$2=""; sub(/^[ \t]+/, ""); print}')
|
||||||
|
wine reg add "HKLM\\System\\CurrentControlSet\\Control\\Session Manager\\Environment" /v PATH /t REG_EXPAND_SZ /d "C:\\Python27;${CUR}" /f
|
||||||
|
echo "PATH: C:\\Python27;${CUR}"
|
||||||
234
engines/ida66/ida6_license.reg
Normal file
234
engines/ida66/ida6_license.reg
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
; =============================================================================
|
||||||
|
; IDA Pro 6.6 — License, settings, file associations, and runtime configuration
|
||||||
|
; Extracted from system.reg + user.reg Wine registry dumps
|
||||||
|
; =============================================================================
|
||||||
|
|
||||||
|
; --- HKEY_CURRENT_USER: IDA application settings and license ---
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA]
|
||||||
|
"48-3057-7374-2C6.6"=dword:00000000
|
||||||
|
"AutoCheckUpdates"=dword:00000000
|
||||||
|
"AutoRequestUpdates"=dword:00000001
|
||||||
|
"DisplayWelcome"=dword:00000001
|
||||||
|
"InformedAboutUpdates2"=dword:00000001
|
||||||
|
"InputDirectory"="C:/Program Files/IDA 6.6"
|
||||||
|
"License Zhou Tao, Jiangsu Australia Sinuo Network Technology Co., Ltd."=dword:00000001
|
||||||
|
"QState"=hex:40,00,42,00,79,00,74,00,65,00,41,00,72,00,72,00,61,00,79,00,28,00,\
|
||||||
|
00,00,00,00,00,00,ff,00,00,00,00,00,00,00,01,00,fd,00,00,00,00,00,00,00,02,\
|
||||||
|
00,00,00,00,00,00,00,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
fc,00,02,00,00,00,00,00,00,00,01,00,fb,00,00,00,00,00,00,00,1c,00,00,00,41,\
|
||||||
|
00,00,00,64,00,00,00,64,00,00,00,72,00,00,00,65,00,00,00,73,00,00,00,73,00,\
|
||||||
|
00,00,44,00,00,00,65,00,00,00,74,00,00,00,61,00,00,00,69,00,00,00,6c,00,00,\
|
||||||
|
00,73,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,\
|
||||||
|
00,00,51,00,00,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,02,00,00,00,00,00,07,\
|
||||||
|
00,80,00,00,00,00,00,00,00,32,00,fc,00,01,00,00,00,00,00,00,00,01,00,fb,00,\
|
||||||
|
00,00,00,00,00,00,12,00,00,00,4e,00,00,00,61,00,00,00,76,00,00,00,69,00,00,\
|
||||||
|
00,67,00,00,00,61,00,00,00,74,00,00,00,6f,00,00,00,72,00,01,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,07,00,80,00,00,00,00,00,00,00,46,00,00,00,ff,00,ff,\
|
||||||
|
00,ff,00,00,00,00,00,07,00,80,00,00,00,00,00,03,00,7b,00,00,00,00,00,00,00,\
|
||||||
|
04,00,00,00,00,00,00,00,04,00,00,00,00,00,00,00,08,00,00,00,00,00,00,00,08,\
|
||||||
|
00,fc,00,00,00,00,00,00,00,01,00,00,00,00,00,00,00,02,00,00,00,00,00,00,00,\
|
||||||
|
19,00,00,00,00,00,00,00,16,00,00,00,46,00,00,00,69,00,00,00,6c,00,00,00,65,\
|
||||||
|
00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,\
|
||||||
|
00,00,72,00,01,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,16,00,00,00,4a,00,\
|
||||||
|
00,00,75,00,00,00,6d,00,00,00,70,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,\
|
||||||
|
00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,01,00,00,00,00,00,00,00,3a,00,\
|
||||||
|
ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,1a,00,00,00,53,00,00,00,65,00,00,00,61,00,00,00,72,00,00,00,\
|
||||||
|
63,00,00,00,68,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,\
|
||||||
|
00,00,00,61,00,00,00,72,00,01,00,00,00,00,00,00,00,8c,00,ff,00,ff,00,ff,00,\
|
||||||
|
ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,1e,\
|
||||||
|
00,00,00,41,00,00,00,6e,00,00,00,61,00,00,00,6c,00,00,00,79,00,00,00,73,00,\
|
||||||
|
00,00,69,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,\
|
||||||
|
00,42,00,00,00,61,00,00,00,72,00,01,00,00,00,00,00,01,00,34,00,ff,00,ff,00,\
|
||||||
|
ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,16,00,00,00,45,00,00,00,64,00,00,00,69,00,00,00,74,00,00,00,54,00,00,00,\
|
||||||
|
6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,01,00,00,\
|
||||||
|
00,00,00,01,00,6e,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,18,00,00,00,44,00,00,00,65,00,00,00,62,\
|
||||||
|
00,00,00,75,00,00,00,67,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,\
|
||||||
|
00,00,42,00,00,00,61,00,00,00,72,00,01,00,00,00,00,00,02,00,27,00,ff,00,ff,\
|
||||||
|
00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,24,00,00,00,42,00,00,00,72,00,00,00,65,00,00,00,61,00,00,00,6b,00,00,\
|
||||||
|
00,70,00,00,00,6f,00,00,00,69,00,00,00,6e,00,00,00,74,00,00,00,73,00,00,00,\
|
||||||
|
54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,\
|
||||||
|
00,01,00,00,00,00,00,03,00,3f,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,1e,00,00,00,53,00,00,00,65,\
|
||||||
|
00,00,00,67,00,00,00,6d,00,00,00,65,00,00,00,6e,00,00,00,74,00,00,00,73,00,\
|
||||||
|
00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,\
|
||||||
|
00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,20,00,00,00,47,00,00,\
|
||||||
|
00,72,00,00,00,61,00,00,00,70,00,00,00,68,00,00,00,56,00,00,00,69,00,00,00,\
|
||||||
|
65,00,00,00,77,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,\
|
||||||
|
00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,\
|
||||||
|
ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,22,\
|
||||||
|
00,00,00,53,00,00,00,74,00,00,00,72,00,00,00,75,00,00,00,63,00,00,00,74,00,\
|
||||||
|
00,00,75,00,00,00,72,00,00,00,65,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,\
|
||||||
|
00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,26,00,00,00,45,00,00,00,6e,00,00,00,75,00,00,00,\
|
||||||
|
6d,00,00,00,65,00,00,00,72,00,00,00,61,00,00,00,74,00,00,00,69,00,00,00,6f,\
|
||||||
|
00,00,00,6e,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,\
|
||||||
|
00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,\
|
||||||
|
00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,24,00,00,00,4f,00,00,00,70,00,00,00,65,00,00,00,72,00,00,00,61,00,00,\
|
||||||
|
00,6e,00,00,00,64,00,00,00,54,00,00,00,79,00,00,00,70,00,00,00,65,00,00,00,\
|
||||||
|
54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,20,00,00,00,46,00,00,00,75,\
|
||||||
|
00,00,00,6e,00,00,00,63,00,00,00,74,00,00,00,69,00,00,00,6f,00,00,00,6e,00,\
|
||||||
|
00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,\
|
||||||
|
00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,1c,00,00,\
|
||||||
|
00,54,00,00,00,72,00,00,00,61,00,00,00,63,00,00,00,69,00,00,00,6e,00,00,00,\
|
||||||
|
67,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,\
|
||||||
|
00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,18,00,00,00,4c,\
|
||||||
|
00,00,00,69,00,00,00,73,00,00,00,74,00,00,00,73,00,00,00,54,00,00,00,6f,00,\
|
||||||
|
00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,1a,00,00,00,47,00,00,00,72,00,00,00,61,00,00,\
|
||||||
|
00,70,00,00,00,68,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,\
|
||||||
|
6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,\
|
||||||
|
00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,24,00,00,00,44,00,00,00,62,00,00,00,67,00,00,00,43,00,00,00,6f,\
|
||||||
|
00,00,00,6d,00,00,00,6d,00,00,00,61,00,00,00,6e,00,00,00,64,00,00,00,73,00,\
|
||||||
|
00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,\
|
||||||
|
00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,2c,00,00,00,43,00,00,\
|
||||||
|
00,72,00,00,00,6f,00,00,00,73,00,00,00,73,00,00,00,52,00,00,00,65,00,00,00,\
|
||||||
|
66,00,00,00,65,00,00,00,72,00,00,00,65,00,00,00,6e,00,00,00,63,00,00,00,65,\
|
||||||
|
00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,\
|
||||||
|
00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,20,00,\
|
||||||
|
00,00,55,00,00,00,74,00,00,00,69,00,00,00,6c,00,00,00,69,00,00,00,74,00,00,\
|
||||||
|
00,69,00,00,00,65,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,\
|
||||||
|
6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,\
|
||||||
|
00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,20,00,00,00,53,00,00,00,69,00,00,00,67,00,00,00,6e,00,00,00,54,\
|
||||||
|
00,00,00,79,00,00,00,70,00,00,00,65,00,00,00,73,00,00,00,54,00,00,00,6f,00,\
|
||||||
|
00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,1c,00,00,00,57,00,00,00,61,00,00,00,74,00,00,\
|
||||||
|
00,63,00,00,00,68,00,00,00,65,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,\
|
||||||
|
6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,16,00,00,00,48,00,00,00,69,00,00,00,64,00,00,00,65,\
|
||||||
|
00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,61,00,\
|
||||||
|
00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,22,00,00,00,53,00,\
|
||||||
|
00,00,74,00,00,00,72,00,00,00,75,00,00,00,63,00,00,00,74,00,00,00,45,00,00,\
|
||||||
|
00,6e,00,00,00,75,00,00,00,6d,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,\
|
||||||
|
6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,\
|
||||||
|
00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,1e,00,00,00,43,00,00,00,6f,00,00,00,6d,00,00,00,6d,00,00,00,65,\
|
||||||
|
00,00,00,6e,00,00,00,74,00,00,00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,\
|
||||||
|
00,00,6c,00,00,00,42,00,00,00,61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,ff,00,ff,00,ff,00,ff,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\
|
||||||
|
00,00,00,00,00,00,18,00,00,00,56,00,00,00,69,00,00,00,65,00,00,00,77,00,00,\
|
||||||
|
00,73,00,00,00,54,00,00,00,6f,00,00,00,6f,00,00,00,6c,00,00,00,42,00,00,00,\
|
||||||
|
61,00,00,00,72,00,00,00,00,00,00,00,00,00,00,00,ff,00,ff,00,ff,00,ff,00,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,29,00
|
||||||
|
"QuickFilterFlags"=dword:00000000
|
||||||
|
"SplashWindow"=dword:00000000
|
||||||
|
"SupportExpiredDisplayed_2.0.0.140605"=dword:6a285788
|
||||||
|
"uiConfig"=hex:ff,ff,ff,ff,00,00,00,00,00,00,00,00,02,00,00,00,01,00,00,00,00,\
|
||||||
|
00,00,00,ff,ff,ff,ff,00,00,01,00,ff,ff,ff,ff,00,00,00,00,00,00,00,00,02,00,\
|
||||||
|
00,00,00,00,00,00,00,00,00,00,4d,00,01,05,00,00,00,00
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\Hidden Messages]
|
||||||
|
"Congratulations the TIL validator is active "=dword:00000001
|
||||||
|
"GOOD NEWS THE BTREE VALIDATOR v66 IS ACTIVE The btree validator plugin will now verify the input d"=dword:00000001
|
||||||
|
"The technical support period of the Hex Rays decompiler has expired You have 3 month grace period t"=dword:00000001
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\History]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\History_A]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\History_I]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\History_UIFLTR]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\Main Windows\Main window]
|
||||||
|
"Geometry"=hex:40,00,42,00,79,00,74,00,65,00,41,00,72,00,72,00,61,00,79,00,28,\
|
||||||
|
00,01,00,d9,00,d0,00,cb,00,00,00,01,00,00,00,00,00,00,00,00,00,01,00,e0,00,\
|
||||||
|
00,00,00,00,01,00,44,00,00,00,00,00,05,00,07,00,00,00,00,00,03,00,bd,00,00,\
|
||||||
|
00,00,00,01,00,e4,00,00,00,00,00,01,00,62,00,00,00,00,00,05,00,03,00,00,00,\
|
||||||
|
00,00,03,00,b9,00,00,00,00,00,00,00,00,00,00,00,00,00,29,00
|
||||||
|
"Maximized"=dword:00000001
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Hex-Rays\IDA\RecentScripts]
|
||||||
|
|
||||||
|
; --- HKEY_LOCAL_MACHINE: File type associations ---
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\.i64]
|
||||||
|
@="IDApro.Database64"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\.idb]
|
||||||
|
@="IDApro.Database32"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\IDApro.Database32]
|
||||||
|
@="IDA Database"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\IDApro.Database32\DefaultIcon]
|
||||||
|
@="C:\\Program Files\\IDA 6.6\\idaq.exe,0"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\IDApro.Database32\shell\open\command]
|
||||||
|
@="\"C:\\Program Files\\IDA 6.6\\idaq.exe\" \"%1\""
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\IDApro.Database64]
|
||||||
|
@="IDA Pro (64-bit) Database"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\IDApro.Database64\DefaultIcon]
|
||||||
|
@="C:\\Program Files\\IDA 6.6\\idaq64.exe,0"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\IDApro.Database64\shell\open\command]
|
||||||
|
@="\"C:\\Program Files\\IDA 6.6\\idaq64.exe\" \"%1\""
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\WinGraph.File]
|
||||||
|
@="WinGraph file"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\WinGraph.File\DefaultIcon]
|
||||||
|
@="C:\\Program Files\\IDA 6.6\\wingraph32.exe,0"
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Classes\WinGraph.File\shell\open\command]
|
||||||
|
@="\"C:\\Program Files\\IDA 6.6\\wingraph32.exe\" \"%1\""
|
||||||
|
|
||||||
|
; --- HKEY_LOCAL_MACHINE: Uninstall registration ---
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\IDA Pro_6.6_is1]
|
||||||
|
"DisplayIcon"="C:\\Program Files\\IDA 6.6\\idaq.exe"
|
||||||
|
"DisplayName"="IDA Pro v6.6 and Hex-Rays Decompiler (ARM,x64,x86)"
|
||||||
|
"EstimatedSize"=dword:00030f1b
|
||||||
|
"Inno Setup: App Path"="C:\\Program Files\\IDA 6.6"
|
||||||
|
"Inno Setup: Deselected Tasks"="desktopicon"
|
||||||
|
"Inno Setup: Icon Group"="(Default)"
|
||||||
|
"Inno Setup: Language"="default"
|
||||||
|
"Inno Setup: Selected Tasks"=""
|
||||||
|
"Inno Setup: Setup Version"="5.5.3 (a)"
|
||||||
|
"Inno Setup: User"="rov"
|
||||||
|
"InstallDate"="20260609"
|
||||||
|
"InstallLocation"="C:\\Program Files\\IDA 6.6\\"
|
||||||
|
"NoModify"=dword:00000001
|
||||||
|
"NoRepair"=dword:00000001
|
||||||
|
"Publisher"="Hex-Rays SA"
|
||||||
|
"QuietUninstallString"="\"C:\\Program Files\\IDA 6.6\\unins000.exe\" /SILENT"
|
||||||
|
"UninstallString"="\"C:\\Program Files\\IDA 6.6\\unins000.exe\""
|
||||||
|
|
||||||
|
; --- HKEY_LOCAL_MACHINE: DLL search behavior for IDA executables ---
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\idaq.exe]
|
||||||
|
"CWDIllegalInDllSearch"=dword:ffffffff
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\idaq64.exe]
|
||||||
|
"CWDIllegalInDllSearch"=dword:ffffffff
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\idaw.exe]
|
||||||
|
"CWDIllegalInDllSearch"=dword:ffffffff
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\idaw64.exe]
|
||||||
|
"CWDIllegalInDllSearch"=dword:ffffffff
|
||||||
|
|
||||||
BIN
engines/ida66/python-2.7.18.msi
Normal file
BIN
engines/ida66/python-2.7.18.msi
Normal file
Binary file not shown.
@ -2,5 +2,6 @@ package ai.decompile.engine.model;
|
|||||||
|
|
||||||
public enum EngineType {
|
public enum EngineType {
|
||||||
IDA5,
|
IDA5,
|
||||||
|
IDA66,
|
||||||
GHIDRA
|
GHIDRA
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,382 @@
|
|||||||
|
package ai.decompile.engine.service;
|
||||||
|
|
||||||
|
import ai.decompile.docker.service.ContainerService;
|
||||||
|
import ai.decompile.engine.config.EngineProperties;
|
||||||
|
import ai.decompile.engine.model.AnalysisResult;
|
||||||
|
import ai.decompile.engine.model.FunctionResult;
|
||||||
|
import ai.decompile.engine.model.LabelEntry;
|
||||||
|
import ai.decompile.engine.model.SegmentEntry;
|
||||||
|
import ai.decompile.engine.model.XrefEntry;
|
||||||
|
import com.github.dockerjava.api.model.Bind;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
public abstract class AbstractIdaEngineService implements EngineService {
|
||||||
|
private static final String KEY_ENTRY_POINT = "\"entry_point\": \"";
|
||||||
|
private static final String KEY_ADDRESS = "\",\n \"address\": \"";
|
||||||
|
private static final String KEY_SIZE = "\",\n \"size\": ";
|
||||||
|
private static final String KEY_FLAGS = ",\n \"flags\": \"";
|
||||||
|
private static final String KEY_XREFS = "\",\n \"xrefs\": \"";
|
||||||
|
private static final String KEY_ASSEMBLY = "\",\n \"assembly\": \"";
|
||||||
|
private static final String KEY_LABELS = "\",\n \"labels\": \"";
|
||||||
|
private static final String KEY_ENTRY_END = "\"\n }";
|
||||||
|
private static final String KEY_SEGMENTS_START = "\"segments\": [";
|
||||||
|
private static final String KEY_SEGMENT_NAME = "\"name\": \"";
|
||||||
|
private static final String KEY_SEGMENT_START = "\", \"start\": \"";
|
||||||
|
private static final String KEY_SEGMENT_END = "\", \"end\": \"";
|
||||||
|
|
||||||
|
protected final ContainerService containers;
|
||||||
|
protected final EngineProperties engineProperties;
|
||||||
|
|
||||||
|
protected AbstractIdaEngineService(
|
||||||
|
ContainerService containers, EngineProperties engineProperties) {
|
||||||
|
this.containers = containers;
|
||||||
|
this.engineProperties = engineProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getDockerfilePath();
|
||||||
|
|
||||||
|
protected abstract String getConfigKey();
|
||||||
|
|
||||||
|
protected abstract String getTempDirPrefix();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnalysisResult analyze(String filePath) {
|
||||||
|
var configKey = getConfigKey();
|
||||||
|
var config = engineProperties.getEngineConfig(configKey);
|
||||||
|
var absolutePath = Path.of(filePath).toAbsolutePath().normalize();
|
||||||
|
var filename = absolutePath.getFileName().toString();
|
||||||
|
|
||||||
|
containers.ensureImage(config.getImage(), new File(getDockerfilePath()));
|
||||||
|
|
||||||
|
var outputDir = createTempDir();
|
||||||
|
var containerId = createContainer(config.getImage(), filename, absolutePath, outputDir);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var exitCode = containers.waitForExit(containerId, config.getTimeoutSeconds());
|
||||||
|
var logs = containers.captureLogs(containerId, 2);
|
||||||
|
|
||||||
|
validateExitCode(configKey, exitCode, logs);
|
||||||
|
var outputFile = validateOutputFile(configKey, outputDir, logs);
|
||||||
|
return parseOutput(readStringTolerant(outputFile));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("{} analysis failed for file {}: {}", configKey, filePath, e.getMessage());
|
||||||
|
throw new RuntimeException(configKey + " analysis failed: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
containers.stopAndRemove(containerId);
|
||||||
|
cleanupTempDir(outputDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path createTempDir() {
|
||||||
|
try {
|
||||||
|
return Files.createTempDirectory(getTempDirPrefix());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to create temporary output directory", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createContainer(String image, String filename, Path absolutePath, Path outputDir) {
|
||||||
|
var inputBind = Bind.parse(absolutePath.getParent() + ":/input:ro");
|
||||||
|
var outputBind = Bind.parse(outputDir.toAbsolutePath() + ":/output:rw");
|
||||||
|
|
||||||
|
var containerId =
|
||||||
|
containers.createContainer(image, new String[] {filename}, inputBind, outputBind);
|
||||||
|
containers.startContainer(containerId);
|
||||||
|
return containerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateExitCode(String configKey, int exitCode, String logs) {
|
||||||
|
if (exitCode != 0) {
|
||||||
|
log.error("{} container exited with code {}: {}", configKey, exitCode, logs);
|
||||||
|
throw new RuntimeException(
|
||||||
|
configKey + " analysis failed with exit code " + exitCode + ". Output: " + logs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path validateOutputFile(String configKey, Path outputDir, String logs) {
|
||||||
|
var outputFile = outputDir.resolve("output.json");
|
||||||
|
if (!Files.exists(outputFile)) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
configKey + " analysis did not produce output.json. Container logs: " + logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cleanupTempDir(Path dir) {
|
||||||
|
try (var walk = Files.walk(dir)) {
|
||||||
|
walk.sorted(Comparator.reverseOrder()).forEach(AbstractIdaEngineService::deleteSilently);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void deleteSilently(Path path) {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(path);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readStringTolerant(Path path) throws IOException {
|
||||||
|
var decoder =
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
.newDecoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
return decoder.decode(ByteBuffer.wrap(Files.readAllBytes(path))).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnalysisResult parseOutput(String raw) {
|
||||||
|
var text = raw.replace("\r\n", "\n");
|
||||||
|
|
||||||
|
var entryPoint = parseEntryPoint(text);
|
||||||
|
var segments = parseSegments(text);
|
||||||
|
|
||||||
|
var functions = new ArrayList<FunctionResult>();
|
||||||
|
|
||||||
|
var sections = text.split("\"function\"\\s*:\\s*\"");
|
||||||
|
for (int i = 1; i < sections.length; i++) {
|
||||||
|
var func = parseFunctionEntry(sections[i]);
|
||||||
|
if (Objects.nonNull(func)) {
|
||||||
|
functions.add(func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (functions.isEmpty()) {
|
||||||
|
log.warn(
|
||||||
|
"IDA produced no parsable function entries. Raw output (first 500 chars): {}",
|
||||||
|
raw.length() > 500 ? raw.substring(0, 500) : raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AnalysisResult(entryPoint, segments, functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseEntryPoint(String text) {
|
||||||
|
var entryRemainder = after(text, KEY_ENTRY_POINT);
|
||||||
|
if (Objects.isNull(entryRemainder)) {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryVal = extractField(entryRemainder, "\"");
|
||||||
|
if (Objects.isNull(entryVal)) {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
return entryVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FunctionResult parseFunctionEntry(String section) {
|
||||||
|
var name = extractField(section, KEY_ADDRESS);
|
||||||
|
if (Objects.isNull(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remainder = after(section, KEY_ADDRESS);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var address = extractField(remainder, KEY_SIZE);
|
||||||
|
if (Objects.isNull(address)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = after(remainder, KEY_SIZE);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var sizeStr = extractField(remainder, KEY_FLAGS);
|
||||||
|
if (Objects.isNull(sizeStr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var size = parseSize(sizeStr);
|
||||||
|
|
||||||
|
remainder = after(remainder, KEY_FLAGS);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var flags = extractField(remainder, KEY_XREFS);
|
||||||
|
if (Objects.isNull(flags)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = after(remainder, KEY_XREFS);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var xrefsRaw = extractField(remainder, KEY_ASSEMBLY);
|
||||||
|
if (Objects.isNull(xrefsRaw)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = after(remainder, KEY_ASSEMBLY);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var assembly = extractField(remainder, KEY_LABELS);
|
||||||
|
if (Objects.isNull(assembly)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder = after(remainder, KEY_LABELS);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var labelsRaw = extractField(remainder, KEY_ENTRY_END);
|
||||||
|
if (Objects.isNull(labelsRaw)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xrefs = parseXrefs(xrefsRaw.trim());
|
||||||
|
var labels = parseLabels(labelsRaw.trim());
|
||||||
|
return new FunctionResult(
|
||||||
|
name,
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
flags.endsWith("|") ? flags.substring(0, flags.length() - 1) : flags,
|
||||||
|
assembly,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
xrefs,
|
||||||
|
labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseSize(String sizeStr) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(sizeStr.trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractField(String text, String endDelim) {
|
||||||
|
var end = text.indexOf(endDelim);
|
||||||
|
return end < 0 ? null : text.substring(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String after(String text, String delim) {
|
||||||
|
var idx = text.indexOf(delim);
|
||||||
|
return idx < 0 ? null : text.substring(idx + delim.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<SegmentEntry> parseSegments(String text) {
|
||||||
|
var segments = new ArrayList<SegmentEntry>();
|
||||||
|
var remainder = after(text, KEY_SEGMENTS_START);
|
||||||
|
if (Objects.isNull(remainder)) {
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
var segmentsBlock = extractField(remainder, "\n ],\n");
|
||||||
|
if (Objects.isNull(segmentsBlock)) {
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = segmentsBlock.split("},\\s*\\{");
|
||||||
|
for (var part : parts) {
|
||||||
|
var segment = parseSegmentEntry(part);
|
||||||
|
if (Objects.nonNull(segment)) {
|
||||||
|
segments.add(segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SegmentEntry parseSegmentEntry(String part) {
|
||||||
|
var clean = part.replace("{", "").replace("}", "").trim();
|
||||||
|
if (clean.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameRemainder = after(clean, KEY_SEGMENT_NAME);
|
||||||
|
if (Objects.isNull(nameRemainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = extractField(nameRemainder, KEY_SEGMENT_START);
|
||||||
|
if (Objects.isNull(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startRemainder = after(nameRemainder, KEY_SEGMENT_START);
|
||||||
|
if (Objects.isNull(startRemainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startAddress = extractField(startRemainder, KEY_SEGMENT_END);
|
||||||
|
if (Objects.isNull(startAddress)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var endRemainder = after(startRemainder, KEY_SEGMENT_END);
|
||||||
|
if (Objects.isNull(endRemainder)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var endAddress = extractField(endRemainder, "\"");
|
||||||
|
if (Objects.isNull(endAddress)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SegmentEntry(name, startAddress, endAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<XrefEntry> parseXrefs(String raw) {
|
||||||
|
if (Objects.isNull(raw) || raw.isBlank()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.stream(raw.trim().split("\\s+"))
|
||||||
|
.map(AbstractIdaEngineService::parseXrefEntry)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static XrefEntry parseXrefEntry(String token) {
|
||||||
|
var colon = token.lastIndexOf(':');
|
||||||
|
if (colon < 0) {
|
||||||
|
return new XrefEntry(token, "CALL");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new XrefEntry(token.substring(0, colon), token.substring(colon + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<LabelEntry> parseLabels(String raw) {
|
||||||
|
if (Objects.isNull(raw) || raw.isBlank()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.stream(raw.trim().split("\\s+"))
|
||||||
|
.distinct()
|
||||||
|
.map(AbstractIdaEngineService::parseLabelEntry)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LabelEntry parseLabelEntry(String token) {
|
||||||
|
var lastColon = token.lastIndexOf(':');
|
||||||
|
if (lastColon < 0) {
|
||||||
|
return new LabelEntry(token, "", "CALL");
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = token.substring(lastColon + 1);
|
||||||
|
var nameAddr = token.substring(0, lastColon);
|
||||||
|
var addrColon = nameAddr.lastIndexOf(':');
|
||||||
|
if (addrColon < 0) {
|
||||||
|
return new LabelEntry(nameAddr, "", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LabelEntry(
|
||||||
|
nameAddr.substring(0, addrColon), nameAddr.substring(addrColon + 1), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,353 +2,34 @@ package ai.decompile.engine.service;
|
|||||||
|
|
||||||
import ai.decompile.docker.service.ContainerService;
|
import ai.decompile.docker.service.ContainerService;
|
||||||
import ai.decompile.engine.config.EngineProperties;
|
import ai.decompile.engine.config.EngineProperties;
|
||||||
import ai.decompile.engine.model.AnalysisResult;
|
|
||||||
import ai.decompile.engine.model.EngineType;
|
import ai.decompile.engine.model.EngineType;
|
||||||
import ai.decompile.engine.model.FunctionResult;
|
|
||||||
import ai.decompile.engine.model.LabelEntry;
|
|
||||||
import ai.decompile.engine.model.SegmentEntry;
|
|
||||||
import ai.decompile.engine.model.XrefEntry;
|
|
||||||
import com.github.dockerjava.api.model.Bind;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.CodingErrorAction;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Log4j2
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
|
||||||
@ConditionalOnProperty(name = "app.docker.enabled", havingValue = "true")
|
@ConditionalOnProperty(name = "app.docker.enabled", havingValue = "true")
|
||||||
public class Ida5EngineService implements EngineService {
|
public class Ida5EngineService extends AbstractIdaEngineService {
|
||||||
private static final String DOCKERFILE_PATH = "engines/ida5/Dockerfile.ida5";
|
public Ida5EngineService(ContainerService containers, EngineProperties engineProperties) {
|
||||||
private static final String KEY_ENTRY_POINT = "\"entry_point\": \"";
|
super(containers, engineProperties);
|
||||||
private static final String KEY_ADDRESS = "\",\n \"address\": \"";
|
}
|
||||||
private static final String KEY_SIZE = "\",\n \"size\": ";
|
|
||||||
private static final String KEY_FLAGS = ",\n \"flags\": \"";
|
|
||||||
private static final String KEY_XREFS = "\",\n \"xrefs\": \"";
|
|
||||||
private static final String KEY_ASSEMBLY = "\",\n \"assembly\": \"";
|
|
||||||
private static final String KEY_LABELS = "\",\n \"labels\": \"";
|
|
||||||
private static final String KEY_ENTRY_END = "\"\n }";
|
|
||||||
private static final String KEY_SEGMENTS_START = "\"segments\": [";
|
|
||||||
private static final String KEY_SEGMENT_NAME = "\"name\": \"";
|
|
||||||
private static final String KEY_SEGMENT_START = "\", \"start\": \"";
|
|
||||||
private static final String KEY_SEGMENT_END = "\", \"end\": \"";
|
|
||||||
|
|
||||||
private final ContainerService containers;
|
|
||||||
private final EngineProperties engineProperties;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnalysisResult analyze(String filePath) {
|
protected String getDockerfilePath() {
|
||||||
var config = engineProperties.getEngineConfig("ida5");
|
return "engines/ida5/Dockerfile.ida5";
|
||||||
var absolutePath = Path.of(filePath).toAbsolutePath().normalize();
|
}
|
||||||
var filename = absolutePath.getFileName().toString();
|
|
||||||
|
|
||||||
containers.ensureImage(config.getImage(), new File(DOCKERFILE_PATH));
|
@Override
|
||||||
|
protected String getConfigKey() {
|
||||||
|
return "ida5";
|
||||||
|
}
|
||||||
|
|
||||||
Path outputDir = createTempDir();
|
@Override
|
||||||
var containerId = createContainer(config.getImage(), filename, absolutePath, outputDir);
|
protected String getTempDirPrefix() {
|
||||||
|
return "ida5-output-";
|
||||||
try {
|
|
||||||
var exitCode = containers.waitForExit(containerId, config.getTimeoutSeconds());
|
|
||||||
var logs = containers.captureLogs(containerId, 2);
|
|
||||||
|
|
||||||
validateExitCode(exitCode, logs);
|
|
||||||
var outputFile = validateOutputFile(outputDir, logs);
|
|
||||||
return parseOutput(readStringTolerant(outputFile));
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("IDA5 analysis failed for file {}: {}", filePath, e.getMessage());
|
|
||||||
throw new RuntimeException("IDA5 analysis failed: " + e.getMessage(), e);
|
|
||||||
} finally {
|
|
||||||
containers.stopAndRemove(containerId);
|
|
||||||
cleanupTempDir(outputDir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EngineType supportedEngine() {
|
public EngineType supportedEngine() {
|
||||||
return EngineType.IDA5;
|
return EngineType.IDA5;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Path createTempDir() {
|
|
||||||
try {
|
|
||||||
return Files.createTempDirectory("ida5-output-");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Failed to create temporary output directory", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String createContainer(String image, String filename, Path absolutePath, Path outputDir) {
|
|
||||||
var inputBind = Bind.parse(absolutePath.getParent() + ":/input:ro");
|
|
||||||
var outputBind = Bind.parse(outputDir.toAbsolutePath() + ":/output:rw");
|
|
||||||
|
|
||||||
var containerId =
|
|
||||||
containers.createContainer(image, new String[] {filename}, inputBind, outputBind);
|
|
||||||
containers.startContainer(containerId);
|
|
||||||
return containerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void validateExitCode(int exitCode, String logs) {
|
|
||||||
if (exitCode != 0) {
|
|
||||||
log.error("IDA5 container exited with code {}: {}", exitCode, logs);
|
|
||||||
throw new RuntimeException(
|
|
||||||
"IDA5 analysis failed with exit code " + exitCode + ". Output: " + logs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Path validateOutputFile(Path outputDir, String logs) {
|
|
||||||
var outputFile = outputDir.resolve("output.json");
|
|
||||||
if (!Files.exists(outputFile)) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"IDA5 analysis did not produce output.json. Container logs: " + logs);
|
|
||||||
}
|
|
||||||
return outputFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void cleanupTempDir(Path dir) {
|
|
||||||
try (var walk = Files.walk(dir)) {
|
|
||||||
walk.sorted(Comparator.reverseOrder()).forEach(Ida5EngineService::deleteSilently);
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void deleteSilently(Path path) {
|
|
||||||
try {
|
|
||||||
Files.deleteIfExists(path);
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readStringTolerant(Path path) throws IOException {
|
|
||||||
var decoder =
|
|
||||||
StandardCharsets.UTF_8
|
|
||||||
.newDecoder()
|
|
||||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
|
||||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
|
||||||
return decoder.decode(ByteBuffer.wrap(Files.readAllBytes(path))).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AnalysisResult parseOutput(String raw) {
|
|
||||||
var text = raw.replace("\r\n", "\n");
|
|
||||||
|
|
||||||
var entryRemainder = after(text, KEY_ENTRY_POINT);
|
|
||||||
var entryPoint = "unknown";
|
|
||||||
if (Objects.nonNull(entryRemainder)) {
|
|
||||||
var entryVal = extractField(entryRemainder, "\"");
|
|
||||||
if (Objects.nonNull(entryVal)) {
|
|
||||||
entryPoint = entryVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var segments = parseSegments(text);
|
|
||||||
|
|
||||||
var functions = new ArrayList<FunctionResult>();
|
|
||||||
|
|
||||||
var sections = text.split("\"function\"\\s*:\\s*\"");
|
|
||||||
for (int i = 1; i < sections.length; i++) {
|
|
||||||
var section = sections[i];
|
|
||||||
|
|
||||||
var name = extractField(section, KEY_ADDRESS);
|
|
||||||
if (name == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var remainder = after(section, KEY_ADDRESS);
|
|
||||||
if (remainder == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var address = extractField(remainder, KEY_SIZE);
|
|
||||||
if (address == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = after(remainder, KEY_SIZE);
|
|
||||||
if (remainder == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var sizeStr = extractField(remainder, KEY_FLAGS);
|
|
||||||
if (sizeStr == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int size;
|
|
||||||
try {
|
|
||||||
size = Integer.parseInt(sizeStr.trim());
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = after(remainder, KEY_FLAGS);
|
|
||||||
if (remainder == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var flags = extractField(remainder, KEY_XREFS);
|
|
||||||
if (Objects.isNull(flags)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = after(remainder, KEY_XREFS);
|
|
||||||
if (remainder == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var xrefsRaw = extractField(remainder, KEY_ASSEMBLY);
|
|
||||||
if (xrefsRaw == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = after(remainder, KEY_ASSEMBLY);
|
|
||||||
if (remainder == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var assembly = extractField(remainder, KEY_LABELS);
|
|
||||||
if (assembly == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder = after(remainder, KEY_LABELS);
|
|
||||||
if (remainder == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var labelsRaw = extractField(remainder, KEY_ENTRY_END);
|
|
||||||
if (labelsRaw == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xrefs = parseXrefs(xrefsRaw.trim());
|
|
||||||
var labels = parseLabels(labelsRaw.trim());
|
|
||||||
functions.add(
|
|
||||||
new FunctionResult(
|
|
||||||
name,
|
|
||||||
address,
|
|
||||||
size,
|
|
||||||
flags.endsWith("|") ? flags.substring(0, flags.length() - 1) : flags,
|
|
||||||
assembly,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
xrefs,
|
|
||||||
labels));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (functions.isEmpty()) {
|
|
||||||
log.warn(
|
|
||||||
"IDA5 produced no parsable function entries. Raw output (first 500 chars): {}",
|
|
||||||
raw.length() > 500 ? raw.substring(0, 500) : raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AnalysisResult(entryPoint, segments, functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String extractField(String text, String endDelim) {
|
|
||||||
int end = text.indexOf(endDelim);
|
|
||||||
return end < 0 ? null : text.substring(0, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String after(String text, String delim) {
|
|
||||||
int idx = text.indexOf(delim);
|
|
||||||
return idx < 0 ? null : text.substring(idx + delim.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<SegmentEntry> parseSegments(String text) {
|
|
||||||
var segments = new ArrayList<SegmentEntry>();
|
|
||||||
var remainder = after(text, KEY_SEGMENTS_START);
|
|
||||||
if (Objects.isNull(remainder)) {
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
var segmentsBlock = extractField(remainder, "\n ],\n");
|
|
||||||
if (Objects.isNull(segmentsBlock)) {
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parts = segmentsBlock.split("},\\s*\\{");
|
|
||||||
for (var part : parts) {
|
|
||||||
var clean = part.replace("{", "").replace("}", "").trim();
|
|
||||||
if (clean.isBlank()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nameRemainder = after(clean, KEY_SEGMENT_NAME);
|
|
||||||
if (Objects.isNull(nameRemainder)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = extractField(nameRemainder, KEY_SEGMENT_START);
|
|
||||||
if (Objects.isNull(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var startRemainder = after(nameRemainder, KEY_SEGMENT_START);
|
|
||||||
if (Objects.isNull(startRemainder)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var startAddress = extractField(startRemainder, KEY_SEGMENT_END);
|
|
||||||
if (Objects.isNull(startAddress)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var endRemainder = after(startRemainder, KEY_SEGMENT_END);
|
|
||||||
if (Objects.isNull(endRemainder)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var endAddress = extractField(endRemainder, "\"");
|
|
||||||
if (Objects.isNull(endAddress)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
segments.add(new SegmentEntry(name, startAddress, endAddress));
|
|
||||||
}
|
|
||||||
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<XrefEntry> parseXrefs(String raw) {
|
|
||||||
if (Objects.isNull(raw) || raw.isBlank()) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arrays.stream(raw.trim().split("\\s+")).map(Ida5EngineService::parseXrefEntry).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static XrefEntry parseXrefEntry(String token) {
|
|
||||||
var colon = token.lastIndexOf(':');
|
|
||||||
if (colon < 0) {
|
|
||||||
return new XrefEntry(token, "CALL");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new XrefEntry(token.substring(0, colon), token.substring(colon + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<LabelEntry> parseLabels(String raw) {
|
|
||||||
if (raw == null || raw.isBlank()) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
return Arrays.stream(raw.trim().split("\\s+"))
|
|
||||||
.distinct()
|
|
||||||
.map(Ida5EngineService::parseLabelEntry)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LabelEntry parseLabelEntry(String token) {
|
|
||||||
var lastColon = token.lastIndexOf(':');
|
|
||||||
if (lastColon < 0) {
|
|
||||||
return new LabelEntry(token, "", "CALL");
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = token.substring(lastColon + 1);
|
|
||||||
var nameAddr = token.substring(0, lastColon);
|
|
||||||
var addrColon = nameAddr.lastIndexOf(':');
|
|
||||||
if (addrColon < 0) {
|
|
||||||
return new LabelEntry(nameAddr, "", type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new LabelEntry(
|
|
||||||
nameAddr.substring(0, addrColon), nameAddr.substring(addrColon + 1), type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
package ai.decompile.engine.service;
|
||||||
|
|
||||||
|
import ai.decompile.docker.service.ContainerService;
|
||||||
|
import ai.decompile.engine.config.EngineProperties;
|
||||||
|
import ai.decompile.engine.model.EngineType;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@ConditionalOnProperty(name = "app.docker.enabled", havingValue = "true")
|
||||||
|
public class Ida66EngineService extends AbstractIdaEngineService {
|
||||||
|
public Ida66EngineService(ContainerService containers, EngineProperties engineProperties) {
|
||||||
|
super(containers, engineProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDockerfilePath() {
|
||||||
|
return "engines/ida66/Dockerfile.ida66";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getConfigKey() {
|
||||||
|
return "ida66";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTempDirPrefix() {
|
||||||
|
return "ida66-output-";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EngineType supportedEngine() {
|
||||||
|
return EngineType.IDA66;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -54,6 +54,9 @@ app:
|
|||||||
ida5:
|
ida5:
|
||||||
image: decompile-ai/ida5:latest
|
image: decompile-ai/ida5:latest
|
||||||
timeout-seconds: 300
|
timeout-seconds: 300
|
||||||
|
ida66:
|
||||||
|
image: decompile-ai/ida66:latest
|
||||||
|
timeout-seconds: 300
|
||||||
ghidra:
|
ghidra:
|
||||||
image: decompile-ai/ghidra:latest
|
image: decompile-ai/ghidra:latest
|
||||||
timeout-seconds: 600
|
timeout-seconds: 600
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user