일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Static안티디버깅
- PEImage
- 서비스프로세스
- x32dbg
- UTF-32
- JIT
- CP949
- 안티디버깅
- OllyDbg
- hxd
- 디버깅
- 서비스디버깅
- IL코드
- 리버싱
- DebugBlocker
- Exe2Aut
- 리버싱핵심원리
- Visual Studio
- Self-Creation
- UTF-16
- Dynamic안티디버깅
- ImageSwitching
- PEImageSwitching
- 분석 보고서
- Advanced안티디버깅
- dotPeek
- Python
- .net
- CodeEngn
- RedLine Stealer
- Today
- Total
-榮-
[CodeEngn] Advance RCE L11 본문
● CodeEngn Advance RCE L11 문제
◎ 파일 실행 화면
◎ 패킹 및 난독화 여부 확인
● CodeEngn Advance RCE L11 분석
[그림 1]에서의 실패 문자열을 찾기 위해 [Search for > All referenced text strings]를 확인하면 성공 문자열과 실패 문자열을 확인할 수 있습니다.
실패 문자열의 주소로 이동하면 분기 조건과 분기점을 확인할 수 있습니다.
스크롤을 올려 [그림 5]에 보이는 함수의 프롤로그에서 호출하는 주소를 알아내 이동하면, GetDlgItemTextA 함수 2개를 볼 수 있습니다. 각각 Name과 Serial을 가져오는 것으로 보입니다.
GetDlgItemTextA 함수까지 실행해보면 CALL 004010EF까지 Name과 Serial을 받아와 입력이 되었는지를 확인하는 코드만 있습니다. 즉, Serial은 004010EF 안에서 만들어지는 것입니다.
CALL 004010EF에 Step Into[F7]해서 진행하다 보면 CALL 00401146을 지나는 순간 00402439에 생성된 Serial이 있는 것을 확인할 수 있습니다. 즉, Serial은 CALL 00401146 내부에서 만들어지는 겁니다.
CALL 00401146 내부로 Step Into[F7]하면 Name으로 Serial을 만드는 부분이 바로 보입니다.
반복문은 0040116D에서 [40217B]에 있는 문자를 읽어와서 그 문자와 같은 부분의 코드를 실행하는 방식입니다.
우선 반복문 내에서 호출되는 함수에 대해 정리하겠습니다.
◎ 반복문 내에서 호출되는 함수
반복문 내에서 호출되는 함수는 총 3가지입니다. (0040138A, 004013BF, 004013AB)
첫 번째로 0040138A는 EBX를 변화시켜 [40217B]에서 가져오는 문자를 조절하는 함수입니다.
두 번째와 세 번째 함수는 Serial 생성과 관련된 함수입니다. 이 반복문에서 생성된 Serial은 [402439]에 기재됩니다. 그리고 Serial이 생성되기까지 계산되는 중간의 값들을 저장하는 위치는 [402538]입니다.
004013BF는 [402538]에 기재된 내용을 지우는 함수이며, 004013AB는 [402538]에 내용을 기재하는 함수입니다.
◎ 문자와 같은 부분의 코드를 실행
앞에서 반복문을 설명할 때, 문자를 읽어와서 그 문자와 같은 부분의 코드를 실행하는 방식이라고 설명하였습니다. 그중 Serial을 생성할 때 사용되는 코드의 일부를 주소 순서대로 설명하겠습니다.
[40217B]에서 읽어온 문자가 숫자이면 10진수로 변환하여(ex, 33h -> 03h) [402538]에 내용을 기재합니다. 또한 22(")를 읽어오고 다음 22(")가 나오기 전의 내용도 [402538]에 기재됩니다. (""사이의 숫자는 숫자로 입력됩니다.(ex, 33h))
7E(~)를 읽어오면 Name의 1 Byte를 [402538]에 기재합니다.
2B(+)는 [402538]의 마지막 두 4Byte를 더해서 기재하는 코드입니다.
2F(/)는 Name이 끝난 뒤에 나옵니다. Name으로 만들어진 값을 F로 나누어 나머지와 몫을 [402538]에 기재합니다.
5C(\)는 [402538]의 마지막 두 4Byte의 주소를 바꾸는 코드입니다.
6C(l)은 나눗셈 후 몫을 ROL해서 다음 나눗셈에서 나누어지는 값을 만듭니다.
60(`)은 [그림 14]의 결과 중 나머지가 숫자인지 문자인지를 확인하는 코드입니다.
◎ Serial 생성
우선 반복문이 Serial을 생성하는 과정은 아래와 같습니다.
x와 ~를 곱하여 3B10을 만듭니다. 그리고 Name 1Byte, m, d, 8을 입력받아 뒤에서부터 순서대로 더하고 곱하고 곱하고 더하는 것을 반복합니다. 즉, ((((d+8)*m)*Name)+3B10)입니다. 이후 반복되는 것은 3B10 자리에 이전의 결과 값이 들어갈 뿐 공식은 같습니다. 이렇게 나누어지는 값을 만든 후 F로 나눗셈을 시작합니다. F로 나누어지는 값을 나누고 나머지를 [402439]에 저장하고, 몫을 ROL한 뒤 F로 나누어지는 값을 나누고... 이것을 몫이 0이 될 때까지 반복합니다. 명칭 : 나누어지는 값 / 나누는 값 = 몫 나누어지는 값 % 나누는 값 = 나머지 |
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]과 같습니다.
◎ 처음 나누어지는 값을 찾기 위한 코드
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))
③ 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이면 되므로 순서는 상관없습니다.
'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 |