반응형

 


 

분석 환경

파일명 chall9.exe
MD5 58f5f02b39f67017beaf7ed3a6f94be7
SHA256 8c548aaaefc26e72ae7b964c7c837bf2d56152ae0dd0745fc42c4c6fe9529982
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, ida64.exe를 통해 분석을 진행하였다.

main 함수에서 Correct와 Wrong 문자열을 출력하는 부분을 결정하는 것으로 추정되는 sub_140001000 함수를 확인해보겠다.

 

# sub_140001000

해당 함수를 더블 클릭하여 단축키[F5]를 누르면 C언어로 디컴파일된다.

 

# sub_140001000  > line 6 - 8

디컴파일 한 코드를 보면 strlen 함수로 문자열의 길이를 저장한다.

문자열 길이에 1을 더하여 8로 나누었을 때 나머지가 0이 아니면 함수를 종료한다. (return 0i64 는 return 0을 의미)

(v3 + 1) % 8

 

# sub_140001000  > line 9 - 11

# sub_1400010A0

for문을 보면 사용자의 입력 값을 한글자씩 sub_1400010A0 함수의 인수로 사용하여 값을 전달하고 있다.

'sub_1400010A0' 함수를 더블 클릭하면 해당 함수의 위치로 이동할 수 있다.

 

'byte_140004020' 에는 아래와 같이 많은 데이터가 들어가 있었다.

이를 이용하여 flag 값을 구하는 부분을 찾은 것 같다.

__int64 __fastcall sub_1400010A0(unsigned __int8 *a1)
{
  __int64 result; // rax
  unsigned __int8 v2; // [rsp+0h] [rbp-48h]
  int j; // [rsp+4h] [rbp-44h]
  int i; // [rsp+8h] [rbp-40h]
  char v5[16]; // [rsp+10h] [rbp-38h] BYREF

  strcpy(v5, "I_am_KEY");
  result = *a1;
  v2 = *a1;
  for ( i = 0; i < 16; ++i )
  {
    for ( j = 0; j < 8; ++j )
    {
      v2 = __ROR1__(a1[((_BYTE)j + 1) & 7] + byte_140004020[(unsigned __int8)v5[j] ^ v2], 5);
      a1[((_BYTE)j + 1) & 7] = v2;
    }
    result = (unsigned int)(i + 1);
  }
  return result;
}

 

sub_1400010A0 함수에서 'I_am_KEY' 라는 key 값을 확인할 수 있다.

key는 'byte_140004020'에서 추출하는 위치를 의미하는 것 같다.

 

입력 값을 구하기 위한 python 코드는 아래와 같다.

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
 
 
key = 'I_am_KEY'
enc = '7E 7D 9A 8B 25 2D D5 3D 03 2B 38 98 27 9F 4F BC 2A 79 00 7D C4 2A 4F 58' #7FF628904000
 
#byte_140004020
tmp = '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 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79 E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08 BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16 CF 32 25 34 56 3D FF FF 30 CD DA CB A9 C2 00 00'
 
 
enc = enc.split(' ')
tmp = tmp.split(' ')
 
 
res = []
chars = []
 
for i in range(len(enc)): # enc 값을 10진수로 변환
    res.append(int(enc[i], 16))
 
for i in range(len(tmp)): # tmp 값을 10진수로 변환
    chars.append(int(tmp[i], 16))

 
for k in reversed(range(3)):
    for i in range(16):
        for j in reversed(range(8)):
            idx = (j + 1) & 7
            res[k * 8 + ((j + 1) & 7)] = (rol(res[k * 8 + ((j + 1) & 7)], 5) - chars[ord(key[j]) ^ res[k * 8 + (j & 7)]]) & 0xFF
            # v2 = __ROR1__(a1[((_BYTE)j + 1) & 7] + byte_140004020[(unsigned __int8)v5[j] ^ v2], 5);
            # a1[((_BYTE)j + 1) & 7] = v2;
 
flag = ''.join(chr(i) for i in res)
print(flag)

 

* 출력 결과

python 코드 실행 시, 출력되는 문자열은 'Reverse__your__brain_;)'이다.

 

# flag 값: DH{Reverse__your__brain_;)}

반응형
반응형

 


 

분석 환경

파일명 chall8.exe
MD5 295fd776691b5f2e0b97716d5b7b98c3
SHA256 8f34dde797dee225dffdd1b0ad8102a023654fbd5ad0798b4ee3b86714360b29
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력

 

[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx          	; [rsp+8] = rcx의 8바이트 값 (사용자 입력 값 저장)
sub rsp,18                            	; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0              	; [rsp] = 0 (index 초기 값 설정)
jmp chall8.7FF74E87101A


[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

; index 값 증가                            
mov eax,dword ptr ss:[rsp]            	; eax에 index 저장
inc eax                               	; index 1 증가
mov dword ptr ss:[rsp],eax            	; 
										  
; jmp chall8.7FF74E87101A로 실행 
movsxd rax,dword ptr ss:[rsp]         	; rax에 index 저장
cmp rax,15                            	; 반복 카운트 설정
jae chall8.7FF74E871055               	; rax >= 0x15이면 점프 → 종료

movsxd rax,dword ptr ss:[rsp]         	; rax = index 저장
mov rcx,qword ptr ss:[rsp+20]         	; rcx = 사용자의 입력 값
movzx eax,byte ptr ds:[rcx+rax]       	; eax = 사용자 입력 값 중 index 위치 값 1바이트
imul eax,eax,FB                       	; eax = eax * 0xFB
and eax,FF                            	; eax = eax and 0xFF
movsxd rcx,dword ptr ss:[rsp]         	; rcx = index
lea rdx,qword ptr ds:[7FF74E873000]   	; rdx = 암호화된 값이 저장된 메모리 주소 저장
movzx ecx,byte ptr ds:[rdx+rcx]       	; ecx = 암호화된 값 중 index 위치 값 1바이트 저장
cmp eax,ecx                           	; eax와 ecx 비교 

; 값이 일치하면 je 실행
je chall8.7FF74E871053

 

해당 문제는 입력 값을 1바이트씩 추출하여 0xFB와 곱하고 0xFF와 and 연산을 진행한다.

and 연산 결과 값과 연산값 1개를 알아도 나머지 연산 값을 찾기 어렵다

따라서, range 함수로 범위를 지정한 다음 대입하여 문자열을 찾아나가는 방식이 더 맞을 것 같다.

 

[ 파이썬 코드 동작 과정(range 대입) ]

  1. 0x7FF74E873000 주소의 hex 값을 리스트화 한다.
  2. 리스트된 값을 가지고 for문을 생성하여 리스트에 맞는 값을 찾아낸다.
  3. 이중 for문을 생성하여 범위 0x00, 0x80을 주고 반복문을 진행한다.
  4. 범위 0x00, 0x80 내에서 0xFB와 곱한 값에 0xFF와 and 연산을 하였을 때 리스트 내 값과 일치하는지 확인한다.
  5. 리스트 값을 문장으로 출력한다.
hex_values = [
    0xAC, 0xF3, 0x0C, 0x25, 0xA3, 0x10, 0xB7, 0x25,
    0x16, 0xC6, 0xB7, 0xBC, 0x07, 0x25, 0x02, 0xD5,
    0xC6, 0x11, 0x07, 0xC5, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]

results = []

for h_value in hex_values:
    for eax in range(0x00, 0x80):
        temp = (eax * 0xFB) & 0xFF # imul eax,eax,FB 과 and eax,FF 
        if temp == h_value:
            results.append(eax)
flag = ''.join(chr(i) for i in results)
print(flag) # Did_y0u_brute_force?

 

* 출력 결과

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.


[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.


[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'Did_y0u_brute_force?' 이다

 

# flag 값: DH{Did_y0u_brute_force?}

반응형
반응형

 


 

분석 환경

파일명 chall7.exe
MD5 6522d40b3d1104009a53fdbe233571a4
SHA256 3e72f1fe5dc47045cb4cab402eaa055c564a760fd8b89e08d5d64999f6cdc4d6
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력


[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx         			; [rsp+8] = rcx의 8바이트 값 (사용자 입력 값 저장)
sub rsp,18                           				; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0             			; [rsp] = 0
jmp chall7.7FF7BC6E101A

 

[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

; index 값 증가                            
mov eax,dword ptr ss:[rsp]           			; eax = index 저장
inc eax                              	
mov dword ptr ss:[rsp],eax           			; [rsp] = index + 1 값 저장(eax)
										  
; jmp chall7.7FF7BC6E101A로 실행           
movsxd rax,dword ptr ss:[rsp]        			; rax = index 저장
cmp rax,1F                           				; cmp를 두 값의 차를 이용하여 비교 (CF = 0, ZF = 0)
jae chall7.7FF7BC6E1065              			; rax >= 0x1F이면 점프

mov eax,dword ptr ss:[rsp]           			; eax = index 저장
and eax,7                            				; eax = index를 7로 and 연산
									; 8비트 기준으로 회전이 일어나기 때문에, 7비트가 넘어가면 안됨
movsxd rcx,dword ptr ss:[rsp]        	
mov qword ptr ss:[rsp+8],rcx         			; [rsp+8] = rcx = index
mov rdx,qword ptr ss:[rsp+20]        			; rdx = [rsp+20] = 사용자 입력 값
movzx ecx,al                         				; ecx = al = eax의 하위 8비트 = and 연산된 eax의 하위 8비트
mov rax,qword ptr ss:[rsp+8]         			; rax = index
movzx eax,byte ptr ds:[rdx+rax]      			; eax = 사용자 입력 값에서의 index 번째 값
rol al,cl                            					; al을 cl 만큼 왼쪽으로 회전 = eax의 하위 8비트 값을 and 연산된 값만큼 왼쪽으로 회전
movzx eax,al                         				; eax = al (회전한 값)
xor eax,dword ptr ss:[rsp]           			; eax = eax와 index와 xor
movsxd rcx,dword ptr ss:[rsp]        			; rcx = [rsp]에 저장된 4바이트 값
lea rdx,qword ptr ds:[7FF7BC6E3000]  		; rdx = 0x7FF7BC6E3000  // 암호화된 값이 저장된 메모리 주소
movzx ecx,byte ptr ds:[rdx+rcx]      			; ecx = 암호화된 값의 index 번째 값
cmp eax,ecx                          				; eax, ecx 값 비교
										  
; 값이 일치하면 je 실행                      
je chall7.7FF7BC6E1063

 

입력값을 구하는 부분은 python으로 코드를 작성하여 계산하고자 한다.

 

[ 파이썬 코드 동작 과정(역순) ]

  1. 메모리에 저장된 암호화된 값과 index를 xor
  2. and 7 연산된 값 구하기
    and 연산된 값을 사용하여 회전이 이루어지기 때문이다.
  3. 2번에서 구한 값을 활용하여 왼쪽으로 회전하는 코드 작성

// (2**8 -1)은 8비트에서 가능한 최대 값을 의미(그 이상의 값은 버림)

 

* python 코드

hex_values = [
    0x52, 0xDF, 0xB3, 0x60, 0xF1, 0x8B, 0x1C, 0xB5, 0x57, 0xD1, 0x9F, 0x38, 0x4B, 0x29, 0xD9, 0x26,
    0x7F, 0xC9, 0xA3, 0xE9, 0x53, 0x18, 0x4F, 0xB8, 0x6A, 0xCB, 0x87, 0x58, 0x5B, 0x39, 0x1E
]
for i, h_value in enumerate(hex_values):
    temp = h_value ^ i
    i = i & 7
    temp = ((temp >> i) | (temp << (8 - i))) & (2**8 - 1)
    print(chr(temp), end='') # Roll_the_left!_Roll_the_right!
print()

 

* 출력 결과

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.


[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.

 

[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'Roll_the_left!_Roll_the_right!' 이다

 

# flag 값: DH{Roll_the_left!_Roll_the_right!}

반응형
반응형

 


 

분석 환경

파일명 chall6.exe
MD5 43ba4532e296dadfe99492a7cdb3eaf2
SHA256 77e584ad4d704dab36cef30ccc1dddcf963ec62d86ef7857744af04f5d34e3af
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력

 

[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; * 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx        			; [rsp+8] = rcx의 8바이트 값 (사용자 입력 값 저장)
sub rsp,18                          				; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0            			; [rsp] = 0
jmp chall6.7FF7723E101A

 

[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

; index 값 증가
mov eax,dword ptr ss:[rsp]          			; eax = [rsp]에 저장된 4바이트 값
inc eax                             					; eax = eax + 1
mov dword ptr ss:[rsp],eax          			; [rsp] = eax 4바이트 값 (index 값 저장)

; jmp chall6.7FF7723E101A로 실행
movsxd rax,dword ptr ss:[rsp]       			; rax = [rsp]에 저장된 4바이트 값
cmp rax,12                          				; cmp를 두 값의 차를 이용하여 비교 (CF = 0, ZF = 0)
jae chall6.7FF7723E1055             			; rax >= 0x12이면 점프

movsxd rax,dword ptr ss:[rsp]       			; rax = [rsp]에 저장된 4바이트 값 (index)
mov rcx,qword ptr ss:[rsp+20]       			; rcx = [rsp+20]에 저장된 8바이트 값(사용자 입력 값)
movzx eax,byte ptr ds:[rcx+rax]     			; eax = [rcx]에서 index 위치의 1바이트 값
lea rcx,qword ptr ds:[7FF7723E3020] 			; rcx = 0x7FF7723E3020
movzx eax,byte ptr ds:[rcx+rax]     			; eax = [rcx]에서 index 위치의 1바이트 값
movsxd rcx,dword ptr ss:[rsp]       			; rcx = [rsp]에 저장된 4바이트 값 (index)
lea rdx,qword ptr ds:[7FF7723E3000] 			; rdx = 0x7FF7723E3000  // 암호화된 값이 저장된 메모리 주소
movzx ecx,byte ptr ds:[rdx+rcx]     			; ecx = [rdx]에서 index 위치의 1바이트 값
cmp eax,ecx                         				; eax와 ecx 비교

; 값이 일치하면 je 실행
je chall6.7FF7723E1053

 

입력값을 구하는 부분은 python으로 코드를 작성하여 계산하고자 한다.

(입력 값의 n 번째 문자의 int 값 + 0x7FF7723E3020 주소)에 저장된 값  =  (0x7FF7723E3000의 n 번째에 저장된 값)

// 편의상 0x7FF7723E3020 → 20로 표기

 

[ 파이썬 코드 동작 과정 ]

입력 값을 구하는 방법

  1. 00~2F 값과 동일한 값을 20~AF에서 찾는다
  2. 해당 값의 주소를 구한 뒤 20을 뺀다.
    입력값과 더해지는 메모리 주소는 20부터 시작하기 때문이다.
  3.  

* python 코드

  1. hex 값을 모두 리스트화 한다.
  2. 3000 리스트에 있는 값이 3020 리스트에서 어느 위치에 존재하는지 index 값을 추출한다. (.index 사용)
  3. 리스트의 index가 int 값이므로 chr 함수를 사용하여 변환한다
values3000 = [
    0x00, 0x4D, 0x51, 0x50, 0xEF, 0xFB, 0xC3, 0xCF, 0x92, 0x45,
    0x4D, 0xCF, 0xF5, 0x04, 0x40, 0x50, 0x43, 0x63
]

values3020 = [
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73
]

results = []

for n in values3000:
    index = values3020.index(n) # 3020 함수 내 특정 값이 어느 위치에 존재하는지 확인
    print(chr(index), end='') # Replac3_the_w0rld
print()

 

* 출력 결과

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.

 

[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.


[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'Replac3_the_w0rld' 이다

 

# flag 값: DH{Replac3_the_w0rld}

반응형
반응형

 


 

분석 환경

파일명 chall5.exe
MD5 e24fdbc9661ad6237fa40e261d74c835
SHA256 34dc330d075222650bb33902504edd4c195b8b3496b70478c505a45bdc4b7256
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력


[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; * 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx        			; [rsp+8] = rcx의 8바이트 값 (사용자 입력 값 저장)
sub rsp,18                          				; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0            			; [rsp] = 0
jmp chall5.7FF6A995101A


[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

; index 값 증가
mov eax,dword ptr ss:[rsp]          			; eax = [rsp]에 저장된 4바이트 값
inc eax                             					; eax = eax + 1
mov dword ptr ss:[rsp],eax          			; [rsp] = eax 4바이트 값 (index 값 저장)

; jmp chall5.7FF6A995101A로 실행
movsxd rax,dword ptr ss:[rsp]       			; rax = [rsp]에 저장된 4바이트 값
cmp rax,18                          				; cmp를 두 값의 차를 이용하여 비교 (CF = 0, ZF = 0)
jae chall5.7FF6A995105D             			; rax >= 0x18이면 점프

movsxd rax,dword ptr ss:[rsp]       			; rax = [rsp]에 저장된 4바이트 값 (index)
mov rcx,qword ptr ss:[rsp+20]       			; rcx = [rsp+20]에 저장된 8바이트 값(사용자 입력 값)
movzx eax,byte ptr ds:[rcx+rax]     			; eax = [rcx]에서 index 위치의 1바이트 값
mov ecx,dword ptr ss:[rsp]          			; ecx = [rsp]에 저장된 4바이트 값 (index)
inc ecx                             					; ecx = ecx + 1 (index+1)
movsxd rcx,ecx                      				; rcx = ecx (index+1)
mov rdx,qword ptr ss:[rsp+20]       			; rdx = [rsp+20]에 저장된 8바이트 값(사용자 입력 값)
movzx ecx,byte ptr ds:[rdx+rcx]     			; ecx = [rcx]에서 (index+1) 위치의 1바이트 값
add eax,ecx                         				; eax = eax + ecx (index 위치 값 + index+1 위치 값)
movsxd rcx,dword ptr ss:[rsp]       			; rcx = [rsp]에 저장된 4바이트 값 (index)
lea rdx,qword ptr ds:[7FF6A9953000] 			; rdx = 0x7FF6A9953000  // 암호화된 값이 저장된 메모리 주소
movzx ecx,byte ptr ds:[rdx+rcx]     			; ecx = [rdx]에서 index 위치의 1바이트 값
cmp eax,ecx                         				; eax와  ecx 비교

; 값이 일치하면 je 실행
je chall5.7FF6A995105B

 

해당 부분은 python으로 코드를 작성하여 계산하고자 한다.

index 와 index+1 의 값을 더하여 이를 결과 값과 비교하는 코드를 작성해보자

 

[ 파이썬 코드 동작 과정(역순) ]

* first_attempt() 함수

  1. range 함수를 통해 첫번째 값의 모든 경우의 수를 리스트로 생성
  2. 리스트의 값을 가지고 계산 진행 (조건: 값이 문자로 변환되는 값이여야하므로 범위는 0x20 ~ 0x7E로 지정)
  3. 결과의 길이가 23이며, 마지막 값은 null 종료 문자와 계산되므로 0x4C를 가지는 값을 출력한다.
def first_attempt():
    num1_list = []

    for num1 in range(0x20, 0x7E):
        num2 = hex_values[0] - num1
        if 0x20 <= num2 <= 0x7E:
            num1_list.append(num1)

    for n in num1_list:
        results = []
        results.append(n)
        for i, h_value in enumerate(hex_values): # index과 값을 동시에 출력
            if i == 0:
                temp = h_value - n
            else:
                temp = h_value - results[i]
            if 0x20 <= temp <= 0x7E: # 문자로 변환될 수 있도록 범위 설정
                    results.append(temp)
            else:
                if len(results) == 23:
                    if results[22] == 0x4C:# 마지막 값
                        flag = ''.join(chr(i) for i in results)
                        print(flag) # All_l1fe_3nds_w1th_NULL
                break    

 

* second_attempt() 함수
- 첫번째 시도로 문제를 풀고, 거꾸로 계산하는 방법이 있다는 것을 알게되어 새로 코드를 작성해보았다.

  1. null 문자를 결과 배열에 추가한다.
  2. 암호화된 값에서 null 문자를 빼서 배열의 두번째에 값을 추가한다.
  3. 암호화된 값에서 index-1 번째 문자를 빼서 배열의 index 번째에 값을 추가한다.
  4. 3을 반복한다.
  5. 결과값이 거꾸로 저장되었으므로, reversed 함수를 사용하여 다시 되돌린 상태로 문장을 출력한다.
def second_attempt():
    results = []
    for i, h_value in enumerate(reversed(hex_values)):
        if i == 0:
            results.append(h_value)
        else:
            results.append(h_value - results[i-1])
    flag = ''.join(chr(i) for i in reversed(results))
    print(flag) # All_l1fe_3nds_w1th_NULL

 

* 출력 결과

 

* python 전체 코드

hex_values = [
    0xAD, 0xD8, 0xCB, 0xCB, 0x9D, 0x97, 0xCB, 0xC4, 0x92, 0xA1,
    0xD2, 0xD7, 0xD2, 0xD6, 0xA8, 0xA5, 0xDC, 0xC7, 0xAD, 0xA3,
    0xA1, 0x98, 0x4C
]

results = []

def first_attempt():
    num1_list = []

    for num1 in range(0x20, 0x7E):
        num2 = hex_values[0] - num1
        if 0x20 <= num2 <= 0x7E:
            num1_list.append(num1)

    for n in num1_list:
        results = []
        results.append(n)
        for i, h_value in enumerate(hex_values): # index과 값을 동시에 출력
            if i == 0:
                temp = h_value - n
            else:
                temp = h_value - results[i]
            if 0x20 <= temp <= 0x7E: # 문자로 변환될 수 있도록 범위 설정
                    results.append(temp)
            else:
                if len(results) == 23:
                    if results[22] == 0x4C:# 마지막 값
                        flag = ''.join(chr(i) for i in results)
                        print(flag) # All_l1fe_3nds_w1th_NULL
                break    

def second_attempt():
    results = []
    for i, h_value in enumerate(reversed(hex_values)):
        if i == 0:
            results.append(h_value)
        else:
            results.append(h_value - results[i-1])
    flag = ''.join(chr(i) for i in reversed(results))
    print(flag) # All_l1fe_3nds_w1th_NULL

first_attempt()
second_attempt()

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.

 

[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.

 

[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'All_l1fe_3nds_w1th_NULL' 이다

 

# flag 값: DH{All_l1fe_3nds_w1th_NULL}

반응형
반응형

 


 

분석 환경

파일명 chall4.exe
MD5 f62b70b358832bfad5e97187d45b451b
SHA256 1eaedab0ad62e82ec277c55e016aaebf88a0dcb663c55d38e4f9459a38d398d5
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력


[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; * 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx         			; [rsp+8] = rcx의 8바이트 값 (사용자 입력 값 저장)
sub rsp,18                           				; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0             			; [rsp] = 0
jmp chall4.7FF745E0101A


[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

; index 값 증가
mov eax,dword ptr ss:[rsp]           			; eax = [rsp]에 저장된 4바이트 값
inc eax                              				; eax = eax + 1
mov dword ptr ss:[rsp],eax           			; [rsp] = eax 4바이트 값 (index 값 저장)

; jmp chall4.7FF745E0101A로 실행
movsxd rax,dword ptr ss:[rsp]        			; rax = [rsp]에 저장된 4바이트 값
cmp rax,1C                           				; cmp를 두 값의 차를 이용하여 비교 (CF = 0, ZF = 0)
jae chall4.7FF745E01065              			; rax >= 0x1C이면 점프

movsxd rax,dword ptr ss:[rsp]        			; rax = [rsp]에 저장된 4바이트 값 (index)
mov rcx,qword ptr ss:[rsp+20]        			; rcx = [rsp+20]에 저장된 8바이트 값(사용자 입력 값)
movzx eax,byte ptr ds:[rcx+rax]      			; eax = [rcx]에서 index 위치의 1바이트 값
sar eax,4                            				; eax = eax의 비트를 오른쪽으로 4만큼 이동
movsxd rcx,dword ptr ss:[rsp]        			; rcx = [rsp]에 저장된 4바이트 값 (index)
mov rdx,qword ptr ss:[rsp+20]        			; rdx = [rsp+20]에 저장된 8바이트 값(사용자 입력 값)
movzx ecx,byte ptr ds:[rdx+rcx]      			; ecx = [rcx]에서 index 위치의 1바이트 값
shl ecx,4                            				; ecx = ecx의 비트를 왼쪽으로 4만큼 이동
and ecx,F0                           				; ecx = ecx와 0xF0의 and 연산 결과 값
or eax,ecx                           				; eax = eax와 ecx의 or 연산 결과 값
movsxd rcx,dword ptr ss:[rsp]        			; rcx = [rsp]에 저장된 4바이트 값 (index)
lea rdx,qword ptr ds:[7FF745E03000]  		; rdx = 0x7FF6F5893000  // 암호화된 값이 저장된 메모리 주소
movzx ecx,byte ptr ds:[rdx+rcx]      			; ecx = [rdx]에서 index 위치의 1바이트 값
cmp eax,ecx                          				; eax와 ecx 비교

; 값이 일치하면 je 실행
je chall4.7FF745E01063

 

해당 부분은 python으로 코드를 작성하여 계산하고자 한다.

 

[ 파이썬 코드 동작 과정(range로 대입) ]

입력 값은 sar, shl 명령어를 사용하여 비트 이동을 진행하니, python 코드의 비트 이동 연산자 '>>', '<<'를 이용한다.

암호화된 값인 0x7FF745E03000에 위치한 Hex 값을 파이썬 리스트로 생성한다.

# 메모리 0x7FF745E03000에 위치한 Hex 값(암호화된 값)
hex_values = [
    0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5, 0x26, 0x96,
    0x47, 0xF5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xC6, 0x56, 0xF5, 0xC3, 0xC3,
    0xF5, 0xE3, 0xE3
]

 

range 함수를 사용하여 0x00 부터 0x100 범위의 값을 모두 대입하여 입력 값을 구한다.

sar eax,4 는 오른쪽 비트 이동 연산자인 '>>'를 사용하고 shl ecx,4는 왼쪽 비트 이동 연산자인 '<<' 를 사용한다.

and ecx,F0 는 비트 AND 연산자인 '&'를 사용한다.

results = []

for h_value in hex_values:
    for num in range(0x00, 0x100):
        # sar, shl, and, or 연산 과정
        sar = (num >> 4)  # sar eax,4
        shl_and = (num << 4) & 0xF0  # shl ecx,4 / and ecx,F0
        sar_shl_and_or = sar | shl_and  # or eax,ecx

        # 결과 비교
        if sar_shl_and_or == h_value: # ex) 연산 값과 0x24 가 일치하는 경우
            results.append(num)

 

results 리스트에 저장된 값을 문장으로 출력하기 위해 join 함수를 사용한다.

ex) list = ['a','b','c'] → ''.join(i for i in list)

flag = ''.join(chr(i) for i in results) # 리스트 내 값을 ASCII 값으로 변환한 뒤 문장으로 join
print(flag) # Br1ll1ant_bit_dr1bble_<<_>>

 

* 출력 결과

 

* python 전체 코드

# 메모리 0x7FF745E03000에 위치한 Hex 값(비교 값)
hex_values = [
    0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5, 0x26, 0x96,
    0x47, 0xF5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xC6, 0x56, 0xF5, 0xC3, 0xC3,
    0xF5, 0xE3, 0xE3
]

results = []

for h_value in hex_values:
    for num in range(0x00, 0x100):
        # sar, shl, and, or 연산 과정
        sar = (num >> 4)  # sar eax,4
        shl_and = (num << 4) & 0xF0  # shl ecx,4 / and ecx,F0
        sar_shl_and_or = sar | shl_and  # or eax,ecx

        # 결과 비교
        if sar_shl_and_or == h_value: # ex) 연산 값과 0x24 가 일치하는 경우
            results.append(num)

flag = ''.join(chr(i) for i in results) # 리스트 내 값을 ASCII 값으로 변환한 뒤 문장으로 join
print(flag) # Br1ll1ant_bit_dr1bble_<<_>>

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.


[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.

 

[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'Br1ll1ant_bit_dr1bble_<<_>>' 이다

 

# flag 값: DH{Br1ll1ant_bit_dr1bble_<<_>>}

반응형
반응형

 


 

분석 환경

파일명 chall3.exe
MD5 00210315dbc5c856c1da95639fd3a9cd
SHA256 40d27927786305029b8e066b22314cb47c815b7ad8229fffc35245473d494f35
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력

 

[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; * 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx         			; [rsp+8]에 rcx의 8바이트 값 저장 (사용자 입력 값 저장)
sub rsp,18                           				; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0             			; [rsp]에 0 저장
jmp chall3.7FF61FA4101A

 

[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

 

cmp rax,18는 반복문에서의 조건문에 해당한다.

rax - 0x18(24)의 결과가 크거나 같으면 jae 를 실행하여 ret으로 함수를 탈출한다.

 

실제 flag값은 23자리 이지만, null 종료 문자(\0, 0x00)까지 확인하여 총 24번 반복문을 실행한다.

 

; index 값 증가
mov eax,dword ptr ss:[rsp]            			; eax = [rsp]에 저장된 4바이트 값
inc eax                               				; eax = eax + 1
mov dword ptr ss:[rsp],eax            			; [rsp] = eax 4바이트 값 (index 값 저장)

; jmp chall3.7FF61FA4101A로 실행
movsxd rax,dword ptr ss:[rsp]        			; rax = [rsp]에 저장된 4바이트 값
cmp rax,18                           				; cmp를 두 값의 차를 이용하여 비교 (CF = 0, ZF = 0)
jae chall3.7FF61FA41053              			; rax >= 0x18이면 점프

movsxd rax,dword ptr ss:[rsp]        			; rax = [rsp]에 저장된 4바이트 값 (index)
lea rcx,qword ptr ds:[7FF61FA43000]  			; rcx = 0x7FF6F5893000  // 암호화된 값이 저장된 메모리 주소
movzx eax,byte ptr ds:[rcx+rax]      			; eax = [rcx]에서 index 위치의 1바이트 값
movsxd rcx,dword ptr ss:[rsp]        			; rcx = [rsp]에 저장된 4바이트 값(index)
mov rdx,qword ptr ss:[rsp+20]        			; rdx = [rsp+20]에 저장된 8바이트 값(사용자 입력 값)
movzx ecx,byte ptr ds:[rdx+rcx]      			; ecx = [rdx]에서 index 위치의 1바이트 값
xor ecx,dword ptr ss:[rsp]           			; ecx = ecx와 [rsp] 값의 xor 결과
mov edx,dword ptr ss:[rsp]           			; edx = [rsp]에 저장된 4바이트 값(index)
lea ecx,qword ptr ds:[rcx+rdx*2]     			; ecx = 'rcx+rdx*2' (xor 결과 + index*2)
cmp eax,ecx                          				; eax과 [rcx+rdx*2] 비교

; 값이 일치하면 je 실행
je chall3.7FF61FA41051               			; index를 1 증가하는 부분 실행

 

입력 값을 계산하기 위해 역으로 계산을 해보자

먼저 암호화된 값이 저장된 0x7FF61FA43000 주소에 저장된 hex 값(eax)을 불러온다.

49 60 67 74 63 67 42 66 80 78 69 69 7B 99 6D 88 68 94 9F 8D 4D A5 9D 45

 

eax 값에 index*2를 빼고 index와 xor하면 flag 값을 확인할 수 있다.

  • ASCII 문자열: I_am_X0_xo_Xor_eXcit1ng

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.

 

[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.

 

[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'I_am_X0_xo_Xor_eXcit1ng' 이다.

 

# flag 값: DH{I_am_X0_xo_Xor_eXcit1ng}

반응형
반응형

 


 

분석 환경

파일명 chall2.exe
MD5 40b22773a82f83fb12b4c68f2607ba14
SHA256 a2141816404d8d73ddaab44fa3d96dcb27cd7f2c0845b3b36af78e78e450f389
분석 환경 Window 10 Pro, 64비트 운영 체제

 

 

분석

1) 파일 헤더 확인

문제 파일은 Visual Studio 2008로 제작된 PE 파일이며, console 환경에서 64bit로 동작하는 것을 확인할 수 있다.

 

[ PE 구조 값 확인 ]

  • first-bytes-text: MZ
  • tooling: Visual Studio 2008
  • file-type: executable
  • cpu: 64-bit
  • subsystem: console

 

2) 파일 실행

파일을 더블 클릭하여 실행시키면, Input 값을 입력하자마자 종료된다.

cmd 에서 실행시켜, 파일의 결과 값을 확인한다.

 

3) 바이너리 분석

문제 파일은 64bit에서 동작하는 프로그램이므로, x64dbg를 통해 분석을 진행한다.

 

[ 참조된 문자열 확인 ]

'마우스 우클릭 > 다음을 찾기 > 모든 모듈 > 문자열 참조' 기능을 통해 파일이 출력하는 문자열을 목록으로 확인한다.

 

참조된 문자열 목록을 통해 'Input, Correct, Wrong' 문자열을 확인하였고, 해당 위치로 이동해본다.

 

문자열이 참조된 부분을 보면, test, je 명령어의 실행 결과에 따라 Correct/Wrong 문자열이 출력되는 것을 확인할 수 있다.

 

test, je 명령어 윗줄의 호출되는(call) 함수에서 문자열을 비교하는 함수가 포함되어 있을 것으로 추정되며, 더블 클릭 또는 BP를 설정하여 해당 함수를 확인해본다.

// eax가 0이면 je가 실행되어 Wrong 출력

// eax가 0이 아니면 je가 실행되지 않고 Correct 출력

 

[ 문자열 비교 과정 확인 ]

 

rcx는 사용자 입력 값이고, [rsp]는 index 값을 저장한다.

사용자 입력 값을 스택에 저장하고, index 값을 설정하는 동작을 한다.

; * 대괄호 '[address]'는 메모리 주소(address)에 저장된 값을 의미한다.
mov qword ptr ss:[rsp+8],rcx          			; [rsp+8]에 rcx의 8바이트 값 저장 (사용자 입력 값 저장)
sub rsp,18                            				; rsp = rsp - 18 (스택 최상단 주소 변경)
mov dword ptr ss:[rsp],0              			; [rsp]에 0 저장
jmp chall2.7FF6F589101A

 

[ 사용자 입력 값과 메모리에 저장된 값을 비교하는 코드 확인 ]

 

cmp rax,12는 반복문에서의 조건문에 해당한다.

rax - 0x12(18)의 결과가 크거나 같으면 jae 를 실행하여 ret으로 함수를 탈출한다.

 

실제 flag값은 17자리 이지만, null 종료 문자(\0, 0x00)까지 확인하여 총 18번 반복문을 실행한다.

#1번 문제는 byte 단위의 비교를 모두 풀어놓았다면 #2번 문제반복문을 사용하여 코드의 길이를 줄였다.

 

; index 값 증가
mov eax,dword ptr ss:[rsp]            			; eax에 [rsp] 값을 저장
inc eax                               				; eax 1 증가
mov dword ptr ss:[rsp],eax            			; [rsp]에 eax의 4바이트 값 저장 (index 값 저장)

; jmp chall2.7FF6F589101A로 실행
movsxd rax,dword ptr ss:[rsp]         			; rax에 [rsp]에 저장된 4바이트 값 저장
cmp rax,12                            				; cmp를 두 값의 차를 이용하여 비교
                                        				; index 값을 사용하여 반복문이 종료하는 범위를 설정
jae chall2.7FF6F5891048               			; rax >= 0x12이면 점프 (CF = 0 or ZF = 0)

movsxd rax,dword ptr ss:[rsp]         			; rax에 [rsp]에 저장된 4바이트 값 저장
lea rcx,qword ptr ds:[7FF6F5893000]   		; rcx = 0x7FF6F5893000  // flag 값이 저장된 메모리 주소
movsxd rdx,dword ptr ss:[rsp]         			; [rsp]는 index로 사용됨
mov r8,qword ptr ss:[rsp+20]          			; r8에 사용자의 입력값 저장
movzx edx,byte ptr ds:[r8+rdx]        			; 입력 값의 index 위치의 값을 edx에 저장
cmp dword ptr ds:[rcx+rax*4],edx      		; [rcx+index*4]의 값과 edx 값을 비교
									; rcx는 flag 값이 저장된 메모리 주소이다.
									; flag 값은 4바이트에 1글자씩 저장되어 있다.

; 값이 일치하면 je 실행
je chall2.7FF6F5891046                			; index를 1 증가하는 부분 실행

 

덤프 창을 보면 flag 값 사이에 공백이 존재하여 [rcx+rax*4] 방식으로 문자열을 불러와 비교하는 것을 확인할 수 있다.

 

[ 문자열 일치 시, eax 값 설정 → 1 ]

mov eax,1를 통해 eax에 1을 저장한 뒤, 스택의 최상단 주소를 변경한다.

 

[ 문자열 미일치 시, eax 값 설정 → 0 ]

xor eax,eax를 통해 eax를 0으로 초기화한다.

 

[ 문자열 비교 부분 탈출 ]

eax가 0이면(문자열이 일치하지 않으면) je가 실행되어 Wrong이 출력되고,

eax가 0이 아니면(문자열이 일치하면) je되지 않아 Correct가 출력된다.

 

따라서, Correct가 나오는 문자열은 'Comp4re_the_arr4y' 이다.

 

# flag 값: DH{Comp4re_the_arr4y}

반응형

+ Recent posts