-榮-

[CodeEngn] Advance RCE L03 Serial 생성 분석 본문

CodeEngn/Advance

[CodeEngn] Advance RCE L03 Serial 생성 분석

xii.xxv 2021. 3. 31. 23:08

 

[※참고]

이 글은 CodeEngn Advance RCE L03의 Serial 생성 부분을 분석한 글로 CodeEngn Advance RCE L03 풀이의 일부분입니다.

CodeEngn Advance RCE L03은 CodeEngn Advance RCE L03 을 참고해 주세요.

 

● CodeEngn Advance RCE L03 풀이 中 Serial 생성

 

 

Serial 생성 함수(00401277)는 함수 3개를 반복하면서 입력받은 Name을 사용하여 Serial을 생성합니다.

 

[그림 1] Serial 생성 함수의 흐름도

 

 

[403258]에 입력받은 Name("CodeEngn")을 옮기고, stack에 0을 넣고 00401142 CALL 00401277 함수 내부로 들어갑니다.

[그림 2] CALL 00401277

 

[그림 3]은 CALL 00401277 내부입니다. 처음 00401277로 들어오면 [403000]에는 00000000이 들어있습니다.

00401277의 시작 코드는 [403000] = ([403000] ^ 0AAAh) - 7F9h로 시작됩니다.

XOR과 SUB 명령어가 끝나면 3개의 반복문이 나옵니다.

[그림 3] CALL 00401277 내부

 

3개의 반복문을 실행하면 00401328 CMP [EBP+8], 0 코드가 있습니다. 이 코드는 CALL 00401277이 처음 시작되었는지를 판단하는 비교문입니다.

설명을 하면, CALL 00401277은 입력받은 Name의 두 문자를 암호화하여 Name이 끝날 때까지 반복됩니다. 이때, 두 문자의 반복은 특정 조건을 만족하면 종료됩니다. 종료된 뒤 Name이 끝이 났는지 확인을 하고 끝이 아니면 00401277을 호출하는데, 호출 전에 push 0을 합니다.

즉, 다음 문자를 가져오는 코드로 가는 분기 조건이 00401328 CMP [EBP+8], 0입니다. CALL 00401277을 다시 시작하게 되면 호출 전의 push 0 이 [EBP+8]에 위치하게 됩니다. 

[그림 4] CALL 00401277의 반복문

   

[EBP+8]에 0이 들어있으므로 입력받은 Name의 두 문자를 암호화하여 CALL 00401241에서 반복합니다.

[그림 5] 입력받은 Name 암호화하여 사용

  ◎ [00401333 ~ 0040134E] 내부 흐름

[00401333 ~ 0040134E] 내부 흐름 설명

설명 및 예시

1. ebx = [eax]
2. ecx = [eax+1]
3. ebx = ebx << 5
4. ebx = ebx + 2328h
5. ebx = ebx + ecx
6. ecx = ecx ^ 0BC614Eh
7. ebx = ebx * 0Dh
8. ebx = ebx ^ 15587h

1. eax는 입력받은 Name의 주소를 가지고 있습니다.
   ebx : 입력받은 Name의 문자 (시작 : 'C')

2. ecx : ebx의 다음 문자 ('o')

3. Shift Left : 각 bit를 왼쪽으로 Shift 한다.
   최상위 bit는 CF에 저장되고, 최하위 bit는 0으로 채워진다.

 

00401241의 내부는 [그림 6]과 같습니다. 앞에서 두 문자의 반복은 특정 조건을 만족하면 종료된다고 하였는데, 그 특정 조건이 00401241에 있습니다.

입력받은 암호화된 두 문자로 CALL 004011D5를 반복하면서 CMP [EBP+C], 0을 만족하면 두 문자에 대한 암호화 반복이 종료됩니다.

첫 00401241은 입력받은 암호화된 'C'와 'o'를 가지고 실행됩니다. [EBP+8]에 암호화된 'C'가 있고, [EBP+C]에는 암호화된 'o'가 있습니다.

[그림 6] CALL 00401241

 

[그림 7]은 004011D5의 내부입니다. 이 곳 코드는 입력받은 파라미터를 사용하여 암호화합니다.

[그림 7]의 붉은 칸이 표의 5.에서 설명한 반환 주소를 00401277로 변환하는 부분입니다.

 

[그림 7] CALL 004011D5

 

004011D5 부분은 코드 중간에 바뀌여 CALL 004011D5가 호출될 때마다 같은 코드를 반복하는 것은 아닙니다.

[그림 8] 코드 바뀌는 부분 중 일부

◎ CALL 004011D5 내부 코드 흐름

CALL 004011D5 내부 코드 흐름 [004011E4 ~ ] 설명

eax = [ebp+8]
//입력받은 파라미터
MOVZX ecx , [403000]
//16비트 복사하고 32비트로 확장
ecx = ecx + 1538h
ecx = ecx && 7A2B36FFh
eax = eax ^ ecx
eax = eax * [403000]



eax = eax - ECX
eax = eax << 7
eax = eax && 17F4Bh
MOVZX ecx , ax
ROR cx, 5
//오른쪽으로 Shift, 최하위 bit를 최상위 bit와 CF에 복사
eax = eax + ecx



eax = eax ^ 1AFh
ebx =ebx
ROL eax, 6
//왼쪽으로 Shift, 최하위 bit를 최상위 bit와 CF에 복사
BSWAP eax
//Byte Swap으로 big endian과 little endian을 변환
eax = eax || 66Ah



ebx = 11h
ecx = eax
CDQ
eax = eax / ebx
edx = eax % ebx
BT ecx, edx
//bit 연산 : CF로 비트 복사 (CF setting)
XCHG eax, ecx
// eax와 ecx 값을 교환 (exchange)
ADC eax, 2
//Add with Carry : CF 값도 같이 더함



MOVZX ecx, ax
 cx = ~cx
LOOPD
   eax = eax - B9h
   eax = eax ^ ecx

[403000] = eax

CALL 004011D5 반복동안의 코드
1회차 2회차 3회차 4회차 5회차 6회차 7회차 8회차 ...
①, ③ ①, ③ ②, ④ ①, ③ ①, ④ ①, ③ ②, ④ ①, ③ ...

 

에서의 설명과 같이 00401277은 [403000] = ([403000] ^ 0AAAh) - 7F9h로 시작됩니다.

[그림 9] 00401277

 

반복문 3개를 실행한 뒤 00401328 CMP [EBP+8], 0의 결과가 바뀝니다. 에서 이 명령어는 CALL 00401277이 처음 시작되었는지를 판단하는 비교문이라 설명했습니다. 따라서 에서 Name의 두 문자('C', 'o') 반복 중에는 [EBP+8]에 0이 들어있지 않으므로 JMP 00401380가 실행됩니다.

[그림 10] 반복 중의 00401277

 

⑤, ⑥

00401277이 끝나면 00401241로 돌아옵니다.

00401241의 CALL 004011D5 이후 표의 4.부터 시작되어 10.의 재귀 함수로 1.부터 다시 시작됩니다. 그리고 3.의 조건이 맞으면 함수가 종료됩니다.

00401241의 코드와 내부 흐름을 살펴보겠습니다.

[그림 11] 00401241 코드

◎ 00401241 내부 흐름

00401241 내부 흐름 설명

설명 및 예시

1. eax = [ebp+8]
2. [403260] = eax

3. CMP [ebp+C], 0

4. PUSH [ebp+8]
5. CALL 004011D5

6. eax = [403260]
7. CDQ
8. eax = eax / [ebp+C]
   edx = eax % [ebp+C]

9. PUSH edx , [ebp+C]
10. CALL 00401241

1. 반복 중 :  나누는 값(8.에서의 [ebp+C])을 가진다.

2. 5.호출 함수가 끝날 시, 8.에서 eax의 값을 나누기 위해 [403260]에 기록해둔다.

3. 반복 중 : [ebp+C]는 8.의 나머지 값(edx)이다.
   즉, 나머지가 0이 되며 반복이 종료된다.

4. 반복 중 : 8.에서의 나누는 값([ebp+C])을 파라미터로 5.을 호출한다.

5. 암호 부분을 호출한다.

   004011D5 내부에 반환 주소를 00401277로 변환하는 부분이 있어 004011D5 , 00401277 코드를 끝내고 반환한다.

6. 반복 중 : 이전 8.에서 나누는 값을 eax에 넣어 이번 8.에서는 나누어지는 값이 된다.

7. DWORD --> QWORD로 변환을 의미한다.
   나눗셈 후 부호 비트 공간을 확보하기 위한 확장 코드로 기존 공간의 2배를 확장한다.

9. 00401241의 파라미터 : 나머지와 나누는 값

10. 00401241은 재귀 함수이다.
   3.이 만족할 때까지 반복된다.

 

⑤,⑥ 표의 3.에서 종료되면 되었을 때입니다. 표의 10.에서 부른 만큼 반환하고 00401277의 CALL 00401241의 이후로 돌아갑니다.

[그림 12] JE 0040126E

 

0040135B에서 암호화된 'C'와 'o'으로 만들어진 [403000]의 값과 00401241에서의 마지막 나누는 값과 XOR 합니다.

그리고 다음 문자가 null인자 확인(CMP [EAX], 0 / [그림 13]에서 다음 문자는 'd')한 뒤, null이 아니면 push 0을 하고 00401277을 호출합니다. 이러면 의 00401277이 호출되는 부분부터 반복됩니다.

[그림 13] 입력받은 Name이 끝날 때까지 반복

 

00401377 push 0으로 CMP [EBP+8], 0가 참이 되어 00401277이 처음 시작인 것으로 판단합니다.

따라서 [그림 5]에서 설명한 입력받은 Name의 두 문자를 암호화하여 CALL 00401241에서 반복이 다시 진행됩니다.

('C'와 'o' 이후는 'd'와 'e'가 아닌 'o'와 'd'를 가져옵니다.)

[그림 14] 다시 시작된 암호화

CALL 00401277 반복동안의 두 문자
1회차 2회차 3회차 4회차 5회차 6회차 7회차 (끝)
'C', 'o' 'o', 'd' 'd', 'e' 'e', 'E' 'E', 'n' 'n', 'g' 'g', 'n'

 

◎ 입력받은 Name 문자열 반복 종료

 

입력받은 Name 문자열의 반복 7회가 모두 완료되면 함수들이 반환됩니다.

[그림 15] Name의 반복 완료 후 ①의 CALL 00401277 반환

 

Name을 사용한 암호화가 완료된 문자열을 확인할 수 있습니다. (C2A776FAh)

이 문자열을 wsprintfA 함수를 이용하여 %u 형식(부호 없는 10진수)으로 00403284에 복사하면 00403284의 값이 CodeEngn Advance RCE L03의 답입니다. (3265754874d)

[그림 16] 0040102C 中

 

 

'CodeEngn > Advance' 카테고리의 다른 글

[CodeEngn] Advance RCE L05  (0) 2021.04.02
[CodeEngn] Advance RCE L04 Serial 생성 분석  (0) 2021.04.02
[CodeEngn] Advance RCE L04  (0) 2021.04.02
[CodeEngn] Advance RCE L03  (0) 2021.03.31
[CodeEngn] Advance RCE L01  (0) 2021.03.31