Ghidra Strudy
Ghidra Script 개발의 핵심 요소인 Ghidra API에 대하여 학습 하였다. 이전 블로그에서 Ghidra Script를 소개하였는데, Script 자주 사용하는 Ghidra API에 대해 알아 보았다.

Ghidra는 API 문서를 제공해 준다. 위의 메뉴에서 Help > Ghidra API Help를 누르면 html 파일을 생성해 준다.
Ghidra에는 Ghidra Program API, FlatProgram API, GhidraScript가 존재하는데 이름도 비슷하여 상당히 헷갈린다.
currentProgram, currentAddress와 같은 상태변수와 println, popup같은 UI 제어 기능을 제공.// __mingw_scanf 호출 지점을 찾고 인자 정보를 분석합니다.
//@author Gemini
//@category Analysis
//@runtime Java
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
public class FindInput extends GhidraScript {
@Override
public void run() throws Exception {
String targetFunc = "__mingw_scanf";
println("--- [분석 시작] " + targetFunc + " 추적 ---");
SymbolTable symbolTable = currentProgram.getSymbolTable();
SymbolIterator iter = symbolTable.getSymbols(targetFunc);
while (iter.hasNext() && !monitor.isCancelled()) {
Symbol sym = iter.next();
if (sym.getSymbolType() == SymbolType.FUNCTION || sym.getSymbolType() == SymbolType.LABEL) {
for (Reference ref : getReferencesTo(sym.getAddress())) {
if (ref.getReferenceType().isCall()) {
Address callAddr = ref.getFromAddress();
// 호출 지점의 함수 정보 가져오기
Function caller = getFunctionContaining(callAddr);
if (caller != null && caller.getName().equals("main")) {
println("[!] 중요 입력 지점 발견 (in main)");
} else {
println("[+] 호출 발견: " + (caller != null ? caller.getName() : "Unknown"));
}
println(" - 주소: " + callAddr);
println("------------------------------------------");
}
}
}
}
}
}
이전에 작성한 Script를 간단하게 살펴 보면
GhidraScript (Base API: currentProgram: 이 변수는 GhidraScript 클래스에 정의된 필드
println(): 콘솔 출력을 담당하는 메서드
monitor: 작업 취소 상태 등을 확인하는 필드
FlatProgramAPI (편의용 메서드):
getReferencesTo(): 특정 주소를 참조하는 곳들을 바로 찾아주는 함수
getFunctionContaining(): 특정 주소가 포함된 부모 함수 객체를 바로 찾아주는 함수
이 메서드들은 원래 복잡한 과정을 거쳐야 하지만, GhidraScript가 FlatProgramAPI를 상속받고 있기 때문에 위 코드처럼 짧게 호출 가능
Program API (Low-level):
currentProgram.getSymbolTable(): currentProgram이라는 객체를 통해 SymbolTable이라는 하위 매니저 객체를 직접 가져오는 방식입니다. 이는 객체 지향적인 Program API 사용법에 해당Ghidra 스크립트 작성 시 빈번하게 사용되는 FlatProgramAPI 기반의 핵심 함수들을 용도별로 정리해 보았다.
현재 분석 중인 바이너리의 전반적인 정보를 가져올 때 사용한다.
| 함수명 | 설명 |
|---|---|
currentProgram.getName() | 현재 열린 프로그램의 이름을 반환 |
currentProgram.getExecutablePath() | 로컬 시스템상의 바이너리 파일 경로를 반환 |
currentProgram.getMinAddress() | 프로그램의 시작(최소) 주소를 반환 |
toAddr(long/string) | 숫자나 문자열을 Ghidra Address 객체로 변환 |
특정 바이트 패턴을 찾거나 데이터를 읽고 쓸 때 사용한다.
| 함수명 | 설명 |
|---|---|
find(startAddr, bytes) | 특정 주소부터 지정한 바이트 패턴을 검색 |
getByte(addr) | 특정 주소에서 1바이트를 읽어옵니다. |
setByte(addr, value) | 특정 주소에 바이트를 쓴다 (바이너리 패치). |
createData(addr, type) | 특정 주소를 문자열, 정수 등의 데이터 타입으로 정의합니다. |
리버싱 자동화의 핵심인 함수 제어 API입니다.
| 함수명 | 설명 |
|---|---|
getFunctionAt(addr) | 해당 주소에 있는 함수 객체를 가져옵니다. |
getFunctionBefore(addr) / After | 현재 위치 인근의 함수를 탐색합니다. |
createFunction(addr, name) | 특정 주소에 새로운 함수를 생성합니다. |
func.setName(name, source) | 함수 객체의 이름을 변경합니다. |
func.getEntryPoint() | 함수의 진입점(시작 주소)을 반환합니다. |
분석 내용을 기록하거나 레이블을 관리합니다.
| 함수명 | 설명 |
|---|---|
createLabel(addr, name, true) | 특정 주소에 이름(레이블)을 부여합니다. |
setEOLComment(addr, comment) | 코드 끝(End of Line) 줄에 주석을 추가합니다. |
setPreComment(addr, comment) | 코드 윗줄에 주석을 추가합니다. |
getSymbolAt(addr) | 특정 주소의 심볼(이름) 정보를 가져옵니다. |
스크립트 실행 중 사용자 입력을 받거나 메시지를 출력합니다.
| 함수명 | 설명 |
|---|---|
askString(title, message) | 사용자로부터 문자열 입력을 받는 창을 띄웁니다. |
askChoice(title, msg, choices) | 여러 선택지 중 하나를 고르게 합니다. |
popup(message) | 알림 메시지 팝업을 띄웁니다. |
println(string) | Ghidra 하단 콘솔(Console)창에 메시지를 출력합니다. |
분석된 C 코드를 문자열로 가져오는 과정입니다.
from ghidra.app.decompiler import DecompInterface
# 1. 디컴파일러 인터페이스 초기화
iface = DecompInterface()
iface.openProgram(currentProgram)
# 2. 분석할 함수 가져오기 (현재 주소 기준)
func = getFunctionAt(currentAddress)
# 3. 디컴파일 수행
results = iface.decompileFunction(func, 0, monitor)
if results.decompileCompleted():
# 4. 결과에서 C 코드 소스 출력
print(results.getDecompiledFunction().getC())