정보 수집
실행하면 위와 같은 메시지를 출력하고 종료되는 실행파일이다.
PEiD를 이용하여 해당 바이너리의 패킹 여부를 확인한다.
UPX 패킹된 바이너리임을 확인할 수 있었다.
upx 리눅스 유틸리티를 이용하여 언패킹을 진행
언패킹된 바이너리
분석
IAT 테이블을 살펴보니 다음과 같이 command line을 읽어오는 함수가 존재했다.
따라서 해당 바이너리를 정상 작동시키기 위해선 command line의 실행 인자를 전달해줘야 할 것이라고 추측이 가능하다.
GetCommandLine 함수 직후에 특정 반복문이 존재하고 해당 반복문은 Command line으로부터 읽어들인 문자열을 edx 레지스터가 1바이트씩 이동하여 마지막 바이트를 가리키도록 한다.
이후 AL 레지스터에 [EDX-5] 위치의 1 바이트를 옮기고 해당 값을 스택에 넣는다.
AL 레지스터에 0x20의 값을 넣고 스택의 최상단과 비교한다.
즉 EDX-5 위치가 0x20(공백문자)와 일치한지를 비교한다.
이후 일치하지 않을 0x004012FC 주소로 점프하며 해당 주소는 다음과 같이 MessageBox API를 호출하며 처음 출력되는 메시지가 출력된다.
따라서 해당 출력문을 피하기 위해선 [EDX-5]가 공백문자여야 하므로 파라미터는 다음과 같이 뒤에서 5번째 문자가 공백문자인 4글자의 파라미터가 전달되어야 한다.
C:\Users\Desktop\FSC_Level2.exe asdf
인자로 "asdf"를 전달하고 해당 조건을 만족시키도록 하고 해당 분기문에 bp를 걸고 나머지를 분석한다.
이후 또 다시 비교문과 해당 비교문이 만족하지 않을 경우 비정상 종료 루틴에 빠지는 부분을 발견했다.
해당 위치에서 EAX에 0x6578652E(".exe")를 넣고 해당 값을 스택의 최상단에 있는 값과 비교한다.
해당 위치로 오기까지 스택의 최상단에 어떤 값이 위치하는지 확인하기 위해 백트레이싱을 수행한다.
인자로 전달한 0x66647361("asdf")을 0x5528566D과 xor 연산을 수행한 결과를 EAX에 저장한다.
EAX에는 0x334C2501이 저장됨
AH(0x25)와 BH(0xC0)를 XOR 연산을 수행하여 0x334CE50C이 EAX에 저장된다.
이후 해당 값을 0x6578652E(".exe")와 비교하여 일치하지 않을 경우 실패 루틴으로 진입한다.
따라서 0x6578652E의 값에 일련의 과정을 역연산해주고 나온 값을 파라미터로 전달할 경우 성공 루틴으로 진입이 가능하다.
0x6578652E의 세 번째 바이트인 0x65와 0xC0를 xor한 결과인 0x6578A52E
해당 값을 0x5528566D와 xor한 결과 = 0x3050F343
확인한 결과 실행할 때마다 ebx의 값이 변함
ebx의 값을 설정하는 코드를 찾아본 결과 다음과 같이 entry 포인트에 존재
그러나 해당 지점에 bp를 걸고 실행해도 해당 부분은 실행되지 않음
원래대로라면 ebx에 고정된 값인 0으로 초기화되고 xor 연산에 영향을 주지 않아 "C3P0"가 정답이 되어야 하는데 안티디버깅 기법이 있는 것으로 볼 수 있음
실행 과정에서 ebx를 강제로 초기화하고 인자를 "C3P0"로 전달할 경우 정상적으로 플래그가 출력됨
PEP와 OEP : Packing entry point, Original entry point
PEP 코드 실행 도중 디버거 사용 유무를 탐지하여 사용중일 경우엔 다른 OEP에서 시작하도록 하는 기법이었음
해당 문제에서는 TLS(Thread Local Storage) callback을 이용한 기법을 사용함
TLS Callback이란 프로세스가 만들어질 때 메인 쓰레드가 초기화되기 전에 가동되는 코드
정리
PE header를 참조한 결과 해당 TLS 코드가 0x4070DC 번지에 존재함
TLS callback 함수에 존재하는 디버거 탐지 코드 (IsDebuggerPresent())
따라서 디버거가 붙을 경우에 xor ebx, ebx 구문을 피해서 ebx가 초기화되지 않도록 하여 정상적인 플래그 인증에 실패하도록 하고 디버거가 붙지 않았을 겨웅 xor ebx, ebx가 정상적으로 실행돼서 플래그 인증이 가능하다.