L06. 패킹된 파일의 OEP + Crackme Serial
평범한 Crackme 문제. Serial 값을 입력하고 check serial 버튼 누르면 됨
틀리면 "Wrong serial!!!!" Messagebox 뜸
HxD로 열어보니 섹션이 UPX0, UPX1, .rsrc 이렇게 3개. 전형적인 UPX packed file
upx.exe -d 06.exe 해서 unpack 한다 (file size 26112 에서 159744로 증가!)
1) EP 찾기
EP는 unpacked된 파일을 PE View로 확인해보면 Image Base + RVA of EP = 00401360
(ollydbg로 열어봐도 마찬가지)
혹은 UPX packed file은 PUSHAD - POPAD - OEP JMP 한다는 아이디어를 바탕으로 PUSHAD
실행 후 스택 맨 위 주소에 Hardware BP 걸고, POPAD에서 bp 걸리면 OEB jmp 코드를 찾는 식도 굳
2) Serial 찾기
실패 문자열 따라갔더니 바로 분기문 만났다
CALL 00401290 함수 호출 결과에 따라 어떤 메시지박스 호출될지 달라짐(성공/실패)
함수 전달 인자로 ASCII "AD46DFS547"의 주소를 다이렉트로 넘겨줌 => 정답 Serial
비교 함수 내부가 복잡해서 IDA로 메인 함수 디컴파일 해봤더니 strcmp(str1, str2) 였음!!
L07. 레이블에 따라 생성되는 serial 값
간단한 crackme. Serial을 입력하고 버튼 눌러 check하는 형식
실패할 경우 "The serial you entered is not correct!" 메시지를 담은 MessageBox 출력
Ollydbg로 referenced string을 통해 메인 함수 (00401056~00401138)로 가니
GetVolumeInformationA 호출 후 lstrcat 여러번 해서 serial 생성하고 lstrcmpiA 해서 user input과 비교하여
성공 Messagebox를 출력할지 실패 Messagebox를 출력할지 분기가 이루어짐
작동 방식은 대충 확인하였으니 IDA로 더 분석. 아래는 메인 함수 디컴파일 결과
int __userpurge sub_401056@<eax>(int a1@<ebp>, int a2, int a3, int a4, int a5)
{
char v6; // dl
if ( *(_DWORD *)(a1 + 16) == 'e' )
{
GetDlgItemTextA(*(HWND *)(a1 + 8), 104, String, 37);
GetVolumeInformationA(
0,
VolumeNameBuffer,
0x32u,
&VolumeSerialNumber,
&MaximumComponentLength,
&FileSystemFlags,
0,
0);
lstrcatA(VolumeNameBuffer, String2);
v6 = 2;
do
{
++*(_DWORD *)VolumeNameBuffer;
++*(_DWORD *)&VolumeNameBuffer[1];
++*(_DWORD *)&VolumeNameBuffer[2];
++*(_DWORD *)&VolumeNameBuffer[3];
--v6;
}
while ( v6 );
lstrcatA(byte_402000, aL2c5781);
lstrcatA(byte_402000, VolumeNameBuffer);
if ( lstrcmpiA(byte_402000, String) )
MessageBoxA(*(HWND *)(a1 + 8), Text, Caption, 0);
else
MessageBoxA(*(HWND *)(a1 + 8), aYepYouEnteredA, aWellDone, 0);
}
else if ( *(_DWORD *)(a1 + 16) != 2 )
{
return 0;
}
return sub_40112D(a2, a3, a4, a5);
}
[1] GetVolumeInformationA에서 현재 디렉터리의 루트를 받아와서 VolumeNameBuffer에 저장하고
=> NULL
왜 NULL일까?
첫 번째 매개 변수가 NULL인 경우, GetVolumeInformation 함수는 현재 작업 디렉토리의 볼륨 정보를 얻으려 합니다. GetVolumeInformation 함수가 작동하면서 VolumeNameBuffer에 저장되는 것은 파일 시스템의 볼륨에 할당된 이름인 볼륨 레이블입니다. 볼륨 레이블이 설정되지 않은 경우 VolumeNameBuffer에 아무것도 저장되지 않을 수 있습니다.
C: 아래에 07.exe 옮겨서 실행시켰는데도 버퍼에 NULL 저장?
지금 내 pc의 C 드라이브에 볼륨 레이블 세팅이 되어있지 않기 때문 ㅋㅋ
[2] VolumeNameBuffer에 String2("4562-ABEX")를 이어붙이고
=> "4562-ABEX"
[3] 두 번에 걸쳐 VolumeNameBuffer index 0~3의 ASCII 값을 1씩 증가시키고
=> "6784-ABEX"
[4] byte_402000(char[200])의 빈 배열에 "L2C-5781" 문자열 이어붙이고 VolumeNameBuffer 이어붙인 후
=> "L2C-57816784-ABEX"
[5] String(char[200], user input이 담긴 배열)과 비교
C드라이브 이름이 CodeEngn이면 (CodeEngn => EqfgEngn으로 바뀐다)
=> "L2C-5781EqfgEngn4562-ABEX"
L08. 패킹된 파일의 OEP
HxD로 보니 UPX 압축되어있네 (패킹된 파일의 EP = 0x01020C40)
- upx.exe로 언패킹하거나 (언패킹 시 파일크기 2배 증가, OEP = 0x01012475)
- 걍 Ollydbg에 던지면 자동으로 언패킹 해주고 OEP부터 시작
- Option - Debugging - SFX - unpack SFX modules automatically 옵션 비활성화하고 PUSHAD부터 분석 시작
=> 하드웨어 bp 걸고 달리면 POPAD 명령어 직후에서 멈춤, 그 뒤에 OEP로 점프하는 코드 존재
L09. Stolen Byte 구하기
upx.exe로 언패킹해본 후 분석하니 OEP = 0x00401000
언패킹 후 실행해보니 깨진 문자열 등장
하지만 Ollydbg로 열어보니 (자동 언패킹 된 결과) 401000~40100B가 NOP로 차 있고 0x0040100C부터 코드 진행
=> 즉 0xB개의 바이트가 stolen 되었음을 알 수 있다
=> MessageBoxA API의 인자가 충분하게 들어가지 않은 상태이다 (OEP 상의 코드에 따르면)
Ollydbg로 Search for - Command 해서 "JMP 0040100C" 검색해보니 언패킹 코드의 마지막 부분으로 이동할 수 있었음
=> POPAD와 JMP to OEP 사이에 0040736E~00407379 (PUSH 명령어 3개)가 Stolen byte구나
=> MessageBoxA의 parameter 3개를 언패킹 루틴에서 미리 스택에 넣어둠!
=> 6A0068002040006812204000 (12개 바이트)
* StolenByte란?
패킹된 프로그램에서 코드의 일부를 OEP로 점프하기 이전에 숨기는 것을 의미.
패커가 위치를 이동시킨 코드.
* StolenByte를 적용하는 이유?
패킹을 풀 때는 OEP를 찾아서 덤프를 뜨게 됩니다.
하지만 StolenByte가 적용된 프로그램은 OEP이전에 코드의 일부가 있습니다.
따라서 OEP를 찾아서 덤프를 떠도 프로그램이 정상적으로 실행되지 않습니다.
* StolenByte 적용 예시?
1) OEP로 점프하기 전에 사용할 값을 스택에 PUSH하고 OEP로 점프한 뒤 스택에 들어간 값을 인자로 하여 함수 호출
=> OEP를 찾아서 단순히 덤프를 뜨면 스택에 올바른 값이 push되지 않아 함수 정상적인 실행 불가능
2) 원본 코드의 윗부분(OEP 부근 명령어들)을 unpacking 코드 내로 이동, 원본 코드의 일부 실행 후 OEP로 점프
=> OEP를 찾아 덤프를 떠도 StolenByte를 복구하지 못하면 덤프된 실행파일 작동 못 함
L10. 패킹된 파일의 OEP + 분기문 (UPX packer 아님!)
Ollydbg로 열었더니 해당 파일이 compressed/packed/encrypted 되어있다는 안내창 뜬다
EP(0x00456001)부터 PUSHAD 명령어 떡하니 존재하네 -> Hardware bp 이용해 POPAD 찾자
0x004564F1에 POPAD 존재. 그 아래 "PUSH 00445834; RETN" 명령을 통해 00445834로 jump를 하고 있음
00445834로 jump 했더니 코드가 깨져있네 (Ollydbg의 analysis 기능 때문)
Remove Analysis from module해서 전체 분석 없애면 00445833부터 OpCode로 인식됨
=> broken alignment 안티 디버깅 기법 때문에 코드가 깨져 보인다
=> 00445834부터 Remove Analysis from selection 해서 선택한 영역만 분석 해제하면 코드 제대로 나옴 (어우 복잡...)
** PUSHAD부터 POPAD까지가 언패킹 루틴 **
=> EP 지점에서 Search for text strings 해도 ASCII 문자열 전부 깨져있음. 언패킹 코드 실행 후 깨진 문자열 잘 보임
=> "PUSH 0; RETN" 이었는데 언패킹 코드 실행 후 "PUSH 00445834; RETN" 으로 바뀜
=> 코드가 동적으로 바뀜. 안티 디버깅 기법.
=> 즉 분석을 할거면 POPAD에 bp걸고 달린 후에 언패킹된 파일을 분석을 하자
언패킹이 끝나고 JMP한 주소 0x00445834가 OEP이고, 메인 함수를 분석하자니 너무 복잡... 다른 방법 찾아보자
Search for text 해서 보니 의심스러운 문자열들 존재 - "Registered..." 문자열과 "~ at least 5 characters long" 문자열
해당 위치로 Jump하니 0044539C부터 시작하는 큰 함수 존재
- 00445470에서 이름 길이 기준 분기 이루어지고 (5 characters up / down)
- 004454D4에서 등록 여부에 대한 분기 이루어짐 (JMP 안하면 "Registered... Well done!" 문자열 가지고 무언가를 함)
- 등록 성공으로 가는 분기문은 75 55 이다
=> 00445834 (OEP) + 75 55 (Op code)
cf) 설마설마하면서 Detect it Easy 썼는데 ASPack 패커로 패킹되어있었네
알아보니 ASPack 패킹된 파일의 unpacking 방법에는 두 가지가 있음
1) OEP 찾기
1-1) Hardware BP로 POPAD 명령어까지 간 다음 PUSH-RETN 명령어 조합에서 OEP 찾기
1-1) Find command에서 "RETN 0C" 찾으면 그 아래에 PUSH-RETN 명령어 조합에서 OEP 찾기
2) OllyDumpEx 플러그인을 이용해 덤프 따기 (OEP는 새로 구한 주소[RVA]로 수정하여 입력)
- 덤프딴 파일을 Detect it easy에 넣어보면 패킹 안되어있다고 뜸 (용량도 증가함)
- IAT가 깨져서인지 실행 안 됨
3) ImportREC 툴을 이용해 IAT 복구.
- 원래 파일 실행시킨 상태에서 "Attach to active process" 목록에서 해당 파일 선택
- OEP[RVA] 수정 후 AutoSearch 클릭
- Get Imports 버튼 클릭
- Fix Dump 버튼 클릭해 덤프 뜬 파일(10_dump.exe) 선택하면 새로운 파일 생성됨
- 10_dump_.exe 파일은 잘 실행된다!! 하지만 ollydbg 코드는 여전히 복잡..
'security > 리버싱 - CodeEngn.com' 카테고리의 다른 글
Advance RCE L01 ~ L07 (0) | 2023.04.20 |
---|---|
Basic RCE L16~L20 (0) | 2023.04.16 |
Basic RCE L11~L15 (0) | 2023.04.13 |
BASIC RCE L01~L05 (0) | 2023.04.09 |