인라인 패치 실습으로 Code Cave를 해 보도록 하겠다.
abex crackme1의 MessageBoxA 함수에 인자로 들어가는 문자열을 패치시킬 것이다.
참고로 abex crackme1은 ASLR이 적용되지 않은 프로그램이다.
일단 abex crackme1을 x32dbg로 열었다.
틀렸을 때의 분기로 점프해서 넣는 인자(문자열)의 주소는 각각 402035, 402038이다.
이 주소에 각각 새로운 문자열을 써 넣어 볼 것이다.
새 문자열은 Code Cave?!와 Code Cave Patched!!이다.
Code Cave를 하기 전에 이 파일의 pe 구조를 보아야 한다.
섹터가 어디부터 이루어져있는지를 봐야 하기 때문이다.
Pe Backpack과 HxD로 열어서 구조를 확인 해 보았다.
코드 섹션의 파일 옵셋은 600이다. hxd에서 봤을 때 정확히 600에서 시작한다.
PE 구조를 보면 파일에서 데이터 섹션의 크기는 200으로 되어있는데, 실제로 파일에서 사용하는 크기는 70밖에 되지 않는다.
VirtualSize는 1000이며, 파일에서의 200의 크기가 다 올라간다고 해도
200 - 70 = 190만큼의 코드 영역이 남는다.
이 영역을 코드를 씌워서 Code Cave의 영역으로 사용 할 것이다.
그리고 문자열의 섹션을 봐 보자.
이 곳도 메모리에 올라가는 크기에 비해 사용하는 크기가 작다.
이 공간에 문자열을 패치시킬 것이다.
우선 문자열을 패치시켜보자.
이렇게 패치시키면 문자열은 메모리에 적재될 것이다.
다음으로, 코드를 패치시켜보자.
문자열을 복사하여 원래 위치의 문자열을 덮어 씌우려고 했지만, 문자열과 문자열 사이의 공간이 빽빽하다.
문자열을 덮어 씌우는 것 대신, 원래의 push 코드를 아예 바꿔 보려고 한다.
디버거로 다시 실행시켜보자.
다시 실행시켰을 때, push의 opcode를 확인하면서 메모리에 문자열이 잘 적재된 것까지 확인을 하였다.
68 0x주소 형태로 되어있는 opcode를 코드를 통해 패치시켜야 한다.
하지만 code cave는 프로그램이 실행 시 실행되는 코드로, 전체 코드 섹션에 쓰기 권한이 부여되어있지 않으면 code 영역을 패치시킬 수 없다.
엑세스 에러가 뜰 것이다. 이것을 pe 구조를 보고 hxd에서 code 섹션의 characteristics를 바꿔 쓰기 권한을 주어야 한다.
섹션의 characteristics는 각각의 권한 비트와 or 연산이 되어 있다.
현재 코드 섹션은 0x60000020으로 되어있다.
IMAGE_SCN_CNT_CODE 0x00000020
IMAGE_SCN_MEM_READ 0x40000000
IMAGE_SCN_MEM_EXECUTE 0x20000000
이 세 개의 비트와 or연산이 되어 있는데, 여기에
IMAGE_SCN_MEM_WRITE 0x80000000
을 더 or 연산 해 주면 섹션에 쓰기 권한이 생긴다.
0x60000020에 0x80000000과 or 연산을 하면 0xe0000020이 될 것이다.
이걸 파일에서 섹션 헤더에 직접 패치시켰다.
파일에서의 위치는 이 곳이 될 것이다. 이곳을 수정하면 된다.
이제 코드 패치를 해 볼 시간이다.
어셈블리 코딩을 하는 여러 방법이 있지만, 간단하게 디버거로 어셈블 해 가며 만든 바이트를 복사하여 사용 해 보자.
abex crackme를 열어서 ep 근처의 빈 공간에 어셈블을 한다.
코드로 코드를 패치하는 것이니 코드의 주소를 잘 봐 두어야 한다.
코드의 바이트를 잘 보기 위해서 코드 위치를 덤프창에 옮겨두고 작업했다.
덤프에서 opcode 뒤의 operand 주소를 덤프창에서 쉽게 복사할 수 있다.
(원하는 주소 오른쪽 클릭 - 복사 - 주소)
push 00402035의 opcode와 operand는 68 35 20 40 00이다.
이것을 원하는 문자열의 주소로 바꾸려면 rva to raw를 계산하여야 한다.
파일에서 메모리에 올라가는 주소를 잘 계산해야 한다.
첫 번째 문자열인 Code Cave?!와,
두 번째 문자열인 Code Cave Patched!! 주소를 계산하면
각각 4020a0과 4020ac가 나온다.
68 35 20 40 00의 두 번째 바이트를 각각 a0과 ac로 바꾸면 된다.
차이나는 바이트가 1개밖에 없으니, 그에 따른 패치 코드를 짠다.
패치 코드를 짠 뒤, 이 코드의 바이트를 복사한다.
C6 05 2B 10 40 00 A0 C6 05 30 10 40 00 AC EB 85
C6 05 2B 10 40 00 A0 C6 05 30 10 40 00 AC EB 80 가 되겠다.
이 바이트를 코드 영역에 hxd로 파일에 직접 패치시킨다.
바이트를 붙여넣기 하거나 직접 패치시키면 된다.
그리고 한 가지 더 해야 할 것이 있는데, code cave를 ep에 도달하기 전 실행시키려면 ep를 call하는 코드를 code cave의 코드가 있는 주소로 점프할 수 있도록 패치시켜야 한다.
그런데 abex crackme1은 어셈블리어로 작성되어 있어, 사용자 코드의 시작 자체가 ep이기 때문에 따로 ep로 점프하여 시작하지 않는다.
그렇다면 pe에서 ep를 바꿔, code cave의 주소가 ep가 되도록 패치를 시켜보자.
hxd에서 code cave의 파일 옵셋은 670이었다.
670을 rva로 계산하면 00401070이다. 이것을 pe의 AddressOfEntryPoint 요소에 수정하면 정상적으로 ep가 바뀔 것이다.
연속적인 숫자를 이용해, 찾기 기능을 통하여 찾았다.
00 10 00 00을 70 10 00 00으로 바꾸면 적용 될 것이다.
수정 후 pe구조를 보면
AddressOfEntryPoint가 잘 바뀐 것을 볼 수 있다.
직접 실행시켜서 확인 해 보자.
EP가 잘 바뀌어서 실행 되는 것을 볼 수 있다.
Code Cave 부분을 실행하여 지나게 되면 코드가 바뀐다.
(수정 - 코드의 마지막 원래의 ep로 점프하는 곳이 바이트가 잘못 복사되어 수정하였음)
Code Cave를 지났더니, 프로그램의 원래 ep주소로 점프하면서, push 코드가 패치되어 바뀌는 것을 볼 수 있다!
실행을 해 보자.
원래 Error라고 출력되는 문자열이 패치가 잘 된 것을 잘 볼 수 있다.
코드 케이브 패치는 성공했다.
아래는 code cave 패치가 된 abex crackme1 실행파일이다.
abex crackme1 code cave patched
abex crackme1의 MessageBoxA 함수에 인자로 들어가는 문자열을 패치시킬 것이다.
참고로 abex crackme1은 ASLR이 적용되지 않은 프로그램이다.
일단 abex crackme1을 x32dbg로 열었다.
틀렸을 때의 분기로 점프해서 넣는 인자(문자열)의 주소는 각각 402035, 402038이다.
이 주소에 각각 새로운 문자열을 써 넣어 볼 것이다.
새 문자열은 Code Cave?!와 Code Cave Patched!!이다.
Code Cave를 하기 전에 이 파일의 pe 구조를 보아야 한다.
섹터가 어디부터 이루어져있는지를 봐야 하기 때문이다.
Pe Backpack과 HxD로 열어서 구조를 확인 해 보았다.
코드 섹션의 파일 옵셋은 600이다. hxd에서 봤을 때 정확히 600에서 시작한다.
PE 구조를 보면 파일에서 데이터 섹션의 크기는 200으로 되어있는데, 실제로 파일에서 사용하는 크기는 70밖에 되지 않는다.
VirtualSize는 1000이며, 파일에서의 200의 크기가 다 올라간다고 해도
200 - 70 = 190만큼의 코드 영역이 남는다.
이 영역을 코드를 씌워서 Code Cave의 영역으로 사용 할 것이다.
그리고 문자열의 섹션을 봐 보자.
이 곳도 메모리에 올라가는 크기에 비해 사용하는 크기가 작다.
이 공간에 문자열을 패치시킬 것이다.
우선 문자열을 패치시켜보자.
이렇게 패치시키면 문자열은 메모리에 적재될 것이다.
다음으로, 코드를 패치시켜보자.
문자열을 복사하여 원래 위치의 문자열을 덮어 씌우려고 했지만, 문자열과 문자열 사이의 공간이 빽빽하다.
문자열을 덮어 씌우는 것 대신, 원래의 push 코드를 아예 바꿔 보려고 한다.
디버거로 다시 실행시켜보자.
다시 실행시켰을 때, push의 opcode를 확인하면서 메모리에 문자열이 잘 적재된 것까지 확인을 하였다.
68 0x주소 형태로 되어있는 opcode를 코드를 통해 패치시켜야 한다.
하지만 code cave는 프로그램이 실행 시 실행되는 코드로, 전체 코드 섹션에 쓰기 권한이 부여되어있지 않으면 code 영역을 패치시킬 수 없다.
엑세스 에러가 뜰 것이다. 이것을 pe 구조를 보고 hxd에서 code 섹션의 characteristics를 바꿔 쓰기 권한을 주어야 한다.
섹션의 characteristics는 각각의 권한 비트와 or 연산이 되어 있다.
현재 코드 섹션은 0x60000020으로 되어있다.
IMAGE_SCN_CNT_CODE 0x00000020
IMAGE_SCN_MEM_READ 0x40000000
IMAGE_SCN_MEM_EXECUTE 0x20000000
이 세 개의 비트와 or연산이 되어 있는데, 여기에
IMAGE_SCN_MEM_WRITE 0x80000000
을 더 or 연산 해 주면 섹션에 쓰기 권한이 생긴다.
0x60000020에 0x80000000과 or 연산을 하면 0xe0000020이 될 것이다.
이걸 파일에서 섹션 헤더에 직접 패치시켰다.
파일에서의 위치는 이 곳이 될 것이다. 이곳을 수정하면 된다.
이제 코드 패치를 해 볼 시간이다.
어셈블리 코딩을 하는 여러 방법이 있지만, 간단하게 디버거로 어셈블 해 가며 만든 바이트를 복사하여 사용 해 보자.
abex crackme를 열어서 ep 근처의 빈 공간에 어셈블을 한다.
코드로 코드를 패치하는 것이니 코드의 주소를 잘 봐 두어야 한다.
덤프에서 opcode 뒤의 operand 주소를 덤프창에서 쉽게 복사할 수 있다.
(원하는 주소 오른쪽 클릭 - 복사 - 주소)
push 00402035의 opcode와 operand는 68 35 20 40 00이다.
이것을 원하는 문자열의 주소로 바꾸려면 rva to raw를 계산하여야 한다.
파일에서 메모리에 올라가는 주소를 잘 계산해야 한다.
각각 4020a0과 4020ac가 나온다.
68 35 20 40 00의 두 번째 바이트를 각각 a0과 ac로 바꾸면 된다.
차이나는 바이트가 1개밖에 없으니, 그에 따른 패치 코드를 짠다.
패치 코드를 짠 뒤, 이 코드의 바이트를 복사한다.
C6 05 2B 10 40 00 A0 C6 05 30 10 40 00 AC EB 80 가 되겠다.
이 바이트를 코드 영역에 hxd로 파일에 직접 패치시킨다.
바이트를 붙여넣기 하거나 직접 패치시키면 된다.
그리고 한 가지 더 해야 할 것이 있는데, code cave를 ep에 도달하기 전 실행시키려면 ep를 call하는 코드를 code cave의 코드가 있는 주소로 점프할 수 있도록 패치시켜야 한다.
그런데 abex crackme1은 어셈블리어로 작성되어 있어, 사용자 코드의 시작 자체가 ep이기 때문에 따로 ep로 점프하여 시작하지 않는다.
그렇다면 pe에서 ep를 바꿔, code cave의 주소가 ep가 되도록 패치를 시켜보자.
hxd에서 code cave의 파일 옵셋은 670이었다.
670을 rva로 계산하면 00401070이다. 이것을 pe의 AddressOfEntryPoint 요소에 수정하면 정상적으로 ep가 바뀔 것이다.
연속적인 숫자를 이용해, 찾기 기능을 통하여 찾았다.
00 10 00 00을 70 10 00 00으로 바꾸면 적용 될 것이다.
수정 후 pe구조를 보면
AddressOfEntryPoint가 잘 바뀐 것을 볼 수 있다.
직접 실행시켜서 확인 해 보자.
EP가 잘 바뀌어서 실행 되는 것을 볼 수 있다.
Code Cave 부분을 실행하여 지나게 되면 코드가 바뀐다.
(수정 - 코드의 마지막 원래의 ep로 점프하는 곳이 바이트가 잘못 복사되어 수정하였음)
Code Cave를 지났더니, 프로그램의 원래 ep주소로 점프하면서, push 코드가 패치되어 바뀌는 것을 볼 수 있다!
실행을 해 보자.
원래 Error라고 출력되는 문자열이 패치가 잘 된 것을 잘 볼 수 있다.
코드 케이브 패치는 성공했다.
아래는 code cave 패치가 된 abex crackme1 실행파일이다.
abex crackme1 code cave patched
'리버스 엔지니어링' 카테고리의 다른 글
[리버싱] 메모장 시간/날짜 표시 순서 변경 (1) | 2020.07.01 |
---|---|
[리버싱] 과연 이게 될까? Ch.1 (0) | 2020.07.01 |
crackmes.one crackme 001 by disip 풀이 + 크래킹 프로그램 제작 (0) | 2020.07.01 |
crackmes.one 크랙미 3개 풀이 (0) | 2020.07.01 |
레나 리버싱 Tut.ReverseMe1 풀이 2 (0) | 2020.07.01 |