틀린 부분이 있다면 언제든지 댓글 남겨주세요!
CodeEngn_Challenges Basic RCE L17
L17. 키값이 BEDA-2F56-BC4F4368-8A71-870B 일때 Name은 무엇인가
힌트 : Name은 한자리인데.. 알파벳일수도 있고 숫자일수도 있고..
정답인증은 Name의 MD5 해쉬값(대문자)
Name이 한자리라고 해서 1을 입력하고 Key에 '123'을 입력했더니 다음과 같이 더 많은 문자를 입력하라는 문자열이 나온다.
파일의 정보를 확인해보니 델파이로 작성된 파일이다.
x32dbg을 사용해보도록 하겠다.
메시지 박스가 뜨는 부분과 'Please Enter More Chars..'문자열을 이용하여 핵심 코드를 찾아가 보았다.
45BB24부분에서 eax값과 뒤의 숫자를 비교해 같거나 크면 45BB3E로 넘어가고 아니면 'Please Enter More Chars..' 문자열을 출력하도록 되어있다.
원래 숫자가 3으로 되어 있는데 문제에서 힌트로name 값이 1이라고 했으므로 이 부분을 1로 변경해준다. (이부분은 못찾았었다 ㅠ)
아무튼 1로 변경하면 입력한 name이 한자리이므로 분기하여 코드가 진행되는 것을 볼 수 있다.
* 43A074 함수는 문자열을 읽어 EDX가 가진 위치에 넣어주는 함수로 추측된다.
입력한 name값과 key값을 읽어온 후 name값을 이용하여 45B850 함수에서 시리얼 번호를 생성한다.
이후 생성한 시리얼 번호와 입력한 key값을 비교한다.
다음은 시리얼 번호 생성 함수 내 번호 연산 루틴이다.
세번째 연산까지 열심히 하던 중 문제를 풀기 위해서는 첫번째 연산만 해서 맞는 문자를 찾으면 된다는 것을 깨달았다...
이제 모든 숫자/문자 중 첫번째 연산 루틴 결과 BEDA 가 나오는 것을 찾아야 한다. 간단한 코드를 통해 값을 구해보도록 하겠다.
1. name의 아스키값 * 772
2. 1번의 연산 결과 * 1번의 연산 결과
3. 1번의 연산 결과 + 2번의 연산결과
4. 3번의 연산 결과 * 474
5. 4번의 연산 결과 + 4번의 연산 결과
간단하게 파이썬으로 구현해보았다.
입력 문자는 0-9, a-z, A-Z 중에 있으므로 아스키코드를 이용하는데 굳이 나눠서 구현하지 않았다.
특수문자는 어차피 답이 아닐테니까...?
10진수로 바꿔서 연산하였고, 변수의 크기가 dword가 아니므로 결과의 뒤부터 8자리만 보면 된다.
그 중 BEDA값은 문자 'F'의 연산 결과였다.
따라서 답은 F의 MD5 해시값 = 800618943025315F869E4E1F09471012이다.
CodeEngn_Challenges Basic RCE L18
L18.Name이 CodeEngn일때 Serial은 무엇인가
파일 정보를 확인해보니 알 수 없는 exe파일이라고 나온다.
파일을 실행시켜서 확인해보니 name의 길이는 5이상이며, 그에 맞는 serial번호를 입력해야 하는 것으로 보인다.
일단 x32dbg를 이용해서 분석을 진행한다.
코드를 실행시키다보니 메시지 박스를 출력하고 진행이 끝난다.
그래서 메시지 박스를 실행시키는 코드 이전에 'Name must be at least~' 문자열을 찾아가 분석을 했다.
404058함수에서 CodeEngn 문자열을 가지고 연산을 한다. 그 결과 다음과 같이 408AF0 주소에 문자열이 생성된다.
이 문자열을 시리얼을 생성에 이용한다.
데이터를 쓸 때 %8x 로 포멧팅하여 최종적인 시리얼을 생성하는 것을 볼 수 있다.
lstrcmpi API를 이용하여 4080F0주소의 데이터(정답 시리얼)와 407EF0주소의 데이터(사용자가 입력한 시리얼)를 비교한다.
두 값이 같으면 EAX에 0을 반환하여 JE명령어가 분기하고, 같지 않으면 1을 반환하여 분기하지 않는다.
따라서 'CodeEngn' 문자열을 입력했을 때 Serial 값은 '06162370056B6AC0' 이다.
CodeEngn_Challenges Basic RCE L19
L19.이 프로그램은 몇 밀리세컨드 후에 종료 되는가
파일을 실행시키면 몇초간 메시지 박스가 떠 있다가 종료된다. 그 시간을 확인하는 문제이다.
우선 UPX로 패킹되어 있기 때문에 언패킹 후 분석을 진행한다.
* GetSystemTimeAsFileTime: 현재 시스템의 날짜 및 시간
* QueryPerformanceCounter : 시간 간격 측정에 사용할 수 있는 고해상도(<1us) 타임스탬프인 성능 카운터의 현재 값을 검색
* GetStartupInfo: 호출 프로세스가 생성될 때 지정된 STARTUPINFO 구조의 내용을 검색
* SystemParametersInfo: 시스템 전체 매개변수 중 하나의 값을 검색하거나 설정
* GetFullPathName: 지정된 파일의 전체 경로와 파일 이름을 검색
* PeekMessageW: 들어오는 보낸 메시지를 전달하고 게시된 메시지에 대한 스레드 메시지 대기열을 확인하고 메시지(있는 경우)를 검색
* timeGetTime: 밀리 초(ms) 단위로 시스템 시간을 검색
* EnumThreadWindows: 각 창에 대한 핸들을 차례로 응용 프로그램 정의 콜백 함수에 전달하여 스레드와 연결된 모든 자식이 아닌 창을 열거
분석하다보니 코드 진행 중 디버거가 작동중이면 다음과 같이 메시지 박스를 띄우는 코드가 존재한다는 것을 찾았다.
IsDebuggerPresent API를 이용해 현재 디버거가 실행중인지 확인한다.
이 명령어의 opcode를 수정하여 디버거 실행중에도 분기하지 않고 코드가 진행되도록 하였다.
계속해서 분석을 진행하다보니 너무 복잡해서 API를 이용해 핵심 부분으로 찾아가보았다.
time을 검색해보니 timeGetTime API가 여러개 존재했다.
문제에서 ms단위로 시간을 확인한다고 했는데 timeGetTime API도 ms단위로 시스템 시간을 체크하는 함수이다.
다 BP를 걸고 하나씩 확인해보도록 한다.
BP를 건 후 가장 먼저 멈춘 곳은 다음과 같다.
여기서 eax값이 timeGetTime의 리턴값 즉 시스템 시간을 의미한다.
처음에 이 값을 esi에 넣고 Sleep후 두번째 timeGetTime를 실행해 eax에 값을 받아온다.
이후 esi(첫번째 시스템 시간)과 eax(두번째 시스템 시간)을 비교하여 두번째 시스템 시간이 크면(당연하다) 444D38로 분기된다.
이후 esi값을 eax에 넣어(두번째 시스템 시간) 이 값을 [ebx+4]=2B70 값과 비교한다.
비교 결과 2B70보다 값이 크다면 분기되어 종료된다.
따라서 시스템 유지 시간은 0x2B70=11,120ms이다.
'Security > Reversing' 카테고리의 다른 글
[CodeEngn] Challenges Basic RCE L20 (0) | 2021.10.18 |
---|---|
디버깅 탐지 방법 (0) | 2021.10.15 |
KeyLogger 분석 (0) | 2021.09.27 |
Debugger software BP의 원리 (0) | 2021.09.08 |
relocation과 .reloc섹션 (0) | 2021.08.28 |
댓글