Ghidra 디버깅

Ghidra Debugging - PART 1 (Introduction & Feature guide) : https://youtu.be/BY2vzHdfwFU

Ghidra Debugging - PART 2 (Debugging with Ghidra) : https://youtu.be/5huuiZu4fIY

I. 개요

Ghidra 10.0이 출시되면서 Debug 기능이 추가되었습니다.

본 포스팅에서는 Ghidra Debug 유형과 기능을 알아보고 디버깅 기능을 활용하는 방법에 대해 알아보겠습니다. Ghidra Debugging 기능 테스트를 진행한 환경은 다음과 같습니다.

대상이름버전
OSUbuntu18.04.5 LTS
소프트웨어Ghidra10.0

[표1. 테스트 환경]

Ghidra의 디버깅 기능은 OS별 기본 디버거와 연동하여 작동하며 유저 모드 디버깅을 지원합니다. (자세한 내용 : https://ghidra-sre.org/releaseNotes_10.0beta.html 참조)

유저 모드 디버깅(User Mode Debugging)이란?
유저가 접근할 수 있는 OS영역내에서 실행되는 애플리케이션을 디버깅하는 작업

OSDebugger분석 지원 아키텍쳐
WindowsWinDbgx64, x86
LinuxGDBamd64/x86_64, i686/x86

[표2. Ghidra 디버거에서 지원하는 OS 아키텍쳐]

Ghidra 10.0 실행 시, 아래 그림과 같이 Debugger 기능 아이콘이 추가가 된 것을 확인할 수 있습니다.

그림1. 추가된 Debugger 기능 아이콘

Windows와 Linux 환경에서 Ghidra Debugger 기능을 사용하는 방법은 동일하며, 다음과 같은 순서로디버거를 실행합니다.

  • 분석 대상 파일 우클릭 → Open With → Debugger 클릭
그림2. Debugger 실행

II. Ghidra Debug 유형

2-1. Debug 유형

그림3. Ghidra에서 지원하는 Debug 유형

1) Native Debuger Direct 연결 Debug 유형

"Debug in GDB locally IN-VM"

Ghidra가 native Debugger에 직접적으로 연결되기 때문에, Debug Agent 창이 표시되지 않습니다. 해당 유형은 디버깅이 빠르다는 장점이 있지만, Crash가 발생하거나 데이터가 손상될 위험이 있다는 단점이 존재합니다.

2) SSH를 통한 Debug 유형

"Debug in GDB via ssh:user@localhost"

ssh를 통해 GDB를 실행합니다.

3) GADP를 통한 Debug 유형

"Debug in GDB locally via GADP"

GADP를 통해 GDB를 실행합니다. GADP를 통한 Debug를 실시할 때, Debug Agent가 실행됩니다.

그림4. GADP를 통한 Debug를 실시할 때, 실행되는 Debug Agent

GADP(Ghidra Asynchronous Debug Protoco)란?
GADP는 GUI와 Ghidra Agent 간의 사용되는 Protocol입니다.

Debug Agent란?
Debug Agent는 로컬 시스템에서 실행되는 프로세스입니다. Ghidra GUI와 native Debugger(WinDbg/GDB) 사이에서 징검다리 역할을 하며, Debug Agent 종료 시, 디버깅 세션이 종료됩니다.

III. Ghidra Debug 기능

2-1. 메인 화면

Debugger 실행 후 메인 화면입니다.

그림5. Debugger 메인 화면

각 영역에 위치하고 있는 주요 요소들은 다음과 같습니다.

  1. Debugger Targets
  2. Objects
  3. Symbol Tree
  4. Debug Console
  5. Dynamic Listing
  6. Listing
  7. Interpreter
  8. Breakpoints
  9. Registers
  10. Modules
  11. Decompiler

2-2. 상세 화면

1) Debugger Targets

The targets window manages connections to live debuggers.

debugger에 대한 연결을 관리합니다. debug 유형에 따라 필요한 옵션을 구성할 수 있습니다.

그림6. Debugger Targets

2) Objects

The Objects window permits the user to interact directly with a connected debugger and its targets.

Objects을 통해 사용자는 연결된 Debugger 및 분석중인 파일과 직접 상호 작용할 수 있습니다.

그림7. Objects

3) Symbol Tree

The Symbol Tree shows symbols from a program in a hierarchical view

분석중인 파일의 Symbol을 Tree 구조로 표시합니다.

그림8. Symbol Tree

4) Debug Console

This console is a central place for reporting activity, errors, and suggesting actions. This is the first place to look when troubleshooting.

Debug Console은 문제 발생시 가장 먼저 살펴 보는 곳으로, 디버깅 활동을 기록하고, 발생하는 오류를 출력합니다.

그림9. Debug Console

5) Dynamic Listing

The dynamic listing is analogous to Ghidra's listing for static analysis, but in the dynamic context. That is, it displays memory contents from a target.

분석 대상 파일의 메모리 내용을 표시합니다.

그림10. Dynamic Listing

6) Listing

The Listing View is the main windows for displaying and working with a program's instruction and data.

분석 대상 파일의 명령과 데이터를 표시합니다.

그림11. Listing

7) Interpreter

For debuggers which have built-in interpreters (many do), and whose connectors expose that interpreter in the model, the interpreters plugin can provide user access to it via a graphical console emulator.

Interpreter 창을 통해 native debugger로 접근할 수 있습니다.

그림12. Interpreter

Native Debugger란?
Ghidra와 연동하여 작동하는 OS별 기본 디버거로 WinDbg와 GDB가 해당됩니다.

8) Breakpoints

Breakpoints refer to any mechanism which may trap execution based on an address.

분석하고자 하는 지점에 생성한 Breakpoint에 대한 정보(대상 파일명, 주소, breakpoint 유형 등)를 나타냅니다.

그림13. Breakpoints

9) Registers

Registers refer to the target processor's register banks. In multi-threaded environments, it is assumed that each thread has its own register context.

분석 대상 파일의 Register Bank를 나타냅니다.

그림14. Registers

Register Bank란?

Register Bank는 범용 레지스터와 특수 레지스터로 구성됩니다.

10) Modules

The concept of a module may vary from platform to platform, but in most cases, it refers to a binary image which is loaded or mapped into memory comprising some portion of the target's executable code.

분석 대상 파일의 소스 코드를 실행하기 위해 로드되는 메모리 정보나 매핑되는 binary image를 나타냅니다.

그림15. Modules

11) Decompiler

The Decompiler plug-in is a sophisticated transformation engine which automatically converts the binary representation of individual functions into a high-level C representation.

C언어로 Decompile된 소스를 확인할 수 있는 기능입니다.

그림16. Decompiler

IV. Ghidra 디버깅 기능 활용하기

3-1. 소스코드 및 소스컴파일

Ghidra 디버깅 연습을 위한 예제 소스코드를 작성해보겠습니다.

아래 소스코드는 알아볼 수 없는 메시지를 xor 연산을 해서 data에 담습니다. data에 우리가 확인할 수 있는 메시지가 있을 수 있습니다.

gcc를 이용해 해당 파일을 빌드합니다.

실습할 프로그램을 실행 시켜 메시지를 확인합니다.

알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 가 출력되는 것을 확인할 수 있습니다.

그림17. message 프로그램 실행 후 메시지 출력 화면

3-2. Ghidra 디버깅 기능을 활용한 파일 디버깅

1) 디버거로 파일 열기

Ghidra에서 message 파일을 Debugger로 열어줍니다.

  • 분석 대상 파일 우클릭 → Open With → Debugger 클릭
그림18. Debugger 실행

2) 디버깅 실행

Debug message in GDB locally IN-VM를 실행합니다.

  • Debug 아이콘 → Debug message in GDB locally IN-VM
그림19. 디버깅 실행

3) 문자열 검색 및 해당 지점으로 이동

문자열 검색을 통해 알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 또는 추가 도움이 될 만한 정보를 확인하기 위해 Search → For String을 통해 문자열을 검색합니다.

그림20. 문자열 검색

Search를 클릭하여 최소 5자리 이상의 문자열을 조회합니다.

그림21. 문자열 검색 범위 설정

알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 와 출력으로 보이는 부분을 확인할 수 있습니다. 더블클릭을 통해 알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 영역으로 이동합니다.

그림22. 검색된 문자열

알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 를 main함수에서 xref하고 있다는 것을 확인 할 수 있습니다. 알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 를 제일 먼저 xref하고 있는 main : 00100722(*)를 더블 클릭해서 해당 영역으로 이동합니다.

그림23. 알 수 없는 메시지 xref

Cross References(xref) 란?
특정 함수와 개체가 호출된 위치 또는 특정 함수에서 사용되는 함수와 개체를 보여주는 디스어셈블러의 기능입니다.

4) 프로그램 구조 확인

main 함수 내에서 알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 를 출력하는 것을 확인 하기 위해 주소 00100755 에 Breakpoint를 생성합니다.

  • Breakpoint 대상 영역 우클릭 → Set Breakpoint→ SW_EXECUTE 클릭
그림24. Set Breakpoint

HW_EXECUTE : HardWare 실행 중지 활성화 READ : 읽기 중지 활성화 READ, WRITE : 읽기, 쓰기 중지 활성화 SW_EXECITE : SoftWare 실행 중지 활성화 WRITE : 쓰기 중지 활성화

interpreter에 GDB명령어 r (F5)을 입력하여 Breakpoint까지 프로그램을 실행합니다.

② message 프로그램 실행시 출력된 알 수 없는 메시지 (4403zoo777n9/545\"%n#/-o#o+)3%# 가 출력된 것을 확인할 수 있습니다.

Breakpoint가 생성된 이후 디스어셈블된 코드를 확인해보면,

③ 문자(1byte)를 0x40으로 xor 연산하고 있고,

④ 문자열 길이 함수인 strlen 가 호출되는 것을 확인할 수 있습니다.

위 내용을 통해서 문자열 길이만큼 연산이 반복됨을 추측할 수 있습니다.

그림25. Breakpoint까지 프로그램 실행

interpreter에 GDB명령어 ni 명령어(F8)를 입력해 한 줄씩 실행시켜, 문자(1byte)를 0x40으로 xor 반복 하고 있는 것을 확인 합니다.

그림26. next instruction 명령어 실행

main 함수 내부가 어떻게 동작하는지 흐름을 파악합니다.

  • Window → Function Graph
그림27. Function Graph 실행

① Function Graph를 통해서 반복문을 탈출하면 printf을 호출하기전에 RSIRAX (xor로 연산된 값) 넣어준다는 점을 확인할 수 있습니다.

② printf에 Breakpoint를 생성합니다.

그림28. Function Graph를 통한 프로그램 로직 확인

5) xor 연산된 값 확인

① interpreter에 GDB명령어 n 명령어를 통해 위에서 생성한 breakpoint까지 실행합니다.

② 이 후, interpreter에 GDB명령어 x/s $rsi를 입력해 RSI 값을 확인합니다.

xor 연산으로 나온 값은 https://www.youtube.com/c/kisec 인 것을 확인할 수 있습니다.

그림29. xor된 데이터 결과 확인

V. 마무리

매력적인 95년도 디자인과 무료 Decompile 기능이 장점이지만 잦은 에러가 발생 한다는 문제가 있습니다.

이번 포스팅을 위해 Ghidra 테스트를 하는 도중 값을 입력 받아야 하는 프로그램에 값 입력을 위해interpreter 입력시 GDB와 tty input signal이 멈추는 문제를 발견했습니다.

그림30. Stopped tty input signal

해당 문제에 대해서 Ghidra Issue로 등록했고 개발팀에서 확인 후 해결을 위해 고민중입니다.

Unable to put input value into interpreter. · Issue #3174 · NationalSecurityAgency/ghidra

그림31. Stopped tty input signal