보안 / AI / 프로그래밍

[Reversing] rev-basic-2 문제풀이 본문

WarGame/DreamHack

[Reversing] rev-basic-2 문제풀이

DevKTW 2021. 2. 23. 21:33
Debugger는 x96dbg 프로그램을 사용했습니다.

먼저 Debugger로 프로그램을 실행시켜 반응을 살펴봅시다.

Debugger로 "Input :" 문자열을 참조하는 장소로 가서 적절한 곳에 BP를 걸어줍니다.

저번 [Reversing] rev-basic-1 문제풀이 에서 사용한 String 참조 검색을 통해서 의심되는 부분을 찾아내는 모습이다.
찾은 의심부분에서 BP를 Call과 여러 부분에 BP를 걸어버린 모습이다.

이번에도 마찬가지로 eax가 0이 아니면 됩니다.

그위에 위치한 call 함수를 따라 가보았습니다.

들어간 함수에 cmp 코드가 있고, 입력한 문자열이 위치한 [rsp+20]을 참조하는 것을 보고, 직감적으로 여기서 판별을 하는구나 생각하고 BP를 전부 걸었다.

함수를 살펴봅시다.

1) jmp chall2.7FF635E2101A
...
2) movsxd rax dword ptr ss:[rsp]
3) cmp rax, 12
4) jae chall2.7FF635E21048

 

1) 먼저 Jmp 를 타고 아래 코드로 향합니다.

2) movsxd 를 통해서 rsp 메모리에 들어있는 값을 DWORD 형으로 rax에 저장합니다.

    (movsxd에 대해서는 아래 참조 자료를 확인해주세요.)

3) 그리고 rax의 값과 12를 비교합니다.

4) 만약 비교한 값이 12보다 크거나 같으면 (Jae = Jump if Above or Equal) 점프합니다.

 

맨 처음에 rsp 메모리에는 0이 저장되었고, 2) 그 값은 rax에 다시 담았기때문에, 처음 rax값은 0입니다.

그러므로 점프를 취하지않고 바로 아래코드로 진행합니다. 계속 보시죠.

 

1) movsxd rax, dword ptr ss:[rsp]
2) lea rcx, qword ptr ds:[7FF635E23000]

1) rax에 또 rsp 메모리에 들어있는 값을 DWORD 형으로 rax에 저장합니다.

2) 그리고 0x7FF635E23000 에 저장된 값의 "유효주소값"을 rcx에 저장합니다.

이 코드를 진행하면, rcx에 0x7FF635E23000가 저장되있음을 알 수 있습니다.

lea 명령에 BP를 걸고, 실제로 0x7FF635E23000값이 rcx에 저장되는지 살펴보았다.
예상대로, RCX에 0x7FF635E2300값이 저장됨을 알 수 있다.

 

<의문> 0x7FF635E23000에는 무엇이 저장되어 있길래 그러는 걸까요? 메모리를 조사해봅시다. 메모리를 조사하는 방법은 아래참조 자료를 참조해주세요.

4바이트 단위로 C/o/m.. 으로 무언가 저장되어 있다. 감이 오지 않는가?

아 무언가 저장되어 있습니다. 잘 보시면 Comp4re_the_arr4y 가 4 Byte 단위로 간격을 두고 저장되어 있음을 알 수 있습니다.

결론적으로, 정답은 Comp4re_the_arr4y 입니다. 그러나, 좀더 살펴보도록 하죠.

[심화 과정]

4 Byte 단위로 저장된 저 값들을 어떠한 방식으로 읽어오는 걸까요? 살펴 보도록 합시다.

1) movsxd rdx, dword ptr ss:[rsp]
2) mov r8, qword ptr ss:[rsp+20]
3) movzx edx, byte ptr ds:[r8+rdx]

1) rsp에 저장된 값을 DWORD 형태로 rdx에 저장합니다. 지금 상황으로는, 0이 rdx에 저장됩니다.

2) r8에 [rsp+20]에 위치한 값을 QWORD 형태로 저장합니다. [rsp+20]에는 제가 무작위로 입력한 값이 저장되어 있는 주소값을 저장하고 있습니다. 이거 헷갈리시면 곤란합니다. 저도 갑자기 너무 헷갈렸네요.

 

사실 위에서 부터 읽으셨다면, DDDDDDDD.. 가아니라 abcdefg..입니다. 아무튼 무작위로 입력한 문자열이 저 DDDD..자리에 저장된다는 사실이 중요합니다.

(글을 읽으시면서, 위의 표를 따라가 주세요.)

rsp 에는 0x8d5bff7f0 이라는 값이 저장되어있습니다. 즉 rsp = 0x8d5bff7f0 입니다.

rsp+20 = 0x8d5bff7f0 + 20 = 0x8d5b0ff810

[0x8d5b0ff810] = 0x8d5b0ff810 주소에 저장된 값 = 0x8d5b0ff830

즉 r8 에는 0x8d5b0ff830가 저장됩니다.

 

3) movzx를 통해서 [rdx+r8]에 저장된 값을 byte형으로 edx에 저장합니다. r8은 무작위로 입력한 값이 저장된 메모리 주소값 이고, rdx는 0입니다. 즉 "제가 입력한 문자열의 첫번째 "문자"를 가져와서 edx에 저장합니다.

(movzx는 참고자료를 확인해주세요.)

 

정리하자면 1~3 과정은 제가 무작위로 입력한 문자열의 rdx번째 (0번째) 문자 하나를 가져와 edx에 저장하는 과정입니다.
1) cmp dword ptr ds:[rcx+rax*4], edx

자, edx에는 제가 무작위로 입력한 문자열중 첫번째 문자만 저장되어 있습니다.

기억하시나요? rcx에는 Comp4re_the_arr4y라는 값의 메모리주소가 저장되어 있습니다.

 

여기서 4Byte로 Comp4re_the_arr4y를 띄고 저장한 이유를 알 수 있습니다.

rax에 4를 곱해서 rcx에 더합니다. 이 의미는 rax가 0이면 'C'를 가져오고, rax가 1이면 총 4가 더해져 'o'를 가져옵니다.

 

, 제가 무작위로 입력한 문자를 하나씩 edx에 담고, 정답의 문자를 하나씩 가져와서 비교해 나가는 것입니다.

첫번째 문자가 'C'인지 확인하고, 이 cmp문이 끝나면 다시 흐름은 위로 올라가서 rax에 1이 더해지고, cmp를 다시 만나면 이번에는 두번째 문자가 'o'인지 확인합니다. 계속 반복해서 12번을 하면 끝나게 됩니다.

 

그래서 코드의 초반부에 이런 코드가 있던 것입니다.

1) cmp rax, 12
2) jae chall2.7FF635E21048

for 문을 돌리는 것처럼 12번 반복해서 (rax가 0->11 까지 증가) 확인합니다. jae에 도달하면 정답을 입력했다는 거겠죠?

 

이상하지 않으신가요? 확인해야하는 문자는 17문자 입니다.

맞습니다. 저같은 경우는 자주 범하는 실수인데요. 심지어 지금 글을 쓰면서 또 실수를 했네요.

저기 cmp rax, 12의 12는 10진수의 12가 아니라 16진수의 12입니다.

차분히 머릿속으로 카운팅 하면.. 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11.. 해서 17번 인 것을 알 수 있습니다.

 

네.. 총 17번을 반복하면서, 한글자 한글자씩 서로 대조하는 함수였던 것입니다.


[참고 자료]

assembly - X86: What does `movsxd rdx,edx` instruction mean? - Stack Overflow

[MOVSXD]
movsxd rdx,edx takes a look at bit 31 (top most bit) of EDX and sets the upper 32 bits of the destination to that value and copies the lower 32 bits as is. If the sign bit is set in EDX, the upper 32 bits of the 64-bit register will be set to 1. If the sign bit is clear the upper 32 bits of RDX will be 0.

-> movsxd rdx, edx 라는 코드는 EDX의 최상위 비트(부호비트)를 RDX의 상위 32비트에 복사하고, EDX의 나머지 하위 비트는 RDX에 있는 그대로 복사합니다. 만약 EDX의 최상위 비트가 1이라면(sign bit is set), RDX의 상위 32 비트는 1로 설정될 것입니다.

As an example, assume EDX has the value 0x80000000. Bit 31 is 1. As a signed number that is -2147483648. If you do movsxd RDX, EDX the value in RDX will be 0xFFFFFFFF80000000.

-> 예를 들어서, EDX가 0x80000000 라는 값을 가진다고 합시다. 31번째(최상위 비트)는 1입니다. 10진수로는 -2147483648가 됩니다.(부호가 음수임에 집중!), 만약 이 상황에서 movsxd RDX, EDX를 한다면, RDX의 값은 0xFFFFFFFF80000000가 될것입니다.

Peemang IT Blog (tistory.com)

Source operand를 Destination operand로 데이터를 복사한 후 나머지 비트를 0(Zero)으로 채운다.
부호가 없는 정수에만 사용된다. (unsigned)
Destination operand는 반드시 레지스터가 되어야 한다.
바이트나 워드의 원시 피연산자를 워드나 더블 워드의 목적지 피연산자로 데이터를 전송할 수 있게 해 준다.


[참고 자료] 메모리 조사 방법

 

해당 주소 클릭 -> 덤프따라가기 -> 주소 선택

'WarGame > DreamHack' 카테고리의 다른 글

[Reversing] rev-basic-1 문제풀이  (0) 2021.02.23
[Reversing] rev-basic-0 문제풀이  (1) 2021.02.22
Comments