이번 문제는 지금까지 잘 풀어왔다면 굉장히 쉬운 문제다.
비교함수를 확인해보자.

대충 보면 암호문의 길이는 0x15고 imul연산과 and연산이 보인다.
[imul a, b, c] 연산은 [a = b * c]와 같다. 따라서 이 비교함수연산을 그대로 대입해보면
(input[i]*0xFB)&(0xFF) == enc[i]이다.
 
이를 역산식으로 만들면
input[i] == (enc[i]&(0xFF)) / (0xFB)
인데 파이썬에서 나눗셈은 무조건 float를 결과값으로 가진다. 타입변환이 귀찮기 때문에 브루트포스로 대입해서 비교하는 식으로 코드를 작성했다.
 

enc = 'AC F3 0C 25 A3 10 B7 25 16 C6 B7 BC 07 25 02 D5 C6 11 07 C5 00'
enc = enc.split(' ')

res = ''

for i in range(21): #0x15 == 21
    for j in range(32, 127): #printable ascii code
        if (j*0xFB)&(0xFF) == int(enc[i],16):
            print(chr(j), end='')

'Security > Reversing' 카테고리의 다른 글

Secure Mail  (0) 2022.07.13
Dreamhack rev-basic-9  (0) 2022.07.05
Dreamhack rev-basic-7  (0) 2022.06.21
Dreamhack rev-basic-6  (0) 2022.06.01
Dreamhack rev-basic-5  (0) 2022.05.28

바로 비교함수를 확인해보자

코드 자체는 그냥 연산을 따라가면 확인할 수 있을 것 같은데 처음보는 어셈블리 명령어가 보인다.

00007FF644D41044 | D2C0                     | rol al,cl                               |

 여기서 rol이라는 명령어를 알아보기 전에 al과 cl에 대해서 먼저 알아보자면 어셈블리어에서는 범용 레지스터가 있다. 32비트로 E(A, B, C, D)X, 그리고 64비트 R(A, B, C, D)X인데 값을 잠깐 저장하는데 쓰인다. (각 레지스터마다 주된 사용처가 있지만 신경쓸 필요 X)
 
 이 범용 레지스터에서 하위 비트를 부르는 명칭이 있다. 예를 들어 AX는 RAX(EAX)의 하위 4바이트(16비트)이다. 아래의 그림을 참고하자.

출처: https://kuaaan.tistory.com/449

그러면 이제 [ROL  AL, CL]이라는 명령어의 의미는 "RAX의 하위 8비트를 RCX의 하위 8비트만큼 ROL해서 AL에 저장해라."라는 의미이다.
 
이번에는 ROL명령어에 대해서 알아보자. 이 부분에 대해서는 아래의 링크를 통해 공부하였다.
https://m.blog.naver.com/sol9501/70091168321

|정수 연산 ①| 시프트와 회전 명령어 및 응용

|정수 연산 1| 시프트와 회전 명령어 및 응용 목표 : 시프트 연산의 기본적이 사용법과 응용 및 각 연산이 ...

blog.naver.com

ROL명령어는 간단히 말해서 왼쪽으로 비트회전이다. 코딩을 조금이라도 해 본 사람이라면 비트시프트에 대해서 알고 있을 것이다. 기본적으로 왼쪽으로 비트시프트를 하면 보통 최상위 비트는 그대로 사라진다. 그러나 비트회전은 최상위 비트를 CARRY 플래그와 최하위 비트로 복사한다. 비트가 말 그대로 회전한다는 것이다. 
 
최종적으로 [ROL  AL, CL]의 의미는  "RAX의 하위 8비트를 RCX의 하위 8비트만큼 왼쪽으로 비트회전해서 AL에 저장해라."라는 뜻이다.
 
따라서 비교함수식은
(rol(input[i], i & 7)) ^ i == enc[i]
 
이를 통해 역산식을 구하면
input[i] = ror(enc[i] ^ i, i & 7)이 된다.
 
ROR코드 및 ROL코드는 아래 링크에서 복사해왔다.
https://hacking-ai.tistory.com/68

어셈블리 ROL, ROR 코드 구현

리버싱 문제를 풀다가 ROL, ROR이 나왔는데, 기드라의 경우 디컴파일 결과가 단지 >>와 같이 쉬프트 연산만 수행하는 방식으로 나오는 문제를 겪었다. 따라서, 이를 해결하기 위해 파이썬으로 ROL, R

hacking-ai.tistory.com

def rol(x, n):
    shiftBit = x << n
    shiftBit &= 255
    carryBit = x >> 8 - n
    return shiftBit | carryBit

def ror(x, n):
    shiftBit = x >> n
    carryBit = x << (8 - n)
    carryBit &= 255
    return shiftBit | carryBit

enc = '52 DF B3 60 F1 8B 1C B5 57 D1 9F 38 4B 29 D9 26 7F C9 A3 E9 53 18 4F B8 6A CB 87 58 5B 39 1E 00'
enc = tuple(enc.split(' '))
res = ''

for i in range(31):
    res += chr(ror((int(enc[i], 16) ^ i), (i&7)))


print(res)

 

 
새로운 명령어로 지식이 늘었다.

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-9  (0) 2022.07.05
Dreamhack rev-basic-8  (0) 2022.07.05
Dreamhack rev-basic-6  (0) 2022.06.01
Dreamhack rev-basic-5  (0) 2022.05.28
Dreamhack rev-basic-4  (0) 2022.05.25

실행부는 앞의 문제와 동일하므로 생략하고 바로 비교함수를 분석해보았다.

00007FF61F771000 | 48:894C24 08             | mov qword ptr ss:[rsp+8],rcx            |
00007FF61F771005 | 48:83EC 18               | sub rsp,18                              |
00007FF61F771009 | C70424 00000000          | mov dword ptr ss:[rsp],0                |
00007FF61F771010 | EB 08                    | jmp chall6.7FF61F77101A                 |
00007FF61F771012 | 8B0424                   | mov eax,dword ptr ss:[rsp]              |
00007FF61F771015 | FFC0                     | inc eax                                 | i++
00007FF61F771017 | 890424                   | mov dword ptr ss:[rsp],eax              |
00007FF61F77101A | 48:630424                | movsxd rax,dword ptr ss:[rsp]           |
00007FF61F77101E | 48:83F8 12               | cmp rax,12                              | str길이 0x12
00007FF61F771022 | 73 31                    | jae chall6.7FF61F771055                 |
00007FF61F771024 | 48:630424                | movsxd rax,dword ptr ss:[rsp]           |	index
00007FF61F771028 | 48:8B4C24 20             | mov rcx,qword ptr ss:[rsp+20]           | str
00007FF61F77102D | 0FB60401                 | movzx eax,byte ptr ds:[rcx+rax]         |	str[i]
00007FF61F771031 | 48:8D0D E81F0000         | lea rcx,qword ptr ds:[7FF61F773020]     | 주소 7FF61F773020
00007FF61F771038 | 0FB60401                 | movzx eax,byte ptr ds:[rcx+rax]         | rcx+rax(주소 7FF61F773020+str[i]에 저장된 값)
00007FF61F77103C | 48:630C24                | movsxd rcx,dword ptr ss:[rsp]           | index
00007FF61F771040 | 48:8D15 B91F0000         | lea rdx,qword ptr ds:[7FF61F773000]     | 
00007FF61F771047 | 0FB60C0A                 | movzx ecx,byte ptr ds:[rdx+rcx]         | rdx[i]
00007FF61F77104B | 3BC1                     | cmp eax,ecx                             | eax와 ecx의 값 비교
00007FF61F77104D | 74 04                    | je chall6.7FF61F771053                  |
00007FF61F77104F | 33C0                     | xor eax,eax                             |
00007FF61F771051 | EB 07                    | jmp chall6.7FF61F77105A                 |
00007FF61F771053 | EB BD                    | jmp chall6.7FF61F771012                 |
00007FF61F771055 | B8 01000000              | mov eax,1                               |
00007FF61F77105A | 48:83C4 18               | add rsp,18                              |
00007FF61F77105E | C3                       | ret                                     |

주석을 달아놓긴 했는데 정확히 어떤 의미냐면

3020덤프

주소 00007FF61F773020을 기준으로 덤프를 캡쳐해온 것이다. 이 주소에서 내가 입력한 문자열 str[i]를 더했을 때

00007FF61F773000  00 4D 51 50 EF FB C3 CF 92 45 4D CF F5 04 40 50  .MQPïûÃÏ.EMÏõ.@P

이 00007FF61F773000주소에 있는 헥스코드 값과 일치해야 된다는 것이다.
맛보기로 수작업으로 했을 때 첫 글자는 ~~~3020주소에서 str[i]를 더해서 00이 나와야 되므로 위 3020덤프에서 헥스값이 00인 주소는 

~~~3072다. 첫 문자는 0x3072-0x3020 = 0x52 따라서 R이다. 첫 문자 R로 입력해서 디버거를 돌렸을 때 다음 루프를 들어가는 것을 확인하여 이가 맞음을 확인했다.
 
이렇게 되면 덤프 밑의 값까지 전부 확인해야되므로 코드를 짤때 어쩔 수 없이 전부 가져가야겠다. 아스키코드의 마지막 값은 7F이므로 ~~~309F까지 저장해서 풀었다.

dump = '63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2'
dump = tuple(dump.split(' '))

str = '00 4D 51 50 EF FB C3 CF 92 45 4D CF F5 04 40 50 43 63'
str = tuple(str.split(' '))

res = ''

for i in range(18):
    res += chr(dump.index(str[i]))

print(res)

이번에는 디컴파일 없이 어셈블리어를 직접 하나하나 분석했다. 눈은 아픈데 굉장히 뿌듯하다. 
 
시험기간인데 시험공부는 안하고 워게임이나 하고있다.

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-8  (0) 2022.07.05
Dreamhack rev-basic-7  (0) 2022.06.21
Dreamhack rev-basic-5  (0) 2022.05.28
Dreamhack rev-basic-4  (0) 2022.05.25
Dreamhack rev-basic-3  (0) 2022.05.24

실행부는 이전의 문제와 동일하다.
 

00007FF7C9A63000  AD D8 CB CB 9D 97 CB C4 92 A1 D2 D7 D2 D6 A8 A5  .ØËË..ËÄ.¡Ò×ÒÖ¨¥  
00007FF7C9A63010  DC C7 AD A3 A1 98 4C 00 00 00 00 00 00 00 00 00  ÜÇ.£¡.L.........

비교함수의 덤프까지 가져왔다.
 
어셈블리 코드의 비교함수를 자세히 살펴보면

00007FF7C9A61000 | 48:894C24 08             | mov qword ptr ss:[rsp+8],rcx            |
00007FF7C9A61005 | 48:83EC 18               | sub rsp,18                              |
00007FF7C9A61009 | C70424 00000000          | mov dword ptr ss:[rsp],0                |
00007FF7C9A61010 | EB 08                    | jmp chall5.7FF7C9A6101A                 |
00007FF7C9A61012 | 8B0424                   | mov eax,dword ptr ss:[rsp]              |
00007FF7C9A61015 | FFC0                     | inc eax                                 |
00007FF7C9A61017 | 890424                   | mov dword ptr ss:[rsp],eax              |
00007FF7C9A6101A | 48:630424                | movsxd rax,dword ptr ss:[rsp]           |
00007FF7C9A6101E | 48:83F8 18               | cmp rax,18                              | 문자열 길이 0x18
00007FF7C9A61022 | 73 39                    | jae chall5.7FF7C9A6105D                 |
00007FF7C9A61024 | 48:630424                | movsxd rax,dword ptr ss:[rsp]           | 
00007FF7C9A61028 | 48:8B4C24 20             | mov rcx,qword ptr ss:[rsp+20]           | [rsp+20]:"asdfasdfasdf"
00007FF7C9A6102D | 0FB60401                 | movzx eax,byte ptr ds:[rcx+rax]         | eax에 입력된 현재 문자 저장(input[i])
00007FF7C9A61031 | 8B0C24                   | mov ecx,dword ptr ss:[rsp]              | ecx에 현재 인덱스 저장(i)
00007FF7C9A61034 | FFC1                     | inc ecx                                 | i++
00007FF7C9A61036 | 48:63C9                  | movsxd rcx,ecx                          | 
00007FF7C9A61039 | 48:8B5424 20             | mov rdx,qword ptr ss:[rsp+20]           | [rsp+20]:"asdfasdfasdf"
00007FF7C9A6103E | 0FB60C0A                 | movzx ecx,byte ptr ds:[rdx+rcx]         | ecx에 input[i+1]저장
00007FF7C9A61042 | 03C1                     | add eax,ecx                             | eax에 input[i] + input[i+1] 문자 저장
00007FF7C9A61044 | 48:630C24                | movsxd rcx,dword ptr ss:[rsp]           | rcx에 현재 인덱스 저장(i)
00007FF7C9A61048 | 48:8D15 B11F0000         | lea rdx,qword ptr ds:[7FF7C9A63000]     | 
00007FF7C9A6104F | 0FB60C0A                 | movzx ecx,byte ptr ds:[rdx+rcx]         | ecx에 저장된 암호문의 현재 인덱스값 저장
00007FF7C9A61053 | 3BC1                     | cmp eax,ecx                             | eax와 ecx비교
00007FF7C9A61055 | 74 04                    | je chall5.7FF7C9A6105B                  |
00007FF7C9A61057 | 33C0                     | xor eax,eax                             |
00007FF7C9A61059 | EB 07                    | jmp chall5.7FF7C9A61062                 |
00007FF7C9A6105B | EB B5                    | jmp chall5.7FF7C9A61012                 |
00007FF7C9A6105D | B8 01000000              | mov eax,1                               |
00007FF7C9A61062 | 48:83C4 18               | add rsp,18                              |
00007FF7C9A61066 | C3                       | ret                                     |

디버거를 따라가면서 확인해봤다.
input[i] + input[i+1] == str[i]가 되어야한다.
그렇다면 우리가 구해야 할 값은 input이고 str을 알고 있는데 i는 24까지 가며 덤프값을 통해 확인해봤을 때 str[24]는 00이다.
input[i] == str[i] - input[i+1]인데 최종 마지막에 들어가는 input[24]를 NULL로 생각하면서 한칸씩 내려오는 식으로 코드를 짜면 된다.

str = 'AD D8 CB CB 9D 97 CB C4 92 A1 D2 D7 D2 D6 A8 A5 DC C7 AD A3 A1 98 4C 00'
str = str.split(' ')
str.reverse()
res = ''
tmp = int(str[0], 16)

for i in range(0, 23): #0x18 == 24
    tmp = int(str[i + 1], 16) - tmp
    res += chr(tmp)


print(res[::-1])

 
사실 이번 문제를 4번문제와 동일하게 디컴파일된 함수를 그대로 파이썬 코드에 넣어서 풀었을 때 이상한 문자열이 출력됐었다. 왜냐하면 이번 문제는 문자열 하나 하나를 독립적으로 보는게 아닌 입력된 문자열에서 현재 인덱스와 다음 인덱스를 불러와서 계산을 하는 문제여서 역산이 필요한 문제였기 때문이다. 앞으로는 계산이 좀 더 필요할 것 같다.

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-7  (0) 2022.06.21
Dreamhack rev-basic-6  (0) 2022.06.01
Dreamhack rev-basic-4  (0) 2022.05.25
Dreamhack rev-basic-3  (0) 2022.05.24
Dreamhack rev-basic-2  (0) 2022.05.23

프로그램을 실행하면 앞문제들과 동일하다.
 

메인함수도 동일
비교함수를 IDA로 바로 디컴파일 해보자.

__int64 __fastcall sub_7FF6A5671000(__int64 a1)
{
  int i; // [rsp+0h] [rbp-18h]

  for ( i = 0; (unsigned __int64)i < 0x1C; ++i )
  {
    if ( ((unsigned __int8)(16 * *(_BYTE *)(a1 + i)) | ((int)*(unsigned __int8 *)(a1 + i) >> 4)) != byte_7FF6A5673000[i] )
      return 0i64;
  }
  return 1i64;
}

 
3번문제와 동일하게 a1은 덤프를 통해 가져올 수 있다.

00007FF6A5673000  24 27 13 C6 C6 13 16 E6 47 F5 26 96 47 F5 46 27  $'.ÆÆ..æGõ&.GõF'  
00007FF6A5673010  13 26 26 C6 56 F5 C3 C3 F5 E3 E3 00 00 00 00 00  .&&ÆVõÃÃõãã.....

 
 
파이썬에 저 계산식 그대로 코드를 작성해보자.

str = '24 27 13 C6 C6 13 16 E6 47 F5 26 96 47 F5 46 27 13 26 26 C6 56 F5 C3 C3 F5 E3 E3'
str = tuple(str.split(" "))
res = ''


for i in range(len(str)):
    res += chr(16*(int(str[i], 16)) | int(str[i], 16)>>4)

print(res)

어딘가 문제가 생긴 것 같다.
어셈블리어 코드를 자세히 살펴봤더니 디컴파일이 잘못됐는건지 계산식에서 [and ecx,F0]라는 구문이 생략된 것 같다.
ecx에 계산결과를 계속 저장하고 있었으니 위 파이썬 코드에서 &0xF0를 추가하면 해결될 것 같다.
 

str = '24 27 13 C6 C6 13 16 E6 47 F5 26 96 47 F5 46 27 13 26 26 C6 56 F5 C3 C3 F5 E3 E3'
str = tuple(str.split(" "))
res = ''


for i in range(len(str)):
    res += chr(16*(int(str[i], 16)) & 0xF0 | int(str[i], 16)>>4)

print(res)

중간에 계산식 오류를 찾느라 어셈블리어와 디컴파일된 코드를 비교하며 하나하나 따라가다보니 어셈블리어 계산식이 살짝씩 눈에 들어오기 시작했다. 목표는 디컴파일 없이 어셈블리어만 보고 직접 계산식을 세워보는 건데 가능할지는 잘 모르겠다.

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-6  (0) 2022.06.01
Dreamhack rev-basic-5  (0) 2022.05.28
Dreamhack rev-basic-3  (0) 2022.05.24
Dreamhack rev-basic-2  (0) 2022.05.23
Dreamhack rev-basic-1  (0) 2022.05.23

역시 실행부는 앞의 문제와 동일하다.
 

메인함수도 거의 동일하다.
7FF794AE1000함수로 들어가보자.

[cmp rax,18]를 보아 문자열의 길이가 0x18 = 24임을 알 수 있다.
rev-basic-2문제와 유사해 보이지만 덤프를 따라가보면

아스키 변환한 값이 이상한 것을 보니 헥스 코드를 사용하는 것 같다. 
 
어셈블리어 해석을 할 수 있어야 되는데 어색하니 IDA를 통해 비교함수부분을 디컴파일 해보면
 

조건식이 나온다. a1에 헥스코드가 들어가고 이를 파이썬을 통해 역산식으로 대입해보면

str = '49 60 67 74 63 67 42 66 80 78 69 69 7B 99 6D 88 68 94 9F 8D 4D A5 9D 45'
str = tuple(str.split(" "))
res = ''

for i in range(24):
    res += chr((int(str[i],16) - (2*i)) ^ i)

print(res)
위의 코드 실행 결과

어떠한 문자열이 나온다. 이를 chall3.exe에 입력하면

이제 어셈블리어를 해석해야되는 수준까지 왔다. 아직 어셈블리어가 익숙하지 않기 때문에 스터디 기간동안 어셈블리어를 공부해야될 필요성을 느끼게됐다

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-5  (0) 2022.05.28
Dreamhack rev-basic-4  (0) 2022.05.25
Dreamhack rev-basic-2  (0) 2022.05.23
Dreamhack rev-basic-1  (0) 2022.05.23
Dreamhack rev-basic-0  (0) 2022.05.23

0번과 1번문제와 또또 같다.
 

실행부도 똑같다.
 

입력: asdfasdfasdf

비교함수가 이전 문제와는 다르다.
문자와 직접 비교하는게 아니라 어딘가에 저장된 문자와 비교하는것 같다. 
[cmp rax,12]는 문자를 0x12번 반복하며 검사한다는 것을 알 수 있다.
실제 문자열 비교문은

cmp dword ptr ds:[rcx+rax*4],edx

이다.
 
이를 덤프에서 따라가면

그 주소에 문자가 저장되어 있다.
 

아직 튜토리얼느낌으로 그냥 디버거를 따라가기만 하면 풀 수 있는 것 같다.

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-5  (0) 2022.05.28
Dreamhack rev-basic-4  (0) 2022.05.25
Dreamhack rev-basic-3  (0) 2022.05.24
Dreamhack rev-basic-1  (0) 2022.05.23
Dreamhack rev-basic-0  (0) 2022.05.23

드림핵의 rev-basic-1 문제다.

rev-basic-0문제와 기본 입출력은 같다.
 

0번문제와 조건문도 같다.
 

문자열 비교함수에 들어가보니 문자열 통째로 비교한 0번문제와는 달리 한글자씩 비교한다.
 

아직까진 너무 쉽다

'Security > Reversing' 카테고리의 다른 글

Dreamhack rev-basic-5  (0) 2022.05.28
Dreamhack rev-basic-4  (0) 2022.05.25
Dreamhack rev-basic-3  (0) 2022.05.24
Dreamhack rev-basic-2  (0) 2022.05.23
Dreamhack rev-basic-0  (0) 2022.05.23

+ Recent posts