본문 바로가기

2. Reversing (리버싱)/1) Write UP

[2023.05.06] 씽씽이 활동보고

활용강의: 리버싱 이 정도는 알아야지 - 섹션 3

 

1. 예제분석 #01 - 도전과제

 

- Sample 02.exe 실행시켰을 때 command 창이 뜨지만 아무 동작 없이 실행이 종료된다.

 

 

!!도전 과제!!

  => Sample 02.exe“ If Code!! “ 문자열을 출력하도록 만들기.

  => 전체적인 동작을 이해하고, 문자 해결을 위해 어디를 어떻게 고쳤는지 파악하기.

  => 힌트 : “if 조건문"을 사용해서 만들었습니다.

 

 

2. 예제분석 #01 - 코드분석_Level.1 | 흐름 파악하기

 

▶Sample 02.exe 예상 동작흐름

- Sample 02.exe“if 조건문을 사용해서 만들어졌으므로 ‘if 에 들어가는 조건이 일치하느냐에 따라 문자열 출력 여부가 결정될 것 같다.

 

▶문제 접근 방향

1. 조건 확인 지점을 찾는다.

2. 조건이 맞지 않는 원인을 파악한다.

3. 문제를 해결하고, 확인한다.

 

 

3. 예제분석 #01 - 코드분석_Level.2 | 원인 도출

 

▲ Sampe02.exe 파일을 디버거에 올리고 메인 함수로 이동한다. 한 줄 씩 실행하다 보면 00401048 주소에서 점프를 뛰고 3초 뒤에 메인 함수가 종료되는 것을 확인할 수 있다.

→ 00401048 ~ 00401076 사이에 문자 출력 코드가 있을 것으로 예상할 수 있다.

 

점프는 제로플래그를 확인한 후 점프를 뛴다. 여기서는 먼저 제로플래그가 0이기 때문에 점프를 뛴다. 따라서 Z(제로플래그) 값을 1로 수정해준다.

 

그리고 다시 코드를 한 줄 씩 실행하다 보면 플래그 값이 0으로 바뀌는 순간이 있다. 그때 그 코드를 확인한다.

→ 00401040CMP 비교 코드에서 플래그 값이 변경된 것을 확인

 

▲ 00401040에 있는 명령어를 읽어본다.

CMP WORD PTR SS:[ESP+4], 7D5

→ ESP+4에 있는 두 바이트 값과 7D5를 비교하는 코드

 

▲ CMP 비교 코드는 값이 같을 때 Z(제로 플래그)에 1을 넣는다. 현재 제로플래그가 0이니까 두 값이 다른 것을 알아챌 수 있다.

값을 같게 만들어서 문제를 해결

 

▲ ESP+4에서 HEXD5 07로 바꾼다.

그러나 밑에 0040104A00401052에도 비교 코드가 있는 것을 확인할 수 있다. 이것도 직접 데이터를 수정한다.

→ ESP+2에서 HEX를 00 00으로 변경, ESP+6에서 HEX를 20 00으로 변경한다.

▲그러면 점프하지 않고 밑의 코드들로 잘 진행됨. 

 

▲ 00401064이 문자열 호출 코드로 예상된다.

실행 결과 문자열 ‘If Code!!’가 출력됨을 확인 가능

 

*Push 명령어 : 인자를 스택에 쌓는다 (32비트 CPU 기준)

 

 

4. 예제분석 #01 – 코드분석_Level.3 | 문제 해결

 

- 문제가 왜 발생하고 어떻게 해결해야 하는지 고민해본다.

▲ Sample02.exe 파일은 총 3군데에서 값을 비교 진행한 것을 앞에서 확인할 수 있었다.

비교 값이 일치하지 않았기 때문에 문자열이 출력되지 않음

첫 번째 비교 코드가 있는 주소로 이동해 00401040 주소의 ESP+4가 무엇을 가리키는지 확인한다.

→ 0012FE38

두 번째 비교 코드가 있는 주소로 이동해 0040104A 주소의 ESP+2가 무엇을 가리키는지 확인한다.

→ 0012FE3A

세 번째 비교 코드가 있는 주소로 이동해 00401052 주소의 ESP+6이 무엇을 가리키는지 확인한다.

→ 0012FE3E

▲ 0040103A 지점에서 ‘GetLocalTime’을 호출한다. 여기에 0012FE38이 인자값으로 들어간다. 그리고 실행하면 값이 들어온다.

GetLocalTime Function
- API 정의
Retrieves the current local date and time
To retrieve the current date and time in Coordinated Universal Time (UTC) format, use the GetSystemTime function.

▲ GetLocalTime()은 현재의 날짜와 시간 정보를 얻을 수 있는 API이다. 인자로 들어온 LPSYSTEMTIME에 시간 정보가 들어온다. SYSTEMTIME은 연, , 요일같이 다양한 시간 정보를 담을 수 있게 구성되어 있다. API를 호출하고 나면 구조체에 시간 정보가 들어가는 것이다.

다시 코드를 살펴보자. 0X7D5 = 2005. 지금 현재 2023년이기 때문에 아무리 Local time을 호출해도 2005가 들어올 수는 없다. 그래서 일치하지 않았던 것이다.

다시 돌아가서 확인하면 7D5, 0, 20 순서대로 연, , 일 정보이다.

문제 해결 방안
- GetLocalTime() 함수 호출 결과, 입력되는 시간 정보를 수정한다.
- 대조군에 해당하는 값인 0x07D5, 0x00, 0x20을 적절히 수정한다.
- CMP 명령어 실행 결과, 0으로 설정되는 ZF 레지스터 값을 1로 수정한다.
- 비교 결과에 대한 실행 코드인 ‘JNZ 조건 점프를 반대로 동작하게 만든다.

 

 

5. 예제분석 #02 - 도전과제

 

- sample03.exe 파일을 실행했을 때 실행 화면은 뜨지만 어떤 동작인지는 모른다.

- Hex 값을 확인하면 For Code! 문자열이 포함되어 있는 것을 확인할 수 있다.

- 따라서 문자를 출력하는 방식에 문제가 있을 것으로 추정하고 문제에 접근한다. (아마 메모리에 문자가 있을 것 같다.)

 

 

!!도전과제!!

  => Sample 03.exe“For Code!!” 문자열을 출력하도록 만들어보세요.

  => 전체적인 동작을 이해하고, 문제 해결을 위해서 어디를 어떻게 고쳤는지 파악해야 합니다.

  => 힌트: ‘for 반복문을 사용해서 문자가 하나씩 출력되도록 만들었습니다.

 

 

6. 예제분석 #02 - 코드분석_Level.1 | 흐름 파악하기

 

[“A” 문자 출력 과정]

1. 0x200주소에서 1바이트 값을 가져온다.

2. 이 값을 출력한다.

 

※ 두 가지 출력 방식을 생각할 수 있다.

1) 0x200에서 값을 가져온 다음, 변형해서 출력한다. (0x41(A의 아스키 값) + 0x6 =0x47(G의 아스키 값)

2) 0x200이 아니라 엉뚱한 주소에서 값을 가져와 출력한다. (0x47 => G 출력)

 

 

▶Sample 03.exe 예상 동작 흐름

-  Sample 03 파일도 아래의 두 가지 출력 방식의 문제 중 한 가지를 가지고 있을 것이다.

 

1) “For Code!” 문자열의 특정 값과 연산해서 다른 문자열로 만든 후에 출력한다.

2) Sample 03.exe.data 섹션에는 “For Code!” 문자열이 기록되어 있다. 그리고 그 외에도 다양한 데이터가 기록되어 있을 것이다. 문자 출력 주소가 바뀌었다면, “For Code!” 문자열이 아닌 다른 데이터가 출력될 수 있다.

 


7. 예제분석 #02 - 코드분석_Level.2 | 원인 도출

 

1. main() 함수 찾기

- [F8]로 코드를 한 줄씩 실행하면 main() 함수를 호출하는 주소를 찾을 수 있다.

- main() 함수의 주소 : 00401140

2. 반복되는 구간 찾기

- main() 함수 내부에서 [F8]로 코드를 한 줄씩 실행하면 코드가 반복되는 부분을 찾을 수 있다.

- 00401031 주소부터 00401048 주소 까지 코드가 반복된다.

- command창을 확인하면 문자가 출력되고 있는 것을 확인할 수 있다.

 

3. 문자 출력 방식 예상하기

 

 

- 2번에서 발견한 반복문 구간을 통해 문자 출력 방식을 추측해볼 수 있다.

- , 해당 반복문을 통해 printf(“%s”, “a”)로 문자열이 출력되는 것을 예상할 수 있다.

- 문자 출력을 확인하기 위해 00401036 주소에 [F2]를 눌러 breakpoint를 설정한다.

- 00401036 주소의 ECX 레지스터에 대문자 “S”의 아스키코드 값을 넣는다.

- [F9]를 눌러 sample 03.exe를 실행하면 CMD에 대문자 “S”가 출력되는 것을 확인할 수 있다.

 
 

4. ECX 레지스터에 저장되는 값의 위치 찾기

 

 

- 00401031 주소의 코드(MOVSX ECX,BYPE PTR SS:[ESP+ESI+4])를 확인한다.

- , [ESP+ESI+4]에 위치한 값을 ECX 레지스터에 넣어 출력하는 것을 알 수 있다.
 
 

5. “For Code!!” 문자열이 있는 위치 찾기

 

 
- Memory Dump 영역을 보면 00407034 주소에 “For Code!!” 문자열이 저장되어 있는 것을 확인할 수 있다.
 
 

6. 00401031 주소에 [F2]를 눌러 breakpoint를 설정한다.

 
 

7. [SPACE]를 눌러 코드를 수정한다.

 

 

 

- 이때, ESP 레지스터는 스택 포인터를 저장하고 있으므로 EDI 레지스터를 이용한다.

- 즉, MOVSX ECX,BYPE PTR SS:[ESP+ESI+4]MOVSX ECX,BYPE PTR SS:[EDI+ESI]로 코드를 수정한다.

 

 

8. 다음, [EDI + ESI]0x407034 주소를 가리키게 한다. 그러나, ESI 레지스터에는 값 “A” 가 들어가 있으므로, 0x407034에서 0xA를 뺀 값인 0x40702A의 값을 넣는다.

 
 

9. 그 후 실행하면 FOR CODE가 실행되는 것을 확인할 수 있다.

 
 
 
 


8. 예제분석 #02 - 코드분석_Level.3 | 문제 해결

 

1. 반복 코드를 분석한다.

- ESP+ESI+4 주소에서 1byte 값을 가져온다.

- 가져온 문자를 출력한다.

- ESI 값을 1 증가시킨다.

- ESI 값을 확인한다. 이때, 0X14보다 작으면 문자 출력 코드를 반복 실행시킨다.

 

2. [ESP+ESI+4] 주소에 있는 값을 확인한다.

 

- 문자열 “For Code!!”0x407034에 위치해있다.

- 그러나 ESI 레지스터에는 값 “A”가 들어있으므로, [ESP+ESI+4]의 주소를 0x40702A로 설정해야 한다.

- 즉, [ESP+ESI+4]의 주소가 0x40702A가 아니어서 이상한 문자열이 출력된 것이다.

 

3. ESI 레지스터의 값을 0으로 바꾸기

- ESI 레지스터에 값 “A”가 들어가 있는 것을 0으로 바꾼다. (ESP값은 스택 포인터기 때문에 건들지 않는다.)

- 변경된 코드의 모습

 

4. “A”의 위치 찾기

 

 

- 0040102C 주소에서 값 “A”ESI 레지스터에 저장되는 것을 확인할 수 있다.

- [F2]를 눌러 Sample 03.exe를 다시 실행한 후 ESI 레지스터에 저장하는 값을 0으로 바꾼다. (NOP으로도 수정 가능)

 

5. 그 뒤 실행하면 “For Code!!” 문자열이 출력되지만, 쓰레기 값도 같이 출력되는 것을 확인할 수 있다.

 

- 총 20개의 문자열이 출력되는 것을 확인할 수 있다. (10개만 출력되어야 하는데 20개가 출력되었다.)

 

6. 쓰레기 값이 같이 출력되는 이유

 

- 반복 코드에서 00401045 주소의 코드(CMP ESI, 14)를 확인한다.

- 이때, 0x14보다 작으면 문자열을 출력하는 것을 알 수 있다.

- 즉, 0x14(10진수:20)의 값까지 반복되기 때문에 쓰레기 값이 같이 출력되는 것이다.

 

7. 00401045 주소의 코드(CMP ESI, 14) 수정하기

 

- 0x140xA(10진수:10) 으로 값을 변경한다.

- 즉, [CMP ESI, 10] 으로 코드를 수정한다.

 

8. 출력 확인

- “For Code!!” 문자열이 제대로 출력되는 것을 확인할 수 있다.