22
Level 35 이번 레벨에서는 다형성 코드로 작성된 바이너리에 대하여 인라인 패치 를 하는 법에 대하여 다룰 것이다. 분석 바이너리에서는 "Yoda's Crypter" 프로텍터가 사용되었다. 그러나 우리의 목표는 언패킹이 아니라 다형성 코드를 분석(다형성 코드를 일반적인 방법에서 분석하기 위해서는 특별한 테크닉을 요구한다) * Yoda's 프로텍터는 무료 프로텍터이다. 처음에는 위 사진처럼 "Yoda's Crypter" 라고 불렸으나, 이후에 프로텍터 제작자가 데이터와 코드 섹션을 패킹하는 기법을 추가하여 지금은 프로텍터로 불리운다. 이 프로텍터는 다형성 코드 암호화/복호화 등과 같이 몇가지 괜찮은 기능들을 가지고 있는데 이 부분이 바로 이번 레벨의 목표라고 할 수 있다. 또 다른 기능 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC 체크, API 리다이렉션, 안티 덤프, PE 헤더 삭제" 등이 있다. 우선 바이너리를 언팩한 뒤 덤프를 뜬 뒤 분석을 시작하고, 이 패커에 대하여 어떤 인라인 패치가 필요한지 알아볼 것이다. 그리고 패커(아니면 바이너리 자체)에서 사용하는 CRC 체크를 우회해야 한다. 나중에 암호화 된 코드 내에 인라인 패치를 해야 하기 때문에 패커의 암호화/복호화 코드가 메인 관점일 것이다.

Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

  • Upload
    lythuy

  • View
    217

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

Level 35

이번 레벨에서는 다형성 코드로 작성된 바이너리에 대하여 인라인 패치

를 하는 법에 대하여 다룰 것이다. 분석 바이너리에서는 "Yoda's Crypter"

프로텍터가 사용되었다. 그러나 우리의 목표는 언패킹이 아니라 다형성

코드를 분석(다형성 코드를 일반적인 방법에서 분석하기 위해서는 특별한

테크닉을 요구한다)

* Yoda's 프로텍터는 무료 프로텍터이다. 처음에는 위 사진처럼 "Yoda's

Crypter" 라고 불렸으나, 이후에 프로텍터 제작자가 데이터와 코드 섹션을

패킹하는 기법을 추가하여 지금은 프로텍터로 불리운다. 이 프로텍터는

다형성 코드 암호화/복호화 등과 같이 몇가지 괜찮은 기능들을 가지고

있는데 이 부분이 바로 이번 레벨의 목표라고 할 수 있다. 또 다른 기능

으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE

탐지, CRC 체크, API 리다이렉션, 안티 덤프, PE 헤더 삭제" 등이 있다.

우선 바이너리를 언팩한 뒤 덤프를 뜬 뒤 분석을 시작하고, 이 패커에

대하여 어떤 인라인 패치가 필요한지 알아볼 것이다. 그리고 패커(아니면

바이너리 자체)에서 사용하는 CRC 체크를 우회해야 한다. 나중에 암호화

된 코드 내에 인라인 패치를 해야 하기 때문에 패커의 암호화/복호화 코드가

메인 관점일 것이다.

Page 2: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

EP에서 트레이싱을 하면서 위 주소까지 진행한다. 이 패커에서 OEP를 찾는

것은 쉬운 편에 속하지만, 우선 코드가 어떻게 변형되었는지 볼 것이다. 이미

바이너리의 EP 부분부터 프로텍터가 코드를 복호화 하고 있는 것을 볼 수 있다.

위 사진의 루프가 동작하면서 루프 바로 이후에 위치하는 코드를 난독화 하고

있는 것을 알 수 있다. 복호화 대상 바이트의 개수는 ECX 레지스터에 저장되어

있다. 따라서 나중에 인라인 패치를 하려면 인라인 코드 역시 암호화를 시켜야

한다.

일반적인 인라인 패치에서는 "JMP OEP" 등의 형태를 찾아서 해당 분기문을

리버서가 삽입한 인라인 코드로 분기되게 패치했지만, 이번에는 "JMP OEP"

코드는 복호화 루프에 의해 덮어 씌어질 확률이 높기 때문에 쓸모 없다고 말할

수 있다. 게다가 나중에 알겠지만, "JMP OEP" 코드는 2번 복호화 된다. 처음에는

실제 코드 내에서 "JMP OEP" 직전에 쓸모 없는 코드로 분기된다. 일단 이 부분은

나중에 알아보도록 하고 우선 OEP 부터 찾을 것이다.

"Yoda's Protector" 내에서 OEP를 찾기 위해, 리버서는 약간의 이점이 있는데

바로 해당 프로텍터가 대부분의 프로텍터처럼 "Exceptions"를 생성한다는 것이다.

올리 디버거에서 모든 "Exception" 체크를 해제한 뒤 실행하면 요다 프로텍터는

1개의 "Exceptions"를 생성하는데, 해당 "Exceptions" 주소가 OEP로 접근하기

이전의 주소이다.

Page 3: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

좌측의 사진이 기본적인 “Exceptions” 설정 값이고, 우측의 사진이 변경 된 값이다.

설정 값 변경 후 실행(F9)하면 “Exceptions”이 발생하면서 “00613982” 주소에서

멈춘다. 아직 OEP가 아니기 때문에 스크롤을 조금 올려 분석을 계속해야 한다.

위 사진의 코드 루틴을 기억하길 바란다. 나중에는 위 코드 루틴에서 패커 코드가

제거되는 것을 볼 수 있다. 루프 이후의 코드 또한 삭제되는데, 아래쪽의 분기문을

“(61397F | JMP 613982)”을 보면 이미 삭제된 주소로 분기되고 있기 때문에

"Exception"이 발생하였다. 해당 "Exception" 이후에는 OEP로 리턴 될 것이다.

Page 4: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

그리고 루프 이전의 코드 또한 몇 몇 줄은 삭제된다. 이 부분은 나중에 알아보도록

하고 우선 OEP 부터 찾을 것이다.

메모리 맵 창에서 코드 섹션에 “MEM BP on ACCESS”를 설치한 뒤 실행(F9) 한다.

실행하면 OEP 주소로 이동된다. 언패킹이 완료되었으니 “Rebuild Import” 옵션을

해제 한 뒤 덤프를 뜬다.

“ImpRec” 툴에서 OEP 값을 이전 사진에서 찾은 주소로 바꾼 뒤 “AutoSeach” 버튼을

클릭하면 나머지 RVA/Size 값이 자동으로 세팅되게 만든다.

Page 5: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

자동으로 세팅 되면 “Get Imports”를 누른다. 누르면 위 사진과 같이 몇몇의

모듈들의 상태가 “valid:NO” 상태로 보여진다.

문제가 되는 모듈들을 디스 어셈블한 결과를 보면 대부분의 API들이 사용되고

있음을 알 수 있다. 어셈블리어 코드 자체가 깨진 것은 아니기 때문에 “Trace”를

이용하여 간단하게 복구할 수 있다.

Page 6: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

“Trace Level1 (Disasm)”을 이용하면 모든 모듈의 상태가 정상적으로 변한다.

이제 “Fix Dump”를 이용하여 언패킹 된 바이너리의 IAT를 수정하면 된다.

이제 수정 된 바이너리를 분석할 것이다. 일단 프로그램의 행위 분석을 위해

실행시켜서 이것 저것 알아볼 것이다.

Page 7: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

메인 프로그램이 실행 되지만, 일단 “unregistered” 이기 때문에 제약 사항이

존재한다고 예측할 수 있다. 어떤 제약 사항이 있는지 찾아야 한다.

좌측의 “Installed Packages” 부분에 자물쇠가 있는 것으로 보아 해당 부분이

제약사항으로 판단된다. 또한 “Help – About” 메뉴를 클릭하면 좌측 하단에

“Trial license” 라는 문자열을 볼 수 있다.

메모리 맵 창에서 해당 문자열을 검색하면 “0051FA10 (코드 섹션)” 주소에

위치하고 있음을 알 수 있다.

해당 주소로 이동한 뒤 어느 주소에서 해당 문자열을 사용하고 있는지 찾는다.

Page 8: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

“00520493” 주소에서 해당 문자열을 사용하고 있었다. 그런데 해당 주소 위쪽

에는 “4767B4” 함수를 호출하고 리턴 값(AL 레지스터 값)을 이용하여 분기를

시키고 있었다. 따라서 프로그램을 재실행(F12) 하여 “52046C | CALL 4767B4”

함수 내부를 분석해야 한다.

함수 내부로 분석하여 트레이싱을 하다 보면 “4767D6” 주소에서 “[EBP-2]”

포인터 값을 ‘0’으로 만들고 있었다. 해당 포인터 값을 주의 깊게 보아야 하는데,

왜냐하면....

해당 포인터 값을 이용하여 나중에 비교 명령어가 종종 사용되고 있기 때문이다.

스크롤을 쭉쭉 내려서 “AL” 레지스터값이 세팅되는 곳을 보면 역시 “[EBP-2]”

포인터 값을 복사하고 있었다.

Page 9: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

등록되지 않은 상태에서는 ‘0’ 값이 리턴되기 때문에 해당 함수가 리턴되고 나면

“JE” 조건문에 의해 분기가 일어나서 “Trial license” 쪽으로 이동된다. 만약 해당

함수에서 리턴 되는 값이 ‘1’ 이라면 아마도 “Full license” 쪽으로 이동 될 것이다.

리턴 값을 세팅하는 “[EBP-2]” 포인터 값을 ‘1’로 만들게 패치한 뒤 AL 레지스터

값을 세팅하는 부분을 보면 “01” 값이 복사되는 것을 볼 수 있다.

리턴 되면 예상했던 것처럼 분기가 일어나지 않아 “Full license” 쪽으로 이동된다.

* 이전 사진에서 "등록 상태 체크" 코드 루틴은 애초에 "MOV BYTE PTR

[EBP-2],0" 명령어가 초반에 있었다. 이는 기본적으로 프로그램을 "등록 되지

않은 상태(unregisterd)"로 만들어 시작한다는 뜻이다. 해당 명령어 이후 등록

상태 검사/올바른 시리얼인지 검사하는 루틴이 존재하였고, "[EBP-2]" 포인터가

'1'로 세팅되면 "등록 상태(registered)"로 인식하였다.

* 패치 후 실행하면 프로세스가 강제로 종료되는 경우도 있을 것이다. 처음에는

소프트웨어의 CRC 체크 때문에 종료된 줄 알았으나, 좀 더 디버깅을 해보니

소프트웨어가 등록에 사용된 가짜 ID/시리얼을 탐지한 것이였다. 프로그램

등록 창을 보면 입력한 ID 값에 해당하는 올바른 시리얼 값을 입력했을 때만

"Register" 버튼이 활성화 되었다. 강제로 가짜 시리얼을 입력받게 만들었기

때문에 소프트웨어를 "등록" 상태로 인식시키기 위해 패치했을 때 또 다른

체크 루틴에 의해 강제로 종료되는 것이다.

Page 10: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

따라서 시리얼 입력 없이 그냥 패치해 버리면 정상적으로 실행이 된다. 하지만

리버싱의 연습을 위해 어느 부분을 패치해야 하는지 알아볼 것이다.

크래시가 난 상태에서 콜 스택 창을 보면 제일 마지막에 “4047D4” 함수를 호출

하였다. 즉, 해당 주소에서 강제 종료시킨 것이기 때문에 위쪽의 “JE” 조건문을

패치 하였지만 그래도 크래시가 발생하였다.

스크롤을 위로 올리면서 이전 사진의 조건문 주소가 포함되어지는 코드 루틴의

함수 프롤로그로 이동한 뒤 BP 설치 / 재실행(Ctrl+F2) 한다. 그러면 시스템 스택

창에서 해당 함수 이전에 종료되는 주소(정확히 말하면 이전 호출 주소의 다음

주소)를 볼 수 있다.

이동된 주소를 보니 “CALL [EBX+38]” 함수에서 크래시가 발생한 것으로 추정할

수 있다. 해당 함수 이전의 조건문을 변경 해 보니 크래시가 일어나지 않았기

때문에 “JMP”로 패치하여 함수 실행을 막으면 된다.

현재 바이너리는 “프로그램 등록상태로 인식하게 패치 + 크래시가 일어나지

않게 패치” 한 상태이다. 새로운 바이너리로 저장하고 실행하여 정상적으로 패치가

되었는지 테스트를 해봐야 한다.

Page 11: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

패치 된 바이너를 실행해보면 이전과 달리 “Installed Packages”의 제약 사항이

사라졌고, “About”에서 보여지는 문자열 또한 “Full license”로 바뀌었으며,

“Register” 창에서도 역시 “Full License” 라는 문자열이 나타났다.

지금까지 한 것을 짧게 정리하면 아래 순서와 같다.

1. 프로텍터의 일부분 학습 (코드 암호화 / 임포트 테이블 망가뜨리기)

2. 바이너리 언패킹 / 덤프

3. 덤프 된 바이너리 리버싱

4. 바이너리 더블 체크 종료 및 패치 완료

이제 인라인 패치를 위해 프로텍터를 살펴 보기 위해, 패킹되어 있는 원본

바이너리를 분석해야 한다.

Page 12: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

이전에 설명했듯이 위 주소의 루프(첫 번째 루프)에서 바이너리의 코드를 복호화

하고 있기 때문에, 해당 루프에서 사용되는 수학적 계산식을 살펴 볼 것이다.

첫 번째 수학적 계산식은 “SUB” 연산이다. 그리고 “CLC / STC” 명령어는 각각

“Clear Carry, Set Carry”를 의미하는데 복호화 연산에서는 크게 의미가 없다.

그 다음에는 ‘“ADD” 연산이 수행되는 것을 볼 수 있다.

그리고 “ROL / ROR” 연산은 각각 “Rotate Left / Rotate Right”를 의미한다.

루프는 ECX 레지스터의 값 만큼 반복되는데, 처음 반복이 종료되기 이전에 루프

이후의 명령어는 위 사진과 같다. (MOV EBP,EAX | DEC ESP | DEC ESP)

Page 13: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

그런데 처음 반복이 종료되면 루프 이후의 명령어가 “MOV EDX,EBP | ADD EAX,

40321F”로 변경된다. 굉장히 중요한 부분인데, 만약 처음 반복이 완료되기 이전에

“6130C6 / 6130C8” 주소에 BP를 설치해 버리면 “Exceptiosn” 발생한다.

“6130C4” 주소에 BP를 설치하여 3-4번 정도 실행(F9) 하며 루프 바로 다음 주소의

명령어들이 복호화가 될 것이다. 이 상태에서 해당 주소(6130C6)에 BP를 설치

하여 실행(F9 == 루프 종료)하면 모든 코드가 복호화 될 것이다.

언패킹이 완료되면 원래 “613971 / 613972” 주소에 “JMP OEP_주소” 형태의 명

령어가 존재해야 한다. 그러나 아직 해당 명령어처럼 보이지 않는다. 아직 완벽

한 복호화가 되지 않은 것이다. ( 이전에 말했듯이 2번의 복호화가 필요 하다)

나중에는 해당 코드가 패커에 의해 삭제되는 것을 볼 것이다.

이전 사진에서 완벽한 복호화를 위해 실행(F9) 해보면 액세스 위반 “Exception”이

발생하면서 “613983” 주소에서 멈춘다.

Page 14: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

스크롤을 위로 올려 보면, 2개의 루프가 보여지는데 해당 루프에서 루프 이전 /

이후의 코드를 삭제하고 있었다. 코드를 삭제하고 “JMP 613982” 명령어가 실행

되는데 이미 해당 주소(613982)는 삭제되었기 때문에 “00000000” 값을 가지고

있어 “Exceptions”이 발생하는 것이다.

일단 재시작 (Ctrl+F2) 하여 “JMP OEP_주소” 명령어로 이동한다.

* 처음에는 좀 헷갈릴 수 있다. 일단 소프트웨어가 암호화 된 원래 코드를

복호화 하기 위해 복호화 루프를 실행하였다. 첫 번째 복호화 루프가 종료

되어도 "JMP OEP" 명령어는 제대로 복호화가 되지 않은 약간의 쓰레기 형태

였다. 따라서 좀 더 정확한 분석을 위해, 코드가 몇번 복호화 되는지, 어디서

복호화 되는지, 어느 코드가 복호화 되는지 알아볼 것 이다. 이는 MEM BP on

WRITE를 이용하면 된다.

“JMP OEP_주소 == (사진과 달리, 613972 주소에 설치해야 함)”에 MEM BP on

WRITE“를 설치하고 실행하면 복호화 루프에 의해 해당 주소에 다른 값이 작성될

때 멈출 것이다.

한번 (F9) 실행하면 위 주소에서 멈춘다. 당연히 루프에서 복호화 된 값을 해당

주소(JMP OEP 주소)에 작성하고 있으니 멈춘 것이다.

Page 15: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

한번 더 실행하면 위 사진의 주소에서 멈춘다. 참고로 “00613852” 주소 다음에는

“LOOPD SHORT 0061384A” 명령어가 있다. 따라서 또 다른 복호화 루프로 추정할

수 있다. 해당 루프를 종료하기 위해 한번 더 실행(F9) 한다.

또 다른 루프로 이동된다. 해당 루프가 어떤 역할을 하는지 보기 위해 여러번 실행

하면서 사용되는 주소( [EDI] 레지스터 값이 주소 )를 살펴 봐야 한다.

실행하면서 루프 이후의 코드(613982 이후 부분)를 전부 “0000”으로 만들면서

삭제하고 있다.

* 위 사진의 루프에서, 바이너리는 삭제된 코드로 분기하기 전에 자신의 코드를

삭제하고 있었다. (때문에 "Exceptiosn"이 발생한다). 이제 인라인 패치를 하는

법과, 인라인 코드로 분기하는 명령어를 어느 주소에 작성해야 하는지 생각해 봐

야 한다. 모든 코드는 복호화 되고, 덮어 씌어지고, 삭제된다. 좀 더 삽질을 해본

결과 첫 번째 루프(VA:61395B)에서는 해당 루프 이전의 명령어를 삭제하고,

두 번째 루프(VA:613973)에서는 해당 루프 이후의 명령어를 삭제한다.

Page 16: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

그러나 만약 프로텍터에게 강제로 인라인 패치를 위한 공간을 생성하게 하면

어떻게 될까?? 어찌 될지는 모르지만 그럼 어떻게 강제적으로 공간을 생성시켜야

할까?? 이런 추론을 테스트 하기 위해서는 프로텍터에게 강제로 인라인 패치 코드를

쓸 수 있는 공간을 찾아야 한다.

프로그램을 재시작 한뒤 “JMP OEP” 명령어의 주소로 이동한다. "00613972 | JMP

OEP" 명령어는 아직 복호화 되지 않았다. 만약에 복호화 루프로 분기되는 명령어

대신에 그냥 "JMP OEP"로 인라인 패치해버리면 어떻게 될까? 이를 위해 위 주소의

"JMP OEP"가 복호화 되는 주소(루프 주소)를 인라인 패치 하면 어떻게 될까?

다시 한번 "MEM BP on WRITE"를 설치하여 어디서 언패킹 코드가 복호화 되는지

알아 볼 것이다.

MEM BP on WRITE 설치 후 한번 실행(F9)하면 위 사진에서 보여지는 루프에서

멈춘다.

Page 17: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

지금까지의 결과를 토대로 바이너리의 흐름을 정리해보면 다음과 같다.

1. 패커에서 모든 코드가 복호화 됨

2. 복호화 된 코드에서 바이너리 언패킹을 수행 함

3. 언패킹 이후, 위 사진의 주소에 도달

4. "JMP OEP" 복호화

5. 모든 패커 코드가 삭제됨

6. 삭제된 코드로 분기가 일어남

7. "JMP OEP" 때문에 "Exceptiosn"이 발생함 (SEH)

참고로 위 사진의 루프가 "JMP OEP"를 실제 코드 형태로 복호화 하는 루프이다.

인라인 패치를 위한 코드를 위 사진의 복호화 루프를 이용할 수 있다. 해당 주소를

패치하면 인라인 패치 코드는 "JMP OEP" 바로 직전에 위치할 것이다. 이 방법은

첫 번째 복호화 루프 이후에 수행되는 무결성 체크(CRC 사용)를 우회하기 위해

2개의 복호화 루프가 필요하다. 그러나, 위 사진의 주소에 EP(우리가 입력한

인라인 패치를 생성하는) 이후에 첫 번째 복호화 루프를 만들면, (좀 더 쉽게

말해서 위 사진의 주소에 인라인 패치 명령어를 생성하는 복호화 루프를 생성)

어떻게 될까??

다시 한번 말하지만 본 바이너리의 일반적인 흐름 순서는 다음과 같다.

1. 패커에서 모든 코드가 복호화 됨

2. 복호화 된 코드에서 바이너리 언패킹을 수행 함

3. 언패킹 이후, 위 사진의 주소에 도달

4. "JMP OEP" 복호화

5. 모든 패커 코드가 삭제됨

6. 삭제된 코드로 분기가 일어남

7. "JMP OEP" 때문에 "Exceptiosn"이 발생함 (SEH)

그러나 지금 하려고 하는 것은 순서를 아래와 같이 바꾸는 것이다.

1. 패커에서 모든 코드 + 인라인 코드가 복호화 됨

2. 복호화 된 코드에서 바이너리 언패킹을 수행 함

3. 언패킹 이후, 위 사진의 주소에 도달

4. 인라인 코드 실행

5. 인라인 코드가 바이너리를 패치 함

6. "JMP OEP"

Page 18: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

첫 번째 명령어에서 프로그램을 등록 시키고, 두 번째 명령어는 더블 체크 부분을

우회시킨 뒤 마지막으로 OEP로 분기한다.

"0061384A" 주소를 패치하고 "opcode"를 적어 두어야 한다. 왜냐하면 이후에 CL

레지스터를 이용하여 복호화 루프 내에서 수학적인 계산을 하기 때문이다.

( 나중에 보면 되지만, 일단 위 사진의 주소를 패치하는 것이 훨씬 쉽다는 것을

기억하길 바란다. )

이전 사진처럼 패치 후 실행하면 메인 프로그램이 잘 실행된다. 이제 프로그램을

재시작하여 EP로 이동한 뒤 인라인 패치를 해야 한다. 이전에 "JMP OEP" 명령어가

되게 복호화 되는 루프 이후의 주소로 인라인 패치 공간을 지정했다. 이제 첫 번째

복호화 루프에서 우리가 작성했던 인라인 명령어처럼 코드를 생성하게 만들어야

한다. 따라서 루프의 복호화 결과가 이전에 보았던 패치 명령어의 opcode가 되게

어떤 암호화 된 opcode가 필요한지 알아봐야 한다. 이를 위해 복호화 루프 내에서

사용되는 계산식을 가지고 거꾸로 인라인 패치 명령어 opcodep에 적용해야 한다.

일단 첫 번째 복호화 루프 주소까지 진행한다. 그리고 정리해보면, 프로텍터에서

바이너리 코드를 복호화 할 때 우리가 작성했던 인라인 패치 명령어 또한 생성되게

Page 19: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

만들어야 한다. 그러면 패치 명령어가 실행되고 바이너리가 실행될 것이다. 이

를 위해 인라인 패치 opcode를 암호화 시킨 다음 작성하면, 루프가 돌 때 복호화

되면서 패치 명령어가 실행될 것이다.

"61384A" 주소는 나중에 패치할 첫 번째 opcode가 있는 주소(복호화 루프 이후에

변경되는 주소)이다.

마지막 복호화 연산은 "ADD AL,CL" 이기 때문에 거꾸로 하면 첫 번째 암호화

연산은 "SUB AL,CL"이 된다. 다행히 복호화 루프의 처음 부분(00613093)에 "SUB

AL,CL"이 존재하고 있다.

다음 "SUB AL,96" 연산(복호화 루프에서 마지막으로부터 2번째 실행되는 연산)은

암호화 루프로 생각하면 "ADD AL,96"으로 변하고, 변한 명령어는 "0061309B" 주소

(복호화 루프에서 처음으로부터 2번째 실행되는 연산 주소)에 위치해야 한다.

이런 식으로 복호화 루프를 암호화 루프로 변경시키면 다음 사진과 같다.

Page 20: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

위와 같이 암호화 루프를 만들었다면, 인라인 패치의 opcode를 대상으로 실행해야

한다.

“0061384A” 주소의 opcode를 위 사진처럼 바꾼다. 바꾼 값은 이전에 보았던 패치

에서 사용되었던 opcode 목록이다.

즉, 패치에 사용되었던 opcode를 대상으로 암호화 연산을 수행하면 암호화 된 E

값이 나올 것이다. 해당 E 값을 이용하여 원래 바이너리에서 사용하는 복호화 연산

을 수행하면 우리가 원하는 패치 명령어가 복호화 루프에 의해 생성되는 것이다.

Page 21: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

암호화 루프를 실행하면 인라인 패치에 사용되었던 opcode 들이 위 사진처럼

변경된다.

그럼 위 사진에서 얻은 암호화 된 값을 “Binary Copy”한 다음에 원본 바이너리를

열어서 해당 주소(“61384A”)에 있는 값들을 위 사진의 암호화 된 값으로 “Binary

Paste” 한 뒤 새로운 바이너리로 저장하고 실행하면 정상적으로 실행이 되야 한다.

그런데 위 주소처럼 에러가 발생한다. 간과했던 부분이 있는데 바로 무결성 체크

부분이다. 첫 번째 루프가 종료되기 전에 원본 값을 패치해 버리면 무결성 체크

때문에 강제로 종료되는 것이다. 이제 이 부분을 패치해야 한다.

에러 창이 발생 한 상태에서 올리 디버거에서 ‘-’ 키를 누르면서 백트레이싱을

하면 위 사진의 조건문으로 이동된다.

( RETN 명령어가 실행되면 dll 파일 주소로 이동되고, 해당 주소에서 호출되는

함수에 의해 프로그램이 종료된다. )

위 사진의 “JE” 조건문을 패치 하였지만 그래도 프로그램이 종료 되었기 때문에

좀 더 이전에 호출되는/실행되는 주소를 봐야 한다.

“JE” 조건문 이전에 “CMP” 명령어가 있는데, 해당 명령어는 “6135B5” 주소의

Page 22: Lena’s Reversing Tutorial 풀이 - bob3rdnewbie.tistory.com27548F505633AC… · 으로는 "임포트 테이블 난독화/파괴, 안티 디버깅 API 사용, SoftICE 탐지, CRC

명령어 “JMP 6137C8”에 의하여 이동 되고 있었다. 그렇다면 “6135B3 | JE 6135BA”

주소의 명령어를 실행하면 “6135B5 | JMP 6137C8” 명령어가 실행되지 않아

프로그램이 강제적으로 종료 되지 않을 것이라고 예상 할 수 있다.

해당 주소의 명령어를 “JMP 6135BA”로 패치한 뒤 실행하면 프로그램 종료 되지

않고 정상적으로 메인 프로그램이 실행된다.

그러면 이제 원본 바이너리를 대상으로 진짜 인라인 패치를 해야 한다.

1. 암호화 된 인라인 패치 opcode 값으로 패치

2. 무결성 체크 우회를 위해 명령어를 “JMP 6135BA”로 패치

* 참고로 “JMP”의 opcode는 “EB” 이다. 그러나 “E1” 으로 패치하는 것은

저렇게 패치 해야 복호화 루틴에 의해 “E1 => EB”로 변경되기 때문이다.

원본 바이너리를 위와 같이 2개의 패치를 적용시킨 후 새로운 바이너리로 저장

한 뒤 실행하면 완벽하게 등록된 프로그램으로 실행된다.