'OllyDbg'에 해당되는 글 8건

  1. 2017.01.31 (Themida) 디버깅 경고 메시지 출력 해결 방법!! 4
  2. 2017.01.30 리틀엔디언 VS 빅엔디언
  3. 2017.01.21 RVA to RAW(PE File Format)
  4. 2017.01.21 Hello World 디버깅 실습
  5. 2017.01.19 Crackme 두번째 관문
  6. 2017.01.19 Crackme 첫번째 관문
  7. 2017.01.19 PE File Format에대해 알아보겠습니다. 1
  8. 2017.01.19 OllyDbg 기본 사용법 및 단축키

(Themida) 디버깅 경고 메시지 출력 해결 방법!!

|









안녕하세요.

얼마전 SSD를 MX300 750G로 새로 구매 후 윈도우를 새로 깔고 오랜만에 OllyDbg를 실행시켰더니

(Themida) 동작중인 디버거가 발견 되었습니다. 디버거를 종료 후 사용하시기 바랍니다.

이런 메시지가 뜨더군요.. 생전 처음 겪는 일..

아마 리버싱에 처음 입문하시는 분들 중에 이런 메시지가 출력도 분들도 많을꺼 같아서 제가 해결한  방법을 올려 봅니다.






구글에서검색을 해도 바로 이상한 검색 결과만 뜨거나 해결방법대로 따라했는데도 경고메시지는 계속 출력이 되더군요.

자력으로 해결해보기로 하고 Process Explorer 를 실행시켜서 찾아봤더니 유달리 의심되는 녀석이 하나 보이 더군요.





여기 delfino.exe 설명에..Wizvera Delino라고 적혀있는데 듣도 보도 못한 듣보잡...

일단 삭제해도 대세에는 지장이 없는 프로그램인거 같지만 혹시나 몰라서 검색을 해봤더니..

역시 나.... 컴퓨터 자원만 처묵처묵하는 프로그램이라는 것과 공인증서 관련 프로그램인데 삭제해도 지장없다는

결과가 있더군요.


은행 공인인증서 프로그램을 일괄 설치할때 WIZVERA로 시작하는 프로그램을 다운받아 통합 설치를 하게 되는데 아마도 거기서 파생된 프로그램인거 같습니다.

아마 디버깅 프로그램도 피싱이나 키로거 같은데 악용될 소지가 있어서 프로그램에서 경고창을 띄우는거 같은데 학습용으로 디버깅 툴을 사용하는 저에게는 경고창은 그저 귀찮을 뿐이라서 해당 폴더로 들어가서 프로그램을 삭제를 했습니다.


위치 : C:\Program Files (x86)\Wizvera\Delfino-G3



삭제 했더니 아래와 같이  경고창이 뜨지 않고 잘 OllyDbg가 잘 실행되는걸 확인했습니다.


도움이 되셨다면 공감 한방 쿠욱~ 부탁합니다.



script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">

'Reverse Engineering' 카테고리의 다른 글

리틀엔디언 VS 빅엔디언  (0) 2017.01.30
어셈블리어(Assembly) 기초  (5) 2017.01.29
RVA to RAW(PE File Format)  (0) 2017.01.21
Hello World 디버깅 실습  (0) 2017.01.21
Crackme 두번째 관문  (0) 2017.01.19
And

리틀엔디언 VS 빅엔디언

|

엔디언이란 무엇인가?

 

엔디언(Endianness) 컴퓨터의  메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻하며바이트를 배열하는 방법을 특히  바이트 순서(Byte order)라 한다.

엔디언은 보통 큰 단위가 앞에 나오는 빅 엔디언(Big-endian)과 작은 단위가 앞에 나오는  리틀 엔디언(Little-endian)으로 나눌 수 있으며, 두 경우에 속하지 않거나 둘을 모두 지원하는 것을  미들 엔디언(Middle-endian)이라 부르기도 한다.

빅 엔디안은 최상위 바이트(MSB - Most Signficant Byte)부터 차례로 저장하는 방식이며리틀 엔디안은 최 하위 바이트(LSB - Least Significant Byte) 부터 차례로 저장하는 방식이다.

 

그렇다면 최 상위와 최 하위라는 것은?

 


 

뭔가 공감이 되지 않는 설명인데, 이런 설명이 조금 더 씌여있다.

빅 엔디언은 사람이 숫자를 쓰는 방법과 같이 큰 단위의 바이트가 앞에 오는 방법이고

리틀 엔디언은 반대로 작은 단위의 바이트가 앞에 오는 방법이다.

빅 엔디언    <- 높은 주소 - - - - 낮은 주소 - >

리틀 엔디언 <- 낮은 주소 - - - - 높은 주소 - >



 

즉 단순히 1234567887654321과 같이 거꾸로 저장된다기 보다는..

해당 수치를 데이터의 단위 단위로 나누었을 때 그 단위가 거꾸로 배열되는 형태인듯 하다.

 



메모리의 0에서부터 끝으로 쓰는 방식이 빅 엔디안.

메모리의 끝에서부터 0으로 쓰는 방식이 리틀 엔디안이라 할 수 있다.

 

오늘날 x86 아키텍처를 사용하는 대부분의 데스크톱 컴퓨터는 리틀 엔디언을 쓰며 이를 인텔 포맷이라 한다. 거꾸로  네트워크에서는 주소를 빅 엔디언으로 쓰는데, 역사적으로  라우팅이 전화를 거는 식으로  접두 부호로 이루어졌기 때문이다.

 

장단점

빅 엔디언은 소프트웨어의 디버그 를 편하게 해 주는 경향이 있다사람이 숫자를 읽고 쓰는 방법과 같기 때문에 디버깅 과정에서 메모리의 값을 보기 편한데, 예를 들어 0x59654148은 빅 엔디언으로  59 65 41 48로 표현된다.

반대로 리틀 엔디언은  메모리에 저장된 값의 하위 바이트들만 사용할 때 별도의 계산이 필요 없다는 장점이 있다. 예를 들어, 32비트 숫자인 0x2A는 리틀 엔디언으로 표현하면  2A 00 00 00 이 되는데, 이 표현에서 앞의 두 바이트 또는 한 바이트만 떼어 내면 하위 16비트 또는 8비트를 바로 얻을 수 있다반면 32비트 빅 엔디언 환경에서는 하위 16비트나 8비트 값을 얻기 위해서는 변수 주소에 2바이트 또는 3바이트를 더해야 한다 보통 변수의 첫 바이트를 그 변수의 주소로 삼기 때문에 이런 성질은 종종 프로그래밍을 편하게 하는 반면, 리틀 엔디언 환경의 프로그래머가 빅 엔디언 환경에서 종종 실수를 일으키는 한 이유이기도 하다.

또한 가산기가 덧셈을 하는 과정은 LSB로부터 시작하여 자리 올림을 계산해야 하므로, 첫 번째 바이트가 LSB인 리틀 엔디언에서는 가산기 설계가 조금 더 단순해진다. 빅 엔디언에서는 가산기가 덧셈을 할때 마지막 바이트로부터 시작하여 첫 번째 바이트까지 역방향으로 진행해야 한다. 그러나 오늘날의 프로세서는 여러개의 바이트를 동시에 읽어들여 동시에 덧셈을 수행하는 구조를 갖고 있어 두 엔디언 사이에 사실상 차이가 없다.

 

엔디안 방식이 중요해지는 것은 네트워크인데.데이터 전송을 할 때 엔디안  방식의 차이에 주의해야 한다. 서로 다른 데이터 저장 방식의 시스템끼리 통신하게 되면 전혀 엉뚱한 값을 
주고받기 때문이다

네트웍 데이터 통신에서는 네트워크 바이트 순서(network byte order, 빅 엔디안) 
따르도록 데이터의 바이트 순서를 변경해야 한다

(TCP/IP, XNS, SNA 규약은 16비트와 32비트 정수에서 빅 엔디안 방식을 사용함

이럴 때에 htonl 같은 함수(host to network)를 이용해서 바이트 순서를 정렬시켜주면 된다.

 

 

몇몇 아키텍처는 빅 엔디언과 리틀 엔디언 중 하나를 선택할 수 있도록 설계되어 있고, 이를 바이 엔디언(Bi-endian)이라 부른다.

종종 한 방향으로 순서가 정해져 있는 게 아니라, 이를테면 32비트 정수가 2바이트 단위로는 빅 엔디언이고 그 안에서 1바이트 단위로는 리틀 엔디언인 경우가 종종 있는데 이를  미들 엔디언(Middle-endian)이라 한다.

 

둘이 차이점은 계산할때와 비교할 때 눈에 띈다.

 빅 엔디언을 통한 숫자 비교시숫자의 비교는 가장 큰 값이 들어가는 왼쪽부터 하게 되는데 엔디언은  수치를 앞에서부터 차곡차곡 스택에 집어 넣는 반면  리틀  엔디언으로 하게된다면 리틀  엔디언은 숫자 뒤에서부터 스택에 집어 넣기 때문이 빅  엔디언보다 속도가 느리다.


그러나 수치 계산시에는  리틀 엔디안이 빅 엔디안보다 속도가 더 빠른데  두 숫자를 계산을 할 경우 가장 낮은 자리수에 있는 숫자를 계산을 해보고서 자리 올림수가 있는지 없는지 판단을 하고서 자리 올림수와 다음 숫자를 계산을 하기 때문이다.

  

 엔디언은 UNIX 에서 사용하는 RISC 프로세서에서 사용 하는 바이트 오더, 동시에 소켓 프로그래밍에서 중요한 네트워크 바이트 오더리틀  엔디언은 Intel 계열의 프로세서에서 사용하는 바이트 오더이다.

 

빅 엔디안

리틀 엔디안 

Unix RISC계열의 프로세서가 사용하는 바이트 오더링

네트워크에서 사용하는 바이트 오더링

앞에서부터 스택에 PUSH

비교연산에서 리틀 엔디안보다 속도가 빠름

Intel 계열의 프로세서가 사용하는 바이트 오더링

 

뒤에서부터 스택에 PUSH

계산연산에서 빅 엔디안보다 속도가 빠름

 

출처 및 참고 

: 위키백과

script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">

'Reverse Engineering' 카테고리의 다른 글

(Themida) 디버깅 경고 메시지 출력 해결 방법!!  (4) 2017.01.31
어셈블리어(Assembly) 기초  (5) 2017.01.29
RVA to RAW(PE File Format)  (0) 2017.01.21
Hello World 디버깅 실습  (0) 2017.01.21
Crackme 두번째 관문  (0) 2017.01.19
And

RVA to RAW(PE File Format)

|




 이번에는 "RVA to RAW"에 대해서 포스팅하도록 하겠습니다. "RVA to RAW"를 간단하게 설명하자면, RVA to RAW 는 PE 파일이 메모리에 로딩되었을 때 각 섹션에서 메모리의 주소(RVA)와 File Offset(RAW)을 매핑하는 것을 말합니다.

   이전 PE File Format 포스팅에서 언급했던 VA와 RVA에 대해서 간단하게 다시 얘기하고 시작하도록 하겠습니다.

   RVA ↔ RAW(File Offset) 변환 작업은 PE 헤더를 공부할 때 가장 기본이 되는 개념이므로 잘 숙지하도록 합시다.



# RVA to RAW


- RVA는 PE 파일이 메모리에 로딩되었을 때의 메모리 주소를 의미하고, RAW는 PE 파일이 로딩되기 전에 File Offset을 의미합니다.

- RVA to RAW는 PE 파일의 각 섹션이 메모리에 로딩되기 전의 File Offset과 메모리에 로딩된 후에 로딩된 위치의 주소를 매칭하는 것입니다.




# RVA to RAW 방법 (매핑하는 방법)


  ① RVA가 속해 있는 섹션을 찾는다.

  ② RAW 구하는 비례식을 이용하여 RAW(File Offset)을 구한다.







# RVA to RAW 매핑하는 방법은 위의 설명 처럼 간단합니다. 익숙해질 수 있도록 연습을 해보도록 하겠습니다.




- 바로 위에 보이시는 그림이 Windows7 운영체제의 Calc,exe 파일이 메모리에 로딩되는 모습을 그려놓은 것입니다. 이그림을 가지고 매핑

  하는 연습을 해보도록 하겠습니다.


- 우선 왼쪽편이 File (Offset) 입니다. 오른쪽편이 Memory 구요. 이전 PE File Format 포스팅에서 언급했었는데, PE File 일 때의 섹션 크기와

  위는 Memory에 로딩되면 각 섹션의 크기와 위치가 달라진다고 했었습니다. ( 크게 변하진 않지만, 동일하진 않다고 했었습니다 )


- 위에 그림에서 보이시는 File의 Offset과 Memory에서의 VA(Virtual Address)는 제가 PE View 툴을 이용하여 다 구한 값들입니다. File에서의

  섹션 크기와 Memory에서의 섹션 크기가 조금씩 다르신게 보이시죠? 앞에서 말했듯이 크게 변하진 않았습니다. 우선 이런점들을 미리 알고 계

  셔야 "RVA to RAW"를 이해하시기 편하실것 같습니다.


- 예제 문제를 풀기전에 VA(Virtual Address)와 RVA(Relative Virtual Address)에 대해서 다시 한번 정리하고 예제 문제를 풀어보도록 하겠습

  니다.





- RVA to RAW 비례식을 이용하여 RAW 주소를 구할 수 있는데요. 이 비례식에 존재하는 VirtualAddress는 우리가 지금껏 말하고 있던 VA

  (Virtual Address)가 아닙니다. VA는 프로세스의 메모리에서의 절대주소라고 설명했습니다. 위에서 RAW(Offset)를 구하기 위한 식에서의

  VA(VirtualAddress)는 Section Header의 멤버인 VirtualAddress를 의미합니다. Section Header 구조체의 멤버인 VirtualAddress 멤버는

  메모리에서 섹션의 시작 주소(RVA)를 의미합니다. 결론적으로 RAW 비례식에서의 VirtualAddress는 우리가 앞에서 계속 얘기하던 RVA

  (프로세스 메모리에서의 상대주소)를 말하는 것입니다. 이 부분만 주의하신다면 쉽게 매핑할 수 있으리라 생각합니다.

  ( 저는 RAW를 구하는 비례식의 VirtualAddress가 프로세스 가상 메모리의 절대주소를 의미하는 것인줄 알고 한참 삽질을 했습니다. )








# 1. RVA = 3000일 때 File Offset (RAW) 은 얼마인가 ?


  - RVA 값이 3000입니다. RVA는 프로세스 가상 메모리에서의 상대주소입니다. 위에 Memory 쪽의 VA를 보시면 제일 처음 "01000000"으로 시작

     하는 것을 볼 수 있는데요. 바로 저 값이 기준 위치가 되는 ImageBase 값입니다. [ ImageBase = 01000000 ]

   

  - 지금부터 RVA to RAW 매핑 방법대로 순서대로 해보도록 하죠. 첫 번째가 주어진 RVA가 속해있는 섹션이 어떤 섹션인지 찾는 것입니다. 

    위 그림을 보시면 메모리상에서 .text 섹션의 시작 RVA가 1000이고, 크기가 52CA1이니까 53CA1까지 .text 섹션이 되겠네요. RVA 3000은 

    .text 섹션에 포함되는 군요. [ RVA = 3000 해당 섹션 → .text 섹션 ]


 - 이제 두 번째 해야할 것이 RAW 비례식을 이용하여 RAW (Offset)을 구하는 것입니다. 비레식을 이용해서 구해보도록 하죠.

   식 : 3000 (RVA) - 1000 (VirtualAddress) + 400 (PointerToRawData) = 2400 (RAW)


 * RAW 비례식에서의 VirtualAddress와 PointerToRawData는 Section Header 구조체의 멤버들로, VirtualAddress는 메모리에서 섹션의 시작

   주소(RVA)를 의미하고, PointerToRawData는 파일에서 섹션의 시작 위치를 의미합니다.



 * RVA to RAW 가 어떤것인지 이제 어느정도 감이 오시죠? 리버싱 관련 작업들을 할 땐 이렇게 RVA에서 RAW로 RAW에서 RVA로 바꾸는 작업이

   가장 기본이 된다고 하네요. 꼭 숙지하도록 해야겠습니다. 예제 하나만 더 풀어보고 이번 포스팅을 마치도록 하겠습니다.




# 2. RVA = BC123일 때 File Offset (RAW) 은 얼마인가 ?


  - RVA = BC123 입니다. 해당 RVA가 속해 있는 섹션을 찾아보니, .reloc 섹션에 포함되어있네요.  [ RVA = BC123 해당 섹션 → .reloc 섹션 ]


  - 식 : BC123 (RVA) - BC000 (VirtualAddress) + B9C00 (PointerToRawData) = B9D23 (RAW)


  - 위에서 처럼 RAW (Offset)을 구한 후에는 실제로 그 Offset에 해당 섹션이 있는지 확인하셔야합니다. 혹시 메모리상에서 포함되었던

    섹션이 아니거나, 벗어난다면 " 해당 RVA에 대한 RAW를 정의할 수 없다" 라고 해야 합니다. 이렇게 딱 떨어지지 않는 경우가 Virtual

    Size와 SizeOfRawData 값이 서로 달라서 벌어지는 일들이라고 하는데요. 이러한 부분은 좀 더 공부를 한 후에 알 수 있다고 하네요.




# "RVA to RAW"에 대해서 알아 봤는데요. 생각보다 쉬운 내용이었던 것 같습니다. 




# 참고 도서 : 리버싱 핵심원리



출처: http://yokang90.tistory.com/21 [yO Kang]

script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">

'Reverse Engineering' 카테고리의 다른 글

리틀엔디언 VS 빅엔디언  (0) 2017.01.30
어셈블리어(Assembly) 기초  (5) 2017.01.29
Hello World 디버깅 실습  (0) 2017.01.21
Crackme 두번째 관문  (0) 2017.01.19
Crackme 첫번째 관문  (0) 2017.01.19
And

Hello World 디버깅 실습

|

# 안녕하세요. 이번 포스팅에서는 디버깅시에 자주 쓰이는 OllyDbg, IDA Pro, GDB를 이용하여 Window 및 Linux 환경에서 간단한 실행 파일 디버깅해보겠습니다. 리버싱 공부를 시작한지 얼마안되서 틀린 내용이나 부족한 부분이 많을 수 있으니, 그러한 점은 양해부탁드립니다. (틀린 내용이 있을 시 지적해주시면 정말 감사하겠습니다 ^^)



우선 Visual Studio를 이용하여 C 언어로 "Hello World!" 라는 문구를 출력하는 프로그램을 작성했습니다.



[ 그림 1 ] "Hello World!" 문구를 출력하는 콘솔 프로그램 작성





1. 첫 번째로 OllyDbg를 이용하여 디버깅해보도록 하겠습니다.


  

[ 그림 2 ] OllyDbg를 이용하여 "Hello World.exe" 파일 불러오기

- OllyDbg로 앞에서 작성한 "Hello World.exe" 파일을 실행했을 때, 처음 시작하는 부분입니다. (ENTRY POINT)

- 가장 먼저 mainCRTStartup 이라는 부분으로 점프하는 명령어를 볼 수 있는데요. 이 mainCRTstartup 이라는 함수는 저희가 작성한 코드의 main

  함수부분일까요? 해당 명령어를 실행시켜 어떤 함수인지 한번 살펴보도록 하겠습니다.





[ 그림 3 ] mainCRTStartup 함수로 이동

- mainCRTStartup 부분으로 점프한 모습입니다. 대충 명령어들을 살펴보면, security_init_cookie 함수와 tmainCRTStartup 이라는 함수를 호출

  하고, 리턴하는간단한 함수입니다. 호출하는 security_init_cookie 함수와 tmainCRTStartup 함수가 어떤 작업을 하는지 간단하게 확인해보도

  록 합시다.





[ 그림 4 ] security_init_cookie 함수

[ 출처 : http://msdn.microsoft.com/ko-kr/library/ms235362.aspx ]


- 전역 보안 쿠키를 초기화하는 함수라고 합니다. 우선 이러한 함수가 있다는 점으로 보아, 우리가 찾던 C 코드의 main 부분이 아니라는 것을 확실히 알 

  수 있습니다.





[ 그림 5 ] Entry

[ 출처 : http://msdn.microsoft.com/ko-kr/library/f9t8842e.aspx ]


- 디버깅 툴을 이용하여 어떠한 파일을 열었을 때, 시작하는 첫 부분을 Entry Point 라고 합니다. 위의 그림에서 설명하고 있는 Entry 함수에 내용을 

  살펴보니, mainCRTStartup 함수에 관해서 나와있네요. 제가 작성한 프로그램은 CONSOLE 프로그램이므로, mainCRTStartup 함수가 해당 파일의 

  시작점임을 확인할 수 있습니다.


- 참고로 mainCRTStartup 함수는 다음과 같은 일들을 수행합니다.

   

   1. 전체 명령행(commandLine)을 가리키는 포인터를 획득

   2. 환경 변수를 가리키는 포인터를 획득

   3. C/C++ 런타임 라이브러리의 전역 변수를 초기화

   4. C/C++ 런타임 라이브러리의 메모리 할당 함수(malloc, calloc...)와 저수준 입출력 루틴이 사용하는 힙을 초기화

   5. 모든 전역 오브젝트와 static c++ 클래스 오브젝트의 생성자를 호출

   6. 사용자가 정의한 진입점 함수(WinMain 또는 main)를 호출



- 위의 내용들을 정리해보면, 어떠한 프로그램을 디버깅할 때 시작 Entry는 우리가 만든 프로그램의 main 함수가 아닌, 실행전 필요한 작업들을 위한 

  함수들이 먼저 호출된다는 것입니다. 그러한 함수들을 호출한 후, 그 함수들로 하여금 우리가 작성한 main 함수를 호출하게 하는 것입니다.


- 사실 저런 함수들은 실제로 디버깅할 시에는 자세하게 볼 필요가 없을 듯 합니다. 저런 함수가 어떤 기능인지 간단하게 알고 있다면, 다음에 디버깅

  땐 저러한 함수들을 그냥 쉽게 쉽게 넘어가면 될 것 같습니다.



- 이제 우리가 작성한 프로그램의 main 함수를 찾아 보겠습니다.





[ 그림 6 ] main 함수

- mainCRTStartup 함수내에서 실행시키다보면, 쉽게 main 함수를 호출하는 부분을 찾을 수 있을 겁니다. 위 화면은 main 함수를 호출한 모습입니다.

  main 함수 자료형이 int 형이였고, 인자는 void로 작성했는데 첫 째줄 주석부분을 보면 쉽게 확인할 수 있습니다.


- 명령어들을 살펴보면, "Hello World!" 라는 데이터를 입력하기 위해 0C0만큼 할당하는 부분을 볼 수 있고, "Hello World!" 문자열을 인자로 printf

  함수를 호출하는 것을 볼 수 있습니다. 그 이후에 할당했던 스택을 정리하고 리턴, RTC_CheckEsp 함수는 정확하게는 모르겠는데, 대충 함수 

  호출 전후의 ESP를 체크하는 함수로 예상되네요.






2. 두 번째로 IDA를 이용하여 디버깅해보도록 하겠습니다.



[ 그림 7 ] IDA를 이용한 디버깅

- IDA를 이용하여 "Hello World.exe" 파일을 열었을 때 모습입니다. 보시는것 처럼 제가 작성한 코드의 main 함수를 바로 찾아주었네요.

  코드 내용은 차이가 거의 없습니다.


- IDA는 저도 이번에 처음 써보는데요. 정말 좋은 디버깅 툴인것 같습니다. 우선 왼쪽 상단에 보시면 Function name 이라고 해당 프로그램에서

  사용되는 함수들의 이름을 나열해주구요. 저기서 함수명을 더블클릭하면 오른쪽 편에 코드를 바로 찾아서 띄워줍니다. 그 아래에 있는 Graph

  overview 부분은 프로그램의 흐름도를 그림으로 보여주는 건데, 해당 메인 함수의 코드가 너무 간단해서 아무것도 뜨지 않네요.



[ 참고 ] Graph overview

- 이게 바로, Graph overview 기능입니다. 왼쪽에 check_managed_app 함수 부분을 본거에요. OllyDbg로 디버깅할 땐 디버깅하다보면, 어디 

  부분을 디버깅하고 있는지, 헷갈릴 때가 있는데, IDA는 이런 좋은 기능들이 많아서 편하게 디버깅할 수 있을 것 같습니다. 이 외에도 엄청

  나게 많은 기능들이 있는 걸로 아는데, 사용법좀 익힌후 블로깅하도록 하겠습니다.






3. 세 번째로 GDB를 이용하여 디버깅해보도록 하겠습니다.


- 참고 : GDB는 C, C++, Modula-2 로 구현된 프로그램을 디버그할 수 있는 도구입니다.


[ 그림 8 ] 우분투(리눅스)에서 C 코딩

- 우분투에서도, vim 편집기를 이용하여 "Hello World!"라는 문구를 출력하는 프로그램을 작성합니다.





[ 그림 9 ] 작성한 코드 컴파일 후 실행

- 리눅스 환경에서는 gcc 라는 프로그램을 이용하여 코딩한 파일을 컴파일 합니다.

- 작성한 c 파일을 컴파일하고, 실행한 모습입니다. "Hello World!"라는 문구를 출력하는 것을 볼 수 있습니다.

- 이 프로그램을 이제 GDB를 이용하여 디버깅 해보겠습니다.


- 참고 : 저는 gcc로 컴파일할 때 -g 옵션을 주지 않았는데, -g 옵션 주어 컴파일하면 gdb가 필요로 하는 부가 정보들이 추가되어 디버깅할 때

           더 도움이 된다고 합니다.





[ 그림 10 ] GDB를 이용한 디버깅 - 1

- 위 사진을 보시면 우선 첫 째줄에서 'hello' 파일을 gdb로 불러옵니다. 


- (gdb) set disassembly-flavor intel     :  어셈블리 코드를 intel 기반 어셈블리어로 변경하는 작업입니다.

- (gdb) disassemble main                  :  intel 기반으로 변경한 후, main 함수를 보기위한 명령어입니다.


- 보시는 코드가 앞에서 작성한 C 코드의 Main 함수부분에 해당하는 코드입니다. Visual에서 작업했던것보다 더 짧네요.

- 보시면 <+4> 부분에서 edi 레지스터에 0x4005f4 라는 주소를 복사하고 있는데요. 0x4005f4 부분이 아마도 "Hello World!" 

  문자열이 들어가있는 주소로 예상되네요. 그런 후 출력하는 함수를 호출하는 것으로 예상되고, pop으로 할당한 공간을 비우고

  리턴합니다. 


- 그럼 break point를 걸어서 디버깅해보도록 하겠습니다.





[ 그림 11 ] GDB를 이용한 디버깅 - 2

- 우선 break point를 3곳으로 설정하겠습니다. 

   1. 첫번째는 main 함수의 첫번째 줄입니다.

   2. 두번째는 *main + 9 입니다. 이렇게 break point를 걸고, 실행하면 break point가 지정된 코드 직전 코드까지 실행되게 됩니다.

      따라서, *main + 4 까지 실행되는거죠. *main + 4 의 코드가 0x4005f4 라는 주소를 edi 레지스터에 복사하는 것인데 저 레지스터에

      들어가는 주소에 "Hello World!"라는 문구가 있다고 예상했습니다. 그 주소에 있는 문자열을 확인하기 위해 *main + 9 에 break point를

      설정한 것입니다.

   3. 마지막으로 *main+14 입니다. 여기까지 실행하면 중간에 함수를 호출하는 부분을 실행하게 되는데, 예상한 것이 맞다면 "Hello World!"를

      출력할 것입니다. 출력한다면 호출하는 함수는 printf 함수임을 알 수 있습니다.


- break point를 설정한 후 run 명령어를 이용하여 디버깅을 시작합니다. break point에서 멈추는 것이 보이시죠?

  그 다음 break point 직전 코드까지 실행하기 위해서는 continue 명령어를 사용합니다.

  자, 우리가 추측했던 코드를 실행한 후까지 왔습니다. $edi 레지스터에 있는 String을 확인했더니, "Hello World!" 가 정확하네요.

  그런후 한번더 continue 하니 해당 문자열이 출력됩니다. 




# 코드가 간단한 파일이라 정말 쉽게 디버깅할 수 있었는데요. 프로그램이 커질수록 디버깅하기가 정말 힘든것 같습니다. 그리고, 명령어들도 

  많이 알아야 하구요. 명령어나 디버깅 툴에 대한 사용법은 구글링으로 쉽게 찾으실 수 있을 겁니다. 다음에는 조금 더 복잡한 프로그램을 가지고

  디버깅해보도록 하겠습니다. 그럼 이번 포스팅은 여기서 마치겠습니다.



script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">

'Reverse Engineering' 카테고리의 다른 글

어셈블리어(Assembly) 기초  (5) 2017.01.29
RVA to RAW(PE File Format)  (0) 2017.01.21
Crackme 두번째 관문  (0) 2017.01.19
Crackme 첫번째 관문  (0) 2017.01.19
PE File Format에대해 알아보겠습니다.  (1) 2017.01.19
And

Crackme 두번째 관문

|

# 아주 간단한 Crack me 샘플 분석 (abex' crackme#2)

 

Crackme2를 분석해보도록 하겠습니다.

 

1. 우선 해당 크랙미 파일이 어떤 동작을 하는지 실행시켜 본다.

2. 파일을 실행시켜 실마리를 찾고, 디버깅을 할 때 참고한다.

3. 크랙미 파일에서 요구하는 부분을 만족 시키기 위한 패치를 하거나 

   혹은 디버깅을 통하여 필요한 정보를 확인한다.

 

 

 

 

[ 그림 1 ] abex' crackme#2 파일 실행

- 파일을 실행시켰더니, Name 그리고 Serial 이란 입력란이 있고, 3가지 버튼이 존재했다.


- 이번 크랙미 문제는 시리얼 번호를 알아내야하는 문제인것으로 예상된다.


- 임의의 입력값들을 주고 Check 버튼을 눌러보도록 하자.

 

 


 

 

[ 그림 2 ] 임의의 입력값을 주고 Check 버튼을 눌러본 경우

- Name의 값은 적어도 4글자 이상으로 설정해야 한다는 것을 확인할 수 있었다.

- Name의 값을 4글자 이상으로 수정한후 다시 Check 버튼을 눌렀더니, 잘못된 시리얼 값이라고 에러 메시지 박스가 떳다.


- 여기서 다시 정리를 해보면, 우리가 찾아야 할 것은 시리얼 값인데, 이 시리얼 값이 Name값과는 상관없는 고정된 하나의 값을 가지고 있거나

  혹은 입력값으로 같이 받는 Name 값을 이용하여 유동적으로 변하는 값일 수 도 있을 것이다. 이제 디버깅을 시작해보도록 하자.

 

 

 

 

 

[ 그림 3 ] OllyDbg를 이용한 디버깅 (EP 코드)

- 크랙미 파일을 불러온 첫 화면이다. 주소 00401238이 Entry Point 이며, EP로부터 두세줄을 살펴보면 0040123D에서 CALL 명령어를 이용하여

  MSVBVM60.ThunRTmain 이라는 것을 호출한다. 그 윗부분에서도 MSVBVM60 이라는 문자가 많이 보이는데 이것은 VB 전용 엔진

  (The Thunder Runtime Engine)을 의미한다. 

  즉, 이 크랙미 파일은 VB 전용 엔진을 사용하는 Visual Basic으로 제작되었다는 것을 확인할 수 있다.

 

* 참고 : VB(Visual Basic) 파일은 MSVBVM60.dll(Microsoft Visual Basic Virtual Machine 6.0)이라는 VB 전용 엔진을 사용

          한다. VB 파일은 컴파일 옵션에 따라서 N code와 P code로 컴파일이 가능하다. N code는 일반적인 디버거에서 해석 가능한

          IA-32 Instruction을 사용하는 반면에 P code는 인터프리터(Interpreter) 언어 개념으로서 VB 엔진으로 가상 머신을 구현하여

          자체적으로 해석 가능한 명령어(바이트코드)를 사용한다. 따라서 VB의 P code를 정확히 해석하기 위해선 VB 엔진을 분석하여 

          에뮬레이터를 구현해야한다.

 

- 이 크랙미 파일이 VB 파일이라는 것을 알게 되었다. VB는 주로 GUI 프로그래밍을 할 때 사용되며, IDE 인터페이스 자체도 GUI 프로그래밍에

  최적화되어 있다.


  즉 VB 프로그램은 Windows 운영체제의 Event Driven 방식으로 동작하기 때문에 main() 혹은 WinMain()에 사용자 코드(우리가 

  디버깅을 원하는 코드)가 존재하는 것이 아니라, 각 event handler에 사용자 코드가 존재한다. 따라서 event handler를 중점적으

  로 하여 디버깅을 하면 될 것이다.


- 따라서, 위의 EP 코드는 VB 엔진의 메인 함수를 호출후에 돌아올 리턴 어드레스를 스택에 입력한 후 VB 엔진의 메인 함수를 

  호출한다. (ThunRTMain())






[ 그림 4 ] ThunRTMain() 함수 호출

- ThunRTMain() 함수를 호출한 모습이다. 메모리 주소를 살펴보면 완전히 달라진 것을 확인할 수 있다. (0040123D에서 733735A4)

  이 주소는 MSVBVM60.dll 모듈의 주소 영역이다. 즉, 우리가 분석하는 프로그램의 코드가 아니라 VB 엔진의 코드라는 것이다.

  따라서 이러한 코드들을 모두 분석할 필요가 없다.






[ 그림 5 ] 문자열 검색을 이용하여 코드 찾아가기

- 문자열 검색을 이용하여 핸들러 근처의 사용자 코드를 찾아야 한다. 우리가 Name 값과 Serial 값을 주고 Check를 눌렀을 때, 에러 메시지 

  박스의 타이틀 문자열인 "Wrong serial!" 이 있는 코드로 우선 이동하여 그 코드가 실행되기전의 조건 분기문을 찾아보도록 하자.

 

 

 

 

 

[ 그림 6 ] "Wrong serial!" 문자열이 있는 주소로 이동

- 지금 위치해 있는 주소는 "Wrong serial!" 문자열을 타이틀로 가진 메시지 박스에 관련된 코드 영역으로 추측된다.


- 따라서 이 코드가 나오기전에 시리얼이 맞는지, 틀린지 확인을 하는 조건 분기문이 있을 것으로 생각되며 따라서 그 조건 분기문을 찾아 

  보도록 하자.

 

 

 

 

 

[ 그림 7 ] 조건 분기문 발견

- [ 그림 6 ] 의 메모리 주소 영역에서 스크롤을 올려가며 코드를 살펴보니 분기문이 있는 메모리 주소를 찾을 수 있었다.


- 00403332 주소에 명령어 : JZ 00403408  (JZ 명령어는 연산 결과가 0 일경우, 즉 ZF(Zero Flag)=1로 세팅되면 00403408로 점프하라)


- 00403332 바로 직전의 0040332F 주소의 명령어를 살펴보면 TEST AX,AX 연산을 하는 것을 확인할 수 있다. 

  (TEST 명령어는 두 개의 오퍼랜드를 AND 연산하여 그 값이 0인지를 확인하기 위한 명령어이다. AX 값이 0이라면 AND 연산후에 0이 되고, 

   ZF 값은 1로 세팅된다. 만약 AX 값이 0이 아닌 값이라면  TEST AX, AX 를 실행한 후의 값은 AX 자기 자신이 나오는데 이 값은 0이아닌 쓸모없는 

   값이다. 즉, 앞에서 말했듯이 0인지 확인하는 명령어라고 생각하면 쉽다.)


- AX 값이 만약 0 이였다면 ZF=1로 세팅되고 조건 분기문이 참이되어 00403408 주소로 점프하게 될 것이다. 조건 분기문 아래의

  코드들을 차례대로 살펴보면 조건 분기문이 참이되어 00403408로 점프한다는 것은 Serial 값이 틀린 경우라는 것을 확인할 수 있다. 

  (왜냐하면 맞췄을 경우 뜨는 메시지박스에 관련된 문자열 코드에 해당하는 부분들을 모두 뛰어 넘어 가는 것이기 때문이다)


- 이제 분기문 위의 코드들을 살펴보도록 하자.

 

 

 

 

 

[ 그림 8 ] 조건 분기문 이전의 코드 분석

- 위에서 확인했듯이 TEST AX, AX 에서 연산 결과가 0이되어 ZF=1이 되면, 실패인 경우로 가게 된다.

  TEST 연산의 결과는 그 윗줄에서 호출되는 함수에 의해서 결정된다. (AX 값이 호출된 함수안에서 변경되기 때문에)


- [ 그림 7 ]에서 00403332 주소의 JZ 00403408 명령어는 결국 시리얼값이 맞는지 틀린지 확인한 직후의 조건 분기문이라고 했다.

  그렇다면 그 직전에 호출되는 함수가 결국 사용자가 입력한 시리얼값과 프로그램의 시리얼값과 비교하는 함수이고, 직전의 

  2개의 PUSH는 함수에서 참조될 파라미터 값이라고 예상할 수 있을 것이다. (하나는 사용자가 입력한 시리얼값, 나머지 하나는 

  프로그램의 시리얼값)


- PUSH 되는 값들을 확인해보도록 하자.

 

 

 

 

 

[ 그림 9 ] PUSH되는 EDX와 EAX 값 확인

- PUSH 되는 값을 보니 메모리 EDX=0018F41C   ,   EAX=0018F42C  라는 메모리 주소 값이 스택에 입력되는 것을 확인할 수 있다.

- 저 2개의 메모리 주소를 확인해보도록하자.

 

 

 

 

 

[ 그림 10 ] EDX 및 EAX가 가리키는 메모리 주소의 값을 확인

- Dump 창에서 해당 메모리 주소의 Hex 값을 확인해보니 각 각 16바이트 중 4바이트를 제외한 모든 값들이 같다는 것을 확인할 수 있었다.

 

* 참고 : VB의 문자열은 C++의 string 클래스와 마찬가지로 가변 길이 문자열 타입을 사용한다고 한다.

           따라서 위의 [ 그림 10 ]에서 보는 바와 같이 바로 문자열이 나타나지 않고 16바이트 크기의 데이터가 나타난다.

           (이것이 바로 VB에서 사용하는 문자열 객체이다.)

 

위에서 다른 4바이트 부분은 메모리 주소를 나타내는 것으로 추측된다. 

  (가변 길이 문자열 타입은 내부에 동적으로 할당한 실제 문자열 버퍼 주소를 가지고 있다.)


- Dump 창에서 Hex dump 형식이 아닌 Integer-Address with ASCⅡ dump 모드로 변경하여 보면 [ 그림 10 ] 의 두번째와 같이 볼 수 있다.


- 변경한 모드에서 서로 다른 4바이트의 값을 확인해봤더니 (74985000, 1CAC5000 : 리틀엔디언형식으로 저장된 주소값) 

   00509874   에는  UNICODE "B7A5B2AB"

   0050AC1C 에는  UNICODE "12345678"          이라는 문자열이 저장되어 있는 것을 볼 수 있다.

   

   0050AC1C 에 저장된 "12345678" 이란 값은 사용자가 입력한 임의의 시리얼값이라는 것을 확인할 수 있다.

   즉, 00509874 에 저장된 "B7A5B2AB" 이란 값이 시리얼 값이라는 것이다.  


결론적으로, Name이 "SANGDAE"라는 값을 주었을 때 시리얼값이 "B7A5B2AB"라는 것이다.


- 크랙미 파일을 다시 실행하여 "NAME에는 SANGDAE, Serial에는 B7A5B2AB 라는 값을 주고 Check 버튼을 눌러 확인해보도록 하자. 

 

 

 

 

 

[ 그림 11 ] 디버깅으로 확인한 시리얼 값 입력 결과

- 위에서 확인한 시리얼 값을 입력한 결과, 크랙이 성공했다는 메시지 박스를 확인할 수 있었다.

 

 

 

 

 

[ 그림 12 ] 다른 임의의 NAME으로 시도했을 경우

- Name 값을 다르게 주고 위와 동일한 시리얼 값을 주었더니 시리얼 값이 다르다는 메시지 박스가 떳다.

  즉, 사용자가 입력하는 Name의 값을 참조하여 시리얼을 생성한다는 것을 추측할 수 있었다.


- 그렇다면, 시리얼을 생성하는 부분을 다시 분석해보도록 하자.

 

 

 

 

 

[ 그림 13 ] Check Event Handler 시작 부분 찾아가기

- 위의 코드에서 Break Point 된 부분이 Check Event Handler의 시작 부분이다.


우리가 [ 그림 7 ]에서 발견한 조건 분기문은 여러가지 의미를 담고 있다. 그 조건 분기문 이후에 크랙 성공 여부가 나눠지는 메시지박스를 

   띄우므로 그 직전에 입력한 시리얼 값과 프로그램 시리얼 값을 비교하는 부분이 있을 것이고, 더 생각해보면 그 시리얼 값을 비교하기 위해

   그 전 코드에 시리얼 값을 생성하는 코드 부분이 존재할 것이다. 


- 즉, 이러한 작업들은 사용자가 Check 버튼을 입력했을 때 수행되는 작업들이다. 다시 말하면 그 조건 분기문을 기준으로 거슬러

  올라간다면 Check Event Handler 시작 부분을 찾을 수 있다는 말이다.

  (조건 분기문도 Check Event Handler의 일부분에 포함하므로)


- Check Event Handler 코드의 시작 부분을 찾았으므로, 이제 시리얼 값을 생성하는 코드부분을 찾아보도록 하자.

 

 

 

 

----------------------------------------------------------------------------------------------------------------------------------

* 참고 

[ 참고 그림 ] VB 파일에서의 NOP 명령어

- VB 파일에서는 함수와 함수 사이에 NOP 명령어가 존재한다.

- NOP 명령어는 No Operation 을 의미하며, 즉 아무 동작을 하지 않는 명령어이다. (그냥 CPU 클럭만 소모된다)

----------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

[ 그림 14 ] Name 문자열 읽어오는 코드 부분

- Check Event Serial 함수 시작 부분에서 순차적으로 내려오면서 Name 문자열을 읽어올것으로 예상되는 부분(CALL 명령어 위주로)들을 

  살펴보면 이러한 코드들이 있는 곳을 찾을 수 있다.


- 00402F8E 주소의 명령어는 LEA EDX, [EBP-88] (레지스터 EDX에 [EBP-88]의 주소값을 복사하는 것이다.)

   즉, Name 문자열을 저장할 스트링 객체를 생성한다고 보면된다.)


- 그리고 5번째 줄인 00402F98 주소에서 사용자가 입력한 문자열을 읽어오는 것으로 추측된다.


- 함수를 호출한 후의 EDX가 가리키는 주소를 확인해보도록 하자.

 

 

 

 


[ 그림 15 ] 함수 호출 후의 [EBP-88] 값 확인

- 00402F98 주소에서 CALL 명령어 (함수호출) 이 후에 문자열을 읽어와 저장할것으로 예측한 주소를 확인해본 결과

  사용자가 입력한 Name 값이 들어있는 것을 확인할 수 있었다.


- 두 번째 그림 : Stack 창에서 확인한 값


- 나머지 부분들을 계속 디버깅해보자.

 

 

 

 


[ 그림 16 ] 새로운 조건 분기문 발견 (Name 값이 4글자 미만인지 확인하는 부분)

- [ 그림 15 ] 에서 이어 뒷부분을 디버깅하다가 새로운 조건 분기문을 발견하였다.


- 빨간 코드영역 : 어떠한 값들(함수 파라미터 값으로 추측)을 스택에 PUSH하고 어떠한 함수를 호출한 후, TEST AX,AX를 실행하고 조건

                        분기문에 의해 두 가지 경우로 나누어진다.


- 노란 코드영역 : 이 노란 코드 영역을 해석하면 일단 00403026 주소에서 조건 분기문이 거짓이 될 때(ZF=0 일때) 실행되는 코드 영역이다.

                        코드를 살펴보니 "Error!" , "Please enter at lest 4 chars as name!" 이라는 유니코드를 확인할 수 있다.

                        이러한 정보들을 봤을 때, 이 노란 코드영역은 사용자가 Name의 값을 4글자 미만으로 줬을 때 진행되는 부분인것으로 추측할 

                        수 있다.


우리가 처음에 실행 파일이 어떻게 동작하는지 확인하기 위해서 Name 값을 임의로 "ABC"로 주었을 때 떳던 메시지 박스에 관한

  코드 부분인 것이다. ( [ 그림 2 ] 참고 )


- Name 값이 4글자 미만일 경우 다시 입력받는 곳으로 돌아가기 때문에 이 부분은 따로 디버깅을 할 필요는 없어보인다.

  따라서, 00403026 주소에서 점프하는 004030F9 주소부터 디버깅을 다시 하면, 암호화 방법에 대해서 실마리를 찾을 수 있을 것이다.

 

 

 

 

 

[ 그림 17 ] Name 문자열에서 한 글자씩 읽어오는 함수 발견 

사용자가 입력한 Name이 "SANGDAE" 였었다. 004031F0 주소에서 __vbaStrVarVal 함수를 호출하고 EAX의 값을 스택에 PUSH 

  하는데, 함수호출 직후의 변경된 EAX값을 참조하여 데이터를 확인했더니 Name의 값 "SANGDAE"에서 한문자를 가져온 "S"임을

  확인할 수 있었다.

 

 

 

 

 

[ 그림 18 ] 004031F7 에서 rtcAnsiValueBstr 함수 호출 직후에 변경된 EAX 값 (유니코드 한문자를 아스키코드로 변환)

- [ 그림 17 ] 에서 __vbaStrVarVal 함수 호출 후 변경된 EAX 값(Name 값에서 유니코드 한문자를 가져온)을 파라미터로 rtcAnsiValueBstr 함수

   를 호출한다.


호출 직후에 파라미터로 주었던 EAX 값이 변경되는데, 이 변경된 값을 살펴보면 "S"라는 유니코드를 아스키코드로 변경한 것을 

  확인할 수 있다.


- 즉, Name의 값에서 유니코드 한 글자를 가져와 아스키코드로 변경하는 작업을 두 개의 함수를 이용하여 한 것이다.






[ 그림 19 ] 그 이후의 작업들 

( # 암호화시키는 키를 생성한후 위에서 변경해뒀던 아스키코드 값과 더한후 데이터형을 문자로 변환시킴 )

- 위에서 유니코드 한 글자를 가져와 아스키코드로 변경하는 작업을 한 후에 나머지 부분들을 디버깅해본 결과, 임의의 수를 생성하고 

  (64라는 값), 그 다음 그 값을 이용하여 앞에서 아스키 코드로 변경한 값을 더해준다. (암호화하는 과정) 그 후 다시 rtcHexVarFromVar 함수를 

  호출하여, 더했던 값을 문자형(유니코드)으로 변경시킨다. 

 

정리를 다시 해보면,  

                                 암호화 과정은 아래의 순서와 같다.  (4번 반복)

                                 1. 주어진 Name 문자열을 앞에서부터 한 문자씩 읽어온다.

                                 2. 읽어온 한 문자를 숫자(아스키 코드)로 변환시킨다.

                                 3. 변환 시킨 숫자(아스키 코드)에 64를 더한다.

                                 4. 더한 결과 값을 다시 문자형으로 변환시킨다.(데이터형을)

                                 5. 변환된 문자를 연결시킨다.

 

이러한 암호화 과정을 사용자가 입력한 Name의 4글자만 가지고 작업을 반복한다. 

( 사진에는 코드가 빠져 있는데, 사용자가 입력한 Name의 입력값이 4글자 미만인지 확인 한후, EBX 레지스터에 4라는 값을 주어, EBX 값만큼 

  루프를 돌린다. )

 

                              

- 결국 "SANGDAE" 라는 Name 값을 주었을 경우에 시리얼 값 생성은 SANG 문자열을 가지고 생성한다는 말이다.

   유니코드인 S 를 아스키코드인 53(Hex 값)으로 변경시키고, 64를 더한다. 

   0x53 + 0x64 = 0xB7,     결과 값을 다시 문자형으로 변경  ->  "B7"

   

   유니코드인 A 를 아스키코드인 41(Hex 값)으로 변경시키고, 64를 더한다. 

   0x41 + 0x64 = 0xA5,     결과 값을 다시 문자형으로 변경  ->  "A5"

   

   유니코드인 N 를 아스키코드인 4E(Hex 값)으로 변경시키고, 64를 더한다. 

   0x4E + 0x64 = 0xB2,    결과 값을 다시 문자형으로 변경  ->  "B2"

   

   유니코드인 G 를 아스키코드인 47(Hex 값)으로 변경시키고, 64를 더한다. 

   0x47 + 0x64 = 0xAB,     결과 값을 다시 문자형으로 변경  ->  "AB"


   변경된 문자들을 모두 연결시키면 "B7A5B2AB" 라는 시리얼이 생성된다.


 

 

# 암호화 방법을 모두 확인하였다. 

   그렇다면 마지막으로 [ 그림 12 ] 에서 실패했던 "YOKANG"이라는 Name의 시리얼값을 계산해서 

   입력해보자.


* YOKA 문자열을 이용하여 시리얼 값 계산

   

  Y   ->   0x59 + 0x64 = 0xBD   ->   "BD"

  O   ->   0x4F + 0x64 = 0xB3   ->   "B3"

  K   ->   0x4B + 0x64 = 0xAF   ->   "AF"

  A   ->   0x41 + 0x64 = 0xA5   ->    "A5"


 Name 값이 "YOKANG" 일 때의 시리얼 값은  "BDB3AFA5" 이다.





[ 그림 20 ] 직접 구한 시리얼 값으로 확인

- 앞에서 분석한 암호화 방법을 이용하여 계산한 시리얼 값이 정확한 것을 확인할 수 있었다.

 


script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">

'Reverse Engineering' 카테고리의 다른 글

RVA to RAW(PE File Format)  (0) 2017.01.21
Hello World 디버깅 실습  (0) 2017.01.21
Crackme 첫번째 관문  (0) 2017.01.19
PE File Format에대해 알아보겠습니다.  (1) 2017.01.19
OllyDbg 기본 사용법 및 단축키  (0) 2017.01.19
And

Crackme 첫번째 관문

|

# abex' crackme #1 분석

 

 

Crackme첫번째 관문인 Make me think your HD is a CD-ROM을 분석해보도록 하겠습니다.


1. 우선 abex' crackme #1  파일이 어떤 동작을 하는지 확인하기 위해 실행 시켜본다.

2. Crack me 파일에서 수정해야 할 부분들을 패치해준다. (조건에 맞도록)

 

 

 

 

[ 그림 1 ] abex' crackme 파일을 실행 시킨 화면 (왼쪽에서 오른쪽으로 순차)

  

- 파일을 실행 시키니까 "Make me think your HD is a CD-Rom"이란 문자열을 포함한 메시지 박스가 뜨고, 확인 버튼을 누르자,

  "Nah... This is not a CD-ROM Drive!" 라는 문자열을 포함한 에러 메시지 박스가 떳다.


- 이 크랙미 문제는 현재 인식하고 있는 하드 디스크를 CD-ROM 드라이브로 인식하게끔 만들어 달라는 문제로 파악이 된다.


- 이제 OllyDbg를 이용하여 파일을 열어보자.

 

 


 

[ 그림 2 ] OllyDbg를 이용하여 Crack me 파일 디버깅

- 우선 위에서 디스어셈 코드를 살펴보면 EP 코드가 짧은 것을 확인할 수 있다. EP 코드가 비교적 짧은 이유는 abex' crackme 파일이 어셈블

  리 언어로 만들어진 실행 파일이기 때문이다. 어셈블리 언어가 아닌 다른 언어로 만들어진 실행 파일일 경우에는 그 언어의 컴파일러가 Stub

  Code를 추가 시키기 때문에 EP 코드가 복잡하게 보이며 길어지게 되는 것이다.


- 위의 디스어셈 코드에서 주의 깊게 살펴 보아야 할 것은 API 함수들, 그리고 점프문이 있는 부분들을 중점으로 봐야한다.

 

* 참고 : Stub Code는 사용자가 입력한 코드가 아니라 컴파일러가 임의로 추가시킨 코드이다. 이 코드는 EP 코드 영역에 추가

          되며, 이러한 코드를 StartUp Code라고 부르기도 한다. 디버깅을 할때엔 자세히 볼 필요가 없다.

 

 


 

[ 그림 3 ] 디스어셈 코드 분석.1

- 위 코드는 EP 코드의 첫 부분에 해당된다. 명령어를 살펴보니 PUSH 명령어를 이용하여 스택에 어떠한 값들을 입력하고 마지막으로 

  MessageBox API 함수를 호출하고 있다. 오른쪽편에 있는 주석부분을 살펴보면 MessageBox API 함수에 전달되는 파라미터에 대한 값들이 

  스택에 입력되고 있다는 것을 알 수 있다.


- 주의해야 할 점은 스택이 FILO(First In Last Out) 구조라서 API 함수에서 참조하는 파라미터의 값들을 역순으로 입력하고 

  있다는 점이다.

 

* 참고 : 위의 디스어셈 코드 5줄을 C언어로 번역하면 아래의 코드와 같다.

            MessageBox(NULL, "Make me think your HD is a CD-Rom.", "abex' 1st crackme", MB_OK|MB_APPLMODAL);    





[ 그림 4 ] 디스어셈 코드 분석.2    (파란 부분: 우리가 원하는 메시지 // 빨간 부분: 기존의 에러메시지)

- 위의 디스어셈코드는 나머지 EP 코드 부분들이다. 쭉 읽어보면, GetDriveTypeA(드라이브 타입을 가져오는 API 함수)를 호출하고, ESI의 값은

  1씩 증가시키고, EAX의 값은 1씩 감소시킨다. 그리고 점프문도 있는데 여기의 점프문은 바로 아래 코드의 주소값을 참조하기 때문에 신경쓸 필요가

  없어 보인다.

   

  중요한 부분은 CMP 명령어를 이용하여 EAX와 ESI의 값을 비교해서 같으면 0040103D 주소로 점프를 하고 같지 않다면 다음 

  코드로 이동하여 진행한다는 부분이다. 0040103D 주소로 이동하면 우리가 원하는 문자열을 가지고 있는 MessageBox를 띄울 

  수 있을 것이다. 따라서 우리가 이 크랙미 문제에서 우리가 해줘야 하는 것은 코드의 흐름이 에러 메시지 박스를 띄우는 곳으로

  가는 것이 아니라, 파란색 부분의 코드로 이동을 하게 해주어야 한다는 것이다.





[ 그림 5 ] Address 0040101F의 어셈 코드 수정

- 위의 JMP 문이 참조하는 주소를 0040103D로 변경하게 되면 우리가 원하는 메시지를 띄울 수 있다.

 

 



[ 그림 6 ] Address 0040101F의 어셈 코드 수정 결과


- 우리가 원했던 의도대로 변경이 됬음을 확인할 수 있다.

 

 

 

* 이 방법 외에도 이 크랙미를 풀 수 있는 방법은 여러 가지가 있을 것이다. EAX와 ESI의 값이 임의로 같아지게 코드를 수정하여 

  기존의 JE 조건을 만족시켜 성공 메시지를 띄우는 방법과 JE 명령어를 JNE 명령어로 변경하여 EAX와 ESI의 값이 다를 때 점프

  하도록 할 수도 있다.




# 참고 도서 : 리버싱 핵심원리 



출처: http://yokang90.tistory.com/7 [yO Kang]

script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">
And

PE File Format에대해 알아보겠습니다.

|












안녕하세요. 이번 장은 PE File Format에 대해 알아보고 구조를 분석해보록 하겠습니다.


# PE File Format



- PE(Portable Executable) 파일의 의미를 영단어대로 해석해보면 [Portable=이식가능한, Executable=실행가능한]

  대략 이식이 가능한 실행 파일 정도로 해석이 가능합니다. 처음 PE 파일이 만들어질 때는 다른 운영체제에 이식성을

  좋게 하려했으나 의도대로 되지 않고, 실제로는 Windows 계열의 운영체제에서만 사용되고 있다고 합니다.


  다시 말해, PE File Format은 Windows 운영체제에서만 사용되는 실행 파일의 형식입니다.

  이 PE File Format은 기존 UNIX에서 사용되는 COFF(Common Object File Format)를 기반으로 Microsoft 사에서 만들었습니다.


  PE 파일은 32비트 형태의 실행 파일을 의미하며, PE32라고 불리기도 합니다. 64비트 형태의 실행 파일은 PE+ 또는 PE32+

  라고 표현하며, 이 PE+ 또는 PE32+는 32비트 형태의 실행 파일인 PE 파일의 확장 형태입니다.


  PE File에도 여러 종류가 있는데, 그 종류에 대해서 알아보도록 하겠습니다.





1. PE File 종류


- PE File의 종류에는 실행 계열, 라이브러리 계열, 드라이버 계열, 오브젝트 파일 계열로 총 4가지로 분류되어 있습니다.

- 오브젝트 파일을 제외한 모든 종류는 실행이 가능한 파일입니다. 

- PE 공식 스펙에는 컴파일 결과물인 OBJ(오브젝트) 파일도 PE 파일로 간주합니다. 하지만, OBJ 파일 자체로는 

  어떠한 형태의 실행도 불가능하므로 리버싱에서 관심을 가질 필요는 없습니다. 







간단하게 PE File의 종류에 대해서 알아보았습니다. 그럼 지금부터 PE File의 기본 구조에 대해서 알아보도록 하겠습니다.





2. PE File 기본 구조




- PE File의 기본 구조는 PE 헤더와 PE 바디로 구성되어져 있습니다. PE 헤더에는 DOS header와 DOS Stub, NT header 

  그리고 각 Section header 들로 구성되어 있으며, PE 바디에는 PE 헤더 부분을 제외한 모든 Section 들을 합쳐 PE 바디라고

  부릅니다


- 위 그림이 PE File의 기본 구조를 도식화하고, PE File이 Memory에 로딩되는 모습입니다.


- 왼쪽편의 File에서는 offset으로, 오른쪽편의 File이 로딩된 Memory에서는 VA(Virtual Address)로 위치를 표현합니다.


- PE File이 Memory에 로딩되면 Section의 크기나 위치등이 달라지게 됩니다. 

  (PE 파일은 디스크 상의 모습과 메모리 상의 모습은 거의 같습니다.)


- 참고로 Section의 이름, 크기, 개수, 저장 내용 등은 개발도구와 빌드 옵션에 따라서 달라집니다.


- Section header에는 각 Section에 대한 파일/메모리에서의 크기, 위치, 속성 등의 정보가 정의되어 있고,

  Section 영역에는 실제 실행 코드와 데이터 혹은 리소스와 같은 여러가지 실제적인 정보들이 Section header에서 

  명시한 위치와 크기별로 포함되어 있습니다.




- Section의 종류



# 여기까지가 기본적인 PE File의 기본 구조였습니다. 지금부터는 PE 헤더 부분을 좀 더 자세하게 알아보도록 하겠습니다.

# 앞으로 나오는 내용들을 이해하기 위해서는 VA(Virtual Address)와 RVA(Relative Virtual Address)를 알아야 하는데요. 

  간단하게 VA와 RVA가 어떤것인지 알아보도록 하겠습니다.


# VA & RVA

- VA(Virtual Address)는 프로세스 가상 메모리의 절대 주소를 의미합니다.

- RVA(Relative Virtual Address)는 어느 기준 위치(ImageBase)에서부터의 상대주소를 의미합니다.

- VA와 RVA의 관계는 다음 식과 같습니다.

  RVA + ImageBase = VA


- PE 헤더에서는 VA(절대주소)가 아닌 RVA(상대주소)를 많이 사용합니다. 그 이유는 여러개의 PE File(DLL)이 같은 위치에 로딩될 수 있기

  때문에 정상적인 엑세스가 이루어지지 않을 수 있게 됩니다. 사실 이렇게 같은 위치에 로딩이 되게 될 경우 재배치(Relocation) 과정을 통해

  비어 있는 위치로 PE File(DLL)을 로딩시키게 됩니다. VA(절대주소)를 사용한다고 가정하면, 재배치 과정을 통해 위치가 변하게 되면 VA도

  변해야 정상적인 엑세스가 될 것 입니다. 반면에 RVA(상대주소)인 경우에는 재배치 과정을 통하더라도 기준위치에 대한 상대주소는 변함없

  기 때문에 아무 문제 없이 엑세스할 수 있습니다. 이것이 VA가 아닌 RVA를 사용하는 이유입니다.







3. PE 헤더


- PE 헤더에는 크게 DOS header, DOS stub, NT header, Section header들로 구성되어 있습니다. 그리고 많은 구성 요소들이 구조체로 이루어져 

  있습니다. 지금부터 각 구성요소들에 대해서 하나씩 살펴보도록 하겠습니다.




3.1. DOS Header


- 첫 번째로 살펴볼 부분은 DOS Header의 IMAGE_DOS_HEADER 구조체 입니다. PE File에서 제일 앞부분에 위치하고 있는 구조체로 DOS 파일에 대한  호환을 위한 DOS 실행 이미지입니다. (기존 DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER 구조체)






- IMAGE_DOS_HEADER의 구조체입니다. 많은 구조체 멤버를 가지고 있지만, 모든 멤버들을 다 알아야 할 필요는 없습니다. IMAGE_DOS_HEADER 

  구조체에서 꼭 알아야할 중요한 멤버는 e_magic과 e_lfanew 멤버입니다.


e_magic : DOS signature (4D5A => ASCII 값 "MZ") - 2Byte

e_lfanew : NT Header의 옵셋을 표시 (파일에 따라 가변적인 값을 가짐) - 4Byte


- 모든 PE 파일의 e_magic에는 DOS signature ("MZ")가 존재하며, e_lfanew 값이 가리키는 위치에 NT Header 구조체가 존재합니다.

- IMAGE_DOS_HEADER 구조체의 크기는 64Byte (0x40) 입니다.

  ( DOS signature "MZ"는 DOS 실행 파일을 설계한 마크 주비코브스키라는 사람의 영문 이니셜입니다 )







- Calc.exe(계산기)를 헥사 에디터로 열어본 모습입니다. Calc.exe 파일의 DOS Header 부분이며, 구조체의 첫 2Byte는 File Signature 값으로

  DOS Signature 인 "4D5A(MZ)" 값임을 확인할 수 있습니다. Calc.exe 역시 PE 파일이기 때문입니다. 그리고 DOS Header의 가장 마지막에

  위치하는 e_lfanew 멤버 값을 확인해보면 "000000D8" 임을 확인할 수 있습니다. 000000D8 주소에 NT Header가 존재하겠네요.

  ( 참고로 Intel 계열 CPU는 데이터를 역순으로 저장하는 리틀 엔디언 방식을 이용하여 메모리에 데이터를 저장합니다. )


- 000000D8 주소에 NT Header가 정말 존재하는지 PEview를 이용하여 확인해 보도록 하겠습니다.







3.2. DOS Stub


- DOS Stub은 있는 경우도 있고, 없는 경우도 있습니다. DOS Stub의 존재 여부는 개발 도구의 지원 여부에 의해 결정됩니다. 한 마디로 옵션이며,

  VB, VC++, Delphi 등에서는 DOS Stub을 기본적으로 지원하고 있습니다. 그리고 DOS Stub의 크기 또한 개발 도구에 따라 다르며, DOS Stub이 없어도

  파일 실행에는 아무런 문제가 되지 않습니다. 즉, PE HEADER의 중요 요소가 아니므로 생략이 가능합니다.


- DOS Stub은 코드와 데이터의 혼합으로 이루어져 있으며, OS가 PE File 형식을 인지하지 못할 때 실행되는 부분이라고 생각하시면 되겠습니다.

- Calc.exe 파일의 DOS Stub 영역을 헥사 에디터를 이용하여 한번 보도록 하죠.




- 노란 부분 : 이 부분이 바로 DOS Stub 영역입니다. 파일 Offset (h) 40~4D 영역(DOS Stub 영역 처음부터 14Byte)은 16비트 어셈블리 명령어입니다. 

                   ( 초록 박스 ) 이 16비트 어셈블리 명령어는 32비트 Windows OS에서는 실행되지 않는 명령어입니다. DOS 환경이나 DOS용 디버거를

                   이용하여 실행했을 때, 16비트 어셈블리 명령어가 실행됩니다. 이 코드는 "This program cannot be run in DOS mode" 라는 문구를 출력

                   하고 종료하는 간단한 코드로 구성되어 있습니다. 


- 빨간 부분 : 이 부분은 바로 앞에서 이야기한 DOS Header 영역입니다. 반복해서 계속보는게 공부에 더 도움이 될 것 같아 같이 정리해 보았습니다.

                   제일 첫 부분의 2Byte는 DOS signature, DOS Header 영역의 가장 마지막 4Byte는 e_lfanew 멤버 값입니다. NT Header의 주소를 값으로

                   가지고 있다고 했었습니다. 보시는 것 처럼 NT Header의 첫 부분, IMAGE_NT_HEADERS 구조체의 첫 번째 멤버인 Signature를 가리키고

                   있습니다. 



# DOS Stub 영역에 대해서 정리해보겠습니다.

  ① DOS 환경에서 실행할 때 실행되는 코드와 데이터가 저장되어 있는 영역이다.

  ② PE Header의 중요 요소가 아니므로 생략되어도 된다. (DOS Stub이 없어도 실행엔 문제 없음)

  ③ DOS Stub 크기는 고정되어 있지 않다.

  ④ DOS Stub 영역은 옵션이다. (개발 도구에서 지원을 해주어야 함 - VB, VC++, Delphi 등은 기본으로 지원)

  



3.3. NT Header 


- 이번에는 PE Header의 세 번째 구성요소인 NT Header에 대해서 알아보도록 하겠습니다. NT Header는 IMAGE_NT_HEADERS라는 구조체를 가지고 있습니다.

  IMAGE_NT_HEADERS 구조체는 3개의 멤버로 구성되어 있습니다. 첫 번째 멤버는 Signature, 두세번째는 구조체 멤버로 FileHeader와 OptionalHeader

  있습니다. PE File의 Signature는 50450000("PE"00)의 값을 가집니다. 우선 헥사 에디터를 이용하여 Calc.exe 파일의 IMAGE_NT_HEADERS 구조체를 한번 

  보도록 하죠.



[IMAGE_NT_HEADERS 구조체의 크기는 F8(248Byte) 입니다]




- 위 그림이 Calc.exe 파일의 NT Header 부분입니다. 처음 4Byte는 첫 번째 멤버인 Signature입니다. (50450000 = "PE"00)

  그 아래에 있는 나머지가 FileHeader와 OptionalHeader인데요. 구체적으로 FileHeader의 구조체인 IMAGE_FILE_HEADER의 멤버는 어떤게 있는지, 구조체의

  크기는 얼마나 하는지, FileHeader와 OptionalHeader에 대해서 각각 알아보도록 하겠습니다.




3.3.1 NT Header - File Header 


- NT Header의 세 번째 멤버인 File Header의 IMAGE_FILE_HEADER 구조체는 파일의 개략적인 속성 정보를 가지고 있습니다.

- IMAGE_FILE_HEADER 구조체의 멤버들엔 어떤것들이 있는지 살펴보도록 합시다.



[ IMAGE_FILE_HEADER 구조체의 크기는 14(20Byte) 입니다]




- 위에 보이시는 것이 _IMAGE_FILE_HEADER 구조체입니다. 멤버로 Machine, NumberOfSections, ... 등 총 7개의 멤버를 가지고 있는데요. 이 중에서 중요시

  봐야할 멤버는 4가지 정도 간추려낼 수 있습니다. Machine, NumberOfSection, SizeOfOptionalHeader, Characteristics 이렇게 4가지 멤버인데요.

  이 멤버들이 정확한 값으로 세팅되어 있지 않으면 파일은 정상적으로 실행되지 않습니다. 이 4가지 멤버에 대해서 좀더 자세하게 알아보도록 합시다.


  


#1. Machine

- Machine 넘버는 CPU별로 고유한 값이며, 해당 실행 파일이 실행될 수 있는 시스템(CPU)을 나타내는 값입니다. 아래는 winnt.h 파일에 정의된 Machine 넘버의

  값들입니다.




#2. NumberOfSection

- PE 파일의 PE 바디에는 여러 섹션들이 존재한다고 했습니다. 일반적인 섹션의 종류로는 코드 섹션, 데이터 섹션, 리소스 섹션 등 여러 섹션이 올 수 있는데요.

  NumberOfSection 멤버는 이러한 섹션들의 개수를 값으로 가집니다. 그런데 이 값은 0보다 커야 하며, 정의된 섹션 개수와 실제 섹션이 다르면 실행 에러가 발생

  하게 된다는 점도 알아두시면 좋을 것 같습니다.




#3. SizeOfOptionalHeader

- NT Header의 세 번째 멤버인 OptionalHeader의 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 저장하고 있는 필드입니다. PE 형태의 파일인 경우 IMAGE_

  OPTIONAL_HEADER32 구조체를 사용하고 PE+ 형태의 파일일 경우에는 IMAGE_OPTIONAL_HEADER64 구조체를 사용합니다. 두 구조체는 크기가 다르기 때문

  에 SizeOfOptionalHeader 멤버에 OptionalHeader의 구조체 크기를  명시하는 것입니다.




#4. Characteristics

- Characteristics 멤버는 파일의 속성을 나타내는 값으로, 실행이 가능한 형태인지(executable or not) 혹은 DLL 파일인지 등의 정보들이 bit OR 형식으로 조합됩니다.

  다음은 winnt,h 파일에 정의된 Characteristics 값입니다. (0002h와 2000h의 값을 기억하시길 바랍니다)



[ 출처 : Microsoft Platform SDK - winnt.h ]


- 위에 보이시는 Characteristics 값들을 bit OR 하여 파일의 속성을 나타내는 값을 만드는데요. 0002h가 없는 경우는 실행이 불가능한 파일이라는 것을 의미합니다.

  예를 들면 object 파일 및 resource DLL 같은 파일이 이에 해당합니다. 





#5. TimeDateStamp (참고)

- IMAGE_FILE_HEADER의 TimeDateStamp 멤버는 링커(Linker)에 의해 프로그램이 만들어진 시간 값이 저장되어 있습니다. 이 값은 1970년 1월 1일 오전 9시 

  GMT로 부터 1초 단위의 증가 값으로 표시된 것이며, 파일의 실행에 영향을 미치지 않는 값으로 중요한 멤버는 아닙니다. 단, 개발 도구에 따라서 이 값을 세팅해

  주는 도구(VB, VC++)가 있는 반면 그렇지 않은 도구(Delphi)도 있습니다.



# 지금까지 NT Header의 두 번째 멤버인 IMAGE_FILE_HEADER 구조체의 멤버들에 대해서 알아보았습니다. 실제로 Calc.exe 파일을 헥사 에디터와

   PE view를 이용하여 보도록 하겠습니다.




- Calc.exe 파일을 헥사 에디터로 열어본 모습입니다. 위에서 해당하는 영역이 바로 IMAGE_FILE_HEADER 영역입니다. PE View를 이용하여 Calc.exe 파일을 확인

  해 보도록하겠습니다.



- IMAGE_FILE_HEADER 구조체에 대해서 알아보았는데요. 구조체 이름 처럼 파일에 관한 정보를 담고 있는 구조체였습니다. 그럼 NT_HEADER의 마지막 멤버인 

  IMAGE_OPTIONAL_HEADER 구조체에 대해서 알아보도록 하겠습니다.




3.3.2 NT Header - Optional Header 


- PE 헤더 구조체 중에서 가장 크기가 큰 IMAGE_OPTIONAL_HEADER32입니다. 지금부터 IMAGE_OPTIONAL_HEADER32 구조체에 대해서 알아보도록 하겠

  습니다.

- IMAGE_OPTIONAL_HEADER32 구조체에 대해서 알아보도록 하겠습니다. 아래의 그림에서 강조한 부분에 해당하는 멤버들이 IMAGE_OPTIONAL_HEADER32

  구조체에서 주의깊게 봐야할 멤버들입니다. 이 멤버들의 값은 파일 실행에 있어 필수적이기 때문에 잘못 세팅되면 파일이 정상적으로 실행되지 않습니다.



[ 출처 : Microsoft Platform SDK - winnt.h ]



#1. Magic

- Magic 넘버는 IMAGE_OPTIONAL_HEADER32 구조체인 경우 10B, IMAGE_OPTIONAL_HEADER64 구조체인 경우 20B, ROM 이미지일 경우

  107의 값을 가집니다.




#2. AddressOfEntryPoint

- AddressOfEntryPoint 멤버는 PE 로더가 파일을 메모리에 매핑한 후 처음 실행을 시작할 EP(Entry Point)의 RVA(Relative Virual Address)값을

  가지고 있습니다. 이 값이야말로 프로그램에서 최초로 실행되는 코드의 시작 주소로, 매우 중요한 값입니다.




#3. ImageBase

- ImageBase는 프로세스의 가상 메모리 범위인 0~FFFFFFFF(32비트 운영체제인 경우)에서 PE 파일이 로딩되는 시작 주소를 나타냅니다. EXE, 

  DLL 파일은 user memory 영역인 0~7FFFFFFF 범위에 로딩되고, SYS 파일은 kernel memory 영역인 80000000~FFFFFFFF 범위에 로딩됩니다.

  일반적인 개발 도구인 VB, VC++, Delphi를 이용하여 만든 EXE 파일의 ImageBase 값은 00400000이고, DLL 파일의 ImageBase 값은 10000000

  입니다. PE 로더는 PE 파일을 실행시키기 위해 프로세스를 생성하고 파일을 메모리에 로딩한 후 EIP 레지스터 값을 ImageBase + AddressOfEntry Point 

  값으로 세팅합니다.




#4. SectionAlignment, FileAlignment

- PE File 기본 구조에 대한 그림을 참고하시면 File 영역과 Memory 영역으로 나누어져있는것을 확인할 수 있습니다. PE 파일을 실행하게 되면 File 영역에

  있던 데이터가 Memory 영역에 맵핑되게 됩니다. SectionAlignment 멤버는 메모리에서 섹션의 최소단위를 나타내는 것이고, FileAligment 멤버는 파일에서

  섹션의 최소단위를 나타냅니다. (하나의 파일에서 FileAlignment와 SectionAlignment의 값은 같을 수도 있고 다를 수도 있습니다.) 파일/메모리의 섹션 크기

  는 반드시 각각 FileAlignment/SectionAlignment의 배수가 되어야 합니다.




#5. SizeOfImage

- SizeOfImage는 PE 파일이 메모리에 로딩되었을 때 가상 메모리에서 PE Image가 차지하는 크기를 나타냅니다. 일반적으로 파일의 크기와 메모리에 로딩된

  크기는 다릅니다. SizeOfImage에 들어가는 값의 크기는 PE 헤더를 포함해 모든 세션을 다 포함한 크기이며, 이는 Windows 로더가 해당 파일을 메모리에 

  로드할 때 얼마만큼의 메모리를 할당할지를 결정하는 값이기도 합니다.




#6. SizeOfHeader

- SizeOfHeader는 PE 헤더의 전체 크기를 나타냅니다. (DOS Header, DOS Stub, NT Header, Section Headers) 이 값 역시 FileAlignment의 배수

  여야 합니다. 파일 시작에서 SizeOfHeader 옵센만큼 떨어진 위치에 첫 번째 섹션이 위치합니다.




#7. Subsystem

- Subsystem 멤버에 들어가는 값은 실행 파일의 유저 인터페이스 부분에 대한 필요한 서브 시스템 값입니다. 이 Subsystem의 값을 보고 시스템 드라이버

  파일(*.sys)인지, 일반 실행 파일(*.exe, *.dll)인지 구분할 수 있습니다. Subsystem 멤버가 가질 수 있는 값은 아래와 같습니다.



[ 출처 : Microsoft Platform SDK - winnt.h ]



- 일반적으로 자주 쓰이는 Subsystem 값은 1~3 까지 입니다. 

  * 1 - Driver File - 시스템드라이버 (예 : ntfs.sys)

  * 2 - GUI(Graphic User Interface)파일 - 창 기반 애플리케이션 (예: notepad.exe)

  * 3 - CUI(Console User Interface)파일 - 콘솔 기반 애플리케이션 (예: cmd.exe)




#8. NumberOfRvaAndSizes

- NumberOfRvaAndSizes는 IMAGE_OPTIONAL_HEADER32 구조체의 마지막 멤버인 DataDirectory 배열의 개수를 나타냅니다. 구조체 정의에 

  분명히 배열 개수가 IMAGE_NUMBEROF_DIRECTORY_ENTRIES (16) 이라고 명시되어 있지만, PE 로더는 NumberOfRvaAndSizes의 값을 보고 배열의 

  크기를 인식합니다. 따라서, 꼭 16이 되는 것은 아닙니다.




#9. DataDirectory

- DataDirectory는 IMAGE_DATA_DIRECTORY 구조체의 배열로, 배열의 각 항목 마다 정의된 값을 가집니다.



[ 출처 : 리버싱 핵심원리 ]


- 여기서 중요한 구조체 배열 멤버는 EXPORT, IMPORT, RESOURCE, TLS Directory 정도가 있겠습니다. 특히 IMPORTEXPORT Directory 구조는 

  PE 헤더에서 매우 중요하기 때문에 기억해두시기 바랍니다.



# NT Header의 Optional Header 멤버에 대해서 알아보았습니다. 헥사 에디터, PE View를 통해 실제로 한번 살펴보고 마무리하도록 하겠습니다.





- Calc.exe 파일에서의 Optional Header 영역을 헥사 에디터로 본 모습입니다. 제일 처음 Magic 멤버로 시작하는 것을 확인할 수 있습니다. (010B)

- 다음 사진은 PE View를 이용하여 Calc.exe 파일의 Optional Header 영역을 살펴본 모습입니다.






3.4. Section Header 


- Section Header는 각 Section의 속성을 정의한 것입니다. Section은 비슷한 성격의 자료를 모아 각 각의 Section으로 나누어지는데 이렇게 하는 이유가 프로그램의

  안정성을 위해서라고 합니다. (메모리 관리를 편하게 하기 위함이라는 이유도 있습니다) 우선 Section Header 구조체인 IMAGE_SECTION_HEADER 구조체를 살펴

  보도록 하겠습니다.



[ IMAGE_SECTION_HEADER 구조체의 크기는 28(40Byte) 입니다]

[ 출처 : Microsoft Platform SDK - winnt.h ]



- Section의 속성에는 file/memory에서의 시작 위치, 크기, 엑세스 권한 등이 있습니다. Code, Data, Resourse Section은 각각의 특성, 접근 권한 

  등이 다르게 설정됩니다.



# 메모리 속성별 액세스 권한

[ 출처 : 리버싱 핵심원리 ]



- Section Header 구조체인 IMAGE_SECTION_HEADER 구조체에서 알아야 할 중요 멤버는 5가지가 있습니다. 

  ( VirtualSize, VirtualAddress, SizeOfRawData, PointerToRawData, Characteristics )



 #1. VirtualSize              

- 메모리에서 섹션이 차지하는 크기

 

 #2. VirtualAddress          

- 메모리에서 섹션의 시작 주소 (RVA)

 

 #3. SizeOfRawData        

- 파일에서 섹션이 차지하는 크기

 

 #4. PointerToRawData   

- 파일에서 섹션의 시작 위치

 

 #5. Characteristics         

- 섹션의 속성 (bit OR)


 #6. Name (참고)      

- 8Byte 크기의 세션 이름 값이 저장, PE 스펙에너느 섹션 Name에 대한 어떠한 명시적인 규칙이 없기 때문에 어떠한 값을 넣어

  도 되고 심지어 NULL로 채울 수도 있습니다. 따라서 섹션의 Name은 중요하지 않고, 참고 정도만 하시면 될 것 같습니다.



- VirtualAddress와 PointerToRawData는 아무 값이나 가질 수 없으며, 각각 (IMAGE_OPTIONAL_HEADER32에 정의된) SectionAlignment와 

  FileAlignment에 맞게 결정 됩니다. 앞에서 한번 언급했었는데, 파일에서의 섹션 크기와 메모리에 로딩되는 섹션의 크기는 다르다고 말했었습니다. 때문에, 

  VirtualSize 멤버의 값과 SizeOfRawData 멤버의 값은 일반적으로 다른 값을 가지게 됩니다.


- 마지막 멤버인 Characteristics 멤버의 값은 섹션의 속성 값을 bit OR 연산하여 나온 결과 값을 가지게 됩니다. 섹션의 속성들에는 어떤것들이 있는지 살펴보도록 

  하겠습니다.



[ 출처 : Microsoft Platform SDK - winnt.h ]



#1. IMAGE_SCN_CNT_CODE

- 실행 가능한 코드를 포함하는 세션임을 의미한다.

 

#2. IMAGE_SCN_CNT_INITIALIZED_DATA

- 이 세션은 코드가 실행되기 이전에 초기화가 되어 있던 데이터들이 포함되어 있는 데이터 세션임을 의미한다.

 

#3. IMAGE_SCN_CNT_UNINITIALIZED_DATA

- 이 세션은 초기화가 필요 없는 데이터가 포함된 세션을 의미하며, 로더는 이 메모리 내용에 대하여 0으로 초기화 한다.


#4. IMAGE_SCN_MEM_SHARED

- 이 모듈을 사용하는 모든 프로세스들이 이 플래그가 정의된 세션을 공유하도록 하는 플래그 값이다.


#5. IMAGE_SCN_MEM_EXECUTE

- 로더가 이 세션을 위한 메모리 할당 시 실행이 가능하도록 메모리를 할당하는 플래그 값이다.


#6. IMAGE_SCN_MEM_READ

- 로더가 이 세션을 위한 메모리 할당 시 읽기가 가능하도록 메모리를 할당하는 플래그 값이다.


#7. IMAGE_SCN_MEM_WRITE

- 로더가 이 세션을 위한 메모리 할당 시 쓰기가 가능하도록 메모리를 할당하는 플래그 값이다.


#8. IMAGE_SCN_MEM_DISCARDABLE

- 이 세션은 프로세스가 시작된 이후에 필요 없는 데이터임을 나타내며, 재배치 테이블이 이러할 수 있다.


#9. IMAGE_SCN_MEM_NOT_PAGED

- 이 세션이 페이지 아웃이 안 되고 항상 메모리에 있어야 함을 나타내는 플래그로 디바이스 드라이버의 세션에서 주로 확인할 수 있다.




# 지금까지 Section Header에 대해서 알아보았습니다. 마지막으로 Calc.exe 파일의 Section Header 영역을 PE View, 헥사 에디터로 한번 보도록 

  하겠습니다.




[ Calc.exe 파일의 Section Headers - HXD ]





- 헥사 에디터를 이용하여 Calc.exe 파일의 Section Header 영역을 살펴본 모습입니다. 오른쪽에 ANSI 형태로 표현된 값들을 살펴보면 총 4개의

  섹션이 존재한다는 것을 확인할 수 있습니다. ( .text  ,  .data  ,  .rsrc  ,  .reloc = 각각 40Byte씩 차지하고 있습니다)





[ Calc.exe 파일의 Section Headers - PE View ]


- 위의 사진은 PE View를 이용하여 본 모습입니다. 각 섹션별로 헤더 정보들을 자세하게 볼 수 있습니다.




# 마지막 Section Header 까지, 이렇게 PE File Format에 대하여 알아보았습니다. 저도 공부하는 입장이라 관련 도서 및 관련 자료들을 많이 참고

  하면서 포스팅하게 되었네요. 처음에는 대충 훑어보는데 엄청 어려운 내용들인줄 알았는데, 천천히 그리고 반복해서 계속 읽어보고 모르는 부분은

  여러 자료들 보면서 공부하니 조금이나마 이해가 된 것 같습니다. 제가 참고한 자료들은 아래에 정리해두겠습니다. 개인적으로 리버싱 핵심원리를

  많이 참고했습니다. 책에 좋은 팁들도 많고 초심자에게 정말 좋은 책인것 같습니다. 




# 참고 도서 및 참고 자료


- 리버싱 핵심원리

- Windows 구조와 원리

http://ko.wikipedia.org/wiki/MZ_%EC%8B%A4%ED%96%89_%ED%8C%8C%EC%9D%BC

PE File Format [koromoon].pdf

- PE Structure [Deok9].pdf





script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">
And

OllyDbg 기본 사용법 및 단축키

|

1. OllyDbg 사용법

.

OllyDbg란 일반적으로 소스코드가 없는 실행 파일을 분석할 때, 디버깅하여 사람이 이해할 수 있는 어셈블리 언어로 변환해주는 디버거 툴입니다. OllyDbg는 무료로 제공되며 가볍고 빨라서 많은 리버서들이 사용하는 디버거 중 하나 입니다.





[OllyDbg첫화면]




각 화면을 셜명하면 다음과 같습니다.

 - Code Window     : 기본적으로 disassembly code를 표시해주는 창

 - Register Window : CPU register 값을 실시간으로 표시하면 특정 register들은 이 창에서 수정이 가능

 - Dump Window    : 프로세스에서 원하는 memory주소 위치를 Hex와 ASCII/유니코드 값으료 표시 및 수정 가능

 - Stack Window     : ESP Register가 가리키는 프로세스 stack memory를 실시간으로 표시하며 수정이 가능







- EP란 Entry Point의 약자이며, 실행 파일을 디버거로 불러왔을 때, 처음 멈춰있는 곳의 코드를 EP 코드라고 합니다.

- EP는 Windows 실행 파일(EXE,DLL,SYS)등 코드 시작점을 의미합니다.

- 프로그램이 실행될 때 CPU에 의해 가장먼저 실행되는 코드의 시작 위치입니다.




#OllyDbg 기본 단축키



script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">
And
prev | 1 | next