-榮-

[CodeEngn] Advance RCE L11 본문

CodeEngn/Advance

[CodeEngn] Advance RCE L11

xii.xxv 2021. 4. 8. 17:48

● CodeEngn Advance RCE L11 문제

 

CodeEngn Advance RCE L11

 

◎ 파일 실행 화면

[그림 1] 파일 실행 화면 및 실패 화면

 

◎ 패킹 및 난독화 여부 확인

[그림 2] exeinfo PE

 


 

● CodeEngn Advance RCE L11 분석

 

[그림 3] 디버거 - OllyDbg / 디버기 - 11.exe (Advance RCE L11 실행파일)

 

[그림 1]에서의 실패 문자열을 찾기 위해 [Search for > All referenced text strings]를 확인하면 성공 문자열과 실패 문자열을 확인할 수 있습니다.

[그림 4] Search for > All referenced text strings

 

실패 문자열의 주소로 이동하면 분기 조건과 분기점을 확인할 수 있습니다.

[그림 5] 분기 조건과 분기점

 

스크롤을 올려 [그림 5]에 보이는 함수의 프롤로그에서 호출하는 주소를 알아내 이동하면, GetDlgItemTextA 함수 2개를 볼 수 있습니다. 각각 Name과 Serial을 가져오는 것으로 보입니다.

[그림 6] CALL 004010EF

 

GetDlgItemTextA 함수까지 실행해보면 CALL 004010EF까지 Name과 Serial을 받아와 입력이 되었는지를 확인하는 코드만 있습니다. 즉, Serial은 004010EF 안에서 만들어지는 것입니다. 

[그림 7] GetDlgItemTextA 함수

 

CALL 004010EF에 Step Into[F7]해서 진행하다 보면 CALL 00401146을 지나는 순간 00402439에 생성된 Serial이 있는 것을 확인할 수 있습니다. 즉, Serial은 CALL 00401146 내부에서 만들어지는 겁니다.

[그림 8] CALL 00401146 결과

 

 

CALL 00401146 내부로 Step Into[F7]하면 Name으로 Serial을 만드는 부분이 바로 보입니다.

반복문은 0040116D에서 [40217B]에 있는 문자를 읽어와서 그 문자와 같은 부분의 코드를 실행하는 방식입니다.

[그림 9] CALL 00401146 내부

 

우선 반복문 내에서 호출되는 함수에 대해 정리하겠습니다.

 

◎ 반복문 내에서 호출되는 함수

반복문 내에서 호출되는 함수는 총 3가지입니다. (0040138A, 004013BF, 004013AB)

첫 번째로 0040138A는 EBX를 변화시켜 [40217B]에서 가져오는 문자를 조절하는 함수입니다.

두 번째와 세 번째 함수는 Serial 생성과 관련된 함수입니다. 이 반복문에서 생성된 Serial은 [402439]에 기재됩니다. 그리고 Serial이 생성되기까지 계산되는 중간의 값들을 저장하는 위치는 [402538]입니다. 

004013BF는 [402538]에 기재된 내용을 지우는 함수이며, 004013AB는 [402538]에 내용을 기재하는 함수입니다.

[그림 10] 004013BF와 004013AB

 

◎ 문자와 같은 부분의 코드를 실행

앞에서 반복문을 설명할 때,  문자를 읽어와서 그 문자와 같은 부분의 코드를 실행하는 방식이라고 설명하였습니다. 그중 Serial을 생성할 때 사용되는 코드의 일부를 주소 순서대로 설명하겠습니다.

 

 [40217B]에서 읽어온 문자가 숫자이면 10진수로 변환하여(ex, 33h -> 03h) [402538]에 내용을 기재합니다. 또한 22(")를 읽어오고 다음 22(")가 나오기 전의 내용도 [402538]에 기재됩니다. (""사이의 숫자는 숫자로 입력됩니다.(ex, 33h))

[그림 11] 22(")와 숫자 처리 방법

 

7E(~)를 읽어오면 Name의 1 Byte를 [402538]에 기재합니다.

[그림 12] Serial 생성에 Name이 사용되는 부분

 

2B(+)는 [402538]의 마지막 두 4Byte를 더해서 기재하는 코드입니다.

[그림 13] 덧셈

 

2F(/)는 Name이 끝난 뒤에 나옵니다. Name으로 만들어진 값을 F로 나누어 나머지와 몫을 [402538]에 기재합니다.

[그림 14] 나눗셈

 

5C(\)는 [402538]의 마지막 두 4Byte의 주소를 바꾸는 코드입니다. 

[그림 15] 순서 변경

 

6C(l)은 나눗셈 후 몫을 ROL해서 다음 나눗셈에서 나누어지는 값을 만듭니다.

[그림 16] ROL

60(`)은 [그림 14]의 결과 중 나머지가 숫자인지 문자인지를 확인하는 코드입니다.

[그림 17] 나머지 확인

 

◎ Serial 생성

우선 반복문이 Serial을 생성하는 과정은 아래와 같습니다.

x와 ~를 곱하여 3B10을 만듭니다. 그리고 Name 1Byte, m, d, 8을 입력받아 뒤에서부터 순서대로 더하고 곱하고 곱하고 더하는 것을 반복합니다.
즉, ((((d+8)*m)*Name)+3B10)입니다. 이후 반복되는 것은 3B10 자리에 이전의 결과 값이 들어갈 뿐 공식은 같습니다.
이렇게 나누어지는 값을 만든 후 F로 나눗셈을 시작합니다.

F로 나누어지는 값을 나누고 나머지를 [402439]에 저장하고, 몫을 ROL한 뒤 F로 나누어지는 값을 나누고... 이것을 몫이 0이 될 때까지 반복합니다.


명칭 : 나누어지는 값 / 나누는 값 = 몫
        나누어지는 값 % 나누는 값 = 나머지

[그림 18] Serial 생성 도중 및 생성된 Serial(Name : 1234)

 

Serial 생성이 규칙적인 것은 [40217B]에서 읽어오는 문자가 규칙성을 가지고 있기 때문입니다.

(문자 순서로 보면 많은 행위를 하는 것처럼 보이지만 Serial을 만드는 행위는 위에서 설명한 행위만입니다.)

Serial 생성 시 문자 순서

$5%$#v<"x~"*v>~:!4#$_"md8"G+*%*+>~:!4#$_"md8"G+*%*+>~:!4#$_"md8"G+*%*+>~:!4#$_"md8"G+*%*+>~:!4#$v>
(08h)(07h)+/\"0"^+<:"9"\`^_"as"*3$$vx%>,+l:!#_>(08h)(07h)+/\"0"^+<:"9"\`^_"as"*3$$vx%>,+l:!#_>....(몫이 0이 될때까지)..... (08h)(07h)+/\"0"^+<:"9"\`^_"as"*3$$vx%>,+l:!#_@


나누어지는 값 생성 = >~:!4#$_"md8"G+*%*+가 반복

나눗셈 나머지 =  숫자일 경우 : (08h)(07h)+/\"0"^+<:"9"\`^_"as"*3$$vx%>,+l:!#_>가 반복 (나머지 + 30h)
                       문자일 경우 : (08h)(07h)+/\"0"^+<:"9"\`^_#w#%5$8+vx%>,+l:!#_>가 반복 (나머지 + 30h + 8h)
                                         ex. 나머지 0Ah일 경우, 0Ah + 30h + 8h = 42h == B

@ : 종료

 


 

● CodeEngn Advance RCE L11 풀이 및 확인

 

① 나누어지는 값을 찾습니다.

앞의 분석에서 설명한 Serial 생성 부분에서 나누어지는 값이 만들어지는 과정을 식으로 만들어 봅시다.

Name의 각 문자가 x1, x2, x3, x4로 네 글자라고 가정을 합시다.

나누어지는 값의 기본 공식은 ((((d+8)*m)*Name)+3B10)입니다. 이후 반복되는 것은 3B10 자리에 이전의 결과 값이 들어가죠.

즉, Name 1 byte와 곱하는 ((d+8)*m)값은 고정입니다. (d = 64 / 8 = 38 / m = 6D / ((d+8)*m) = 426C)

따라서 나누어지는 값을 만드는 공식은 426C*x1+426C*x2+426C*x3+426C*x4+3B10입니다.

정리하면 426C*(x1+x2+x3+x4)+3B10가 됩니다.

 

② 나눗셈의 처음 나누어지는 값을 찾습니다.

CodeEngn Advance RCE L11의 문제는 Serial이 94E7DB1B인 Name을 찾는 것입니다. 또한 분석 부분에서 나누는 값이 F이고, Serial은 나머지 값이라는 것을 알고 있습니다. 

즉, 나누어지는 값 = 나누는 값 * 몫 + 나머지 공식을 사용하면 됩니다.

예를 들어 1단계를 진행해 보겠습니다.

Serial의 마지막은 몫이 0입니다. 즉, 나누어지는 값 = A입니다. (B가 아닌 이유는 분석의 Serial 생성 부분 참고)

분석 부분의 Serial 생성을 보면 나누어지는 수는 이전 결과의 몫을 ROL하여 결정됩니다. 즉, A를 ROR하면 이전 결과의 몫을 알 수 있을 것입니다. 정리하면 [그림 18]과 같습니다.

[그림 19] 나눗셈

 

◎ 처음 나누어지는 값을 찾기 위한 코드

Serial의 길이를 보았을 때, 코드 작성까지는 필요 없지만 한번 작성해 봅시다.

def ROR(number):  ## Rotate Right
    shiftBit = number >> 1
    carryBit = number << 7
    carryBit &= 255

    result = shiftBit | carryBit
    return result



if __name__ == '__main__':
    Serial = input('Serial : ')
    quotient = 0  ## 처음 몫의 값은 0이다.
    count = 0

    for remainder in Serial[::-1]:
        
        if (remainder > 'A' and remainder < 'z'):  ## Serial의 값이 문자일 경우, 그 전 문자로 변경
            remainder = hex(int(remainder, 16) - 1)
            
        dividend = quotient * 0xF + int(remainder, 16)
        count += 1
        
        if len(Serial) != count:  ## Serial이 끝이 아니면 ROR로 다음 몫을 찾는다.
            quotient = ROR(dividend)
            
    print("나누어지는 값은 ", hex(dividend))

[그림 20] 처음 나누어지는 값

 

③ Name의 값을 찾습니다.

의 결과 처음 나누어지는 값은 D2A734입니다.

즉, 426C*(x1+x2+x3+x4)+3B10 = D2A734입니다.

따라서 (x1+x2+x3+x4) = 32B입니다.

x1+x2+x3+x4는 에서 가정한 것이므로 Name의 각 문자의 합이 32B라는 의미입니다.

문제에서 Name은 여러 개 나오는 문제라고 하였고, Name의 길이를 규정하지 않았으므로 Name 문자의 합이 32B이면 정답입니다.

저는 헥사 값이 가장 큰 문자 z(7Ah)를 이용하여 Name을 찾았습니다. 32B는 z*6+O (7A * 6 + 4F)입니다.

 


 

● CodeEngn Advance RCE L11 답

 

Name 문자의 합이 32B이면 되므로 순서는 상관없습니다.

 

[그림 21] Serial accepted

 

 

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

[CodeEngn] Advance RCE L15  (0) 2021.04.19
[CodeEngn] Advance RCE L14  (0) 2021.04.16
[CodeEngn] Advance RCE L10  (0) 2021.04.07
[CodeEngn] Advance RCE L09  (0) 2021.04.05
[CodeEngn] Advance RCE L08  (0) 2021.04.05