
Ghidra를 활용한 AI 지원 리버스 엔지니어링
Rev·Deck은 로컬 단일 사용자 정적 분석 워크스테이션입니다. 헤드리스 Ghidra 서비스가 분석한 바이너리 위에서 증거 우선(evidence-first) 웹 UI와 LLM 코파일럿을 결합합니다: 결정적 증거(함수, 문자열, 임포트, 교차 참조, 제한된 호출 그래프)를 직접 탐색하거나, 사실적 주장이 검사 가능한 증거를 인용해야 하는 제한된 질문을 어시스턴트에게 할 수 있습니다.
분석된 바이너리는 절대 실행되지 않습니다. 브라우저는 이 Flask 앱과만 통신하며, 앱은 검증된 타입 요청을 Ghidra 서비스로 프록시합니다.
https://github.com/user-attachments/assets/fba14dc5-7ad5-4137-9349-ed824da64fbe
cp .env.example .env # set API_BASE and MODEL_NAME; set API_KEY if required
docker compose up --build
Docker Compose는 값 보간을 위해 .env를 자동으로 읽습니다. API_BASE 또는 MODEL_NAME이 없으면 시작 전에 실패합니다. API_KEY=not-used는 로컬/키 없는 제공업체에서 유효합니다. 이 스택은 두 서비스를 모두 시작합니다. http://127.0.0.1:5000을 엽니다.
Ghidra 서비스만 실행하려면:
docker pull biniamfd/ghidra-headless-rest:latest # ensure the newest image
docker run --rm \
-p 127.0.0.1:9090:9090 \
-v "$(pwd)/data:/data/ghidra_projects" \
--security-opt no-new-privileges:true \
biniamfd/ghidra-headless-rest:latest
재현 가능한 고정을 위해 latest 대신 테스트된 릴리스 다이제스트를 사용하세요:
docker run --rm \
-p 127.0.0.1:9090:9090 \
-v "$(pwd)/data:/data/ghidra_projects" \
--security-opt no-new-privileges:true \
biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3a8448d8ed969079b452306e806f36079c3ddd298f4a618d6e2f1442d
biniamfd/ghidra-headless-rest:latest..env.example을 .env로 복사하여 입력하세요. 전체 목록과 기본값은 해당 파일을 참조하세요.
Rev·Deck은 OpenAI SDK를 통해 모든 OpenAI 호환 Chat Completions 엔드포인트와 통신하며, 전적으로 API_BASE / API_KEY / MODEL_NAME으로 구성됩니다. 제공업체별 헤더, 매개변수 또는 모델 로직은 없습니다. 로컬 Ollama 서버(API_BASE=http://127.0.0.1:11434/v1), 자체 호스팅 vLLM/llama.cpp/LM Studio 엔드포인트, OpenAI 자체, 또는 OpenRouter 같은 게이트웨이 모두 동일한 방식으로 작동합니다.
.env 제공업체 설정 예시(자리 표시자를 사용하고 실제 키를 커밋하지 마세요):
# Ollama
API_BASE=http://127.0.0.1:11434/v1
API_KEY=not-used
MODEL_NAME=qwen3:8b
# OpenRouter
API_BASE=https://openrouter.ai/api/v1
API_KEY=replace-with-your-key
MODEL_NAME=anthropic/claude-opus-4.8
# OpenAI
API_BASE=https://api.openai.com/v1
API_KEY=replace-with-your-key
MODEL_NAME=replace-with-a-supported-model-id
# LM Studio, vLLM, or llama.cpp (adjust port/model to the server)
API_BASE=http://127.0.0.1:1234/v1
API_KEY=not-used
MODEL_NAME=replace-with-the-served-model-id
기본적으로(LLM_STREAM=auto) 어시스턴트는 스트리밍 응답을 요청하고 토큰이 도착하는 대로 브라우저에 전달합니다. 스트리밍은 또한 더 강력한 취소 보장을 제공합니다: 응답을 중지하거나(또는 탭을 닫으면) Rev·Deck이 기본 제공업체 스트림을 즉시 닫고 추가 도구 또는 모델 라운드를 수행하지 않으므로, 업스트림 생성이 백그라운드에서 완료될 때까지 남겨지지 않고 중단됩니다.
주의 사항:
auto 모드에서 제공업체가 콘텐츠나 도구 호출 출력이 전혀 나오기 전에 호환성 오류(HTTP 400/404/405/422)로 스트리밍 요청을 거부하면, Rev·Deck은 한 번만 블로킹 호출로 폴백하고 프로세스의 나머지 동안 이를 기억합니다. 인증(401/403), 속도 제한(429) 및 서버(5xx) 오류는 호환성 문제로 취급되지 않으며, 자동 재시도 대신 오류로 표시됩니다. 스트리밍을 완전히 건너뛰려면 LLM_STREAM=false를, 스트리밍을 필수로 하려면(폴백 없음) LLM_STREAM=true를 설정하세요.앱을 열고 바이너리를 업로드하여 분석 작업을 시작합니다. 명백한 평문 콘텐츠는 Ghidra로 전송되기 전에 확인을 요청합니다. 콘텐츠가 의도적으로 실행 파일 형식이 아닌 펌웨어/데이터인 경우에만 명시적 원시 바이너리(raw-binary) 재정의를 사용하세요. 분석이 완료되면 두 개의 워크스페이스 탭을 전환합니다:
두 모드 모두 작업별 단계 예산(step budget) 과 작업이 끝날 때까지 실행되는 단계 제한 없음(No step limit) 옵션이 있습니다(루프에 빠진 모델이 무한정 실행되지 않도록 여전히 MAX_STEP_BUDGET으로 제한됨). 실행이 예산에 도달하면 부분 결과를 보고하고 계속(Continue) 을 제안합니다. 이는 이미 검색된 증거를 사용하여 동일한 대화를 이어가며 완료된 도구 호출을 다시 수행하지 않습니다. 비용은 도구/모델 호출 수에 비례하여 증가하므로 예산이 높을수록 비용이 더 많이 듭니다.
사용 가능한 워크플로우:
각 분석 작업에는 메인(Main) 채팅과 선택적인 집중 하위 스레드가 있습니다. 새 하위 조사(New sub-investigation) 를 선택하고 한 줄 브리핑을 입력한 후, 동일한 바이너리와 동일한 읽기 전용 도구를 사용하여 새로운 대화 컨텍스트로 작업합니다. 스레드 기록은 격리된 상태로 유지되며 한 번에 하나의 스레드만 스트리밍됩니다.
집중 작업이 완료되면 부모에게 결론 반환(Return conclusion to parent) 을 선택합니다. Rev·Deck은 해당 하위 스레드에 대해서만 한 번의 제한된 모델 호출을 수행하고, 증거 인용을 검증한 다음, 출처가 표시된 결론 카드 하나를 부모 스레드에 추가합니다. 전체 분기는 다시 열 수 있는 상태로 유지되며, 부모 컨텍스트는 분기 기록이 아닌 간결한 결론만 받습니다. 검증된 인용이 없는 반환 카드는 명시적으로 미검증으로 표시됩니다.
어시스턴트 답변은 [function:0xADDR], [string:0xADDR], [import:name] 형식으로 증거를 인라인 인용합니다. 인용은 턴 중에 실제로 검색된 내용과 대조하여 확인됩니다. 일치하지 않는 인용은 "(unverified)"로 표시되며 사실이 아닌 미확인 주장으로 취급해야 합니다.
어시스턴트 출력의 Mermaid 다이어그램(예: 호출 그래프 스케치)은 외부 네트워크 접근이 없는 샌드박스 프레임에서 렌더링됩니다.
브라우저는 Rev·Deck 웹 애플리케이션과만 통신합니다. Rev·Deck은 구성된 LLM과 헤드리스 Ghidra 서비스를 조정한 다음, 결과 증거와 에이전트 활동을 하나의 워크스페이스에 표시합니다.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
npm ci && npm run vendor # one-time: vendors the pinned Mermaid runtime
cp .env.example .env # edit API_BASE / MODEL_NAME / API_KEY
set -a; source .env; set +a # plain Python does not load .env automatically
# Start the separate Ghidra service, then:
python webui/app.py
http://127.0.0.1:5000을 엽니다. Docker Compose는 .env를 자동으로 읽습니다. 소스 실행은 위와 같이 내보내기(export)가 필요합니다. Flask 개발 서버는 로컬 사용에 적합하며, Docker 이미지는 Gunicorn을 실행합니다.
이 도구는 개인 머신에서 작업하는 신뢰할 수 있는 단일 분석가를 위해 설계되었습니다 — 다중 사용자 또는 공개 호스팅용이 아닙니다. 기본적으로 앱과 Ghidra 서비스는 127.0.0.1에만 바인딩되고, 디버그 모드는 꺼져 있으며, 업로드된 바이너리는 절대 실행되지 않고, LLM 제공업체 키는 서버 측에 유지됩니다.
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest
node --test "webui/static/js/tests/**/*.test.mjs"
npm ci && npm run vendor:verify # verifies the vendored Mermaid bundle's integrity
/readyz가 503 반환 — GHIDRA_API_BASE에서 Ghidra 서비스에 연결할 수 없거나 API_BASE/MODEL_NAME이 설정되지 않았습니다.API_BASE/API_KEY/MODEL_NAME과 제공업체가 LLM_TIMEOUT 내에 연결 가능한지 확인하세요.MAX_UPLOAD_BYTES를 높이세요.ANALYSIS_TIMEOUT을 늘리고(예: 함수가 1만 개 이상인 C++/Android 바이너리의 경우 5400) 다시 업로드하세요. LLM_TIMEOUT과는 관련이 없습니다.| 변수 | 기본값 | 의미 |
|---|
API_BASE | 필수 | OpenAI 호환 기본 URL(http/https). 없으면 Compose가 조기에 실패합니다. |
API_KEY | not-used | 제공업체 키. 절대 기록되거나 브라우저로 전송되지 않습니다. not-used는 키 없는 로컬 제공업체에 유효합니다. |
MODEL_NAME | 필수 | 구성된 엔드포인트가 기대하는 모델 ID. 없으면 Compose가 조기에 실패합니다. |
LLM_STREAM | auto | 스트리밍 전송: auto(스트리밍, 출력 전 호환성 오류 시 한 번 블로킹으로 폴백), true(항상 스트리밍), false(항상 블로킹). |
GHIDRA_API_BASE | http://127.0.0.1:9090 | Ghidra 서비스 기본 URL. |
GHIDRA_IMAGE | biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3... | 불변 다이제스트로 고정된 테스트 릴리스. :latest도 이 다이제스트로 확인됩니다. 다른 릴리스를 고정하려면 재정의하세요. |
HOST / PORT | 127.0.0.1 / 5000 | 개발 서버 바인딩. |
MAX_UPLOAD_BYTES | 104857600 | 업로드 크기 상한. |
CHATS_DIR | webui/chats | 채팅 기록 디렉터리. |
| 워크플로우 | 용도 | 대상 함수 주소 필요 |
|---|
program_triage | 메타데이터, 임포트, 문자열, 함수에서 프로그램의 예상 용도를 요약합니다. | 아니요 |
suspicious_behavior | 먼저 결정적 지표를 표시한 다음, 제한되고 명확히 라벨링된 가설을 제시합니다. | 아니요 |
selected_function | 함수를 디컴파일하고 호출자/피호출자와 함께 설명합니다. | 예 |
call_chain | 시작 함수에서 제한된 네이티브/합성 호출 그래프 이웃 하나를 탐색합니다. | 예 |
attack_surface_triage | 결정적 점수 커버리지/top-K를 읽은 다음 최대 3개 후보를 심층 검사합니다. 점수는 우선순위이지 판정이 아닙니다. | 아니요 |
vulnerability_hypothesis | 제한된 후보 하나를 선택하고 증거, 반박 증거, 미해결 질문을 제시합니다. 절대 자동 확인하지 않습니다. | 아니요 |