본문 바로가기

리버싱

[리버싱] 브레이크포인트란






브레이크포인트 (breakpoint)



브레이크포인트는 디버깅을 목적으로 실행중인 디버깅 대상 프로세스를 의도적으로 멈추게 하는 장소를 가리킨다.


프로세스가 멈추면 멈춘 시점의 변수나 스택 파라미터, 혹은 특정 메모리 지점의 값 등을 조사할 수 있다.


브레이크포인트는 구현되는 방법에 따라 소프트 브레이크포인트, 하드웨어 브레이크포인트, 메모리 브레이크포인트로 구분된다.




소프트 브레이크포인트 (Soft Breakpoint)



명령을 실행하는 CPU를 일시 중지하는 데 사용되며 디버깅할 때 가장 흔하게 사용되는 형태의 브레이크포인트다.





종류


- 일회성 브레이크포인트


한 번 브레이크포인트 이벤트가 발생하면 디버거의 내부 브레이크포인트 리스트에서 해당 브레이크포인트 정보가 제거되는 것이다. 한 번만 브레이크포인트를 발생시키고 싶을 때 사용하면 된다.


- 지속적인 브레이크포인트


브레이크포인트가 발생하고 CPU가 원래의 opcode로 돌려놓은 다음에 다시 브레이크포인트 설정을 수행하기 때문에 브레이크포인트 리스트에 정보가 계속 유지된다.





동작 과정


1. 현재 주소의 첫 번째 opcode 의 1 바이트와 해당 주소를 브레이크포인트 리스트에 저장한다


2. 저장된 바이트 위치의 데이터를 CC로 바뀐다


3. CPU가 CC라는 1 바이트의 코드를 만나서 INT3 이라는 이벤트 발생시켜 동작이 일시 정지된다


4. 다시 진행될 때 디버거는 현재 EIP의 값이 브레이크포인트 리스트에 존재하면 이전에 저장해두었던 원래의 opcode 로 돌려놓는다



#INT3 

:  CPU가 예외처리를 하기 위해 인터럽트를 발생시키는 것을 INT라고 하는데 그 종류 중에서 INT3 은 브레이크포인트 인터럽트를 발생시킨다





장단점



장점  :  브레이크 포인트 횟수에 제한이 없고 사용이 쉽다


단점  :  메모리 상의 실행 바이너리의 바이트를 변경하기 때문에 CRC (Cyclic Redundancy Check) 값이 변경된다. 변경된 것이 확인되면 스스로 종료해버리기 때문에 악성코드 분석이 어려워진다.


#CRC (순환 중복 검사)  

:  데이터가 변경되었는지 확인하기 위해 사용되는 방법이다.








하드웨어 브레이크포인트 (Hardware Breakpoint)



설정할 브레이크포인트의 개수가 적을 때나 디버깅 할 소프트웨어의 코드가 변경되면 안될 때 유용하게 사용할 수 있다. 디버그 레지스터(8개) 를 이용하여 CPU 레벨에서 브레이크포인트를 설정하는 것이다.


하드웨어 브레이크포인트는 소프트 브레이크포인트와 동일한 방법으로 처리되지만 로우 레벨에서 수행된다.



동작 방법


1. CPU가 명령을 실행하기 전에 해당 주소의 하드웨어 브레이크포인트 설정 여부를 확인


2.  수행할 명령이 하드웨어 브레이크포인트가 설정된 주소에 접근하는 지 확인


3. 해당 주소가 DR0 ~ DR3 레지스터에 저장되어 있고 읽기 / 쓰기나 실행 조건이 설정되어 있다면 CPU는 명령에 대한 실행을 중지시키고 INT 1 이벤트 발생


4. 해당 주소가 디버그 레지스터에 저장되어 있지 않다면 CPU는 해당 명령을 실행하고 다음 명령으로 이동해 하드웨어 브레이크포인트 설정 내용 확인



#INT1

:  디버그 인터럽트




디버그 레지스터 종류



- DR0 ~ DR3 


브레이크포인트의 주소 저장


- DR4 ~ DR5 


예약된 레지스터 (사용 ×)


- DR6 


브레이크포인트에 의해 발생되는 디버깅 이벤트의 종류를 저장


- DR7 


하드웨어 브레이크포인트의 ON / OFF 스위치로 사용되며, 서로 다른 브레이크포인트의 조건도 저장





장단점



장점  :  디버깅 할 소프트웨어의 코드가 변경되지 않는다


단점  :  브레이크포인트의 주소를 저장할 디버그가 4개이기 때문에 한 번에 최대 4개까지의 하드웨어 브레이크포인트만 설정할 수 있다. 그리고 브레이크포인트를 설정할 수 있는 데이터의 최대 크기가 4바이트라서 큰 메모리 영역을 다루기 어렵다.








메모리 브레이크포인트 (Memory Breakpoint)



실제 브레이크포인트는 아니고 메모리 브레이크포인트를 설정하면 메모리의 영역이나 페이지의 접근 권한이 바뀐다. 하지만 Guard page를 이용하여 브레이크포인트처럼 사용할 수 있다.


memory page는 OS가 메모리를 처리하는 가장 작은 단위인데 이 단위는 접근 권한을 갖게 된다.




접근 권한 종류



- Page execution (페이지 실행)


이 권한이 할당된 메모리 페이지는 실행시킬 수 있다. 하지만 이 메모리 페이지에서 데이터를 읽거나 쓰려고 하면 접근 위반 예외가 발생한다.


- Page read (페이지 읽기)


프로세스는 이 권한이 할당된 메모리의 내용을 읽을 수 있다. 하지만 데이터를 쓰거나 실행시키려고 하면 접근 위반 예외가 발생한다.


- Page write (페이지 쓰기)


이 접근 권한은 프로세스가 해당 메모리 페이지에 데이터를 쓰는 것만 허용한다.


- Guard page (보호 페이지)


이 권한이 할당된 페이지에 대해 어떤 종류의 접근이라도 발생하면 예외를 발생시킨다. 예외를 발생시킨 이후에는 페이지의 원래 상태로 복귀된다.


이 접근 권한은 스택에서 힙을 분리해내거나 특정 메모리 영역이 어떤 범위 이상으로 커지는지 확인하는 데 유용하다. 또한 특정 메모리 영역에 대한 접근이 발생할 때 프로세스를 중지시키고자 할 때 매우 유용하게 사용된다.





대개의 OS는 위의 권한들을 결합할 수 있다. 예를 들면 메모리에 읽기/쓰기 권한을 가진 페이지가 있을 수 있고, 읽기/실행 권한을 가진 다른 페이지가 있을 수 있다. 메모리 현재 권한을 원하는대로 변경하게 해주는 고유 함수들을 통해 바꿔줄 수 있다.


 



동작 방법



1. 특정 메모리에 메모리 브레이크포인트를 설정한다


2. 특정 메모리의 권한이 변경되어 보호 권한이 설정된다


3. 브레이크포인트를 설정한 메모리에 대한 접근이 발생했을 때 CPU가 보호 페이지 디버그 예외 처리를 발생시킨다 (전달된 패킷의 내용을 언제, 어떻게, 어떤 작업을 수행하는 것인지 알아낼 수 있다)


4. 해당 디버그를 통해 메모리를 분석한다





장단점



장점  :  코드가 변경되지 않고, 메모리 영역의 변화를 관찰하는 데 유용하다. 스택에서 힙을 분리하는 데 유용하고, 어떤 범위 이상으로 memory를 쓰지 않게 할 수 있다.


단점  :  디버거가 항상 그 프로세스의 메모리에 대한 접근을 필요로 하기 때문에 디버거의 처리속도가 늦어지게 된다.