들어가며
1. 들어가며
- 지난 강의에서 WinDbg의 기초적인 사용법을 학습했다.
- 이때 공부한 기능들은 디버깅 작업의 근간이자 동적 분석에서 가장 강력한 부분이다.
- 이 기능들만 충분히 연마해도 매우 넓은 스펙트럼의 동적 분석 작업을 수행할 수 있지만, 본래 CLI 기반 앱인 만큼 강력하고 편리한 명령어들을 다수 갖추고 있다.
- 이번 강의에서는 지난 강의에서 다루지 못했던 핵심 WinDbg 명령어들을 소개하고, 이를 활용한 실제 디버깅 실습을 함께해 볼 것이다.
이번 강의에서도 지난 강의의 실습 바이너리인 exercise-lecture.exe를 활용하여 실습한다.
built-in 명령어
1. built-in 명령어
- built-in 명령어는 WinDbg에 기본적으로 포함되어 있는 내장 명령어로, 프로세스의 흐름 제어와 메모리 및 레지스터
조회 등을 담당한다.
- 지난 강의에 배웠던 bp, g, r 등의 명령어들이 이에 해당한다.
- 앞서 살펴본 명령어들 이외에 유용한 내장 명령어들을 알아보자.
2. Enter
- Enter는 직전에 실행한 명령어를 그대로 실행하게 해 준다.
- 이 Enter는 메모리를 수정하는 명령어가 아닌 키보드에서 Enter 키를 입력하는 것에 해당한다.
- 명령어를 여러 번 반복하고 싶은 경우 최초 명령어만 입력하고 이후에는 Enter를 입력하는 것으로 이전 명령어를 쉽게 재실행할 수 있다.
- 아래는
db명령어로 메모리 값을 읽은 뒤 Enter로 명령어를 다시 실행하는 예시이다.
0:000> db @rip L3
00007ff6`72f21330 48 81 ec H..
0:000>
00007ff6`72f21330 48 81 ec H..
3. lm
-
lm은 List Loaded Modules의 약자로 메모리에 로드된 모듈들의 목록을 가상 주소와 함께 출력하는 명령어이다.lm명령어는 다양한 옵션과 함께 사용할 수 있다.
-
lm명령어를 옵션 없이 사용하면 아래와 같이 메모리에 로드된 모든 모듈들의 목록을 살펴볼 수 있다.0:000> lm start end module name 00007ff6`72f20000 00007ff6`72f28000 exercise_lecture C (private pdb symbols) C:\ProgramData\Dbg\sym\exercise-lecture.pdb\CB34EC2AAFA74582AD0DB4F3932076064\exercise-lecture.pdb 00007ffa`87cf0000 00007ffa`87d0e000 VCRUNTIME140 (deferred) 00007ffa`abb30000 00007ffa`abc7b000 ucrtbase (deferred) 00007ffa`abdf0000 00007ffa`ac1d8000 KERNELBASE (deferred) 00007ffa`ad490000 00007ffa`ad559000 KERNEL32 (pdb symbols) C:\ProgramData\Dbg\sym\kernel32.pdb\60819BF7E89AA952820421609F5980191\kernel32.pdb 00007ffa`ae7a0000 00007ffa`aea05000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\ntdll.pdb\C7A9C64B64408F28DE6508D118DED28D1\ntdll.pdb -
가상 주소 없이 모듈들의 이름만 보고 싶다면 아래와 같이
1m옵션을 사용하면 된다.0:000> lm 1m exercise_lecture VCRUNTIME140 ucrtbase KERNELBASE KERNEL32 ntdll -
혹은
lm a [Address]와 같이 사용하여 특정 주소가 어느 모듈에 속해 있는지알 수 있다.- 아래는 현재 실행 중인 명령어의 주소인
rip값이 어느 모듈에 속해 있는지 확인하는 명령어이다.
0:000> r rip rip=00007ffaae8c4789 0:000> lm a 00007ffaae8c4789 Browse full module list start end module name 00007ffa`ae7a0000 00007ffa`aea05000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\ntdll.pdb\C7A9C64B64408F28DE6508D118DED28D1\ntdll.pdb - 아래는 현재 실행 중인 명령어의 주소인
-
rip주소를 직접 입력하는 대신에@rip형식으로 작성할 수도 있다.- WinDbg에서는 레지스터 이름 앞에 “@”를 붙이는 것으로 레지스터의 값을 표현할 수 있다.
0:000> lm a @rip Browse full module list start end module name 00007ffa`ae7a0000 00007ffa`aea05000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\ntdll.pdb\C7A9C64B64408F28DE6508D118DED28D1\ntdll.pdb
4. ?
?명령어는 인자로 전달된 표현식의 값을 계산해 출력하며,? [Expression]형태로 사용할 수 있다.- 이때 표현식은 기본적으로 MASM (Microsoft Macro Assembler) 표현식 문법을 따라야 한다.
MASM (Microsoft Macro Assembler) 표현식
MASM은 마이크로소프트의 x86 계열 아키텍처의 어셈블러이다. WinDbg에서는 이 MASM 표현식 문법을 사용하여 식을 표현하고 있다. 전반적으로 C, C++과 비슷하지만 레지스터와 메모리를 다루는 규칙이 다른 것이 특징이다.
기본적으로 WinDbg는 숫자를 16진수로 해석하며, “0x” 접두사(16진수), “0n” 접두사(10진수), “0t 접두사(8진수)” 또는 “0y 접두사(이진)”를 지정하여 다른 진법으로 숫자를 표현할 수 있다.
레지스터의 값을 표현할 때는
@rax와 같이@<레지스터명>으로 표현하며, 특정 주소의 메모리 값을 표현할 때는 다음 연산자를 활용하여 표현한다.
by: 1bytewo: 2bytedwo: 4byteqwo: 8bytepoi: 포인터 크기예를 들어,
rip가 가리키는 주소에서 2byte 값을 표현하기 위해서는wo(@rip)와 같이 작성할 수 있다.이외에는 대부분 C, C++과 비슷한 문법을 지니고 있으며 자세한 내용은 마이크로소프트 공식 문서에서 확인할 수 있다.
-
예를 들어,
?명령어를 아래와 같이 계산기 대용으로 사용할 수 있다.0:000> ? 1 + 2 * 3 Evaluate expression: 7 = 00000000`00000007 -
그 외에도 MASM 표현식 문법을 이용해 디버깅 작업 중 마주치는 여러 값들을 계산해 볼 수 있다.
0:000> ? exercise_lecture!main + 0x20 Evaluate expression: 140696467149648 = 00007ff6`72f21350
5. bl
-
bl은 Breakpoint List의 약자로 설정한 중단점들의 목록을 출력하는 명령어이다.- 아래는
bl명령어를 실행한 예시이다.

- 아래는
-
가장 왼쪽의 번호는 중단점의 ID로, 중단점을 식별하는 식별자 역할을 한다.
- “Disable”과 “Clear”의 경우 클릭할 수 있으며, 클릭할 경우 해당 중단점을 일시적으로 비활성화(Disable) 하거나 삭제(Clear)할 수 있다.
6. bc
bc는 Breakpoint Clear의 약자로 중단점을 삭제하는 명령어이다.bc [ID]형태로 사용하며, 인자로 전달된 ID의 중단점을 삭제하게 된다.- 여러 개의 중단점을 동시에 삭제하고 싶다면
bc [ID1] [ID2] …처럼 동시에 여러 ID를 인자로 전달하면 된다.
7. bd & be
bd는 Breakpoint Disable의 약자로 중단점을 비활성화하는 명령어이다.bd [ID]형태로 사용하며 인자로 전달한 ID를 가진 중단점을 비활성화 할 수 있다.- 이 경우 다시 활성화하기 전까지 프로세스가 중단점에 도달해도 중단되지 않는다.
- 중단점을 비활성화하더라도 중단점 삭제와는 다르게 중단점 목록에 여전히 남아 있으며, Breakpoint Enable의
약자인
be명령어를 이용해 비활성화된 중단점을 다시 활성화 할 수 있다.be는bd와 동일하게be [ID]형태로 사용한다.
8. k
k명령어는 스택 백트레이스를 출력하는 명령어이다.- 스택 백트레이스란 현재 중단된 지점까지 어떤 함수들이 어떤 순서로 호출되어 왔는지를 스택 프레임을 따라 역순으로 나열한 정보이다.

-
위 그림과 같이 함수가 호출 될 때마다 스택에는 함수의 반환 주소가 함께 적재되며,
k명령어를 수행할 시 스택에 적재되어 있는 함수들의 반환 주소들을 모아서 출력하게 된다.- 이 정보들을 통해 지금 실행하고 있는 지점까지 어떤 함수들이 어떤 순서로 호출되어 왔는지 한 눈에 확인할 수 있기에 디버깅 중 매우 유용하게 사용할 수 있다.
-
아래는
rip가exercise_lecture!gen_flag()에 위치한 상태에서k명령어를 수행한 예시이다.0:000> k # Child-SP RetAddr Call Site 00 00000085`0054fc58 00007ff6`72f21370 exercise_lecture!gen_flag 01 00000085`0054fc60 00007ff6`72f215e0 exercise_lecture!main+0x40 02 (Inline Function) --------`-------- exercise_lecture!invoke_main+0x22 03 00000085`0054fd20 00007ffa`ad4be8d7 exercise_lecture!__scrt_common_main_seh+0x10c 04 00000085`0054fd60 00007ffa`ae7dc34c KERNEL32!BaseThreadInitThunk+0x17 05 00000085`0054fd90 00000000`00000000 ntdll!RtlUserThreadStart+0x2c -
해당 출력을 일부 해석해보면, 가장 윗 줄에 현재 실행 중인 함수인
exercise_lectrue!gen_flag()가 있는 상황이고, 이 함수의 반환 주소가0x00007ff672f21370임을 알 수 있다.- 또한 Call Site 상으로 바로 아랫줄에
exercise_lecture!main()이 있는 것을 확인할 수 있으며 이를 통해exercise_lecture!main()에서exercise_lecture!gen_flag()를 호출했음을 알 수 있다.
- 또한 Call Site 상으로 바로 아랫줄에
meta 명령어
1. meta 명령어
- 메타 명령어(meta command)란 WinDbg 명령어의 디버거 환경을 설정하는 명령어들과 조건 분기, 반복, 예외 처리, 흐름 제어 등을 가능하게 하는 명령어들을 종합적으로 지칭하며 대부분
.(dot)으로 시작한다.- 메타 명령어를 이용하면 복잡한 디버깅 작업을 하나의 스크립트로 자동화하거나, 루프와 분기를 이용한 세밀한 제어 흐름을 작성할 수 있다.
2. .cls
-
Clear Screen의 약어로 디버거 콘솔의 출력을 비운다.
- 윈도우의 파워쉘이나 리눅스 셸 등에서 사용하는
clear명령과 동일하다. 아래는 명령어를 수행하기 전과 후를 비교한 것이다.
- 윈도우의 파워쉘이나 리눅스 셸 등에서 사용하는
-
Before
.cls
-
After
.cls
3. 프로세스 제어 명령어
.kill,.restart,.detach세 가지 명령어는 디버깅 대상 프로세스의 실행 상태를 제어하기 위한 핵심 도구이다..kill명령어는 디버거가 연결된 대상 프로세스를 즉시 종료할 때 사용한다.- 이 명령을 실행하면 WinDbg는 해당 프로세스에 중단 신호를 보내어 프로세스를 종료시킨다.
.restart명령어는 현재 디버깅 중인 프로세스를 재시작한다.- 이 과정에서 중단점과 디버거 설정은 유지되므로 초기 상태로 돌아가 반복 검증하거나 수정 사항을 확인하는 데 유용하다.
.detach명령어는 디버거의 제어만 해제하고 대상 프로세스는 그대로 실행되도록 할 때 사용한다.- 이 명령어는 프로세스를 중단하지 않고 디버거와의 연결만 해제하므로, 디버깅 작업을 마친 후 프로세스에 영향을 주지 않고 디버거만 종료하고자 할 때 사용할 수 있다.
4. 출력 명령어
-
.echo와.printf는 WinDbg 스크립트 내에서 사용자 정의 메시지나 변수를 출력할 때 사용하는 명령어이다.- 이 둘은 비슷해 보이지만, 출력 형태와 유연성 면에서 차이를 보인다.
-
.echo명령어는 단순한 문자열 출력에 최적화된 명령어로, 변수 치환이나 서식 지정 기능이 없다.- 고정된 텍스트를 그대로 콘솔에 출력할 때 사용하며, 스크립트 진행 중 로그 형태로 상태를 표시하거나 메시지를 구분할 때 유용하다.
0:000> .echo hello world hello world -
.printf명령어는 C 언어의printf()와 유사한 서식 지정 기능을 제공한다.- 문자열 내에
%d,%x,%c등 서식 문자를 사용하여 레지스터 값이나 메모리 내용을 포맷팅해 출력할 수 있으므로, 보다 정교한 디버깅에 적합하다. - 아래는
rax레지스터의 값을 포맷팅해 출력하는 예시이다.
0:000> .printf "RAX=0x%x\n", @rax RAX=0x2c - 문자열 내에
5. 로그 명령어
-
WinDbg에서는 메타 명령어를 이용해 디버거 콘솔의 출력을 로깅할 수 있다.
- 먼저
.logopen은 명령어 실행 시점부터 디버거 콘솔의 모든 출력을 지정한 파일로 새로 생성해 기록한다. - 명령어의 형식은 아래와 같다.
.logopen [FileName] .logopen /d - 먼저
-
디버거 콘솔의 출력을 기록할 파일명을 인자로 전달할 수 있고, 인자로 파일명 대신
/d를 전달하면 파일명을 자동으로 지정해.log확장자 파일에 기록한다. -
만약 이미 존재하는 파일명을 인자로 전달할 경우 해당 파일을 덮어쓰게 되고, 덮어쓰지 않고 이어서 기록하기를 원한다면
.logappend를 사용해야 한다..logappend의 명령어 형식은.logopen과 동일하다.- 만일 로깅을 마쳤다면
.logclose로 기록을 중단할 수 있다.
-
아래와 같이
.logopen명령어로 로그 파일을 생성하고 몇 가지 명령어를 실행한 뒤.logclose명령어로 로그 파일을 닫아 보겠다.0:000> .logopen C:\WinDbg\test.log Opened log file 'C:\WinDbg\test.log' 0:000> db @rip L20 00007ffe`d0b24419 cc eb 00 48 83 c4 38 c3-cc cc cc cc cc cc cc 48 ...H..8........H 00007ffe`d0b24429 83 ec 28 65 48 8b 0c 25-60 00 00 00 33 d2 41 b8 ..(eH..%`...3.A. 0:000> .logclose Closing open log file C:\WinDbg\test.log -
이후 생성된 로그 파일인 _test.log_를 열어 보면 아래와 같이 디버거 콘솔의 출력을 그대로 기록하고 있음을 알 수 있다.
Opened log file 'C:\WinDbg\test.log' 0:000> db @rip L20 db @rip L20 00007ffe`d0b24419 cc eb 00 48 83 c4 38 c3-cc cc cc cc cc cc cc 48 ...H..8........H 00007ffe`d0b24429 83 ec 28 65 48 8b 0c 25-60 00 00 00 33 d2 41 b8 ..(eH..%`...3.A. 0:000> .logclose .logclose Closing open log file C:\WinDbg\test.log
6. 흐름 제어 명령어
- WinDbg에서는 특정 조건을 만족하는 경우에만 명령어를 실행하거나 반복문으로 명령어를 반복 실행할 수 있는
문법을 지원한다.
- 이를 잘 활용하면 WinDbg 명령어들을 스크립트처럼 작성하여 디버깅을 자동화할 수 있다.
6.1 if 조건문
-
if문과 else문은 아래와 같이 구현할 수 있다.
.if ( Condition ) { Command } .if ( Condition ) { Command } .else { Command } .if ( Condition ) { Command } .elsif ( Condition ) { Command } .if ( Condition ) { Command } .elsif ( Condition ) { Command } .else { Command } -
위 문법을 이용하면 특정 조건에 따라 WinDbg 명령어를 실행할 수 있다.
- 이때
Condition은 기본적으로 MASM 표현식 문법을 따르게 된다. - 예를 들어
rax의 값이 0이 되는 경우에만 “RAX=0”을 출력하고 싶은 경우 다음과 같이 명령어를 작성할 수 있다.
.if (@rax==0) {.echo RAX=0} - 이때
-
아래와 같이 WinDbg에서 실행해보면
rax값에 따라서.echo명령어의 실행 여부가 결정됨을 확인할 수 있다.0:000> r rax rax=0000000000000000 0:000> .if (@rax==0) {.echo RAX=0} RAX=0 0:000> r rax=1 0:000> r rax rax=0000000000000001 0:000> .if (@rax==0) {.echo RAX=0}
6.2 while 반복문
.while ( Condition ) { Command }
-
while반복문은 조건문을 만족하는 동안 중괄호 안의 명령어를 반복해 실행한다.- 예를 들어 중단점을 설정한 뒤 중단점에서 값이 0일 때만 중단하고 싶은 경우 다음과 같이 작성할 수 있다.
.while (@rax!=0) { g } -
명령어를 실행하면 중단점에 도달했을 때의
rax값이 0이 아닌 경우g명령어가 반복적으로 실행되며,rax값이 0일 때에만 프로그램이 중단된다.
6.3 for 반복문
.for (InitialCommand ; Condition ; IncrementCommands) { Command }
-
WinDbg에서는
.for를 이용해 C와 유사하게 반복문을 작성할 수 있다.- 예를 들어, 반복문을 돌면서 반복 횟수를 출력하는 명령어를 아래와 같이 작성할 수 있다.
0:000> .for (r $t0 = 0; @$t0 < a; r $t0 = @$t0 + 1) { .printf "Loop index = %d\n", @$t0 } Loop index = 0 Loop index = 1 Loop index = 2 Loop index = 3 Loop index = 4 Loop index = 5 Loop index = 6 Loop index = 7 Loop index = 8 Loop index = 9
$t0은 무엇인가요?
$t0은 WinDbg에서 제공하는 의사 레지스터(pseudo-register)의 일종으로써 그 중$t0,$t1, …$t19까지 20개의 의사 레지스터는 사용자가 변수처럼 정의하여 사용할 수 있도록 제공하는 사용자 정의 의사 레지스터이다. 의사 레지스터에는 일반 레지스터와 마찬가지로r명령어를 사용할 수 있으며, 레지스터명 앞에 “@”를 붙여@$t0과 같은 형식으로 의사 레지스터의 값을 표현할 수 있다. 위 반복문과 같이 반복문 변수를 선언하는 목적으로 의사 레지스터를 사용할 수 있다.WinDbg는 사용자 정의 의사 레지스터 외에도 여러 의사 레지스터들을 정의하고 있으며, 자세한 내용은 마이크로소프트 공식 문서에서 확인할 수 있다.
중단점 명령어
1. Breakpoint
-
WinDbg의 중단점은 단순히 중단점을 설정하는 것에 더해 여러 가지 옵션을 제공한다.
- 예를 들어, 중단점에 도달했을 때 특정 명령어를 실행하거나 중단점이 걸리는 조건을 추가할 수 있다.
- 이런 다양한 옵션들을 제공하기 위해, 중단점 명령어는 다음의 형식을 가지고 있다.
bp[ID] [Options] [Address [Passes]] ["CommandString"]
1.1 Address
-
[Address]는 중단점 명령어에서 유일하게 필수적으로 전달되야 하는 인자로 중단점을 설정할 지점을 의미한다.- 주소를 직접 숫자로 표현하여 작성하여도 되지만, 심볼을 이용하거나 모듈명과 상대 가상 주소를 이용하여 주소를 직접 계산하지 않고 중단점을 설정할 주소를 표현할 수 있다. 아래는 그 예시이다.
# 숫자로 표현하여 중단점 설정 bp 00007ff6`936b1330 # 심볼 이용하여 중단점 설정 bp exercise_lecture!main # 상대 가상 주소 이용하여 중단점 설정 bp exercise_lecture+0x1330
1.2 ID
- ID는 중단점에 대한 식별자를 의미하며 10진수 숫자로 표현된다.
- 중단점을 설정할 때 ID를 별도 지정하지 않으면 WinDbg가 임의로 식별자를 부여하게 된다.
1.3 Options
- Options는 WinDbg의 중단점 기능을 한 단계 업그레이드하는 기능이다.
- WinDbg는 중단점에 대한 여러 가지 옵션을 제공하며, “/”로 시작하는 옵션 명령어들로 옵션을 지정할 수 있다.
- 별도의 설명이 없으면 두 개 이상의 옵션을 동시에 지정할 수 있다.
| 옵션 | 기능 |
|---|---|
/1 | 한 번 중단된 이후 삭제되는 일회성 중단점을 설정한다. |
/w "식" | 인자로 전달된 조건을 만족하는 경우만 중단되도록 한다. |
/c N | 함수 호출 스택의 깊이가 N보다 작아지는 경우 중단되도록 한다. /C 옵션과 동시에 사용할 수 없다. |
/C N | 함수 호출 스택의 깊이가 N보다 커지는 경우 중단되도록 한다. /c 옵션과 동시에 사용할 수 없다. |
-
아래는
/w옵션을 이용해 조건부 중단점을 설정하는 예시이다.eax레지스터의 값이 8이 될 때 중단점이 설정되도록 한다.
0:000> bp /w "@eax == 8" exercise_lecture+0x1275 0:000> g Breakpoint 0 hit exercise_lecture!gen_flag+0xb5: 00007ff6`d0e71275 ffc0 inc eax 0:000> r eax eax=8 -
중단점이 걸린 이후
eax레지스터 값이 8인 모습을 확인할 수 있다. 8이 아닌 경우 중단되지 않고 그대로 실행된다.
1.4 Passes
- 중단점에서 중단하지 않고 건너뛸 횟수를 지정한다.
- 예를 들어
bp [Module]![Symbol] 5로 중단점을 설정한 경우 중단점에 4번째 도달하는 시점까지는 프로세스가 중단되지 않고 실행되다가, 5번째 도달했을 때 비로소 프로세스가 중단된다.
- 예를 들어
1.5 CommandString
-
중단점에 도달했을 때 특정 WinDbg 명령어를 실행하고 싶을 경우 사용한다.
- 주로 중단점에 도달했을 때
.echo등의 명령어를 활용하여 자동으로 프로세스 상태를 출력하게 하거나 함수가 실행될 때 함수의 인자 및 반환값을 출력하는 등 다양한 목적으로 사용할 수 있다. - 여러 명령어를 실행하고 싶은 경우 세미콜론(;)으로 각 명령어를 구분하여 넣어줄 수 있다.
- 아래는 반복문 내부에 중단점을 설정하면서 중단점에 도달할 때마다 “BREAKPOINT!”를 출력함과 동시에
g로 실행을 재개하는 명령어를 추가한 예시이다.
0:000> bp exercise_lecture+0x1275 ".echo BREAKPOINT!; g" 0:000> g BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! BREAKPOINT! - 주로 중단점에 도달했을 때
-
위와 같이
g명령어를 활용하면 중단점에서 프로세스가 중단되었을 시 수행할 디버깅 작업을 자동화하는 효과도 얻을 수 있다.
2. Access Breakpoint
-
WinDbg에서는 단순히 특정 명령어가 실행될 때 중단점을 설정하는 것 이외에도 특정 메모리에 접근할 때 중단 되도록 설정하는 것이 가능하다.
- 이를 Access Breakpoint라고 하며, 읽기, 쓰기, 실행의 접근에 대해 중단되도록 설정할 수 있다.
ba명령어를 통해 중단점을 설정할 수 있으며 명령어의 형식은 아래와 같다.
ba[ID] [Access] [Size] [Options] [Address [Passes]] ["CommandString"] -
[Access]와[Size]를 제외한 나머지 인자들의 사용법은bp명령어와 동일하다.
2.1 Access
- 읽기, 쓰기, 실행 중 어느 접근에 대해 중단할 지 설정할 수 있다.
[Access]의 경우 다음 중 하나의 옵션을 필수로 사용해야 한다.
| 옵션 | 기능 |
|---|---|
| e | CPU가 특정 주소의 명령어를 실행할 때 중단된다. |
| r | CPU가 특정 주소의 메모리를 읽거나 쓸 때 중단된다. |
| w | CPU가 특정 주소의 메모리에 쓸 때 중단된다. |
2.2 Size
-
64비트 운영체제에서는 1, 2, 4, 8만
[Size]인자로 허용한다.- 다만,
[Access]가e인 경우 크기는 1이어야 한다.
- 다만,
-
아래는 현재
rcx레지스터에 담긴 주소의 4 바이트 메모리에 읽거나 쓰기 작업이 일어날 경우 중단되도록 하는 명령어 예시이다.0:000> ba r 4 @rcx 0:000> g Breakpoint 1 hit exercise_lecture!gen_flag+0x125: 00007ff6`72f212e5 488b442408 mov rax,qword ptr [rsp+8] ss:000000c8`0852fa78=000000c80852fae0 -
실행하면 위와 같이 중단된 지점과 함께 중단된 지점의 명령어를 확인할 수 있다.
-
ba명령어는 특정 메모리에 읽기 또는 쓰기 접근이 언제 일어나는지 추적하는 용도로 많이 사용된다.- 예를 들어, 랜섬웨어 등 암호화 루틴이 포함된 바이너리를 분석하는 경우 평문 메모리 버퍼를 찾은 뒤 해당 메모리에 읽기 중단점을 걸고 실행하면 평문 버퍼에 접근하는 지점을 쉽게 찾을 수 있으며, 이 지점이 곧 암호화 루틴의 시작점이 될 것이다.
- 그 외에도 버퍼 오버플로가 일어나는지 찾아보거나, 구조체의 멤버 변수에 중단점을 설정하여 값이 바뀌는 시점을 추적하는 등 특정 상황들에서 매우 효과적인 디버깅이 가능하도록 하는 유용한 기능이다.
WinDbg 스크립트
1. WinDbg 스크립트
- WinDbg 명령어를 작성하다 보면 길어지는 경우가 매우 많다.
- 특히
.if나.for등의 조건 분기문을 작성하는 경우 개행 없이 명령어를 작성하는 것은 상당히 불편하며 복잡한 작업을 수행하는 데 큰 걸림돌이 되기 마련이다. - WinDbg는 이를 위해 복잡한 명령어나 여러 가지 명령어를 한 번에 실행하기 쉽도록 디버거 명령어 스크립트를 지원하고 있다.
- 특히
1.1 $$><
-
스크립트 파일을 작성한 뒤 아래와 같이
$$><명령어를 이용해 스크립트를 실행할 수 있다.$$>< [ScriptFile] -
이 명령어는
ScriptFile의 개행을 모두 세미콜론(;)으로 변환하여 실행하는 단순한 원리로 동작한다. -
기존에
.for (r $t0 = 0; @$t0 < a; r $t0 = @$t0 + 1) { .printf "Loop index = %d\n", @$t0 }와 같은 형태로 작성된.for명령문의 경우 아래와 같이 스크립트 파일로 작성할 수 있다..for (r $t0 = 0; @$t0 < a; r $t0 = @$t0 + 1) { .printf "Loop index = %d\n", @$t0 .printf "Scripting..\n" } -
이를 저장하고 WinDbg에서 실행하면 위 스크립트는
.for (r $t0 = 0; @$t0 < a; r $t0 = @$t0 + 1) { ; .printf "Loop index = %d\n", @$t0; .printf "Scripting..\n"; }와 같이 변환되어 실행된다.- 실행한 결과는 아래와 같다.
0:000> $$>< C:\WinDbg\script.dbg Loop index = 0 Scripting.. Loop index = 1 Scripting.. Loop index = 2 Scripting.. Loop index = 3 Scripting.. Loop index = 4 Scripting.. Loop index = 5 Scripting.. Loop index = 6 Scripting.. Loop index = 7 Scripting.. Loop index = 8 Scripting.. Loop index = 9 Scripting..
Cheat Sheet
1. Built-in 명령어
| 명령어 | 기능 |
|---|---|
bp | 중단점을 설정한다. 옵션과 명령어 등을 조합하면 다양하게 중단점을 활용할 수 있다. |
ba | 특정 메모리에 Read, Write, Execute 세 종류의 접근이 일어날 때 중단되도록 설정할 수 있다. |
g | 프로세스의 실행을 재개한다. |
bl | 중단점의 목록을 출력한다. |
bc | 특정 중단점을 삭제한다. |
bd & be | 특정 중단점을 비활성화하거나 활성화한다. |
p | Step Over를 수행한다. |
t | Step Into를 수행한다. |
gu | Step Out을 수행한다. |
r | 레지스터 값을 확인하거나 변경한다. |
| `d{a | b |
| `e{a | b |
Enter | 이전 명령어 창의 명령어를 그대로 실행한다. |
lm | 메모리에 로드된 모듈들의 목록을 출력한다. |
? | MASM 표현식을 계산하여 결과를 출력한다. |
k | 함수 호출 스택을 출력한다. |
$$>< | WinDbg 명령어 스크립트의 줄바꿈을 모두 세미콜론(;)으로 변환하여 실행한다. |
2. meta 명령어
| 명령어 | 기능 |
|---|---|
.cls | 명령어 창의 출력을 비워준다. |
.kill | 현재 디버깅 중인 프로세스를 종료한다. |
.restart | 현재 디버깅 중인 프로세스를 재시작한다. 프로세스만 재시작하는 명령어로 디버거에서 설정한 중단점은 유지된다. |
.detach | 대상 프로세스와 디버거의 연결만 끊고, 프로세스는 실행을 재개하도록 한다. |
.echo | 인자로 전달된 문자열을 그대로 출력한다. |
.printf | C에서의 포맷 스트링과 동일한 기능으로 전달된 포멧 스트링과 추가 인자들에 맞게 출력한다. |
.logopen | 로그 파일을 생성하여 디버거 명령어 창의 모든 출력을 기록한다. |
.logappend | 기존 로그 파일에 덧붙여서 디버거 명령어 창의 모든 출력을 기록한다. |
.logclose | .logopen 혹은 .logappend의 기록을 중단한다. |
.if & .elsif & .else | 조건에 맞게 WinDbg 명령어를 실행한다. |
.for | C 형태의 for 반복문이다. |
.while | 조건을 만족하는 동안 반복하는 반복문이다. |
마치며
- 이번 강의에서는 WinDbg가 가지고 있는 유용한 기능들을 소개하였다.
- 이번 강의에서 소개한 기능들 외에도 WinDbg에는 수많은 기능들이 구현되어 있으며 이러한 기능들 덕분에 WinDbg는 윈도우 생태계에서 가장 널리 사용되는 디버거로서 자리매김하고 있다.
- 디버깅은 리버스 엔지니어링 및 익스플로잇에 있어서 매우 강력한 무기지만 사용 역량을 갖추기 위해서는 실제 바이너리들을 직접 디버깅해보면서 학습한 명령어들과 기능들을 실제로 적용해 보아야 한다.
- WinDbg의 공식 문서와 강의 내용을 곁에 두며 WinDbg 기능들을 실제로 활용하는 경험을 쌓아가다 보면 어느새 WinDbg를 능숙하게 활용할 수 있을 것이다.