471 lines
20 KiB
Markdown
471 lines
20 KiB
Markdown
# Library Symbols — Feature Design Document
|
|
|
|
## 1. Visão Geral
|
|
|
|
Sistema de importação e resolução de símbolos de bibliotecas externas (ex: Borland OWL, MFC, Qt, .NET BCL). O parser consome dumps de símbolos (formato TurboDump inicialmente), armazena-os como referência autoritativa, e cruza automaticamente com os resultados de análise estática para resolver imports opacos (ordinais) em nomes semânticos.
|
|
|
|
A informação resolvida é injetada no pipeline de embeddings (pgvector) e na construção de contexto do chat RAG, melhorando dramaticamente a qualidade das respostas do LLM.
|
|
|
|
**Fase atual (1):** Parser TurboDump + integração completa com AI/RAG. Sem sessões de chat temático (Fase 2 futura).
|
|
|
|
## 2. Arquitetura
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────────┐
|
|
│ LIBRARY UPLOAD FLOW │
|
|
│ │
|
|
│ POST /libraries/upload │
|
|
│ │ {parserType, rawContent, libraryName, aliases[]} │
|
|
│ ▼ │
|
|
│ LibraryService.parseAndStore() │
|
|
│ ├─ LibrarySymbolParser (interface) │
|
|
│ │ └─ TurboDumpParser ← implementação atual │
|
|
│ │ └─ DefFileParser ← futuro │
|
|
│ │ └─ MapFileParser ← futuro │
|
|
│ ├─ Cria Library entity │
|
|
│ ├─ Cria LibraryAlias entities (mapeamento DLL ↔ library) │
|
|
│ ├─ Chama parser.parse() → List<ParsedSymbol> │
|
|
│ └─ Salva LibrarySymbol entities em lote │
|
|
└──────────────────────────────────────────────────────────────────┘
|
|
|
|
┌──────────────────────────────────────────────────────────────────┐
|
|
│ SYMBOL RESOLUTION FLOW │
|
|
│ │
|
|
│ StaticAnalysisCompletedEvent (disparado após IDA/Ghidra) │
|
|
│ ▼ │
|
|
│ SymbolResolutionService.resolveImports(analysisId) │
|
|
│ ├─ Carrega todos os StaticImport do analysis │
|
|
│ ├─ Para cada import com ordinal != 0: │
|
|
│ │ ├─ Busca LibraryAlias pelo campo dll do import │
|
|
│ │ ├─ Se encontrado: busca LibrarySymbol por (library, ordinal)│
|
|
│ │ └─ Se encontrado: atualiza StaticImport.resolvedName │
|
|
│ │ e StaticImport.librarySymbolId │
|
|
│ └─ Loga estatísticas: quantos imports resolvidos │
|
|
└──────────────────────────────────────────────────────────────────┘
|
|
|
|
┌──────────────────────────────────────────────────────────────────┐
|
|
│ AI / RAG ENRICHMENT │
|
|
│ │
|
|
│ EmbeddingChunkBuilder │
|
|
│ ├─ buildImportChunks(): inclui resolvedName + className │
|
|
│ ├─ buildMetadataChunk(): inclui Library Diagnostics section │
|
|
│ └─ buildFunctionChunkContent(): callees resolvidos via OWL │
|
|
│ │
|
|
│ ChatContextBuilder │
|
|
│ ├─ buildSystemPrompt(): inclui [LIBRARY CONTEXT] seção │
|
|
│ └─ detectFunctionReferences(): busca por classes/métodos OWL │
|
|
└──────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## 3. Modelo de Dados
|
|
|
|
### 3.1 Tabelas Novas
|
|
|
|
#### `library`
|
|
| Coluna | Tipo | Descrição |
|
|
|--------|------|-----------|
|
|
| `id` | UUID PK | Identificador |
|
|
| `name` | VARCHAR(255) NOT NULL | Nome da biblioteca (ex: "Borland OWL 2.52") |
|
|
| `description` | TEXT | Descrição opcional (ex: "Object Windows Library for Borland C++") |
|
|
| `created_at` | TIMESTAMPTZ NOT NULL | Data de criação |
|
|
|
|
#### `library_alias`
|
|
| Coluna | Tipo | Descrição |
|
|
|--------|------|-----------|
|
|
| `id` | UUID PK | Identificador |
|
|
| `library_id` | UUID FK → library(id) ON DELETE CASCADE | Biblioteca pai |
|
|
| `alias` | VARCHAR(255) NOT NULL | Nome do DLL/arquivo no import table (ex: "OWLWI", "owl252.dll") |
|
|
| `UNIQUE(library_id, alias)` | | Um alias pertence a uma única biblioteca |
|
|
|
|
#### `library_symbol`
|
|
| Coluna | Tipo | Descrição |
|
|
|--------|------|-----------|
|
|
| `id` | UUID PK | Identificador |
|
|
| `library_id` | UUID FK → library(id) ON DELETE CASCADE | Biblioteca pai |
|
|
| `module_name` | VARCHAR(255) NOT NULL | Módulo original (ex: "Owl252" do TurboDump) |
|
|
| `class_name` | VARCHAR(500) | Classe C++ (nulo para funções livres) |
|
|
| `symbol_name` | VARCHAR(500) NOT NULL | Nome completo do símbolo (ex: "TButtonGadget::Paint(TDC*)") |
|
|
| `ordinal` | INTEGER NOT NULL | Número ordinal de importação |
|
|
| `symbol_type` | VARCHAR(50) | Tipo: CONSTRUCTOR, DESTRUCTOR, METHOD, FUNCTION, STATIC_MEMBER |
|
|
| `parameters` | VARCHAR(1000) | Assinatura dos parâmetros (extraído do nome) |
|
|
| `category` | VARCHAR(100) | Categoria (ex: "UI/Gadget", "Core/Application", "Graphics/Bitmap") |
|
|
| `created_at` | TIMESTAMPTZ NOT NULL | Data de criação |
|
|
|
|
Índices: `idx_library_symbol_library_ordinal ON library_symbol (library_id, ordinal)`
|
|
`idx_library_symbol_class ON library_symbol (class_name) WHERE class_name IS NOT NULL`
|
|
|
|
### 3.2 Tabela Alterada
|
|
|
|
#### `static_import` (adições)
|
|
| Coluna | Tipo | Descrição |
|
|
|--------|------|-----------|
|
|
| `library_symbol_id` | UUID FK → library_symbol(id) ON SET NULL | Símbolo de biblioteca que corresponde a este import |
|
|
| `resolved_name` | VARCHAR(500) | Nome resolvido (ex: "TButtonGadget::Paint(TDC*)") |
|
|
|
|
Índice: `idx_static_import_library_symbol ON static_import (library_symbol_id) WHERE library_symbol_id IS NOT NULL`
|
|
|
|
## 4. Fluxo de Dados Detalhado
|
|
|
|
### 4.1 Upload da Biblioteca
|
|
|
|
```
|
|
POST /libraries/upload
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"name": "Borland OWL 2.52",
|
|
"description": "Object Windows Library for Borland C++ 5.0",
|
|
"parserType": "TURBO_DUMP",
|
|
"aliases": ["OWLWI", "owl252.dll", "Owl252"],
|
|
"rawContent": "Turbo Dump Version 5.0.1..."
|
|
}
|
|
```
|
|
|
|
1. `LibraryService.parseAndStore()` recebe o request
|
|
2. Busca um `LibrarySymbolParser` pelo `parserType` (pode ser um Map<String, LibrarySymbolParser> ou via qualifier annotation)
|
|
3. Cria `Library` entity e salva
|
|
4. Cria `LibraryAlias` entities para cada alias
|
|
5. Chama `parser.parse(rawContent, libraryId)` → `List<ParsedSymbol>`
|
|
6. Salva `LibrarySymbol` entities em batch (500 por vez)
|
|
7. Retorna `LibraryResponse` com contagem de símbolos
|
|
|
|
### 4.2 Resolução de Import Symbols
|
|
|
|
Após a análise IDA/Ghidra completar, o `StaticAnalysisCompletedEvent` é publicado. O listener `SymbolResolutionListener` (ou `SymbolResolutionService`) escuta o evento:
|
|
|
|
1. Carrega `StaticImport` para o `analysisId`
|
|
2. Para cada import com `ordinal != 0`:
|
|
a. Busca `LibraryAlias` onde `alias ILIKE dll` (case-insensitive)
|
|
b. Se encontrado, busca `LibrarySymbol` por `(library_id, ordinal)`
|
|
c. Se encontrado, atualiza `StaticImport.librarySymbolId` e `resolvedName`
|
|
3. Loga: `Resolved 152/200 imports (76%) for analysis <id>`
|
|
4. Opcional: publica um evento `SymbolsResolvedEvent` para notificar downstream
|
|
|
|
Alternativamente, se o listener não for viável (timing), o `StaticAnalysisHandler` chama `symbolResolutionService.resolveImports(analysisId)` no final do `process()`.
|
|
|
|
### 4.3 Enriquecimento de Embedding Chunks
|
|
|
|
#### Import Chunks
|
|
```java
|
|
// Antes
|
|
"[IMPORT] OWLWI.ord_75 @ 0x00405A00"
|
|
|
|
// Depois (com library symbol resolvido)
|
|
"[IMPORT] OWLWI.TButtonGadget::Paint(TDC*) @ 0x00405A00 " +
|
|
" Library: Borland OWL 2.52 | Class: TButtonGadget | Method: Paint"
|
|
```
|
|
|
|
#### Metadata Chunk — Nova seção [LIBRARY DIAGNOSTICS]
|
|
```
|
|
[LIBRARY DIAGNOSTICS]
|
|
Framework: Borland OWL 2.52 (Object Windows Library)
|
|
Detected classes (15): TApplication, TFrameWindow, TButtonGadget, TBitmap,
|
|
TControlBar, TChooseColorDialog, TChooseFontDialog, TClipboard, TBrush,
|
|
TDC, TCursor, TBitSet, TCharSet, TCommonDialog, TControlGadget
|
|
Category distribution: UI/Gadget (6), Core/Application (3), Graphics (4),
|
|
UI/Window (2)
|
|
Architecture: Event-driven GUI, message dispatch via TEventHandler::Find()
|
|
```
|
|
|
|
#### Function Chunks
|
|
No `buildFunctionChunkContent()`, ao listar callees, se o callee for um import com `resolvedName`:
|
|
```java
|
|
// Antes
|
|
"Callees: sub_401000, ordinal_75, ordinal_67"
|
|
|
|
// Depois
|
|
"Callees: sub_401000, TButtonGadget::Paint [OWL/ord75], TButtonGadget::TButtonGadget [OWL/ord67]"
|
|
```
|
|
|
|
### 4.4 Enriquecimento do System Prompt
|
|
|
|
```java
|
|
// Antes
|
|
String.format("""
|
|
[BINARY OVERVIEW]
|
|
Filename: %s
|
|
Format: %s
|
|
Architecture: %s
|
|
Compiler: %s
|
|
""", ...);
|
|
|
|
// Depois
|
|
String.format("""
|
|
[BINARY OVERVIEW]
|
|
Filename: %s
|
|
Format: %s
|
|
Architecture: %s
|
|
Compiler: Borland C++
|
|
|
|
[LIBRARY CONTEXT]
|
|
This binary links against Borland OWL 2.52 (Object Windows Library), a
|
|
C++ framework for Windows GUI applications. Key classes detected:
|
|
%s
|
|
|
|
OWL uses a message-driven architecture similar to MFC. Windows messages
|
|
are routed through TEventHandler response tables. Common patterns:
|
|
- TFrameWindow is the main application window
|
|
- TButtonGadget handles button controls with Paint() for WM_PAINT
|
|
- Dialogs extend TCommonDialog via TChooseColorDialog/TChooseFontDialog
|
|
- Graphics use TDC (Device Context wrapper) with TBrush/TPen/TBitmap
|
|
|
|
When analyzing functions that call OWL imports, consider the OWL class
|
|
hierarchy and event dispatch model to infer the function's role.
|
|
""", ..., libraryDiagnostics);
|
|
```
|
|
|
|
## 5. API REST
|
|
|
|
### 5.1 Endpoints
|
|
|
|
| Método | Path | Descrição |
|
|
|--------|------|-----------|
|
|
| `POST` | `/libraries/upload` | Upload de um dump de símbolos de biblioteca |
|
|
| `GET` | `/libraries` | Listar todas as bibliotecas carregadas |
|
|
| `GET` | `/libraries/{id}` | Detalhes de uma biblioteca |
|
|
| `DELETE` | `/libraries/{id}` | Remover uma biblioteca e seus símbolos |
|
|
| `GET` | `/libraries/{id}/symbols` | Listar símbolos (com paginação, filtro por nome/ordinal) |
|
|
| `POST` | `/analysis/{analysisId}/resolve-symbols` | Forçar resolução de símbolos (debug) |
|
|
|
|
### 5.2 DTOs
|
|
|
|
```java
|
|
// Request
|
|
public record LibraryUploadRequest(
|
|
@NotBlank String name,
|
|
String description,
|
|
@NotBlank String parserType,
|
|
@NotEmpty List<String> aliases,
|
|
@NotBlank String rawContent
|
|
) {}
|
|
|
|
// Response
|
|
public record LibraryResponse(
|
|
UUID id, String name, String description, int symbolCount,
|
|
int aliasCount, Instant createdAt
|
|
) {
|
|
public static LibraryResponse from(Library lib) { ... }
|
|
}
|
|
|
|
public record LibrarySymbolResponse(
|
|
UUID id, String moduleName, String className, String symbolName,
|
|
int ordinal, String symbolType, String parameters, String category
|
|
) {
|
|
public static LibrarySymbolResponse from(LibrarySymbol sym) { ... }
|
|
}
|
|
```
|
|
|
|
### 5.3 ImportResponse (estendido)
|
|
|
|
```java
|
|
public record ImportResponse(
|
|
UUID id, String dll, String name, String address, int ordinal,
|
|
// Novos campos opcionais:
|
|
String resolvedName, // nulo se não resolvido
|
|
String libraryName, // nulo se não resolvido
|
|
String className, // nulo se não for método de classe
|
|
UUID librarySymbolId // nulo se não resolvido
|
|
) {
|
|
public static ImportResponse from(StaticImport imp) {
|
|
var sym = imp.getLibrarySymbol();
|
|
return new ImportResponse(
|
|
imp.getId(), imp.getDll(), imp.getName(), imp.getAddress(),
|
|
imp.getOrdinal(),
|
|
imp.getResolvedName(),
|
|
Objects.nonNull(sym) ? sym.getLibrary().getName() : null,
|
|
Objects.nonNull(sym) ? sym.getClassName() : null,
|
|
imp.getLibrarySymbolId()
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## 6. Parser Architecture
|
|
|
|
### 6.1 Interface
|
|
|
|
```java
|
|
package ai.decompile.analysis.service.parser;
|
|
|
|
import java.util.List;
|
|
|
|
public interface LibrarySymbolParser {
|
|
/** Identificador único do formato (ex: "TURBO_DUMP", "DEF_FILE") */
|
|
String getFormatName();
|
|
|
|
/** Valida se o conteúdo é compatível com este parser */
|
|
boolean canParse(String rawContent);
|
|
|
|
/** Parseia o conteúdo e retorna símbolos para association com libraryId */
|
|
List<ParsedSymbol> parse(String rawContent);
|
|
}
|
|
|
|
/** Registro intermediário entre parser e serviço */
|
|
public record ParsedSymbol(
|
|
String moduleName,
|
|
String className,
|
|
String symbolName,
|
|
int ordinal,
|
|
String symbolType,
|
|
String parameters,
|
|
String category
|
|
) {}
|
|
```
|
|
|
|
### 6.2 TurboDumpParser
|
|
|
|
Implementação concreta para o formato do utilitário `TDUMP.EXE` da Borland:
|
|
|
|
**Formato de entrada:**
|
|
```
|
|
000020 THEADR TAppDictionary::TAppDictionary()
|
|
00003E COMENT Purge: Yes, List: Yes, Class: 160 (0A0h)
|
|
Dynamic link import (IMPDEF)
|
|
Imported by: ordinal
|
|
Internal Name: TAppDictionary::TAppDictionary()
|
|
Module Name: Owl252
|
|
ordinal: 2
|
|
000069 MODEND
|
|
```
|
|
|
|
**Lógica de parsing:**
|
|
1. Divide o texto por blocos delimitados por `MODEND`
|
|
2. Para cada bloco:
|
|
- Extrai `symbolName` da linha `THEADR`
|
|
- Extrai `moduleName` do campo `Module Name:`
|
|
- Extrai `ordinal` do campo `ordinal:`
|
|
- Se não encontrar `ordinal:` ou o bloco não for `IMPDEF`, pula
|
|
- Classifica `symbolType`:
|
|
- Contém `::~` → `DESTRUCTOR`
|
|
- Contém `::` e termina em `()` sem `~` → `CONSTRUCTOR`
|
|
- Contém `::` → `METHOD`
|
|
- Sem `::` → `FUNCTION`
|
|
- Extrai `className` do trecho antes de `::`
|
|
- Extrai `parameters` dos parênteses após o nome do método
|
|
- Classifica `category` por prefixo de classe:
|
|
```
|
|
TApp, TModule, TStatus, TXCompatibility → Core/Application
|
|
TFrame, TWindow, TDialog, TClipboard → UI/Window
|
|
TButton, TGadget, TControl, TSlider → UI/Control
|
|
TBitmap, TPalette, TDib, TIcon, TDC → Graphics
|
|
TBrush, TPen, TFont → Graphics/Drawing
|
|
TColor → Graphics/Color
|
|
TCursor → UI/Cursor
|
|
TBitSet, TCharSet → Core/DataTypes
|
|
default → Core/Other
|
|
```
|
|
|
|
### 6.3 Registro de Parsers
|
|
|
|
```java
|
|
@Component
|
|
@RequiredArgsConstructor
|
|
public class LibrarySymbolParserRegistry {
|
|
// Spring injeta todos os beans LibrarySymbolParser
|
|
private final Map<String, LibrarySymbolParser> parsers;
|
|
|
|
// Construtor monta o mapa formatName → parser
|
|
public LibrarySymbolParserRegistry(List<LibrarySymbolParser> parserList) {
|
|
this.parsers = parserList.stream()
|
|
.collect(Collectors.toMap(
|
|
LibrarySymbolParser::getFormatName,
|
|
Function.identity()));
|
|
}
|
|
|
|
public LibrarySymbolParser get(String formatName) {
|
|
var parser = parsers.get(formatName);
|
|
if (Objects.isNull(parser)) {
|
|
throw new IllegalArgumentException(
|
|
"No parser found for format '" + formatName + "'. " +
|
|
"Available: " + parsers.keySet());
|
|
}
|
|
return parser;
|
|
}
|
|
|
|
public List<String> getAvailableFormats() {
|
|
return List.copyOf(parsers.keySet());
|
|
}
|
|
}
|
|
```
|
|
|
|
## 7. Estrutura de Código
|
|
|
|
```
|
|
src/main/java/ai/decompile/analysis/
|
|
├── model/
|
|
│ ├── entity/
|
|
│ │ ├── Library.java (novo)
|
|
│ │ ├── LibraryAlias.java (novo)
|
|
│ │ ├── LibrarySymbol.java (novo)
|
|
│ │ └── StaticImport.java (modificado: +resolvedName, +librarySymbolId, +librarySymbol)
|
|
│ ├── repository/
|
|
│ │ ├── LibraryRepository.java (novo)
|
|
│ │ ├── LibraryAliasRepository.java (novo)
|
|
│ │ └── LibrarySymbolRepository.java (novo)
|
|
│ └── dto/
|
|
│ ├── LibraryResponse.java (novo)
|
|
│ ├── LibrarySymbolResponse.java (novo)
|
|
│ ├── LibraryUploadRequest.java (novo)
|
|
│ └── ImportResponse.java (modificado)
|
|
├── service/
|
|
│ ├── parser/
|
|
│ │ ├── LibrarySymbolParser.java (novo - interface)
|
|
│ │ ├── ParsedSymbol.java (novo - record)
|
|
│ │ ├── LibrarySymbolParserRegistry.java (novo)
|
|
│ │ └── TurboDumpParser.java (novo)
|
|
│ ├── LibraryService.java (novo)
|
|
│ ├── SymbolResolutionService.java (novo)
|
|
│ └── AnalysisService.java (modificado: chama resolution após saveResult)
|
|
├── event/
|
|
│ └── SymbolsResolvedEvent.java (novo, opcional)
|
|
└── controller/
|
|
└── LibraryController.java (novo)
|
|
|
|
src/main/resources/db/migration/
|
|
├── V021__create_library_tables.sql (novo)
|
|
└── V022__alter_static_import_library.sql (novo)
|
|
|
|
src/main/java/ai/decompile/ai/service/
|
|
├── EmbeddingChunkBuilder.java (modificado: enriquecido com library info)
|
|
└── ChatContextBuilder.java (modificado: system prompt + context enriquecido)
|
|
```
|
|
|
|
## 8. Casos de Teste
|
|
|
|
### 8.1 TurboDumpParser
|
|
|
|
| Teste | Entrada | Esperado |
|
|
|-------|---------|----------|
|
|
| Construtor de classe | `TAppDictionary::TAppDictionary()` | type=CONSTRUCTOR, class=TAppDictionary |
|
|
| Destrutor | `TBitmap::~TBitmap()` | type=DESTRUCTOR, class=TBitmap |
|
|
| Método com parâmetros | `TDC::SelectObject(const TPen*)` | type=METHOD, class=TDC, params=const TPen* |
|
|
| Método sem parâmetros | `TBitSet::IsEmpty()` | type=METHOD, params=null |
|
|
| Função livre | `NColors(unsigned short)` | type=FUNCTION, className=null |
|
|
| Bloco sem ordinal | Qualquer não-IMPDEF | Ignorado (não retorna símbolo) |
|
|
| Parser chamado 2x | Conteúdo com 1854 IMPDEF | 1854 símbolos retornados |
|
|
|
|
### 8.2 SymbolResolutionService
|
|
|
|
| Teste | Setup | Esperado |
|
|
|-------|-------|----------|
|
|
| Match por ordinal + alias | Import(dll="OWLWI", ordinal=75), LibrarySymbol(ordinal=75, library=L1), Alias("OWLWI"→L1) | resolvedName = "TButtonGadget::Paint(TDC*)" |
|
|
| Ordinal zero (nomeado) | Import(ordinal=0) | Não resolvido (pula) |
|
|
| Alias não encontrado | Import(dll="UNKNOWN", ordinal=5) | Não resolvido |
|
|
| Ordinal não encontrado | Import(dll="OWLWI", ordinal=9999) | Não resolvido |
|
|
| Sem bibliotecas carregadas | Nenhuma Library no banco | Nenhum import resolvido |
|
|
| Case insensitive alias | Import(dll="owlwi"), Alias("OWLWI") | Resolvido |
|
|
|
|
### 8.3 EmbeddingChunkBuilder (enriquecido)
|
|
|
|
| Teste | Setup | Esperado |
|
|
|-------|-------|----------|
|
|
| Import com resolvedName | StaticImport com resolvedName="TDC::Paint(TDC*)" | Chunk content contém o resolvedName |
|
|
| Metadata com library diagnostics | Library + LibrarySymbol carregados | Seção [LIBRARY DIAGNOSTICS] presente |
|
|
| Function callees resolvidos | Function com xrefs para imports resolvidos | Callee list mostra class::method [OWL] |
|
|
|
|
## 9. Rollout e Rollback
|
|
|
|
- **Nova feature, sem quebra de compatibilidade**: migration adiciona colunas nullable ao `static_import`
|
|
- **Sem bibliotecas carregadas**: comportamento idêntico ao atual (resolvedName = null, nenhum chunk afetado)
|
|
- **Rollback**: reverter migrations V021 e V022, remover código novo (as APIs novas simplesmente não existiriam mais)
|