
Go 바이너리에서 문자열 정의를 복구하기 위한 Ghidra 스크립트
P-Code 분석을 통해 Go 바이너리에서 문자열 정의를 복구하는 스크립트입니다. x86, x86-64, ARM 및 ARM64에서 테스트되었습니다.
이 스크립트들은 Script Manager의 Golang 카테고리에서 찾을 수 있습니다.
GoDynamicStrings.java
GoFuncCallStrings.java
GoStaticStrings.java
GoKnownStrings.java
data/known_strings.json에서 로드됩니다.GoStringFiller.java
go.string.*의 빈 부분을 채웁니다.동적 문자열 분석 스크립트의 특별한 변형도 몇 가지 있습니다:
GoDynamicStringsSingle.java
GoDynamicStrings.java와 동일한 분석을 수행하지만 단일 디컴파일러 프로세스를 사용합니다. 바이너리 분석 시 병렬 디컴파일러 프로세스가 시스템 메모리를 고갈시키는 경우 이 스크립트를 사용하세요.GoDynamicStringsHigh.java
이 스크립트는 Script Manager의 PCode 카테고리에서 찾을 수 있습니다.
PrintHighPCode.java
Go 바이너리에서 문자열 정의를 복구하기 위해 이러한 스크립트를 사용하는 일반적인 흐름은 다음과 같습니다:
.rodata, .rdata 또는 __rodata의 모든 문자열을 선택합니다. 그런 다음 코드 목록에서 마우스 오른쪽 버튼을 클릭하고 "Clear Code Bytes"를 선택합니다.GoKnownStrings.java를 실행하여 일부 표준 문자열을 감지합니다.GoStaticStrings.java를 실행합니다.GoFuncCallStrings.java를 실행합니다 (Ghidra의 내장 Golang 기능이 해당 Golang 바이너리 버전을 지원하는 경우).GoDynamicStrings.java를 실행합니다.GoStringFiller.java를 실행합니다.
go.string.* 레이블을 만듭니다.go.string.*에 남은 간격이 있는지 확인하고 명확한 시작점과 끝점이 있는 문자열을 정의합니다.
GoStringFiller.java를 최대한 활용하려면 정의되지 않은 문자열 데이터에서 문자열 길이가 변경되는 지점을 식별하고 해당 경계에 가장 가까운 문자열을 정의하세요. 그런 다음 를 다시 실행하여 나머지 정의되지 않은 문자열의 길이를 올바르게 결정할 수 있는 부분을 자동으로 채웁니다.Ghidra에서:
GhidraDev 플러그인이 있는 Eclipse의 경우:
Eclipse로 빌드:
Gradle로 직접 빌드:
$ cd Ghostrings
$ gradle -PGHIDRA_INSTALL_DIR=<ghidra_install_dir>
Go 프로그램을 리버스 엔지니어링할 때 잘 알려진 문제 중 하나는 Go 문자열에 null 종결자가 없어 컴파일된 바이너리에서 문자열 정의를 복구하기 어렵다는 것입니다. Go 프로그램의 많은 상수 문자열 값은 컴파일된 빌드에서 하나의 거대한 블롭(blob)으로 함께 저장되며, 문자열 데이터에 한 문자열이 끝나고 다른 문자열이 시작되는 위치를 표시하는 종결자 문자가 없습니다. 단순히 "Hello world!"를 출력하는 간단한 프로그램조차도 Go 런타임 시스템 및 기타 표준 라이브러리와 관련된 1,500개 이상의 문자열을 포함합니다. 이로 인해 Ghidra가 제공하는 것과 같은 일반적인 ASCII 문자열 검색 구현이 수만 자 길이의 오탐 문자열 정의를 생성할 수 있습니다.
Go는 null 종결자 문자열 대신 포인터와 길이 값으로 구성된 문자열 구조를 사용합니다. 이러한 문자열 구조 중 상당수는 런타임에 프로그램의 스택에서 생성되므로 개별 문자열의 시작 위치와 길이 값을 복구하려면 컴파일된 기계어 코드를 분석해야 합니다. 특정 x86-64 명령어 패턴을 확인하여 이 분석을 수행하는 기존 스크립트가 몇 가지 있지만, 궁극적으로 스택에 동일한 효과를 주는 처리되지 않은 명령어 변형으로 생성된 구조를 놓치며 특정 ISA로 제한됩니다.
Ghostrings는 Ghidra의 디컴파일러 분석이 생성하는 단순화된 아키텍처 독립적 P-Code 연산을 사용하여 이 두 가지 문제를 모두 피합니다.
Copyright 2022 NCC Group. GPLv3 라이선스로 배포됩니다(LICENSE 참조).
프로젝트 주요 작성자: James Chambers [email protected]
GoStringFiller.java