UPack 으로 압축된 메모장(notepad_upack.exe) 파일을 디버깅하여 OEP 로 가보겠습니다. UPack 은 PE Header 를 독특하게 변경하는 게 문제가 될 뿐 Anti-Debugging 기법은 없으므로 디버깅 자체는 어렵지 않습니다.

UPack 의 PE Header 변경 기법에 대해서는 이전의 제 글을 참고하시기 바랍니다.

UPack 상세 분석 - PE Header 완전 정복 (1)
UPack 상세 분석 - PE Header 완전 정복 (2)
UPack 상세 분석 - PE Header 완전 정복 (3)
UPack 상세 분석 - PE Header 완전 정복 (4)



OllyDbg 실행 에러



UPack 은 IMAGE_OPTIONAL_HEADER 에서 NumberOfRvaAndSizes 값을 A 로 변경(기본값 10)하기 때문에 OllyDbg 의 초기 검증 과정에서 아래와 같은 에러 메시지를 출력합니다.


<Fig. 1>

크리티컬한 에러가 아니므로 [확인] 버튼을 눌러서 넘어갑니다.

위 에러 때문에 OllyDbg 는 EP 로 가지 못하고 아래 그림과 같이 ntdll.dll 영역에서 멈춰버립니다.


<Fig. 2>

어디까지나 OllyDbg 의 버그(혹은 엄격한 PE 체크)때문에 이런 현상이 발생한 것이므로 강제로 EP 를 설정해줘야 합니다.

먼저 EP 가 어디인지 알아야겠죠? Stud_PE 를 이용하여 확인해 보겠습니다.


<Fig. 3>

위 그림을 보면 ImageBase 가 01000000 이고, EP 의 RVA 값이 1018 입니다.
따라서 EP 의 VA 값은 01001018 입니다.

OllyDbg 의 Code 윈도우에서 01001018 로 이동 한 후 "New origin here" 명령을 이용하여 강제로 EIP 를 변경시킵니다.


<Fig. 4>

"New origin here" 명령을 실행하면 경고 메시지 박스가 뜨는데 [확인]을 눌러주면, 이제부터 정상적인 디버깅이 가능합니다.



Decoding Loop


모든 Packer 에는 Decoding Loop 가 존재합니다. 압축/해제 알고리즘 자체가 많은 조건 분기와 루프로 구성되어 있다는 걸 알고 계시다면 Decoding Loop 가 왜 그리 복잡하게 보이는지 이해하실 수 있을 것입니다.

이러한 Decoding Loop 를 디버깅할 때는 조건 분기를 적절히 건너뛰어서 루프를 탈출해야 합니다. 경우에 따라서는 한눈에 루프가 파악되지 않을 수도 있습니다. 레지스터를 잘 보면서 어떤 주소에 값을 쓰고 있는지 잘 살펴야 합니다. (사실 이건 많은 경험이 필요한 부분입니다.)

UPack 은 2nd Section 에 압축된 원본 데이터가 존재하고 이 데이터를 Decoding Loop 를 돌면서 1st Section 에 압축해제 시킵니다.

EP 코드부터 디버깅을 시작해 보겠습니다.


<Fig. 5>

첫 두 명령은 010011B0 주소에서 4 byte 를 읽어서 EAX 에 저장하는 명령입니다. EAX 는 0100739D 값을 가지는데 이는 원본 notepad 의 OEP (Original Entry Point) 입니다.

사실 이 값이 OEP 인 것을 알고 있다면 Hardware BP 를 설치하고 실행[F9] 하면 정확히 OEP 에서 멈추게 됩니다. (리버서들은 보통 BP 설치 후 실행한다는 말을 'BP 걸고 달린다' 라고 표현합니다.)

우리는 디버깅 실력 향상이 목표이므로 정석대로 계속 진행하겠습니다. (익숙해 지시면 BP 걸고 달리세요~)

조금 진행하다 보면 아래와 같은 함수 호출 코드가 나타납니다.


<Fig. 6>

이때 ESI 값은 0101FCCB 이고 이게 바로 decode() 함수의 주소입니다. 앞으로 이 함수가 반복적으로 호출될 것입니다.

decode() 함수를 살짝 살펴보겠습니다.

 
<Fig. 7>

이 부분만 봐서는 뭐 하는 코드인지 아직 감이 안 오실 겁니다.

StepIn[F7] 을 계속 하다 보면 아래와 같은 코드를 만나게 됩니다.


<Fig. 8>

0101FE57 과 0101FE5D 주소에는 EDI 값이 가리키는 곳에 뭔가를 쓰는 명령어가 있습니다. 이때 EDI 값은 1st Section 내의 주소를 가리킵니다.

즉, 압축을 해제 한 후 실제 메모리에 쓰는 명령어 들입니다.
1001FE5E 와 0101FE61 주소에는 CMP/JB 명령어를 통해서 EDI 값이 01014B5A 가 될 때까지 계속 루프를 돌게 됩니다. ([ESI+34] = 01014B5A)

0101FE61 주소가 바로 Decoding Loop 의 끝부분입니다.

실제로 이곳에 BP 를 걸고 달리시면 EDI 가 가리키는 주소에 어떤 값들이 쓰여지는 것을 볼 수 있습니다.



IAT 세팅


일반적인 Packer 에서는 Decoding Loop 가 끝나면 원본 파일에 맞게 IAT 를 새롭게 구성합니다. UPack 도 같은 과정을 거칩니다. 아래 그림을 봐주세요.


<Fig. 9>

UPack 이 Import 하는 2개의 함수 LoadLibraryA 와 GetProcAddress 를 이용하여 루프를 돌면서 원본 notepad 의 IAT 를 구성합니다. (notepad 에서 Import 하는 함수들의 실제 메모리 주소를 얻어서 원본 IAT 영역에 쓰는 것입니다.)

이 과정이 끝나면 0101FEAF 주소의 RETN 명령어로 드디어 OEP 로 갑니다.


<Fig. 10>

수고하셨습니다. 익숙해 질 때 까지 몇 번 반복해 보시기 바랍니다.

이것으로 UPack 의 모든 설명을 마치겠습니다.

질문은 댓글이나 메일을 이용해 주세요~


ReverseCore

Trackback Address :: http://www.reversecore.com/trackback/51 관련글 쓰기

  1. HS 2009/09/21 12:34 댓글주소 | 수정 | 삭제 | 댓글

    ㅋ.. 예전에 MUP 자체에 푹 빠져서... 이것저것 풀어보려고 애썼던 기억이 납니다..^^;
    요즘에는 MUP 는 거의 시도를 안하고 있는데.. 귀찮은 것도 있지만...
    애초에 메모리에 올려버리면 풀린 코드들이 보이니까.. MUP 에 대한 매력이 반감되었다고나 할까요;ㅋ...

    ps.. 사실 Themida 라는 거대한 장벽에 가로막힌 후에 흥미를 잃어버린게 컸던거 같아요;ㅋ

    • reversecore 2009/09/21 15:25 댓글주소 | 수정 | 삭제

      HS님, 안녕하세요 ^^

      네, 맞습니다.
      MUP 할일이 별로 없는게 사실이죠.
      게임 혹은 보안 분야 업무에서나 어쩌다 가끔 하게되지요.

      Themida, EXECryptor, SVKP 를 비롯한 많은 Protector 류들은 정말 어려울 뿐더러 분석가에게 너무나 가혹한(?) 노가다를 요구합니다.
      (엄청난 쓰레기 코드, 끝없는 루프, 무지막지한 call depth, 곳곳에 anti-debugging, 그리고 한번 실수하면 다시 처음부터...)

      그런데 그게 리버싱의 '재미'인거 같애요.
      시도하고 실패하고의 반복인거죠. ^^

  2. reversecore 2009/09/22 17:30 댓글주소 | 수정 | 삭제 | 댓글

    실수로 애자몽님의 댓글을 삭제 해버렸네요. ㅠㅠ
    죄송합니다.
    (제 댓글을 삭제한건데 왜 애자몽님의 글이 삭제될까요???)

    애화몽님 께서 <Fig. 6> 와 설명이 맞지 않다는 문의를 해주셨습니다.

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

    올바른 지적 감사드립니다. ^^
    제가 다른 그림을 올렸었군요.
    지금 수정 하였습니다.

    디코딩 코드 찾는 법은 먼저 디코딩 루프를 찾아서 코드 중에 메모리에서 값을 읽어들여 산술연산 후 값을 다시 메모리에 쓰는 코드가 있다면 그곳이 바로 디코딩 코드입니다.
    루프내에서 반복적으로 호출될 것입니다.

  3. 애화몽 2009/09/22 23:24 댓글주소 | 수정 | 삭제 | 댓글

    설명 감사합니다.
    '애자몽' ㅠ.ㅠ

  4. 애화몽 2009/09/23 10:59 댓글주소 | 수정 | 삭제 | 댓글

    MUP를 OEP 찾는 요령만 배워서요 ^^;
    따라하기식..
    과정 이해에 큰 도움이 되는 좋은 강좌 입니다.
    감사합니다.

    • reversecore 2009/09/23 14:27 댓글주소 | 수정 | 삭제

      네, 제가 찾아봐도 "어디에 BP 걸고 달려라" 식의 글들뿐이었습니다. 원리에 대한 설명은 아직 많이 없더라구요.

  5. 애화몽 2009/09/23 17:47 댓글주소 | 수정 | 삭제 | 댓글

    네... 알겠습니다.

  6. 2009/09/25 20:59 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  7. hyun 2010/08/26 02:31 댓글주소 | 수정 | 삭제 | 댓글

    어떤 패커인지는 모르겠습니다.
    peid가 검출은 못하구요..
    올리로 열어보면 글자도 낱개로 보이고 함수도 안보여서 패킹되어있는거같습니다.
    mup로 언패킹을 하려고 했는데 리빌더 까지 하면 실행은 안되고
    Don't know how to continue because memory at address 73CE1C28 is not readable. Try to change EIP or pass exception to program
    이렇게 구문이 나오고 실행이 되지않습니다.
    도저히 모르겠어요.. 도와주세요..

    • reversecore 2010/08/28 00:41 댓글주소 | 수정 | 삭제

      안녕하세요.

      unpack 을 도와 달라는 말씀이신가요?

      해당 파일을 보내주시면 한번 봐드릴께요.

      확장자를 exex/dllx 등으로 변경하여 아래 메일 주소로 보내주세요.

      reversecore@gmail.com

      * 저도 protector 류를 만나면 크게 뾰족한 방법이 없습니다. 몇 달씩 매달리는 수 밖에요... ^^

      감사합니다.

  8. tintin 2011/06/26 02:59 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 Pe파일 기본부터 지금까지 달려왔습니다
    블로그 정말 유익한거같네요^^ 국내에서는 구하기 힘든자료들이 와우 ㅋㅋ

    질문은 이렇습니다

    이번강좌에서 notepad 의 OEP를 구해서
    언팩해봤습니다

    그런데... 언팩을 해도 처음의 정상notepad 랑 구조가 다릅니다...헤더랑 섹션이랑 겹쳐잇고...dos stub가 없고..

    완벽하게 처음의 notepad 로 언팩 할순업나요..?

    • reversecore 2012/01/11 07:44 댓글주소 | 수정 | 삭제

      안녕하세요.

      패커의 종류에 따라서 원본과 완벽히 동일하게 할 수 있는 경우도 있습니다.

      하지만 대부분은 압축이 풀리고 실행이 가능하다 수준으로 언패킹 하지요. 왜냐하면 원본 파일의 정보를 싹 지우기 때문에 해주고 싶어도 해줄 수 없는 경우가 많거든요. 그리고 안정성 문제 때문에 100% 완벽하게 복구하기는 힘들지요.

      감사합니다.

  9. redbit 2011/12/27 10:33 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 ㅎ
    오랫만에 왔는데 여전하시군요~ 책 축하드립니다 ..ㅋ 아직 이른가요??
    군대가서 잘 못들어왔는데 한번에 정독하고 갑니다 ㅎ

  10. albye 2012/01/05 04:21 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 주인장님 새해복 많이 받으세요.
    다름이 아니라 obsidium 1.3.0.4로 패킹된 프로그램을 스크립트를 이용해 언패킹중인데
    자세한건 http://coolsoft2.com/919634 해당 링크의 글을 읽어주시면 감사하겠습니다. (__) 꾸벅

    해당 프로그램의 언패킹 관련해서 파일 제작자이신 twitter.com/a4slayer님의 허락을 받아두었습니다. 가능하시다면 언패킹좀 해주실 수 있는지.,, 파일과 스크립트는 위에 적으신 reversecore@gmail.com로 보냈습니다. 시간 있으실때 봐주시면 감사하겠습니다.
    로 보

    • reversecore 2012/01/11 07:41 댓글주소 | 수정 | 삭제

      안녕하세요.

      ImpRec 을 이용한 언패킹을 해달라는 말씀이신가요?

      ^^ 저는 그런 툴을 이용한 단순 언패킹은 예전부터 거부감이 들어서 사용하지 않습니다. ImpRec 도 한번 구경만 해봤지 쓰는법도 몰라요.

      제가 좋아하는건 그 패커의 동작 원리를 이해하면서 디버거로 하나하나 따라가는 것입니다. 중간에 너~무 힘들어서 때려치는 경우도 많구요. ^^

      감사합니다.

  11. albye 2012/01/14 03:24 댓글주소 | 수정 | 삭제 | 댓글

    답장 감사합니다.
    제가 쓴글에 어폐가 있었던듯 합니다. imprec로 언패킹을 했으면 하는게 아니라 해당 스크립트에
    스크립트를 돌리고 imprec로 iat를 복구하라고 써있어서 써본건대 의사전달에 오류가 있었네요..ㅠㅠ 저 그럼 디버거 툴을 이용하셔서 언패킹을 해주실 수 있으신지 더미다처럼 코드 가상화기술도 들어가 있어서 지금에 제 실력으로는 도저히 못풀겠더군요..
    http://www.stares.co.kr/10350 여기에 좀 더 자세하게 써놨는데 한 번 읽어주시면 감사하겠습니다.



이전 포스트에서 이어집니다.

UPack 상세 분석 - PE Header 완전 정복 (1)
UPack 상세 분석 - PE Header 완전 정복 (2)
UPack 상세 분석 - PE Header 완전 정복 (3)


#7. Import Table (IMAGE_IMPORT_DESCRIPTOR array)

UPack 의 Import Table 은 매우 특이하게 구성되어 있습니다. (아주 기발한 트릭이 숨어 있지요.)
hex editor 를 이용해서 IMAGE_IMPORT_DESCRIPTOR 구조체를 따라가 보겠습니다.

먼저 Directory Table 에서 Import Directory Table(IMAGE_IMPORT_DESCRIPTOR 구조체 배열) 주소를 얻어야 합니다.

<Fig. 1>

위 그림에서 오른쪽에 빨간색 테두리의 8 byte 의 data 가 바로 Import Table 을 가리키는IMAGE_DATA_DIRECTORY 구조체 입니다. 앞의 4 byte 가 Import Table 의 주소(RVA), 뒤의 4 byte 가 Import Table 의 크기(Size) 를 나타냅니다.

따라서 Import Table 의 RVA 는 271EE 입니다.

이곳을 hex editor 로 보기 위해서는 RVA <-> RAW 변환을 해야 합니다.
먼저 이 RVA 값이 어느 섹션에 속해 있는지 알아야 하는 데요, 메모리 주소 271EE 는 메모리 상에서 3rd Section 영역입니다. (아래 그림 참조)

<Fig. 2>

이전 포스트에서 UPack 은 파일에서 Header, 1st Section, 3rd Section 이 같은 영역이라는 내용과 UPack 의 RVA <-> RAW 트릭에 대해서 설명 드렸습니다. (참고 : UPack 상세 분석 - PE Header 완전 정복 (3))

위 내용을 잘 이해 하신 후 RVA <-> RAW 변환을 하면 아래와 같습니다.

RAW = RVA(271EE) – VirtualOffset(27000) + RawOffset(0) = 1EE

* 주의 : 3rd Section 의 RawOffset 값이 10 이 아니라 0 으로 강제 변환되는 사실을 꼭 이해하시기 바랍니다.

Hex editor 로 파일 옵셋 1EE 를 살펴보겠습니다.

<Fig. 3>

여기가 바로 UPack 의 섹션을 이용한 트릭이 숨겨진 곳입니다.
먼저 IMAGE_IMPORT_DESCRIPTOR 구조체 정의를 보고 난 후 계속 진행하겠습니다. (구조체 크기는 14 byte)

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

        DWORD   Characteristics;            

        DWORD   OriginalFirstThunk;        // INT

    };

    DWORD   TimeDateStamp;

    DWORD   ForwarderChain;

    DWORD   Name;
    DWORD   FirstThunk;                    // IAT

} IMAGE_IMPORT_DESCRIPTOR;

PE 스펙에 따르면 Import Table 은 IMAGE_IMPORT_DESCRIPTOR 구조체 배열로 이루어지고 마지막은 NULL 구조체로 끝나야 합니다.

위 <Fig. 3> 을 보시면 파란색 영역이 Import Table 을 나타내는 IMAGE_IMPORT_DESCRIPTOR 구조체 배열입니다. 1EE ~ 201 옵셋 까지 첫 번째 구조체이며, 그 뒤는 두 번째 구조체도 아니고 그렇다고 (Import Table 의 끝을 나타내는) NULL 구조체도 아닙니다.

얼핏 보면 이는 분명 PE 스펙에 어긋난 듯이 보입니다.

하지만 <Fig. 3> 의 200 옵셋 위에 있는 빨간 선을 주목해주시기 바랍니다.
이 빨간 선은 파일에서 3rd Section 의 끝을 의미합니다. (<Fig. 2> 참고)
따라서 실행 시 200 옵셋 이하는 3rd Section 메모리에 매핑 되지 않습니다.

설명을 위해서 아래 그림을 봐주시기 바랍니다.


<Fig. 4>


3rd Section 이 메모리에 로딩될 때 파일 옵셋 0 ~ 1FF 영역이 메모리 주소 27000 ~ 271FF 에 매핑 되고 (3rd Section 의 남은 메모리 영역인) 메모리 27200 ~ 28000 영역은 NULL 로 채워집니다.

똑 같은 영역을 디버거로 확인해 보겠습니다.

<Fig. 5>


정확하게 010271FF 까지만 매핑 되고 01027200 이후부터는 NULL 로 채워진걸 확인 할 수 있습니다.

다시 PE 스펙의 Import Table 조건으로 돌아가서 01027202 주소 이후부터 NULL 구조체가 나타난다고 본다면 PE 스펙에 어긋나지 않는 셈입니다.

이것이 바로 섹션을 이용한 UPack 의 트릭입니다. 파일로 볼 때는 Import Table 이 깨진 것처럼 보이지만, 실제 메모리에서는 Import Table 이 정확히 나타나는 것입니다.

대부분의 PE 유틸리티들이 파일에서 Import Table 을 읽을 때 이 트릭에 걸려서 잘못된 주소를 쫓아가다가 메모리 참조 에러로 죽어버립니다. (한마디로 대단한 트릭입니다!)


#8. IAT (Import Address Table)

UPack 은 어떤 DLL 에서 어떤 API 를 Import 하는지 실제로 IAT 를 따라가서 확인해 보겠습니다.

위에서 소개된 IMAGE_IMPORT_DESCRIPTOR 구조체와 <Fig. 3> 를 매핑하면 아래와 같습니다.

offset  member                      RVA
-----------------------------------------
1EE     OriginalFirstThunk (INT)       0
1FA     Name                           2
1FE     FirstThunk (IAT)            11E8

먼저 Name 의 RVA 값은 2 이고, 이는 Header 영역에 속해 있습니다. (1st Section 이 RVA 1000 에서 시작하기 때문입니다.)

헤더 영역에서는 그냥 RVA 와 RAW 값이 같으므로 hex editor 로 파일 옵셋(RAW) 2 를 살펴보겠습니다.

<Fig. 6>

“KERNEL32.DLL” 문자열이 보이는군요. 이 위치는 DOS 헤더(IMAGE_DOS_HEADER) 의 사용되지 않는 영역이라서 UPack 이 이곳에 Import DLL 이름을 써두었습니다. 빈 공간을 그냥 낭비하지 않습니다. (알뜰한 UPack ^^)

DLL 이름을 알았으니 어떤 API 를 Import 하는지 알아보겠습니다.
보통은 OriginalFirstThunk(INT) 를 쫓아가면 API 이름 문자열이 나타나지만, UPack 과 같이 OriginalFirstThunk(INT) 가 0 일 때는 FirstThunk(IAT) 값을 따라가도 상관없습니다. (INT, IAT 둘 중 어느 한 쪽에서만 API 이름 문자열이 나타나면 됩니다.)

<Fig. 2> 에 의하면 IAT 값 11E8 은 1st Section 영역이므로 RVA <-> RAW 변환을 하면 아래와 같습니다.

RAW = RVA(11E8) – VirtualOffset(1000) + RawOffset(0) = 1E8

* 주의 : 1st Section 의 RawOffset 값이 10 이 아니라 0 으로 강제 변환되는 사실을 꼭 이해하시기 바랍니다.

IAT 의 파일 옵셋 1E8 은 아래 그림에 나타나 있습니다.

<Fig. 7>

위 그림의 파란색 으로 표시된 영역은 IAT 이면서 동시에 INT 역할도 하고 있습니다. 즉, 이곳은Name Pointer(RVA) 배열이고 배열의 끝은 NULL 입니다. 위 그림에서는 2개의 API 를 Import 하는 것을 알 수 있습니다. 각각 RVA 28 과 BE 입니다. 이 곳에 Import 함수의 [ordinal + 이름 문자열]이 있습니다.

모두 헤더 영역이므로 RVA 값은 RAW (file offset) 값과 동일합니다.

<Fig. 8>

위 그림을 보시면 “LoadLibraryA” 와 “GetProcAddress” API 를 Import 한다는 것을 알 수 있습니다. 이 두 함수는 원본 파일의 IAT 를 구성할 때 편리하기 때문에 일반적인 Packer 에서도 많이 Import 해서 사용되고 있습니다.


+---+

지금까지 UPack 의 독특한 PE Header 에 대해서 상세히 살펴보았습니다.
다음 포스트에서는 디버거를 이용해서 OEP 를 찾아가는 실습을 해보도록 하겠습니다.


UPack 디버깅 - OEP 찾기


ReverseCore

Trackback Address :: http://www.reversecore.com/trackback/50 관련글 쓰기

  1. Sun2Day 2009/09/17 09:35 댓글주소 | 수정 | 삭제 | 댓글

    덕분에 upack에 대한 좋은 정보 많이 얻고 갑니다. (+__)ㅋ

    감사합니다 '-'//



본 포스트는 이전 포스트에 이어지는 내용입니다.

UPack 상세 분석 - PE Header 완전 정복 (1)
UPack 상세 분석 - PE Header 완전 정복 (2)



#4. IMAGE_SECTION_HEADER

IMAGE_SECTION_HEADER 구조체에서 프로그램 실행에 사용되지 않는 항목들에 UPack 자신의 데이타를 기록합니다.

이 기법 역시 PE Header 에서 쓰이지 않는 영역에 자신의 코드와 데이타로 덮어쓰는 기법입니다.
(생각보다 PE Header 에서 제대로 사용되지 않는 영역이 상당히 많죠?)

Hex editor 로 IMAGE_SECTION_HEADER 구조체를 보겠습니다. (옵셋 170 ~ 1E7 영역)

<Fig. 1>

위 그림에서 보이는 영역을 IMAGE_SECTION_HEADER 구조체에 맞게 보기좋게 뽑아보면 아래 그림과 같습니다. (예전에 제가 만든 PE Viewer 를 사용했습니다.)

[ IMAGE_SECTION_HEADER ]

---------------------------------------------------
  file  memory   data   description
---------------------------------------------------
00000170 01000170 5053FFD5 Name (PS諾揖?
00000174 01000174 ABEBE7C3
00000178 01000178 00014000 virtual size
0000017C 0100017C 00001000 RVA
00000180 01000180 000001F0 size of raw data
00000184 01000184 00000010 offset to raw data
00000188 01000188 0101BDD0 offset to relocations
0000018C 0100018C 0101FCCB offset to line numbers
00000190 01000190     0132 number of relocations
00000192 01000192     0000 number of line numbers

00000194 01000194 E0000060 characteristics

00000198 01000198 00100001 Name ()
0000019C 0100019C 00FD0101
000001A0 010001A0 00012000 virtual size
000001A4 010001A4 00015000 RVA
000001A8 010001A8 0000AE28 size of raw data
000001AC 010001AC 00000200 offset to raw data
000001B0 010001B0 0100739D offset to relocations
000001B4 010001B4 01013FFF offset to line numbers
000001B8 010001B8     FE28 number of relocations
000001BA 010001BA     0101 number of line numbers

000001BC 010001BC E0000060 characteristics

000001C0 010001C0 5A4B0101 Name (ZK?)
000001C4 010001C4 FC0F0001
000001C8 010001C8 00001000 virtual size
000001CC 010001CC 00027000 RVA
000001D0 010001D0 000001F0 size of raw data
000001D4 010001D4 00000010 offset to raw data
000001D8 010001D8 0101FC98 offset to relocations
000001DC 010001DC 0101FC9B offset to line numbers
000001E0 010001E0     FCAA number of relocations
000001E2 010001E2     0101 number of line numbers

000001E4 010001E4 E0000060 characteristics

<Fig. 2>

위에 노란색으로 표시된 구조체 멤버들은 프로그램 실행에는 의미 없는 멤버들입니다. 일례로 파일 옵셋 1B0 위치에 있는 값 0100739D 는 원본 notepad.exe 의 OEP 값입니다.

이 외에도 섹션헤더에는 몇 가지 비밀이 더 숨어 있습니다. (아래에서 설명됩니다.)


#5. 섹션 겹쳐 쓰기

UPack 의 주요 특징 중 하나가 바로 섹션과 헤더를 마구 겹쳐 쓰는 것입니다.
(PE Header 의 기초를 공부하신 분들은 이러한 기법에 당황하실 수 있습니다.)

간략한 뷰를 제공하는 Stud_PE 를 이용해서 UPack 의 IMAGE_SECTION_HEADER 를 살펴보겠습니다. Stud_PE 의 “Sections” 탭을 선택해 주세요.

<Fig. 3>

위 그림을 잘 보시면 뭔가 이상한 부분들이 보입니다.

먼저 눈에 띄는 것은 첫 번째 섹션과 세 번째 섹션의 파일 시작 옵셋(RawOffset) 값이 10 으로 되어 있습니다. 옵셋 10 은 헤더 영역인데 UPack 에서는 이곳에서부터 섹션이 시작됩니다.

그 다음 눈에 띄는 내용은 첫 번째 섹션과 세 번째 섹션의 파일 시작 옵셋(RawOffset) 과 파일에서의 크기(RawSize) 가 완전히 동일하다는 것입니다.
단, 섹션의 메모리 시작 RVA (VirtualOffset) 항목과 메모리 크기(VirtualSize) 값이 서로 틀립니다. PE 스펙에 따르면 이렇게 해도 문제가 없습니다. (더 정확히는 PE 스펙에 이러지 말아야 한다는 내용이 없는 것이죠.)

위 두 가지 이상한 사실을 종합해 보면 UPack은 Header, 1st 섹션, 3rd 섹션이 겹쳐 있습니다.

숫자만 보고서는 이게 무슨 의미인지 잘 와 닿지 않습니다.
이해를 돕기 위해서 아래 그림을 봐주시기 바랍니다.


<Fig. 4>

위 그림 왼쪽은 파일에서 섹션 정보를, 오른쪽은 메모리에서의 섹션 정보를 보여주고 있습니다.

섹션헤더(IMAGE_SECTION_HEADER)에 정의된 값에 의해서 PE Loader 는 파일 옵셋 0 ~ 1FF 영역을 3군데 다른 메모리 위치(Header, 1st Section, 3rd Section)에 매핑합니다.

같은 파일 이미지를 가지고 각각 다른 위치와 다른 크기의 메모리 이미지를 만들 수 있다는 사실에 주목하시기 바랍니다.

파일의 Header(1st/3rd Section) 영역의 크기는 200 입니다. 사실 매우 작은 크기입니다.
반면에 두 번째 섹션(2nd Section) 영역의 크기는 파일의 대부분을 차지할 정도로 큽니다. 바로 이곳에 원본 파일(notepad.exe)이 압축되어 있습니다.

또 하나 주목해야 하는 부분은 메모리에서의 1st Section 영역입니다. 섹션의 메모리 크기는 14000 입니다. 이는 원본 파일(notepad.exe) 의 Size of Image 와 같은 값입니다.

즉, 2nd Section 에 압축된 파일 이미지를 1st Section 에 (notepad 의 메모리 이미지) 그대로 압축해제 하는 것입니다. 참고로 notepad.exe 원본은 3개의 섹션이 있습니다. 이를 하나의 섹션에 풀어내는 것이지요.

압축이 해제된 1st Section 은 아래 그림과 같습니다.

<Fig. 5>

다시 한번 정리하면 메모리 2nd Section 영역에 압축된 notepad 가 들어 있고, 압축이 풀리면서 1st Section 영역에 기록됩니다. 중요한 건 notepad.exe (원본 파일)의 메모리 이미지가 통째로 풀리기 때문에 프로그램이 정상적으로 실행될 수 있습니다. (주소가 정확히 일치하게 됩니다.)



#6. RVA to RAW

각종 PE 유틸리티들이 UPack 앞에서 맥을 못 추던 원인이 바로 RVA <-> RAW 변환에 어려움을 겪었기 때문입니다. UPack 제작자는 많은 테스트 (혹은 PE Loader 리버싱)를 통해서 Windows PE Loader 의 버그(혹은 예외처리)를 알아낸 후 이를 UPack 에 적용하였습니다.

이 기법을 처음 접한 PE 유틸리티들은 대부분 “잘못된 메모리 참조에 의한 비정상 종료” 를 당해버렸습니다. (후에 많은 유틸리티들은 패치가 이루어졌습니다.)

먼저 일반적인 RVA <-> RAW 변환 방법을 복습해 보겠습니다.

RAW - PointerToRawData = RVA - VirtualAddress
                   RAW = RVA - VirtualAddress + PointerToRawData

(VirtualAddress, PointerToRawData 는 RVA 가 속하는 섹션 헤더에서 얻은 값입니다.

참고 : PE(Portable Executable) File Format (5) - PE Header


그렇다면 위 공식대로 UPack 의 EntryPoint(EP) 의 file offset (RAW) 을 계산해 볼까요?

UPack 의 EntryPoint(EP) 는 RVA 1018 입니다. (아래 그림 참조)

<Fig. 6>

RVA 1018 은 <Fig. 3>, <Fig. 4> 에 의하면 1st Section 에 존재합니다.

따라서 공식에 그대로 대입하면 아래와 같습니다.

RAW = 1018 – 1000 + 10 = 28

RAW (file offset) 28 영역을 hex editor 로 살펴보겠습니다.


<Fig. 7>

RAW 28 영역은 코드가 아니라 (ordinal:010B)”LoadLibraryA” 문자열 영역입니다. 우리는 지금 UPack 의 트릭에 걸려들었습니다. (OllyDbg 초기 버전은 실제로 UPack 의 EP 를 찾아내지 못하였습니다.)

비밀은 바로 1st section 의 PointerToRawData 값 10 에 있습니다.

일반적으로 섹션 시작의 파일 옵셋을 가리키는 PointerToRawData 값은 FileAlignment 의 배수가 되어야 합니다. UPack 의 FileAlignment 는 200 이므로 PointerToRawData 값은 0, 200, 400, 600 등의 값을 가져야 합니다.

따라서 PE Loader 는 1st Section 의 PointerToRawData (10) 이 FileAlignment (200) 의 배수가 아니므로 강제로 배수에 맞춰서 인식합니다. (이 경우는 0)

이것이 바로 UPack 파일이 정상적으로 실행은 되지만, PE 유틸리티들이 에러를 발생했던 이유입니다.

정상적인 RVA <-> RAW 변환은 아래와 같습니다.

RAW = 1018 – 1000 + 0 = 18

디버거로 해당 영역의 코드를 살펴보겠습니다.

<Fig. 8>

이제 여러분은 UPack 파일에 대해서 정상적인 RVA <-> RAW 계산을 할 수 있게 되었습니다.


+---+

다음 포스트로 이어집니다.

UPack 상세 분석 - PE Header 완전 정복 (4)


ReverseCore


Trackback Address :: http://www.reversecore.com/trackback/49 관련글 쓰기

  1. 할라 2010/05/25 01:23 댓글주소 | 수정 | 삭제 | 댓글

    오랜만에 공부를 하려고 찾아왔습니다 ㅋ
    PE파일에 대략적인 구조 공부를 하니 더욱더 세부적인 내용을 알고싶더군요 ㅎㅎㅎ

    여길 보면서 헤더내에 필요없는 부분을 쓰는 부분이있는데..
    OEP가 프로그램을 실행하는데에는 전형 상관이없나요???

    • reversecore 2010/05/25 01:47 댓글주소 | 수정 | 삭제

      안녕하세요. ^^

      OEP 는 말그대로 Original Entry Point 로써
      원본 파일이 실행압축 되기 전의 EP(Entry Point) 입니다.

      실행압축이 해제 된 후 OEP 로 가야만 제대로 프로그램이 실행된다고 이해하시면 될것 같습니다.

      감사합니다.

  2. 할라 2010/05/26 23:13 댓글주소 | 수정 | 삭제 | 댓글

    그러면 OEP 정보는 프로그램 실행하는데 필요한 정보가
    아닌가요??

    • reversecore 2010/05/27 01:06 댓글주소 | 수정 | 삭제

      안녕하세요 ^^

      실행 압축된 PE 파일에서 OEP 정보는 가장 핵심적인 정보입니다.
      압축이 다 풀려도 OEP 를 모르면 정작 프로그램이 실행되지 않을 테니 말이지요.

      물론 PE 헤더에 OEP 라는 항목은 따로 없습니다.
      packer 별로 자신만의 방법을 사용해서 OEP 를 저장하고 있는 것이지요.

      감사합니다.



UPack 에서 사용되는 독특한 PE Header 구조에 대해서 상세하게 살펴보도록 하겠습니다.


본 포스트는 이전 포스트에 이어지는 내용입니다.

참고 : UPack 상세 분석 - PE Header 완전 정복 (1)



UPack 의 PE Header 분석


#1. 헤더 겹쳐쓰기

다른 Packer 에서도 많이 쓰이는 기법입니다.
MZ 헤더(IMAGE_DOS_HEADER)와 PE 헤더(IMAGE_NT_HEADERS)를 교묘하게 겹쳐 쓰는 것입니다.

헤더를 겹쳐씀으로 해서 헤더 공간을 절약할 수 있습니다. 부가적으로 복잡성을 증가시켜 분석을 어렵게 만드는 효과도 있습니다. (PE 관련 툴들이 힘들어 하지요.)
 
Stud_PE 를 이용해서 MZ 헤더를 살펴보겠습니다.
(‘Headers’ 탭의 [Basic HEADERS tree view in hexeditor] 버튼을 눌러주세요.)

<Fig. 1>

32/64 bit 환경에서는 MZ 헤더(IMAGE_DOS_HEADER)의 2가지 멤버가 중요합니다.

(offset  0) e_magic  : Magic number = 4D5A (‘MZ’)
(offset 3C) e_lfanew : File address of new exe header

그 외 나머지는 중요하지 않습니다. (프로그램 실행에 아무 의미가 없습니다.)

문제는 PE File Format 의 스펙에 따라서 PE 헤더(IMAGE_NT_HEADERS)의 시작 위치가 '가변적' 이라는 것입니다. 위 그림을 보시면 e_lfanew 의 값에 따라서 PE 헤더의 위치가 결정됩니다.

보통 정상적인 프로그램에서는 e_lfanew 값은 (빌드 환경마다 틀리긴 하지만) 보통 아래와 같습니다.

e_lfanew = MZ 헤더 크기(40) + DOS Stub 크기(가변-VC++의 경우 보통 A0) >= E0

UPack 에서는 e_lfanew 값이 10 입니다. PE 스펙에 어긋나진 않았습니다. 그냥 스펙 자체의 허술함을 이용한 것 뿐이지요.

이런 식으로 MZ헤더와 PE헤더의 겹쳐 쓰기가 가능하게 됩니다.


#2. IMAGE_FILE_HEADER.SizeOfOptionalHeader

IMAGE_FILE_HEADER.SizeOfOptionalHeader 의 값을 변경합니다.

이 값을 조작하여 헤더 안에 디코딩 코드를 삽입하기 위한 목적입니다.

이 값의 의미는 PE Header 에서 바로 뒤따르는 IMAGE_OPTIONAL_HEADER 구조체의 크기(E0)입니다. UPack 은 이 값을 아래 그림과 같이 148 로 변경합니다. (아래 그림에서 빨간색으로 표시된 부분)

<Fig. 2>

여기서 한가지 의문사항이 생깁니다. IMAGE_OPTIONAL_HEADER 는 말 그대로 '구조체' 이기 때문에 크기는 이미 E0 로 결정되어 있습니다. 그런데 왜 PE File Format 설계자들은 IMAGE_OPTIONAL_HEADER 구조체의 크기를 따로 입력하게 했을까요?

원래 의도는 PE 파일의 형태에 따라서 각각 다른 IMAGE_OPTIONAL_HEADER 형태의 구조체를 바꿔 낄 수 있도록 설계한 것입니다. 한 마디로 IMAGE_OPTIONAL_HEADER 의 종류가 여러 개 이므로 구조체의 크기를 따로 입력할 필요가 있는 것입니다. (예: PE64 의 IMAGE_OPTIONAL_HEADER 구조체 크기는 F0)

이 값의 또 다른 의미는 섹션 헤더(IMAGE_SECTION_HEADER) 의 시작 옵셋을 결정 하게 됩니다.
PE Header 를 그냥 보면 IMAGE_OPTIONAL_HEADER 에 이어서 IMAGE_SECTION_HEADER 가 나타나는 듯이 보입니다.

하지만 실제로는 (더 정확히 표현하면) IMAGE_OPTIONAL_HEADER 시작 옵셋에  SizeOfOptionalHeader 값을 더한 위치(옵셋)부터 IMAGE_SECTION_HEADER 가 나타나는 것입니다.

UPack 에서는 SizeOfOptionalHeader 값이 148 로써 정상적인 값(E0 혹은 F0)보다 더 크게 설정됩니다. 따라서 IMAGE_SECTION_HEADER 는 옵셋 170 부터 시작하게 됩니다.
(IMAGE_OPTION_HEADER 시작 옵셋(28) + SizeOfOptionalHeader(148) = 170)

UPack 의 의도는 무엇일까요? 왜 이 값(SizeOfOptionalHeader)을 바꿨을까요?
UPack 은 기본적으로 PE Header 를 꽈배기처럼 꼬아놓고 헤더 안에 디코딩에 필요한 코드를 적절히 끼워 넣는 것입니다.

SizeOfOptionalHeader 값을 바꾸면 IMAGE_OPTION_HEADER 와 IMAGE_SECTION_HEADER 사이에 공간을 확보할 수 있습니다. UPack 은 바로 이 영역에 디코딩 코드를 추가합니다. 일반 상식을 뛰어넘는 교묘한 방법입니다.

실제로 문제의 영역을 살펴보겠습니다.

IMAGE_OPTIONAL_HEADER 의 끝은 D7 이고, IMAGE_SECTION_HEADER 시작은 170 입니다.
이 영역을 hex editor 로 보시면 아래 그림과 같습니다.

<Fig. 3>

디버거를 이용해서 디스어셈 코드를 보면 아래 그림과 같습니다.

<Fig. 4>

좀 더 확실히 의미 있는 코드처럼 보입니다.


#3. IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes

IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes 값을 변경합니다.

이것도 역시 헤더내에 자신의 코드를 삽입하기 위한 목적입니다.

이 값의 의미는 바로 뒤에 이어지는 IMAGE_DATA_DIRECTORY 구조체 배열의 원소 개수를 나타냅니다. 정상파일에서는 IMAGE_DATA_DIRECTORY 배열의 원소 개수는 10개 이지만, UPack 에서는 A개로 변경됩니다. (아래 그림의 빨간색 영역 참고)

<Fig. 5>

IMAGE_DATA_DIRECTORY 구조체 배열의 원소 개수는 이미 10 으로 정해져 있지만, PE 스펙에 따르면 NumberOfRvaAndSizes 값을 배열의 원소 개수로 인정하도록 되어있습니다. (위에서 설명드린 SizeOfOptionalHeader 와 비슷한 개념입니다.)

따라서 UPack 의 경우 IMAGE_DATA_DIRECTORY 구조체 배열의 뒤쪽 6개 원소들은 무시 됩니다.


IMAGE_DATA_DIRECTORY 구조체 배열의 각 항목들을 살펴보면 크게 두 가지로 분류할 수 있습니다.

1) 마음대로 수정해도 되는 항목
2) 잘 못된 값이 들어가면 안 되는 항목

* OS 버전에 따라서 약간씩 틀려질 수 있습니다. 여기서는 XP SP3 기준입니다.

아래 표에 설명하였습니다. ([] 는 배열의 index 를 의미함)

[0] EXPORT Directory
[1] IMPORT Directory
[2] RESOURCE Directory

[3] EXCEPTION Directory
[4] SECURITY Directory
[5] BASERELOC Directory
[6] DEBUG Directory
[7] COPYRIGHT Directory
[8] GLOBALPTR Directory
[9] TLS Directory
[A] LOAD_CONFIG Directory
[B] BOUND_IMPORT Directory
[C] IAT Directory

[D] DELAY_IMPORT Directory
[E] COM_DESCRIPTOR Directory
[F] Reserved Directory

검은색은 값을 수정해도 괜찮은 항목들입니다. (실행에 지장이 없음) 그리고 빨간색은 잘 못 변경했을 때 실행 에러가 발생하는 항목들입니다.

UPack 은 IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes 값을 A 로 변경하여 LOAD_CONFIG 항목(파일 옵셋 D8 이후)부터는 사용하지 않습니다. (위에서 설명드린 #2 의 내용에 따르면 IMAGE_OPTIONAL_HEADER 구조체도 역시 D7 옵셋에서 끝납니다.)

그리고 바로 그 무시된 IMAGE_DATA_DIRECTORY 영역에 자신의 코드를 덮어써버렸습니다.
정말 헤더의 1 byte 까지 알뜰하게 사용하는군요.

hex editor 로 IMAGE_DATA_DIRECTORY 구조체 배열 영역을 보도록 하겠습니다.

<Fig. 6>

위 그림에서 파란색으로 표시된 부분이 정상파일의 IMAGE_DATA_DIRECTORY 구조체 배열 영역이고, 그중에서 빨간색으로 표시된 부분이 UPack 에서 무시되는 부분입니다. (D8 ~ 107 영역 - LOAD_CONFIG Directory 이후)

무시되는 영역을 디버거로 확인하면 위 <Fig. 4> 와 같습니다. UPack 자체의 디코딩 코드입니다.

참고로 NumberOfRvaAndSizes 값이 변경되면 OllyDbg 에서 파일을 열 때 아래와 같은 에러 메시지가 나타납니다. 

<Fig. 7>

 OllyDbg 의 PE 파일 검증 과정에서 NumberOfRvaAndSizes 값이 10 인지 체크하는데, 중요한 메시지는 아니므로 무시하셔도 됩니다. 별도의 PlugIn 을 사용하면 아예 없앨 수 도 있습니다. 참고하시기 바랍니다.


다음 포스트로 이어집니다.

UPack 상세 분석 - PE Header 완전 정복 (3)


+---+

질문은 블로그나 메일을 이용해 주세요~ ^^


ReverseCore

Trackback Address :: http://www.reversecore.com/trackback/48 관련글 쓰기

  1. 땅꽁 2009/09/10 19:48 댓글주소 | 수정 | 삭제 | 댓글

    하루에 한 페이씩 보고 있습니다..내용 좋네요...ㅎㅎ

  2. HS 2009/09/11 11:58 댓글주소 | 수정 | 삭제 | 댓글

    한참.. MUP 의 세계에 빠져있을때;; 처음 UPack 을 접했는데..
    심히 황당했던 기억이 나네요~~ㅋ

  3. worldboss 2011/04/19 11:30 댓글주소 | 수정 | 삭제 | 댓글

    선생님 글 참으로 reverser로 될 꿈 키우게하는 명구,명담들로 가득차있어요.
    선생님책 애독자 되고 있어요.
    계속 좋은글 써주시고 IT발전 떠밀어 주세요.


 

UPack (Ultimate PE Packer) 은 실행 압축기(PE Run-Time Packer)입니다. UPack 의 특징은 PE Header 를 굉장히 독특한 방식으로 변형하는 것입니다.

UPack 때문에 기존의 많은 PE 분석 프로그램들이 오작동을 일으켜서 각 제작자(사)들은 프로그램을 패치 해야 했습니다. 그 만큼 획기적인 기법을 사용했다는 얘기겠죠.

UPack 상세 분석을 통해서 여러분의 PE Header 에 대한 지식을 한 단계 더 높이 끌어올릴 수 있습니다.

아래 내용은 여러분께서 알고 계시는 PE Header 에 대한 지식을 완전히 엎어 버리는 내용이며, 리버싱에 대한 흥미와 열정을 끌어 낼 수 있을 것입니다.




PE Header 배경 지식


본 포스트의 내용을 이해하기 위해서는 PE File Format PE Header 구조체에 대한 이해가 선행되어야 합니다. PE File Format 에 대해서는 아래 제 글을 참고 하시기 바랍니다.

PE(Portable Executable) File Format (1) - PE Header
PE(Portable Executable) File Format (2) - PE Header
PE(Portable Executable) File Format (3) - PE Header
PE(Portable Executable) File Format (4) - PE Header
PE(Portable Executable) File Format (5) - PE Header
PE(Portable Executable) File Format (6) - PE Header
PE(Portable Executable) File Format (7) - PE Header
PE(Portable Executable) File Format (8) - PE Header



UPack 설명


UPack (Ultimate PE Packer) 은 중국의 dwing 이라는 사람이 만든 PE Packer 입니다.

홈페이지 : http://wex.cn/dwing/mycomp.htm
UPack 0.39 Final : http://wex.cn/dwing/download/Upack039.7z

* 참고 

많은 AV 제품에서 위 Upack 파일을 실시간 감시에서 진단/삭제 합니다. 다운 받을때만 잠시 실시감 감시를 멈추고 받아주세요.


UPack 제작자는 PE Header 에 대한 깊은 지식을 가지고 있으며, Windows OS 의 PE Loader 를 상세하게 분석한 걸로 추정됩니다.

많은 PE Packer 중에서도 특히 UPack 은 PE Header 를 독특하게 변형하는 기법으로 유명해 졌습니다. UPack 으로 실행 압축된 파일들의 PE Header 를 처음 보시면 "이게 뭐지? 이게 과연 실행될까?" 라는 의문이 들 정도로 특이하게 변형되어 있습니다.

UPack 이 처음 등장했을 때는 이런 특이한 PE Header 때문에 각종 PE 유틸리티들(debugger, PE Viewer, etc)이 정상적으로 동작하지 않았습니다. (아예 비정상 종료되기 일쑤였죠.)

이 특징 때문에 많은 악성 코드 제작자들이 자신들의 악성코드를 UPack 으로 실행압축 하여 배포하였습니다. 그런 악성코드가 너무 많아져서 현재 대부분의 Anti-Virus 제품들은 아예 UPack 으로 실행압축 된 파일에 대해서는 그냥 ‘악성파일’ 로 진단/삭제해버립니다. (이렇게 악성코드에 자주 사용되는 packer 들이 몇 개 더 있습니다.)

아래 내용을 다 이해하신 후 PE Viewer 또는 PE Packer/Crypter 를 직접 만들어 보시면, 여러분은 PE Header 에 관한 전문가가 될 것입니다. 향후 PE Header 를 아무리 변형해도 어렵지 않게 분석해 낼 수 있습니다.



UPack 으로 notepad.exe 실행압축하기


위에서 소개한 링크에서 UPack 0.39 Final 버전을 이용해서 notepad.exe 를 실행압축 시켜 보겠습니다.
먼저 적절한 곳에 upack.exe 와 notepad.exe 를 복사합니다.


<Fig. 1>

커맨드 창에서 아래와 같이 명령(upack notepad.exe)을 내립니다.
(원래 몇 가지 실행 옵션이 있지만 그냥 default 옵션으로도 충분합니다.)

<Fig. 2>

UPack 은 원본 파일 자체를 실행압축 시키며 따로 백업해두지 않으니, 꼭 복사해서 실행압축 하시기 바랍니다.
실행압축된 파일을 notepad_upack.exe 로 이름 변경하겠습니다.

PE View 로 살펴볼까요?

<Fig. 3>

PE View 최신버전 (0.9.8) 입니다만, PE Header 를 제대로 파싱하지 못하고 있습니다. (IMAGE_OPTIONAL_HEADER, IMAGE_SECTION_HEADER 등의 정보들이 없습니다.)
참고로 예전 버전의 PE View 에서는 프로그램이 비정상 종료됩니다.



Stud_PE 이용



최강의 PE Viewer 인 PE View 가 정상 동작하지 않으니 비슷한 다른 유틸리티인 Stud_PE 를 소개합니다.

홈페이지 : http://www.cgsoftlabs.ro/
Stud_PE : http://www.cgsoftlabs.ro/zip/Stud_PE.zip

최신 버전은 2.4.0.1 입니다. 업데이트 소개에 재미있는 내용이 있군요.

<Fig. 4>

UPack 때문에 RVA2RAW 함수를 고쳤답니다. UPack 이 여기저기 민폐를 많이 끼쳤군요. ^^

아래는 Stud_PE 의 실행 화면입니다.

<Fig. 5>

PE View 보다는 좀 더 복잡한 화면 구성이지만 나름 장점이 많은 유틸리티 입니다. (UPack 도 잘 보이구요.) 
Stud_PE 에 대해서는 UPack 파일의 PE Header 분석할 때 자세히 설명하도록 하겠습니다.



PE Header 비교


Hex editor 로 두 파일(notepad.exe, notepad_upack.exe)의 헤더 부분을 비교해서 보겠습니다.

notepad.exe(원본)의 PE Header

<Fig. 6>

평범한 PE Header 의 모습입니다.
IMAGE_DOS_HEADER, DOS Stub, IMAGE_NT_HEADERS, IMAGE_SECTION_HEADER 순으로 전형적인 PE Header 를 보여주고 있습니다.

notepad_upack.exe(실행압축) 의 PE Header

<Fig. 7>

뭔가 좀 이상한 게 보이시나요?
MZ, PE 시그니처가 매우 가깝게 붙어있고, DOS Stub 은 아예 없어졌고, 여러 문자열들이 보이고, 중간에 코드가 존재하는 것 같기도 하고 말이죠. 한마디로 수상한 점 투성입니다.

이어지는 글에서 UPack 의 PE Header 상세 분석과 사용된 기법들에 대해 공부하고, 디버거를 이용해서 OEP 를 찾아가 보도록 하겠습니다. 


UPack 상세 분석 - PE Header 완전 정복 (2)


ReverseCore


Trackback Address :: http://www.reversecore.com/trackback/47 관련글 쓰기

  1. 땅꽁 2009/09/07 17:53 댓글주소 | 수정 | 삭제 | 댓글

    이거 자주 여기서 글을 보는데..정말 도움이 많이 되네요....아직까지는 너저분하게 리버싱 관련 지식을 얻었는데...체계적으로 정리가 잘 되있네요.....앞으로도 자주 찾아뵈겠습니다..

  2. 2010/04/11 18:38 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2010/04/11 23:14 댓글주소 | 수정 | 삭제

      안녕하세요.

      질문의 범위가 좀 넓습니다만,
      간략히 설명드리면 이런 겁니다.

      먼저 파일 타입(PE)을 보겠죠.
      커널에서 프로세스 객체를 생성합니다.
      그리고 필요한 메모리를 확보할 겁니다.
      섹션 헤더를 읽어서 파일의 내용을 메모리에 올려요.
      IAT 를 보고 필요한 DLL 을 매핑 시킨 후 프로그램에서 사용되는 API 주소를 얻어내겠죠.
      그 후 EP 코드를 실행하면 됩니다.

      이 모든 과정이 어떤 API 를 써서 이루어 지느냐고 물어보신다면 저도 100% 설명드릴 수 는 없고요...
      그저 커널 디버거를 이용해서 kernel32!CreateProcess() API 를 상세 분석해야 한다고 알고 있습니다. 이 주제만으로도 책 한권 쓸 수 있다고 생각합니다. ^^ 거의 OS 의 대부분을 다루는 것이니까요.

      감사합니다.

  3. 할라 2010/05/03 01:52 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 코어님~

    PE기초공부를 다진후
    무얼 공부해볼까 하다가 완전분석이라고 있길래 봤는데
    평소에 관심있던 패커 내용도 있고 해서,
    패킹을 해보려고했는데 dwing님의 홈페이지가 안들어가지네요;;
    패킹프로그램 파일을 어떻게 구할수없나요?ㅎㅎ

    바쁘실텐데 죄송합니다^^;

    • reversecore 2010/05/03 11:43 댓글주소 | 수정 | 삭제

      할라님, 안녕하세요~

      네~ 과연 dwing 님 홈페이지가 접속 안되는군요.
      개인적으로 매우 아쉽습니다.

      할라님 이메일로 보내드렸습니다.

      혹시 못받으셨다면 E-Mail 주소를 알려주시면 다시 보내드릴께요~

      감사합니다.

  4. kvirus 2010/05/27 10:19 댓글주소 | 수정 | 삭제 | 댓글

    core님 안녕하세요!
    안그래도 PE 구조에대한 자료가필요햇는데
    가까운곳에 좋은자료가있다니 ㅠㅠ..
    역시 행복은 가까이에있다는 말이 맞는것같습니다 ㅎ
    좋은자료감사합니다 :)
    궁금한점이있을때 언제든 물어봐도괜찮으시죠?ㅎㅎ

    • reversecore 2010/05/31 09:37 댓글주소 | 수정 | 삭제

      네, 언제나 물어보셔도 됩니다.

      kvirus 님께서는 매우 긍정적이고 밝은 성격이신것 같습니다.

      저도 아주 기분이 좋네요.

  5. kvirus 2010/06/01 11:33 댓글주소 | 수정 | 삭제 | 댓글

    좋은이미지로 봐주셔서 감사합니다 :)
    reversecore님의 좋은 노하우가 담긴
    책이 출판되기를 손꼽아기다립니다...ㅎㅎ

  6. 비빅 2010/08/04 01:02 댓글주소 | 수정 | 삭제 | 댓글

    저도 UPack사이트가 접속이 안되는데
    패킹프로그램 보내주실수 있을까요 ;;
    항상 좋은 강좌 감사합니다 ^^

  7. 비빅 2010/08/04 15:33 댓글주소 | 수정 | 삭제 | 댓글

    메일주소를 안적었네요;; posdog1@hotmail.com 입니다ㅎ

  8. 비빅 2010/08/06 13:50 댓글주소 | 수정 | 삭제 | 댓글

    메일 감사합니다^^

    그런데 핫메일에서 첨부파일 다운하려고 하니까 바이러스 의심된다고 다운이 안되네요..
    이리저리 찾아봤는데 방법을 못찾았습니다.. 죄송하지만 posdog@naver.com 으로
    다시 부탁합니다ㅜㅜ

  9. Vio 2010/08/23 16:51 댓글주소 | 수정 | 삭제 | 댓글

    저도 upack 다운이 안받아지네요..
    vio.bo94@gmail.com
    으로 보내주시면 감사하겠습니다..

  10. Vio 2010/08/30 16:51 댓글주소 | 수정 | 삭제 | 댓글

    cmd 창에서
    upack Cpp1.exe 을 하면

    파일 열기 - 보안경고

    실행하겠냐고 물어 본 후

    실행을 하겠다하면 올바른 프로그램이 아니라며 아무것도 되지않고 종료가 됩니다.

    upack 시키기전에 설정해줘야 할것이 있나요?

    • reversecore 2010/09/04 16:52 댓글주소 | 수정 | 삭제

      혹시 AV 제품(V3, 알약)을 사용하고 계신가요?

      실시간 감시를 끄고 해보시기 바랍니다.

      * 악성 증상은 없는데, 많은 악성 파일에서 사용되는 패커라서 악성으로 진단됩니다.

  11. forever850 2012/03/11 00:06 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 ~ 평소에 패커에 관심이많았었는데 ..

    마침 여기에 좋은글이 올라와있네요 ㅠㅠ.. (이런반가운..)

    글은 잘 읽어보았으나.. 아쉬운점이 .. upack 사이트가 안들어가지네여..

    패킹프로그램좀 보내주시거나.. 다른 링크띄어 주 실수있을까요 ?



이전 글에서 Inline Patch 의 기본 개념을 파악하고 실습 예제를 간단히 디버깅 해보았습니다. 이번에는 상세 분석을 하고 Inline Patch 를 적용해 보도록 하겠습니다.


참고 : Inline Patch 실습 (1)

실습 예제 : http://ap0x.jezgra.net/download/patchme_no1.rar
(출처 : ap0x - Reversing Labs)



코드 구조


설명의 편의를 위해서 실습 예제 샘플의 코드 구조를 먼저 보여드리겠습니다.
구조를 파악하면 어느 부분을 어떻게 패치시킬지 알 수 있습니다.


<Fig. 1>

위 그림에서 하늘색/파란색 영역은 암호화 된 코드이며, 하안색 영역은 암호화를 해제하는 복호화 코드를 나타냅니다.

대략적인 코드 흐름은 아래와 같습니다.

401000 [EP Code]
40109B     [Decoding Code]
4010A3         XOR [B] with 44
4010C8         XOR [A] with 7
4010DB         XOR [B] with 11
401039         [A]
401046             Checksum [B]
401090             XOR [C] with 17
401083             JMP OEP


[EP Code] 는 단순히 [Decoding Code] 를 호출하고, 실제 [Decoding Code] 에서 디코딩 작업이 이루어 집니다. [B] -> [A] -> [B] 순서로 디코딩(XOR)을 하고 암호가 해제된 [A] 코드를 실행합니다.

[A] 코드내에서 [B] 영역의 Checksum 을 구하여 [B] 영역의 변경 여부를 판별합니다. 그 후 [C] 영역을 디코딩(XOR) 한다음 마지막으로 OEP (40121E) 로 점프합니다.

위의 코드 구조코드 흐름을 보시고 직접 디버깅을 해보시기 바랍니다.



Inline Patch


우리가 패치시킬 문자열들은 모두 [B] 영역에 존재하는데, 위에서 보시다시피 [B] 영역은 특별히 이중으로 암호화 되어 있습니다. 그리고 따로 Checksum 을 구하여 변경 여부를 판별하기 때문에 문자열을 직접 수정하기는 좀 까다로운 편입니다.

이럴때 쉽게 사용할 수 있는 방법이 바로 패치 코드를 이용한 Inline Patch 방법입니다.
(이러한 패치 코드를 Code Cave 라고 합니다.)

파일의 적절한 위치에 문자열을 패치시키는 코드를 삽입합니다. 그 후 <Fig. 1> 의 [A] 영역내의 JMP OEP 명령을 JMP <Patch Code> 로 수정합니다. (물론 파일에서 [A] 영역은 암호화 되어 있다는 점을 고려해서 수정해야 합니다.)

프로그램이 실행되어 [A] 영역의 JMP <Patch Code> 문을 만나면 - 이미 모든 코드는 복호화(암호 풀린 상태) 되어있고 Checksum 을 통과했기 때문에 - 패치 코드내에서 문자열을 변경한 후 OEP 로 JMP 하면 Inline Patch 가 완성됩니다.



실습


#1. 패치 코드를 어디에 설치할까?

Inline Patch 에 있어서 중요한 질문입니다. 아래 3 가지 정도의 방법이 있습니다.

1) 파일의 빈 영역에 설치
2) 마지막 섹션을 확장한 후 설치
3) 새로운 섹션을 추가한 후 설치


보통 패치 코드의 크기가 작은 경우 1) 번 방법을 사용하고, 그 외의 경우 2) 번 또는 3) 번 방법을 사용하면 됩니다.

먼저 1) 번 방법으로 시도해 보겠습니다.
PEView 로 예제 파일의 첫 번째 섹션(".text") 헤더를 살펴보겠습니다.


<Fig. 2>

PE Header 에 익숙하지 않으신 분은 <Fig. 2> 의 숫자들이 잘 이해되지 않으실 겁니다.
첫 번째 섹션의 파일 모습과 메모리에 로딩 되었을 때의 모습을 그림으로 표현해보겠습니다.


<Fig. 3>

첫 번째 섹션(".text")의 Size of Raw Data 는 400 이고, Virtual Size 는 280 입니다. 즉, 첫 번째 섹션의 (파일에서) 크기는 400 이지만 이 중에서 280 크기만 메모리에 로딩시킨다는 얘기이지요. 나머지 영역(680 ~ 800)은 안 쓰이는 영역입니다. 바로 이곳이 우리가 찾는 빈 영역 입니다.

* 주의
섹션의 Virtual Size 가 280 이라고 해서 실제 섹션의 메모리 크기가 280 이 되는 것은 아닙니다. Section Alignment (위 예제 파일의 경우에는 1000) 의 배수 단위로 확장되어서 실제로는 1000 크기가 됩니다.


이렇게 찾은 빈 영역을 Hex Editor 로 확인해 보겠습니다.


<Fig. 4>

역시 해당 영역(680 ~ 800)이 0 으로 채워져 있습니다. (이런 영역을 NULL-Padding 영역이라고 부릅니다.)
이곳에 패치 코드를 설치하겠습니다.

* 참고
위 <Fig. 2> 에서 또 하나 주목할 부분은 Characteristics 에 IMAGE_SCN_MEM_WRITE (쓰기 속성) 이 추가 되어 있다는 것입니다. 프로그램 내에서 복호화 작업을 수행하기 위해서는 반드시 섹션 헤더에 쓰기 속성을 추가시켜서 해당 메모리에 대한 쓰기 권한을 얻어야 합니다. (메모리에 쓰기 권한이 없는 상태에서 Write 작업을 시도하면 Access Violation 에러가 발생합니다.)

보통 일반적인 PE 파일의 코드 섹션은 쓰기 속성이 없는데, 위 예제를 포함하여 Packer, Crypter 류의 파일들은 코드 섹션에 쓰기 속성이 존재합니다. 향후 파일을 분석하실 때 참고하세요.

* 섹션 헤더에 대한 더 자세한 설명은 아래의 제 글을 참조하세요.

PE(Portable Executable) File Format (5) - PE Header


#2. 패치 코드 만들기

디버거를 이용해서 OEP(40121E) 까지 갑니다.


<Fig. 5>

#1 에서 찾아낸 빈 영역의 file offset 은 680 ~ 800 입니다. 이를 VA 로 변환하면 401280 ~ 401400 입니다. (<Fig. 3> 참고)
위 <Fig. 5> 를 보시면 401280 부터 NULL Padding 영역이 시작됩니다.

401280 위치에 패치 코드를 만들어 보겠습니다.
OllyDbg 의 'Assemble' [Space] 명령과 'Edit' [Ctrl+E] 명령을 이용하여 아래와 같이 편집하세요.


<Fig. 6>

위 어셈 코드는 매우 간단합니다.
40128F 와 4012A0 의 REP MOVSB 명령에 의해서 아래 문자열들이 패치됩니다.
(401123, 40110A 문자열들은 복호화 된 상태이기 때문에 아래처럼 정상적으로 표시됩니다.)

(401123) "You must patch this NAG !!!"  =>  (4012A8) "ReverseCore"
(40110A) "You must unpack me !!!"       =>  (4012B4) "Unpacked"


그 후 4012A2 의 JMP 명령에 의해서 OEP 로 점프합니다.

이것으로 패치 코드(Code Cave) 를 완성하였습니다.

OllyDbg 에서 변경된 내용을 저장합니다.
"Copy to executable -> All modifications"


#3. 패치 코드 실행 시키기

Inline Patch 의 마지막 작업으로써 #2 에서 만든 패치 코드가 실행되도록 해야합니다.
어느 부분을 고치는게 좋을까요?

위에서 소개한 코드 흐름을 보시면 401083 주소에 JMP OEP 명령어가 있습니다.


<Fig. 7>

JMP OEP(40121E) 명령을 JMP "패치코드" (401280) 로 변경하면 될 것 같습니다.

여기서 주의할 것은 이 영역은 원래 암호화 되어 있는 영역이라는 것입니다.
위의 <Fig. 1> 에 따르면 401083 주소는 [A] 영역에 속하며 XOR 7 로 암호화 되어 있는 영역입니다. (코드 흐름 참조)

<Fig. 7> 은 암호가 풀린 모습이구요, 파일에서 실제로 암호화된 모습은 아래와 같습니다.


<Fig. 8>

암호화된 영역은 file offset 으로 485 까지이며 뒤의 00 00 은 암호화 영역이 아닙니다. (<Fig. 1> 참조)

<Fig. 7> 과 <Fig. 8> 을 비교해 보면 EE 91 06 이 XOR 7 복호화를 통해서 E9 96 01 로 바뀌는 것을 알 수 있습니다.
패치 코드 주소는 401280 이므로 JMP 명령문의 OP code 는 아래와 같습니다.


<Fig. 9>

이 OP code 를 그대로 쓰면 안되고 XOR 7 을 해야 합니다.

E9   XOR 7  =   EE
F8   XOR 7  =   FF
01   XOR 7  =   06

Hex editor 로 아래와 같이 수정합니다.


<Fig. 10>

이로써 Inline Patch 가 완성되었습니다.


#4. 결과 확인


패치된 프로그램을 실행합니다.



<Fig. 11>

인라인 패치 기법을 통하여 암호화된 파일을 간단히 패치하였습니다.

Debugger 로 OEP 코드를 잠깐 살펴 보겠습니다.


<Fig. 12>

원래는 JMP 40121E (OEP) 였는데, JMP 401280 (패치 코드) 로 변경되었습니다. (<Fig. 7>, <Fig. 12> 참고)


<Fig. 13>

위 그림과 같이 패치 코드(Code Cave) 를 실행하게 되면 문자열이 패치되고 마지막에 OEP(40121E) 로 점프합니다.


<Fig. 14>

위 <Fig. 14> 는 패치된 문자열이 Dialog 와 MessageBox 에 사용되는 코드를 보여줍니다.


+---+

최대한 상세히 설명하기 위해 글과 그림이 많아졌습니다.
기본 원리는 간단하므로 일단 원리를 이해한 후 직접 따라해 보시면 쉽게 할 수 있으실겁니다.

Inline Patch 는 그 자체로 흥미 있는 주제이면서 동시에 리버싱 실력을 종합적으로 점검할 수 있는 기회라고 할 수 있습니다. (PE File Format, Debugging, Assembly, etc)

Inline Patch 에 대한 기본 동작원리에 대해서는 아래의 제 글을 참고하세요.

참고 : InlinePatch 실습 (1)


ReverseCore

Trackback Address :: http://www.reversecore.com/trackback/46 관련글 쓰기

  1. Tracked from www.reversecore.com 2009/08/30 22:17 삭제

    Subject: Inline Patch 실습 (1)

    실행압축 되거나 암호화된 파일을 패치할 때 자주 사용되는 Inline Patch(인라인 패치) 기법을 실습해 보겠습니다. Inline Patch Inline Code Patch(인라인 코드 패치) 혹은 줄여서 Inline Patch(인라인 패치) 라고 하는 기법은 원하는 코드를 직접 수정하기 어려울 때 간단히 Code Cave(코드 케이브) 라고 하는 패치코드를 삽입한 후 실행시켜 프로그램을 패치시키는 기법입니다. 주로 대상 프로그램이 실행 압축(혹..
  1. Ezbeat 2010/04/14 13:30 댓글주소 | 수정 | 삭제 | 댓글

    헉.. 예전에 문자열 수정은 아니지만.. 코드를 수정하려고 빈 공간에 어셈코드 넣어서 수정시킨 적이 있는데 ;; 이게 인라인 패치였군요.. 뭣도 모르고 무조건 했는데 이 글을 보니 머리속에서 제가 해왔던게 정리가 되는군요..좋은 글 역시 감사합니다 ^^

    하지만 그 당시엔 그 메모리에 쓰기권한이 있는지 없는지도 확인도 안하고 무작정 했었는데 저 부분도 확인을 역시 해주어야겠군요...

    • reversecore 2010/04/15 03:17 댓글주소 | 수정 | 삭제

      Ezbeat님, 안녕하세요. ^^

      네~ 직접 경험해 보셨으니 금방 이해하실 수 있으시겠네요.

      참고로 메모리 상의 빈 공간이 정말 빈 공간인지 파악하려면 PE 헤더의 섹션 헤더를 참조하시면 됩니다. RVA 와 VirtualSize 항목을 보시면 되겠지요.

      감사합니다.

  2. ka7713 2011/02/03 15:26 댓글주소 | 수정 | 삭제 | 댓글

    보고 있자니 댓글 안달수가 없네요.
    정말 좋은글 감사합니다. 많이 배우고 있습니다.
    답답하고 궁금했던것을 팍팍긁어주시네요.
    앞으로도 좋은글 많이 부탁드려요~

    그런데 crackme 링크가 깨졌네요..

    • reversecore 2011/02/09 21:51 댓글주소 | 수정 | 삭제

      안녕하세요.

      칭찬 감사합니다.

      ap0x 님의 사이트가 더이상 존재하지 않는군요. 아쉽네요.

      파일을 직접 첨부하였습니다.

      감사합니다.



실행압축 되거나 암호화된 파일을 패치할 때 자주 사용되는 Inline Patch(인라인 패치) 기법을 실습해 보겠습니다.



Inline Patch


Inline Code Patch(인라인 코드 패치) 혹은 줄여서 Inline Patch(인라인 패치) 라고 하는 기법은 원하는 코드를 직접 수정하기 어려울 때 간단히 Code Cave(코드 케이브) 라고 하는 패치코드를 삽입한 후 실행시켜 프로그램을 패치시키는 기법입니다.

주로 대상 프로그램이 실행 압축(혹은 암호화) 되어 있어서 파일을 직접 수정하기 어려운 경우 많이 사용되는 기법입니다.

자세한 설명을 위해 아래 그림을 보시기 바랍니다.


<Fig. 1>

<Fig. 1> 의 왼쪽 그림을 보시면 전형적인 실행압축(또는 암호화) 코드가 있습니다. EP 코드는 암호화된 OEP 코드를 복호화 시킨 후 OEP 코드로 점프합니다.
만약 우리가 패치하길 원하는 코드가 암호화된 OEP 영역에 존재한다면 (위치를 알고 있다고 하더라도) 그냥은 패치시키기 어렵습니다. 복호화 과정에서 엉뚱하게 복호화 되기 때문입니다.

위와 같은 문제를 해결하는 간단한 방법은 <Fig. 1> 의 오른쪽 그림과 같이 파일내에 Code Cave 라고 하는 별도의 코드를 설치한 후 EP 코드의 복호화 과정 이후 JMP 명령만 수정하여 Code Cave 가 실행되도록 합니다. Code Cave 내에서 원하는 부분을 패치시킨 후에 (이미 OEP 는 복호화 되었기 때문에 그대로 수정 가능) OEP 로 가면 됩니다.

즉, 실행 될 때마다 메모리 상의 코드를 패치하기 때문에 이러한 패치 기법을 Inline Patch (혹은 Inline Code Patch) 라고 부르는 것입니다.



실습 - Patchme


Inline Patch 를 잘 보여줄 수 있는 간단한 예제를 골라봤습니다.

http://ap0x.jezgra.net/download/patchme_no1.rar
(출처 : ap0x - Reversing Labs)

5 KB 크기의 간단한 예제입니다. 일단 악성코드 검사 후 실행해 보겠습니다.


<Fig. 2>

메시지 박스가 나타납니다. 메시지를 읽어보니 표시 문자열을 변경하라고 합니다.
[확인] 버튼을 누르면 아래와 같은 다이알로그가 표시됩니다.


<Fig. 3>

위 두군데의 문자열을 바꾸면 되는 간단한 patchme 파일입니다.
문제는 파일이 암호화 되어 있어서 쉽게 바꿀 수 없다는 것입니다.

OllyDbg 로 해당 파일을 열어보겠습니다.


<Fig. 4>

EP 코드 시작은 매우 간단합니다. 401007 주소 이후는 암호화된 코드가 보입니다.
<Fig. 2> 와 <Fig. 3> 에서 보이는 메시지를 찾기 위해서 마우스 우측 메뉴의 "Search for > All referenced text strings" 를 선택합니다.


<Fig. 5>

역시 예상대로 모든 문자열이 암호화 되어 있습니다.

디버깅을 더 진행해야 겠군요. 일단 편하게 한번 죽~ 따라갈 겁니다.
CALL 함수를 따라 들어가다 보면 아래와 같은 코드를 만나게 됩니다.


<Fig. 6>

이 코드가 바로 복호화 루프입니다. 4010A3 주소에 XOR BYTE PTR DS:[EBX], 44 명령을 사용하여 특정 영역을 복호화 하고 있습니다. 이것 말고도 몇 군데 더 복호화 루프가 존재합니다.


<Fig. 7>

일단은 끝까지 한번 가보는 것이 목표이기 때문에 계속 진행해 나갑니다.

가다 보면 아래와 같은 Checksum 계산 코드가 나타납니다.


<Fig. 8>

401046 주소의 ADD 명령은 특정 주소 영역내에서 4 byte 단위로 값을 읽어들여서 EDX 에 더하고 있습니다. (overflow 신경쓰지 않고 무조건 값을 더합니다.)
401062 주소의 CMP 명령은 만약 이렇게 계산한 EDX 값이 31EB8DB0 과 다르다면 에러 메시지를 출력하고 프로그램이 종료됩니다.

이러한 Checksum 코드는 특정 영역의 코드/데이타가 변조되지 않았음을 검증하는 용도로 많이 사용됩니다. (해당 영역의 코드/데이타를 변경하고 싶으면 관련 Checksum 부분도 수정해야 합니다.)

Checksum 을 구하는 영역에 우리가 패치하려는 문자열이 존재하겠지요.

Checksum 값이 정확하면 (코드가 변조되지 않았다면) 401083 주소의 JMP 명령어로 OEP(40121E) 로 가게 됩니다.


<Fig. 9>

위 그림이 바로 OEP 입니다. 다이알로그를 실행하는 코드입니다.

아래 그림은 DlgProc(4010F5) 의 코드 입니다.


<Fig. 10>

위쪽에 빨간색으로 표시된 부분이 우리가 패치해야 할 문자열이며, 아래 쪽의 빨간색 표시 부분은 각각 문자열이 다이알로그와 메시지 박스에 사용되는 코드 부분입니다.

이로써 프로그램의 개략적인 흐름과 패치해야 할 문자열의 위치를 알아냈습니다.

이 프로그램은 곳곳이 암호화 되어 있으며, 특히 패치하려는 문자열은 2중으로 암호화 되어 있습니다. 또한 프로그램 내에서 문자열 영역에 대하여 Checksum 값을 구해 변경 여부를 검증하기 때문에 쉽게 수정하기 어렵도록 되어 있습니다.

* 이정도 암호화라면 사실 간단한 편이기 때문에 XOR 암호화와 Checksum 코드를 고려해서 직접 파일을 수정해도 됩니다. 하지만 Inline Patch 실습이 주제이기 때문에 그런 방법을 쓰지 않습니다.

다음번에는 Inline Patch 기법을 이용하여 암호화된 문자열을 패치하는 방법에 대해서 알아보도록 하겠습니다.


Inline Patch 실습 (2)


ReverseCore


Trackback Address :: http://www.reversecore.com/trackback/45 관련글 쓰기

  1. Tracked from www.reversecore.com 2009/08/30 22:17 삭제

    Subject: Inline Patch 실습 (2)

    이전 글에서 Inline Patch 의 기본 개념을 파악하고 실습 예제를 간단히 디버깅 해보았습니다. 이번에는 상세 분석을 하고 Inline Patch 를 적용해 보도록 하겠습니다. 참고 : Inline Patch 실습 (1) 실습 예제 : http://ap0x.jezgra.net/download/patchme_no1.rar (출처 : ap0x - Reversing Labs) 코드 구조 설명의 편의를 위해서 실습 예제 샘플의 코드 구조를 먼저 보여드..
  1. 푹빠진나 2010/09/18 12:59 댓글주소 | 수정 | 삭제 | 댓글

    좋은 글 감사합니다 ^^
    그림<fig.10> 0401121 JMP SHORT 에서 "SHORT"의 의미가 무엇입니까?
    독학할려니 힘드네요

    • reversecore 2010/09/25 13:50 댓글주소 | 수정 | 삭제

      안녕하세요.

      답변이 늦어서 정말 죄송합니다.

      IA32 Instruction 에서 SHORT JMP 라고 표시되는 것은 점프할 주소가 1 byte 거리 이내에 있다는 뜻입니다. 즉, 현재 JMP 명령어 주소에서 -128 ~ 127 영역내에 있다는 의미죠. 참고로 LONG 이라고 표시하면 4 byte 주소로 가야하는 거리라는 뜻입니다.(32bit OS 에서는 LONG 이면 어디든 갈 수 있지요.)

      Instruction 으로는 EB 로 표시합니다. EB 뒤의 1 byte 상수가 바로 거리를 의미하는데 signed 값입니다. 음수면 위로, 양수면 아래로 점프하는 것입니다.

      * 좀 더 자세히 설명드리자면... (EB XX) JMP SHORT YY 명령어에서
      YY = XX + 2 의 공식이 적용됩니다. (2 는 EB XX 명령어 길이를 의미합니다.) <Fig.10> 의 코드를 참조하세요.

      * 이 설명은 쪼끔 어려우시죠? IA32 Instruction 에 대한 약간의 지식이 필요합니다. 지금 당장 이해 안하셔도 됩니다. ^^

      감사합니다.

  2. 통맥 2011/08/31 13:43 댓글주소 | 수정 | 삭제 | 댓글

    실행 파일 다운이 안되요..

  3. 2011/12/12 20:32 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2011/12/21 01:34 댓글주소 | 수정 | 삭제

      안녕하세요.

      checksum 이란 쉽게 생각해서 특정 영역의 모든 바이트를 다 더한 값이라고 생각하시면 됩니다.

      예를 들어 0x10000000 ~ 0x10000003 영역(4 byte)이 아래와 같다면...

      0x23 0x78 0xEA 0x8F

      위 4 byte 영역의 checksum 값은 저 모두를 더한 값 0x214 가 됩니다.

      개발자는 이 값을 미리 알고 있고 만약 해커가 저 영역을 변조 한다면 checksum 값이 변경될테니... 변조 사실을 알 수 있는 것이지요.

      감사합니다.


UPX 실행 압축된 notepad_upx.exe 를 디버깅 함으로써 실행 압축에 대한 개념을 이해합니다. 코드를 하나하나 tracing 하면서 결국 원본 notepad.exe 코드를 찾아내는 것이 목표입니다. 마지막에는 UPX 실행 압축 파일을 디버거에서 간단히 통과하는 팁을 설명하겠습니다.


 

notepad.exe 의 EP code

먼저 원본 notepad.exe 의 EP code 를 보겠습니다.

<Fig. 1>

010073B2 주소에서 GetModuleHandleA() API 를 호출해서 notepad.exe 프로세스의 ImageBase 를 구합니다.
그리고 010073B4 와 010073C0 주소에서 각각 MZ 와 PE 시그니처를 비교하는군요.

원본 notepad.exe 의 EP 코드를 눈에 잘 익혀두시기 바랍니다.

 

notepad_upx.exe 의 디버깅

notepad_upx.exe 를 OllyDbg 로 열어보면 아래와 같은 경고 메시지 박스가 나타납니다.

 

<Fig. 2>

디버거가 해당 파일이 압축되었다고 판단하였습니다.
상관 없으니 Y/N 아무거나 선택해서 넘어가면 아래 그림과 같이 UPX stub 코드가 나타납니다.

 

<Fig. 3>

EP 주소는 01015330 으로서 두 번째 섹션의 끝부분입니다.
실제로 압축된 notepad 원본 코드는 EP 주소 위쪽으로 존재합니다.

코드 시작 부분을 살펴보겠습니다.

01015330    60              PUSHAD                
01015331    BE 00100101     MOV ESI, 01011000
01015336    8DBE 0000FFFF   LEA EDI, DWORD PTR DS:[ESI+FFFF0000]

PUSHAD 명령으로 EAX ~ EDI 레지스터 값을 스택에 저장하고,
ESI 와 EDI 레지스터를 각각 2nd 섹션 시작과 1st 섹션 시작 주소로 세팅합니다.
ESI 와 EDI 는 각각 source 와 destination 버퍼를 가리킨다는 것은 다 알고 계실 것입니다.

이후에 나타나는 코드는 <Fig. 3> 에서 처럼 수많은 조건 분기명령어들로 이루어진 코드가 나타납니다.
이 코드가 바로 압축 해제 코드입니다. (ESI 에서 값을 읽어서 EDI 에 써주는 코드)

우리 목표는 이 코드를 전부 tracing 해서 결국 <Fig. 1> 과 같이 원본 notepad 의 EP 코드를 찾아내는 것입니다.

* 리버싱에서는 원본 EP 를 OEP(Original Entry Point) 라고 표현합니다.

* 실제 리버싱에서는 이러한 실행 압축 코드를 일일이 tracing 하지 않고 자동화 스크립트, 노하우 등을 통하여 OEP 로 바로 갑니다. 하지만 실행압축 리버싱을 처음 공부하는 사람들의 입장에서는 하나하나 코드를 tracing 하는 것이 올바른 방법입니다.

이제부터 저와 같이 Tracing 을 시작해 보겠습니다.
일단 아래 동영상을 보시죠.

<Video. 1>

조금 trace (F7) 하다 보면 짧은 루프가 나타납니다.
해당 루프를 아래 그림에서 더 자세히 살펴보겠습니다.

<Fig. 4>

루프 회전 수 ECX = 36Bh 이고, 그 내용은 EDX (01001000) 에서 한 바이트를 읽어 EDI (01001001) 에 쓰는 것입니다.

01001000 주소는 첫 번째 섹션 (“UPX0”) 주소이며, 메모리 상에서만 존재하는 섹션입니다.
(내용은 전부 NULL 입니다.)

* 참고 : 실행 압축 (Run-Time Packer) 에 관하여

실행 압축된 파일을 디버깅 할 때 이러한 루프를 만났다면 그 루프를 빠져 나와야 합니다.

010153E6 주소에 BP 를 설치 후 실행 명령(F9) 으로 해당 루프를 탈출합니다.

조금 더 진행하다 보면 다시 아래와 같은 루프를 만나게 됩니다.

<Video. 2>

이것이 본격적인 디코딩(decoding) 루프입니다.
ESI 가 가리키는 두 번째 섹션(“UPX1”) 의 주소에서 차례대로 값을 읽어서 적절한 연산을 거쳐 압축을 해제하여 EDI 가 가리키는 첫 번째 섹션(“UPX0”) 의 주소에 값을 써주게 됩니다.

이 과정에서 사용되는 명령어들은 아래와 같습니다.
AL/EAX 에는 압축이 풀린 데이타가 있고, EDI 는 첫 번째 섹션의 주소를 가리키고 있습니다.

0101534B    8807    MOV BYTE PTR DS:[EDI],AL
...
010153E0    8807    MOV BYTE PTR DS:[EDI],AL
...
010153F1    8907    MOV DWORD PTR DS:[EDI],EAX

이 두 번째 루프를 탈출하기 위해서는 아래 그림과 같이 01015402 주소에 BP 를 설치하면 됩니다.

 

<Fig. 5>

01015402 주소까지 디버깅을 하셨다면 위 그림처럼 첫 번째 섹션(“UPX0”) 영역에 코드가 쓰여진걸 확인 할 수 있습니다. (원래는 0 으로 채워져 있던 영역입니다.)

조금 더 내려가면 아래와 같은 루프를 만나게 됩니다.

 

<Fig. 6>

이것은 원본 코드의 CALL/JMP 명령어(op code : E8/E9)의 destination 주소를 복원시켜주는 코드입니다.
이 루프도 01015436 주소에 BP 를 설치하여 탈출할 수 있습니다.

이제 거의 막바지입니다. 
IAT 세팅만 하면 UPX 압축 해제 코드는 끝납니다.
조금만 더 내려가 보겠습니다.

 

<Fig. 7>

위 그림에서 파란색으로 표시된 부분이 바로 IAT 를 세팅하는 루프입니다.

01015436 주소에서 EDI = 01014000 으로 세팅되며, 이곳은 두 번째 섹션(“UPX1”) 영역입니다.
이곳에는 원본 notepad.exe 에서 사용되는 API 이름 문자열이 저장되어 있습니다.
이 문자열들을 가지고 01015467 주소의 GetProcAddress() 를 호출하여 API 시작 주소를 얻은 후 EBX 레지스터가 가리키는 원본 notepad.exe 의 IAT 영역에 API 주소를 입력합니다.

이 과정을 API 이름 문자열이 끝날 때까지 반복하면 원본 notepad.exe 의 IAT 를 완벽히 복원하게 됩니다.

원본 notepad.exe 의 압축을 모두 해제하였으므로, OEP 로 제어를 돌려줘야겠지요?
아래 그림이 바로 OEP 로 jump 하는 코드입니다.

 

<Fig. 8>

010154AD 주소의 POPAD 명령은 바로 UPX 코드 중 가장 첫 번째 명령인 PUSHAD 에 대응되는 명령으로써 이제 레지스터를 원래대로 복원시키는 명령입니다. (<Fig. 3> 참고)

최종적으로 010154BB 주소의 JMP 명령어로 OEP 로 갑니다.
JMP 하는 주소 0100739D 는 원본 notepad.exe 의 EP 주소입니다. (각자 확인해보세요.)

 

UPX 의 OEP 를 빨리 찾는 방법

위 설명대로 tracing 을 잘 하셨나요?
리버싱 초보자라면 꼭 한번 해보시기 바랍니다.
다른 packer 로 압축된 파일을 디버깅 할 때 큰 도움이 될 것입니다.

매번 위와 같은 방법(루프 탈출)으로 OEP 로 가는 것은 힘들겠지요?

실전 리버싱에서는 간단히 OEP 로 들어갑니다.
그 방법들을 설명 드리겠습니다. (UPX 기준입니다.)

#1. POPAD 명령어 이후의 JMP 명령어에 BP 설치

UPX 는 POPAD 명령어 이후에 나오는 JMP 명령어가 바로 OEP 로 가는 명령어 입니다.
이곳에 BP 를 설치하고 실행하면 바로 OEP 로 갈 수 있습니다.

#2. Hardware BP 설치

이 방법도 UPX 특징인 PUSHAD/POPAD 명령어의 특성을 이용하는 것입니다.

<Fig. 3> 에서 01015330 주소의 PUSHAD 명령을 실행한 후 스택을 보면 아래와 같습니다.

<Fig. 9>

EAX 에서 EDI 까지 스택에 차곡차곡 저장되어 있습니다.

OllyDbg의 Dump 윈도우에 저 스택 주소 0006FFA4 를 표시한 후 마우스 우측 메뉴를 이용하여 Hardware BP 를 설치합니다.

<Fig. 10>

Hardware BP 란 CPU 에서 지원하는 BP 이며 4개까지 설치 가능합니다.
일반적인 BP 와 다른 Hardware BP 의 특징은 BP 설치된 명령어가 실행 된 이후에 제어가 멈추게 됩니다.

이 상태에서 실행하게 되면 압축이 해제되고 POPAD 가 호출되는 순간에 Hardware BP 가 설치된 0006FFA4 주소를 access 하게 되고, 그때 제어가 멈춥니다. 바로 밑에 OEP 로 가는 JMP 명령어가 있지요.

+—+

이상으로 UPX 실행압축 파일의 디버깅 설명을 마치도록 하겠습니다.

초보자 분들께서는 위 설명대로 직접 디버깅 하여 루프를 하나하나 탈출해서 OEP 로 가보시길 권해드립니다.

여러분들의 디버깅 실력이 부쩍 향상됨을 느낄 수 있으실 겁니다.

 

Trackback Address :: http://www.reversecore.com/trackback/35 관련글 쓰기

  1. Tracked from www.reversecore.com 2009/06/20 02:06 삭제

    Subject: 실행 압축 (Run-Time Packer) 에 관하여

    실행 압축(Run-Time Packer)은 소프트웨어 역공학 (Reverse Code Engineering)의 단골 주제입니다. 이를 잘 이해하기 위해서는 PE 파일 포멧과 운영체제의 기본 적인 사항들(프로세스, 메모리, DLL, etc)에 대해 잘 알고 있어야 하며, 또한 압축/해제(Compress/Decompress) 알고리즘에 대한 기본 적인 내용에 대해서 알아야 합니다. 이미 많은 부분은 제 블로그에서 다뤘던 내용들입니다. 실행 압축에 대한..
  1. 1phee 2009/11/18 18:08 댓글주소 | 수정 | 삭제 | 댓글

    아.. 언팩은 하나도 몰랐는데.. 요새는 시간이 없어서 크랙미를 못풀지만 예전에는 언팩의 언자도 몰라서 크랙미 풀때마다 패킹안된거만 찾아다녔던 기억이 나네요..ㅎㅎ 어쨌든 이런식으로 UPX 언팩에 대해서 자세하게 설명하는 글은 처음 보네요! 언팩에 대해서 전혀 개념도 없다가 이글보고 대략 감 잡았습니다. UPX가 쉬운 패킹이라 그런지 이해가 잘 되네요. 잘 보았습니다. ^^

    • reversecore 2009/11/20 00:16 댓글주소 | 수정 | 삭제

      1phee님, 안녕하세요.

      Unpack 의 기본원리는 거의 비슷합니다.
      압축풀고 IAT 만들고 OEP 로 가는거죠.

      여기에 각종 옵션이 끼어들면서 복잡해지는 것입니다.

      다른 패커에도 도전해 보세요~

  2. 시간의흔적 2010/03/31 10:52 댓글주소 | 수정 | 삭제 | 댓글

    너무너무 글 잘 보고 갑니다.. 어렵지만 열심히 볼께요 ^^

    • reversecore 2010/04/01 10:11 댓글주소 | 수정 | 삭제

      시간의흔적님, 안녕하세요.

      네~ 공부하시다가 의문 사항 있으시면 질문 남겨주세요~ ^^

      감사합니다.

  3. 안정현 2010/06/23 04:19 댓글주소 | 수정 | 삭제 | 댓글

    지금 나이지리아 전 하는데 그냥 안보고 블로그보는중 ㅡ.,ㅡ;; ㅋㅋㅋ 지금현재는 제삶의 낙인듯 ㅋㅋㅋㅋㅋ 미친듯이해서 달려왔는데
    hook.exe 같이 소스코드 짜놓아서 주석달아 설명해주신것들있잖아여~
    c를 공부하긴했는데 소스파일 들이 주석들봐도 해석이안돼더라구요..
    해석을 할수있을려면 시스템프로그래밍을 공부따로해야하나요 ㅋ

    api를 몰라서그런가..
    암튼 잘읽고있습니당~
    중독성이강하네용 ㅋㅋ 한페이지만더읽고자야지 하다가 밤샐기세 ㄷㄷㄷㄷ ㅋㅋㅋ

    • reversecore 2010/06/23 21:21 댓글주소 | 수정 | 삭제

      월드컵보다 제 블로그를 선택해 주시다뇨... @@~
      리버싱 공부에 대한 열정과 각오가 남다르신 것 같습니다.

      C 언어 예제 소스가 잘 이해안가셨나요?

      사실 Win32 API 프로그래밍 경험이 없으시면 좀 어려울 수 있습니다. 그럴때 질문올려주세요~ ^^

      감사합니다.

  4. newbie 2010/07/15 19:08 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 :)
    게시글 잘 보고 있습니다.
    궁금증이 생겨서 질문드리고 싶은데
    언패킹부분중에 E8, E9 찾아서 주소값 수정해주는 부분이 있는데
    이건 왜 그렇게 해주는거죠? 그냥 통째로 원본 소스를 풀어서 UPX0부분에 넣었다면 고쳐줄 내용이 하나도 없지 않나요..또 EAX로 주소를 받아서 쉬프트연산등을 해주는거 같은데 그건 왜그런걸까요? 패킹할때 주소를 거꾸로 저장해놓은건지...


    그리고 원본 파일의 IAT부분은 압축할때 코드랑 같이 된거 아닌가요? 보니까 IAT부분을 기존의 위치가 아닌 UPX1 위치에다 만들어 주는거 같던데.. 그냥 기존 IAT부분에 채워주지않고 왜 새로 만들어주는건지 궁금하네요

    마지막으로 stolen byte가 왜 생기는지 알고 계시면 알려주세요 ㅜㅡ

    너무 두서없이 질문만했네요
    다시한번 좋은 블로그 만들어주셔서 감사합니다

    • reversecore 2010/07/15 23:38 댓글주소 | 수정 | 삭제

      안녕하세요.

      애석하게도 저 또한 UPX 에서 왜 E8, E9 를 보정해 주는지 이유를 잘 모르겠습니다. 모든 패커에서 나타나는 일반적인 특징이라고 볼 수 도 없고 말이죠. 복잡한 연산은 UPX 프로그래머가 그렇게 만든 것이구요. 4 byte 를 디코딩 해서 원래 값으로 복원시키는 명령입니다. 어떻게 연산하던 자유니까요.

      IAT 는 원본 IAT 영역에 정상적으로 풀어주고 있습니다. 새롭게 GetProcAddress() 를 호출해서 만드는 이유는 압축된 PC 의 kernel32.dll 주소와 실행되는 PC 의 kernel32.dll 의 주소가 다를 수 있기 때문입니다.

      stolen byte 는 아마 protector 에서 사용되는 그 stolen byte 말씀이시죠?

      쉽게 덤프 뜨지 못하도록 그런겁니다.
      그 상태로 덤프 떠버리면 stolen byte 때문에 정상 실행이 안됩니다. packer 로 정상 실행 된 경우만 stolen byte 에 해당되는 (혹은 유사한) 코드를 실행하여 stack, register 를 맞춰준 후 그 이후 코드를 실행하기 때문에 잘 실행되는 것이죠.

      괜히 설명이 복잡한데요... protector 류를 한번 분석해 보시면 금방 파악하실듯... ^^

      * 굉장히 깊이 있게 분석을 하셨나 봅니다. ^^ 자주 들러 주시고요... E8/E9 의 이유를 알게 되시면 저에게도 공유해 주세요~

      감사합니다.

  5. newbie 2010/07/18 20:04 댓글주소 | 수정 | 삭제 | 댓글

    친절한 답변 감사드립니다 :)
    시간이 많이 걸리겠지만 포스팅하신거 다 보고 싶네요
    또 궁금한거 있으면 여쭤볼께요
    좋은시간보내세요~

  6. newbie 2010/07/19 19:19 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 또 이렇게 질문드리게되네요
    앞으로 질문으로 도배를 할것 같은 느낌이...
    다름이아니고 언패킹과정에서 덤프를뜨고 IAT를 다시설정해주지않으면 초기화에러가나는데
    도대체 이게왜 생기는걸까요 ?
    사실 OEP만 찾으면 패커에 의해 IAT는 복구가 된 상태고 실행하는데 문제가 없잖아요
    (PE헤더에는 제대로 바껴있지 않지만..)
    로더가 어떤 원리로 PE헤더에 있는 IAT가 패커가 사용한 다른 IAT인지 알고 에러를 내는걸까요?
    코드하나하나보면서 API가 나올때마다 IAT랑 대조해볼것 같지는 않은데..
    어차피 그냥 적힌대로 잘못된 IAT지만 그걸 채우고 실행만 하면 정상 실행이 될것 같은데..
    많은 자료를 찾아봤지만 단지 IAT가 복구되지 않아서 에러를 낸다는 설명들만 있는데 왜 그런지에
    대한 설명은 없네요 혹시 알고 계시면 알려주세요
    감사합니다!

    • reversecore 2010/07/19 23:12 댓글주소 | 수정 | 삭제

      예를 들어 UPX 파일을 실행시킨 후 덤프하면 IAT 에는 정확한 API 주소가 들어있지요. 하지만 INT 가 깨져있습니다.

      PE 로더는 INT 에서 API 이름 문자열을 얻어서 LoadLibrary()/GetProcAddress() 조합을 사용하여 실제 API 주소를 얻어서 IAT 에 적어주는데요. 이 과정에서 실행 에러가 발생하는 것입니다.

      간혹 어떤 패커들은 IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk (INT) 를 0 으로 세팅하기도 합니다.

      그래도 잘 실행되는 이유는 IMAGE_IMPORT_DESCRIPTOR.FirstThunk (IAT) 를 따라가면 이름 문자열이 나오고 이를 바탕으로 함수 주소를 구할 수 있기 때문이지요.

      근데 UPX 덤프 파일은 OriginalFirstThunk, FirstThunk 어느것을 따라가도 이름 문자열이 나오지 않습니다. FirstThunk 에 가보면 실제 함수 주소들만 있지요. 그건 PE 로더가 원하는게 아니거든요.

      그래서 실행 에러가 발생하는 것입니다.

      잘 이해안가시면 다시 질문 올려주세요~

      감사합니다.

  7. newbie 2010/07/20 18:14 댓글주소 | 수정 | 삭제 | 댓글

    답변 정말 감사합니다
    궁금증이 해결되었습니다 :)
    디버거로만 패킹된 파일과 덤프된 파일의 PE헤더를 확인하다보니까 두개가 같아서 착각을 했네요..
    패킹된 파일은 이미 로더가 처리를 해서 그렇게 보였던 것인데...

    리버싱관련 공부하면 궁금증이 생겨도 친절이 알려주는 분이 없어서 답답했는데 정말 근사한 오아시스를 발견했네요 :) 좋은 시간 보내시길 바랍니다~

  8. vio 2011/03/25 16:32 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요
    올려주신 글 덕분에 정말 하루하루 리버싱실력이 느는것 같습니다. 감사드립니다
    질문이 한가지 있는데요
    리버싱을 하다보면 모르는 어셈명령어들때문에 뭔지 모를때가 많습니다
    이번 upx 파일 트레이싱을 하는데도 처음보는 명령어들이 너무많습니다.
    어셈블리 명령어 잘 정리된 사이트를 아시나요?
    인텔 매뉴얼?을 언듯 어디서 들은거같은데 못찾겠더군요
    모르는 명령어를 찾아볼 수 있는 사이트를 알려주시면 감사하겠습니다

  9. 2011/05/04 08:40 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2011/05/03 00:45 댓글주소 | 수정 | 삭제

      안녕하세요.

      그런 파일도 리버싱의 원리가 다르지 않습니다.
      그냥 EP 부터 하시면 되구요.

      대부분의 프로텍터는 스레드가 많이 실행됩니다.
      물론 디코딩 스레드가 대부분 이지요.

      감사합니다.

  10. 2011/05/04 08:41 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2011/05/03 00:42 댓글주소 | 수정 | 삭제

      안녕하세요.

      음, 질문 하시는 내용이 본문 내용과 동떨어진 내용들이군요?

      질문이 잘 이해되지 않습니다. ^^~

      감사합니다.

  11. 아장아장 2011/05/12 23:34 댓글주소 | 수정 | 삭제 | 댓글

    이미 알고있었다고 생각했었는데 다시 보니 새로게 느껴져요.
    째인 설명 인상에 남았습니다.

    한가지 의문은 c++에서 클라스함수호출시 어떤 호출규약을 쓰고 이때 탄창을 어떻게 리용하는지 상세히 알고싶은데요...
    시간나는대로 이 내용을 주재로 하는 강좌 내와주시든가 댓글 보내주시면 고맙겠습니다.

    열심히 강좌 찾겠습니다.

    • reversecore 2011/05/13 20:10 댓글주소 | 수정 | 삭제

      안녕하세요.

      C++ 에서는 C 와 마찬가지로 cdecl 방식을 사용합니다.

      탄창이요? 스택(stack)을 말씀하시는 거죠?
      cdecl 에서는 Caller 가 stack 을 정리합니다.

      향후 C++ 클래스 멤버 함수 호출 관련한 내용을 블로그에 올릴 예정입니다. ^^

      감사합니다.

  12. 2011/06/29 18:54 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요! 여쭤볼게 있습니다 ㅠ.ㅠ
    선생님 말씀대로 루프들 다 빠져나와서 OEP를 찾았는데요
    찾고나서 dump를 뜨면 실행압축 해제한 실행파일이 생성되는건가요?
    LordPE라는 tool을 써야 된다고 하는 사람들도 있던데...
    해제된 실행파일은 어떻게 얻을수 있을까요??
    그리고... 디버깅을 안하고 notepad_upx.exe 를 olldbg로 돌렸을때
    밑으로 쭉~ 내려서 000000많은 부분에서 바로 위에있는 JMP 쪽이
    바로OEP라고 생각해도 되는건가요~?

  13. TeamKhan 2011/09/16 19:59 댓글주소 | 수정 | 삭제 | 댓글

    어떻게 ESI EDI 가 가르키는 주소가 몇번째 섹션에 해당하는 주소인지 어떻게 파악하는지 모르겟습니다.
    그리고 어떤 어셈블리어 코드가 IAT 를 복구해주는 부분의 코드라는걸
    알수있는지 또 이 부분이 어떻게 디코등 루프부분의 코드라는걸 알수있으셧는지
    참으로 궁금합니다.

    • reversecore 2011/09/19 20:10 댓글주소 | 수정 | 삭제

      안녕하세요.

      메모리 복사 명령에서 ESI 는 Source를, EDI 는 Destination 을 각각 의미합니다. 따라서 ESI/EDI 가 가리키는 주소를 본 후 PEView 로 파일을 확인하면 어느 섹션을 가리키는지 알 수 있지요. OllyDbg 의 "Memory Map" 을 확인해도 어느 섹션인지 알 수 있습니다.)

      IAT 복구 하는 것은 GetProcAddress() 호출하면서 API 함수 주소를 반복적으로 얻는 것을 보고 알 수 있었습니다.

      그리고 저는 몇 번의 Unpack 경험이 있기 때문에 처음 하시는 분들보다는 좀 더 수월하게 (예측해 가면서) 진행할 수 있었던 것 같습니다.

      답변이 되었는지요?

      감사합니다.

  14. HAZYCODE 2011/11/25 10:30 댓글주소 | 수정 | 삭제 | 댓글

    010073B2 에서 MZ 를 비교하신다고 했는데 010073B4 를 잘못 적으신게 아닌가 합니다.

  15. 리버싱 초보 2012/04/26 17:56 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요~질문 좀 할께요...

    일단 UPX로 Packing된 MUP로 OEP지점을 찾고 덤프를 떴습니다.
    그리고 나서 ImportREC으로 OEP지점을 수정하고 AutoSearch하면
    Original IAT RVA found at : 00003000 in Section RVA: 00001000 Size:00007000
    이런 식의 메세지를 뿌리게 됩니다.

    저는 여기서 00003000 지점을 찾는 방법이 궁금합니다.

    MUP과정에서 Ollydbg로 분석을 해보면 00003000지점은 각 DLL 내의 함수의 실제 주소를 적어놓은 지점 입니다. FirstThunk가 되겠죠.

    그런데 FirstThunk의 위치를 가르키고 있는것은 원래 NT Header의 Optional Header의 Import Table을 따라가서 알 수 있는 것으로 알고 있습니다.

    그런데 UPX의 압축을 풀고 OEP로 도달한 시점에서 메모리에 Import Table위치에는 Kernel32.dll::Loadlibrary, kernel32.dll::GetprocAddress밖에 없습니다.

    그렇다면 Import REConstructor는 어떤 지점의 Import Table을 읽고 00003000위치가 FirstThunk라는 것을 알며 DLL Name은 어디서 알수 있는 것일까요?

    그리고 한가지가 더 있는데...

    Unpack이 완료된 후 UPX0의 확장된 메모리 영역에는 DLL내의 함수들 Name | 여러주소 수정을 위한 Offset? | NT Header | Setction Header 가 있는거 같은데 NT Header와 Section Header는 Packing전 원본 Header인듯 보입니다. 이것은 쓰지 않는건가요?




KeyboardProc - 키보드 훅 프로시저 디버깅


현재까지의 상황을 간단히 정리해보면 아래와 같습니다.

- KeyLogger.exe 실행 => global keyboard message hook 설치
- notepad.exe 실행
- OllyDbg 로 notepad.exe 를 attach 하고 "Break on new module (DLL)" 옵션 설정
- notepad 에서 키보드 메시지 이벤트 발생 ('a' 키 입력)
- notepad.exe 프로세스에 KeyLogger.dll 이 인젝션 됨
- OllyDbg 에서 KeyboardProc(훅 프로시저) 에 BP 설치

여기까지 진행이 안되셨거나 잘 이해 안되는 내용이 있다면 이전 포스트를 다시 읽어보시기 바랍니다.

키로거(KeyLogger) 분석 (2)

일단 MSDN 의 KeyboardProc 함수 정의를 보겠습니다.

LRESULT CALLBACK KeyboardProc(          
    int code,                  // HC_ACTION(0), HC_NOREMOVE(3)

    WPARAM wParam,             // virtual-key code 
    LPARAM lParam              // extra information
);

위 파라미터 중에서 wParam 은 사용자가 누른 키보드의 virtual key code 를 의미합니다.
키보드의 하드웨어적인 의미로써 영문 'A' 와 'a' 그리고 한글 'ㅁ' 은 모두 같은 virtual key code 를 가집니다.

또한 lParam 값에는 각 bit 별로 다양한 의미를 가지고 있습니다.
(repeat count, scan code, extended-key flag, context code, previous key-state flag, transition-state flag)

ToAscii() API 함수를 사용하면 실제 눌려진 키보드의 ASCII 값을 구할 수 있습니다.



KeyLogger.dll 디버깅


키보드 훅 프로시저인 KeyboardProc() 코드를 집중적으로 살펴보겠습니다.
KeyboardProc() 주소에 BP 를 설치한 후 notepad.exe 에서 'a' 키를 누르면 아래와 같이 디버깅이 가능합니다.
복잡한 분기가 없으므로 그냥 죽 따라가시면 됩니다.

; KeyboardProc(code, wParam, lParam)
;  code = [EBP+8]
;  wParam = [EBP+C]
;  lParam = [EBP+10
]


1000103A    PUSH EBP
1000103B    MOV EBP,ESP                         ; stack frame 생성
1000103D    SUB ESP,330

; 파라미터 체크
10001043    CMP DWORD PTR SS:[EBP+8],0          ; code == 0 (HC_ACTION) 일반적인 경우 이값은 0
10001047    JGE SHORT KeyLogge.10001066         ; (jump)
...
10001066    CMP DWORD PTR SS:[EBP+8],3          ; code == 3 (HC_NOREMOVE) 일반적인 경우 이값은 0
1000106A    JNZ SHORT KeyLogge.1000108A         ; (jump)
...
1000108A    LEA EDX,DWORD PTR SS:[EBP-210]
10001090    PUSH EDX
10001091    CALL DWORD PTR DS:[<&KERNEL32.GetLocalTime>]
10001097    CMP DWORD PTR SS:[EBP+10],80000000  ; lParam == 80000000, press(0)/release(1) 판별
                                                ; 일반적으로는 lParam = 0 (press)
1000109E    JB KeyLogge.1000117F                ; (jump)
                                                ; 제작자는 키가 눌렸을때만(press) 키로깅을 원함
...
1000117F    CMP DWORD PTR DS:[10004108],0       ; [10004108] => 전역변수 (사용되지 않음!)
10001186    JE SHORT KeyLogge.10001190          ; (jump)

; hook chain 의 다음 프로시저 호출
; -> 키 이벤트 메시지를 다음(훅, 어플리케이션)으로 전달

10001190    MOV EAX,DWORD PTR SS:[EBP+10]       ; lParam
10001193    PUSH EAX
10001194    MOV ECX,DWORD PTR SS:[EBP+C]        ; wParam
10001197    PUSH ECX
10001198    MOV EDX,DWORD PTR SS:[EBP+8]        ; code
1000119B    PUSH EDX
1000119C    MOV EAX,DWORD PTR DS:[10004110]     ; [10004110] = hHook (훅 핸들) => 전역변수
100011A1    PUSH EAX
100011A2    CALL DWORD PTR DS:[<&USER32.CallNextHookEx>] ; 키보드 메시지를 다음으로 전달
...

; key log file 생성 (KeyLog.txt)
100011E2    PUSH 0
100011E4    PUSH 80                             ; FILE_ATTRIBUTE_NORMAL
100011E9    PUSH 4                              ; OPEN_ALWAYS
100011EB    PUSH 0
100011ED    PUSH 3                              ; FILE_SHARE_READ|FILE_SHARE_WRITE
100011EF    PUSH 40000000                       ; GENERIC_WRITE
100011F4    PUSH KeyLogge.10004004              ; <log file path("...\KeyLog.txt")>
100011F9    CALL DWORD PTR DS:[<&KERNEL32.CreateFile>]

; 파일 마지막으로 이동
100011FF    MOV DWORD PTR SS:[EBP-320],EAX                
10001205    PUSH 2                              ; FILE_END
10001207    PUSH 0                                        
10001209    PUSH 0
1000120B    MOV ECX,DWORD PTR SS:[EBP-320]
10001211    PUSH ECX                            ; ECX = hFile (file handle)
10001212    CALL DWORD PTR DS:[<&KERNEL32.SetFilePointer>]  

; 입력된 키를 ASCII 값으로 변환
1000133C    PUSH 0
1000133E    LEA ECX,DWORD PTR SS:[EBP-180]
10001344    PUSH ECX
10001345    LEA EDX,DWORD PTR SS:[EBP-318]
1000134B    PUSH EDX
1000134C    MOV EAX,DWORD PTR SS:[EBP+10]       ; lParam (scan code)
1000134F    PUSH EAX
10001350    MOV ECX,DWORD PTR SS:[EBP+C]        ; wParam (virtual key)
10001353    AND ECX,0FFFF                       ; wParam 의 하위 16 bit 만 사용
10001359    PUSH ECX
1000135A    CALL DWORD PTR DS:[<&USER32.ToAscii>]
...

; 변환된 ASCII 값을 file (KeyLog.txt) 에 기록
1000144F    PUSH 0
10001451    LEA EAX,DWORD PTR SS:[EBP-31C]
10001457    PUSH EAX
10001458    LEA ECX,DWORD PTR SS:[EBP-180]
1000145E    PUSH ECX                                       
1000145F    CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>]       
10001465    PUSH EAX                            ; buffer length
10001466    LEA EDX,DWORD PTR SS:[EBP-180]      ; buffer
1000146C    PUSH EDX
1000146D    MOV EAX,DWORD PTR SS:[EBP-320]      ; hFile
10001473    PUSH EAX
10001474    CALL DWORD PTR DS:[<&KERNEL32.WriteFile>]      
...
100014B2    MOV EDX,DWORD PTR SS:[EBP-320]      ; hFile
100014B8    PUSH EDX
100014B9    CALL DWORD PTR DS:[<&KERNEL32.CloseHandle>]

; KeyboardProc() 함수 리턴
100014BF    MOV EAX,DWORD PTR SS:[EBP-214]
100014C5    MOV ESP,EBP                         ; stack 정리
100014C7    POP EBP
100014C8    RETN 0C

핵심적인 코드만 살펴보았습니다.

위 KeyboardProc() 함수 내용을 간단히 정리해보면 아래와 같습니다.

  • 입력 파라미터 체크
  • CallNextHookEx() 호출
  • 로그 파일("KeyLog.txt") 열기
  • 파일 포인터를 마지막으로 이동
  • 입력된 키 값을 ASCII 값으로 변경
  • 파일에 기록

한 글자 입력할 때마다 KeyboardProc() 함수가 호출되어서 KeyLog.txt 파일에 기록하는 알고리즘입니다.


좀 더 정교한 키로거를 만들고 싶을때는 특수키(F1 ~ F2, Ins, Del, PgDn, PgUp, etc) 등을 사람이 알기 쉽게 변환해 주고,
Backspace 와 Del 키의 기능을 실제로 구현하여 사용자가 읽기 쉽게 해줍니다.
또한 키입력마다 파일에 저장하는게 아니라 일정 버퍼를 모았다가 system idle time 에 저장하는 등의 처리를 해주면
훨씬 효율적으로 동작할 수 있습니다.

만약 키로깅 내용을 메일로 전달받고 싶을 때는 email 전송 모듈을 붙이면 되겠죠?


이상으로 메시지 훅 기법을 이용한 키로거의 동작 원리와 분석 방법에 대해 알아보았습니다.



Trackback Address :: http://www.reversecore.com/trackback/28 관련글 쓰기

  1. lucifer 2011/01/04 16:23 댓글주소 | 수정 | 삭제 | 댓글

    좋은 글 감사합니다.
    항상 따뜻한 나날 되시길 바라며...

  2. TeamKhan 2011/06/03 19:43 댓글주소 | 수정 | 삭제 | 댓글

    code = [EBP+8]
    ; wParam = [EBP+C]
    ; lParam = [EBP+10]
    ================
    KeyboardProc 함수의 인자값들의 위치
    그리고 로그파일 생성 코드부분, 이코드부분이 파일 마지막으로 이동하는 코드다
    입력된 키를 ASCII 로 변환하는 코드들을 어셈만 보고 다 파악하신건가요?
    그렇다면 어떻게 어셈블리어만 보시고 그런 것들을 유추해내실수 있으셧는지 궁금하내요..
    아...제가 디버깅을 많이 안해봐서 그런건진 몰라도
    어셈블리어는 알고있지만 도저히 어떻게 분석해야될지 감이 안잡히내요...ㅠ

    • reversecore 2011/06/03 21:03 댓글주소 | 수정 | 삭제

      안녕하세요.

      물론 순전히 위 디스어셈 코드를 읽고 알아낸 것입니다.

      이미 함수 정의를 보면 파라미터의 이름, 타입, 순서를 알 수 있구요.

      각 파라미터가 스택의 어떤 값과 연결 되는지는 "스택 프레임" 알고 있다면 쉽게 이해할 수 있습니다.
      (code = [EBP+8], wParam = [EBP+C], lParam = [EBP+10])

      그리고 로그 파일의 마지막으로 함수 포인터를 옮기는 것은 SetFilePointer() API 를 MSDN 에서 조사한 다음 위 디스어셈 코드에 맞춰서 그대로 해석하시면 됩니다.

      제가 말은 쉽게 했지만 처음 보시는 분께서 잘 모르시는 것은 당연한 것입니다.

      위 코드를 여러차례 디버깅 해보시기 바랍니다. 각 API 를 검색해 보시고 위 디스어셈 코드를 한줄한줄 디버깅 해보세요. 차츰 눈에 익으실 것입니다.

      잘 이해 안가시면 다시 질문해 주세요~

      감사합니다.



분석용 키로거


본문에서 소개되는 키로거 파일을 공개하지 않도록 결정하였습니다. 
 
파일 정보에 대한 요청 또한 거절하겠습니다. 댓글과 이메일로 요청하셔도 저는 답변드리지 않겠습니다.

혹시라도 모를 피해를 막기 위한 최소한의 방지책이므로 블로그 방문하시는 분들의 많은 양해를 바랍니다.

* 경고!
이전 포스트에서 설명드린 바와 같이 키로거들은 AV(Anti-Virus) 제품에서 진단/삭제 됩니다. (위의 분석용 키로거 포함)
즉, 키로거는 컴퓨터 바이러스(트로잔) 라는 얘기입니다.
키로거를 이용하여 다른 사람의 키로깅을 시도하는 행위는 법적으로 문제가 발생할 수 있습니다.
여러분께서 키로거를 검색하고 다운 받아 실행하실때는 이점을 반드시 주의하시기 바랍니다!
부디 분석가로서 키로깅을 공부하는 목적으로만 사용해 주시기 바랍니다!

제가 위 제품을 선택한 이유는 아래와 같습니다.

  • 코드가 단순하여 분석이 쉬움
  • 가장 기본적인 형태의 키로거 동작원리(메시지 훅)를 잘 보여줌
  • 별도의 악의적인 기능(정보 유출)이 없음

위의 분석용 키로거는 윈도우 메시지 훅(Windows Message Hook) 기법을 사용합니다.
메시지 훅에 대해서는 예전에 제가 쓴 글을 참조하세요

Windows Message Hooking (윈도우 메시지 후킹)



파일 분석


분석용 키로거는 2 개의 파일로 구성되어 있습니다.

  • KeyLogger.dll : 실제 키로거 모듈
  • KeyLogger.exe : 키로거 컨트롤러 (간단한 사용자 인터페이스 제공)

디버깅을 시작하기 전에 PEView 를 이용해서 KeyLogger.dll 파일의 export 함수를 살펴보겠습니다.


<Fig. 1>

위 그림을 보시면 KeyLogger.dll 파일은 4 개의 export 함수가 있습니다.
함수 이름이 직관적이라서 이해하는데 어려움은 없습니다.
KeyboardProc() 은 키보드 훅 프로시저 인데 이것마저도 export 한다는 것이 재미있군요.
(훅 프로시저는 콜백 함수이기 때문에 사용자가 직접 호출할 일이 없습니다.)

KeyLogger.exe 의 동작 방식은 쉽게 예측이 가능합니다.
분명 KeyLogger.dll 을 로딩한 후 InstallKeyboardHook() 함수를 호출해서 키보드 메시지 훅을 설치할 것입니다.

PEView.exe 를 이용해서 KeyLogger.exe 의 IAT 를 살펴보면
KeyLogger.dll 파일을 Import 하지 않습니다.

그렇다면 LoadLibrary() 를 이용해서 dll 을 로딩하겠지요?
함수 이름 문자열을 이용해서 GetProcAddress() 를 호출할 것이 분명하구요.

이것이 제가 강조하는 분석 전의 사전 예측입니다.

OllyDbg 를 이용해서 KeyLogger.exe 를 띄워 보겠습니다.


<Fig. 2>

All referenced text strings 메뉴를 선택하면 위 <Fig. 2> 와 같은 그림이 나타납니다.
"InstallKeyboardHook" 문자열을 더블 클릭하면 해당 위치로 이동합니다.


<Fig. 3>

제대로 찾은 것 같습니다.
위의 주소에 BP 를 걸고 실행 시키면 KeyLogger.dll 을 로딩하여 export 함수의 주소를 저장한 후 
나중에 InstallKeyboardHook() 함수를 호출 하는 걸 볼 수 있습니다.

아주 간단하지요.
바로 키로거의 핵심인 dll 파일을 분석하도록 하겠습니다.



KeyLogger.dll 디버깅 시작하기


메시지 훅에 사용되는 dll 파일을 디버깅 할 때 주의사항이 있습니다.

만약 메모장(notepad.exe) 프로세스를 키로깅 한다고 할 때,
먼저 KeyLogger.exe 를 실행시켜서 KeyLogger.dll 의 export 함수인 InstallKeyboardHook() 함수를 호출해야 합니다.
(내부의 SetWindowsHookEx() 함수가 호출되면서 global message hook 이 걸립니다.)

이후에 notepad.exe 프로세스에서 키보드 메시지가 발생하면
OS 는 강제로 KeyLogger.dll 을 notepad.exe 프로세스에 인젝션(injection) 시킵니다.

우리는 바로 notepad.exe 에 인젝션된 KeyLogger.dll 을 디버깅 해야 합니다.
그래야 제대로 된 메시지 훅을 디버깅 할 수 있습니다.

이해 가시나요? 잘 이해 되지 않는 분께서는 위에서 소개해드린 제 글을 다시 한번 읽어보시기 바랍니다.


KeyLogger.dll 파일의 디버깅을 시작하는 방법은 아래와 같습니다.

#1. Keylogger.exe 를 실행
KeyLogger.exe 는 실행과 동시에 KeyLogger.dll(키로거 모듈) 을 로딩하고 InstallKeyboardHook() export 함수를 실행하여
global keyboard message hook 을 걸어버립니다.

#2. Notepad.exe 실행
우리가 후킹하려는 대상 프로세스입니다.

#3. OllyDbg 로 Notepad.exe 를 attach 시킴
일반적으로 디버거에는 실행중인 프로세스를 디버깅 할 수 있는 attach 라는 기술이 있습니다. (File - attach 메뉴)
OllyDbg 를 이용하여 실행중인 Notepad.exe 프로세스에 attach 합니다.


<Fig. 4>

attach 하고 나면 kernel32.dll 코드 영역에서 BP 가 걸려서 멈출 것입니다.
그때는 그냥 <F9> 로 '실행' 시키시면 됩니다.

#4. 디버깅 옵션 변경 (단축키 ALT+O)
OllyDbg 옵션 메뉴의 Events 탭에 "Break on new module (DLL)" 체크 박스 항목이 있습니다.


<Fig. 5>

"Break on new module (DLL)" 옵션은 말 그대로 현재 프로세스(notepad.exe)에 
새로운 DLL 이 인젝션(또는 로딩) 될 때 실행을 멈춰 주는 옵션입니다.

사용자는 어떤 DLL 이 인젝션(또는 로딩) 되는지 파악해서 원하는 곳에 BP 를 설치하고
DLL 의 EP 코드부터 디버깅을 할 수 있습니다.

* 현재 notepad.exe 프로세스에서는 키보드 메시지가 없었기 때문에 아직 KeyLogger.dll 은 인젝션 되지 않았습니다.

#5. Notepad.exe 에서 키보드 메시지 발생 시키기
이제 notepad 에서 'a' 키를 입력해 보세요.
OllyDbg 에서 아래와 같은 화면이 나타나면서 notepad.exe 프로세스의 실행이 멈추게 됩니다.
(아래 화면이 안보이시는 분들께서는 단축키 ALT+E 를 입력하세요.)


<Fig. 6>

global keyboard message hook 이 걸려 있는 상태에서 키 입력이 발생하면 
OS 에서 해당 프로세스(notepad.exe) 에게 강제로 훅 DLL (KeyLogger.dll) 을 인젝션(injection) 시켜줍니다.

OllyDbg 옵션 설정에 따라서 DLL 이 인젝션 되는 순간을 정확히 알아 낼 수 있습니다.
(다시 "Break on new module (DLL)" 옵션을 해제(uncheck) 해주세요.)

Process Explorer 유틸의 검색기능(Ctrl+F)을 이용해서 KeyLogger.dll 이 인젝션된 모든 프로세스 목록을 볼 수 있습니다.


<Fig. 7>

global message hook 이라고 할 지라도 실제로 해당 메시지 이벤트가 발생한 프로세스에게만 DLL 이 인젝션 된다는 내용을 이해하시는 것이 중요합니다. 또한 이미 실행중인 프로세스 뿐만이 아니라 앞으로 실행 되는 모든 프로세스에게도 적용된다는 사실도 같이 기억해 주세요.


#6. KeyLogger.dll 에 BP 걸고 디버깅 시작 하기
그림 <Fig. 6> 에서 빨간색으로 표시된 부분이 KeyLogger.dll 이 인젝션된 메모리 위치입니다.
그곳을 더블 클릭하면 아래 그림과 같이 EP 코드 주소가 나타납니다.

* 간혹 한번에 빨간색 주소가 안보이는 경우도 있습니다.
   그럴때는 디버깅 윈도우에서 Ctrl+G 명령으로 EP 주소(10001000)를 직접 입력해 주세요.


<Fig. 8>

OllyDbg 에서 친절하게도 KeyLogger.dll 의 export 함수 시작위치에 함수 이름을 표시해줍니다.
KeyboardProc 에 BP 를 설치하신 후 디버깅을 시작하도록 하겠습니다.

다음 포스트로 이어집니다.


'analysis' 카테고리의 다른 글

UPack 상세 분석 – PE Header 완전 정복 (1)  (20) 2009/09/04
Inline Patch 실습 (2)  (4) 2009/08/27
Inline Patch 실습 (1)  (6) 2009/08/22
UPX 실행 압축된 notepad 디버깅!  (27) 2009/06/16
키로거(KeyLogger) 분석 (3)  (4) 2009/05/03
키로거(KeyLogger) 분석 (2)  (50) 2009/05/03
키로거(KeyLogger) 분석 (1)  (14) 2009/04/24
Lena's Reversing for Newbies #10 (2)  (7) 2009/03/18
Lena's Reversing for Newbies #10 (1)  (16) 2009/03/17
abex' crackme #2 (2)  (27) 2009/03/01
abex' crackme #2 (1)  (32) 2009/02/28

Trackback Address :: http://www.reversecore.com/trackback/27 관련글 쓰기

  1. Tracked from www.reversecore.com 2009/05/13 05:34 삭제

    Subject: Windows Message Hooking (윈도우 메시지 후킹)

    훅(Hook) (Photo : lanier67 on flickr) 영어로 Hook, 우리말로는 갈고리, 낚시바늘 정도의 뜻을 가지고 있는데요, 원하는 것을 낚아 채고 싶을때 사용하는 도구라는건 다들 알고 계실겁니다. 이 갈고리의 뜻이 확장되어서 정보를 엿보거나 가로채는 경우에도 Hook 이라는 말을 씁니다. 확장된 훅(hook)의 개념을 설명드리기 위해 한가지 예를 들어 보겠습니다. 군사적으로 아주 중요한 시설이 있다고 칩시다. 이곳은 아주 중요하기..
  2. Tracked from www.reversecore.com 2009/05/13 05:35 삭제

    Subject: Process Explorer - 최고의 작업 관리자

    Process Explorer Windows 최고의 프로세스 관리 도구 Process Explorer 입니다. https://technet.microsoft.com/en-us/sysinternals/bb896653.aspx 저 유명한 sysinternals (현재는 MS에 인수되었음) 의 Mark Russinovich 씨가 만든 프로세스 관리 유틸리티입니다. 이분은 Windows 운영체제에 대해서 매우 해박한 지식을 갖고 있으며, 유용한 유틸리티(F..
  1. 최상욱 2010/07/12 16:25 댓글주소 | 수정 | 삭제 | 댓글

    키로거파일을 공부해볼려고하는 학생인데 dll과 exe 파일좀 첨부해주시면 안되나영?

    직접하면서 공부할려고 하는데 안되시면 youlosts@nate.com으로 라도좀 보내주세요 ^^

  2. p0lly 2010/07/19 19:46 댓글주소 | 수정 | 삭제 | 댓글

    p0lly@naver.com으로도 부탁드립니다... Keylogger.exe와 Keylogger.dll을 키워드로 googling해서 모PG을 다운받아 piew와 odbg로 보니 일단 맞긴 한 것같은데... 죄송합니다만...그래도 부탁드립니다...

  3. p0lly 2010/07/19 19:49 댓글주소 | 수정 | 삭제 | 댓글

    그리고 혹시 .net으로 제작된 PG REVERSE에 관심이 있으신지 여쭙니다. 국내 모기업의 PG을 reflector나 {smartkill}로 읽으면 영문문자열은 보이는데 한글이 모두 깨져서 어디에 pin point를 꽂고 시작할지 모르겠습니다... 혹시 잘아신다면 방향을 잡아주시면 감사드리겠습니다.

    • reversecore 2010/07/19 22:02 댓글주소 | 수정 | 삭제

      메일로 질문해주신 분이시죠?

      저도 아직 C# 개발/디버깅은 해본적이 없어서 뭐라 설명드릴 내용이 없네요.

  4. p0lly 2010/07/21 21:17 댓글주소 | 수정 | 삭제 | 댓글

    감사합니다~~

  5. 2010/07/22 13:56 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  6. 박지희 2010/07/28 11:31 댓글주소 | 수정 | 삭제 | 댓글

    저도 공부해볼려고 하는데 메일료 보내주시면 안될까요^^?
    pjihee14@nate.com

  7. 2010/07/28 18:27 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  8. 비빅 2010/07/28 22:39 댓글주소 | 수정 | 삭제 | 댓글

    저도 KeyLogger실행파일과 dll파일좀 메일로 부탁합니다..
    posdog1@hotmail.com 좋은 강의 감사합니다^^

  9. No.190 2010/07/29 17:39 댓글주소 | 수정 | 삭제 | 댓글

    exe 와 dll 파일 부탁드립니다. uiandwe@gmail.com
    정말 글 너무나 잘쓰시네요. 강의 너무나 좋아요.
    책은 언제 나요나요? 바로 질러드리겠습니다! ^-^

  10. reversecore 2010/08/01 22:54 댓글주소 | 수정 | 삭제 | 댓글

    이누님, 비빅님, No.190 님

    키로거 경로를 메일로 보내드렸습니다.

    재밌게 분석하시기 바랍니다. ^^

  11. 2010/08/03 10:18 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  12. 도와주세요 2010/08/11 19:35 댓글주소 | 수정 | 삭제 | 댓글

    telling123@naver.com보내주세요

  13. 도와주세요 2010/08/11 19:35 댓글주소 | 수정 | 삭제 | 댓글

    telling123@naver.com보내주세요

  14. 2010/08/15 21:26 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  15. reversecore 2010/08/15 22:24 댓글주소 | 수정 | 삭제 | 댓글

    본문에 해당 키로거 다운 경로를 적어두었습니다.

    재미있게 분석하시기 바랍니다. ^^

  16. 2010/09/02 08:43 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  17. 이리와 2010/11/21 12:43 댓글주소 | 수정 | 삭제 | 댓글

    wkddnjs33@naver.com

    싸이월드해킹하는법은 없을까요 ㅠㅠ 저도 좀 보내주세요ㅕ

  18. 리온 2010/12/07 17:51 댓글주소 | 수정 | 삭제 | 댓글

    보안 관련 공부중인데 혹시 분석하신 source code 좀 보내주실 수 있을까요?

    위의 파일은 실행파일만 있더라구요...

    제 메일 주소는 jihun0612@naver.com 입니다.

    • reversecore 2010/12/10 11:54 댓글주소 | 수정 | 삭제

      안녕하세요.

      위 키로거 파일의 소스코드 말씀이신가요?

      제가 만든게 아니라서 소스는 없습니다만...

      메시지 후킹 기반의 키로깅 동작 원리는 간단하므로 찾아보시면 많이 있을걸로 생각됩니다.

      감사합니다.

  19. hhy0180 2010/12/11 20:36 댓글주소 | 수정 | 삭제 | 댓글

    hhy0180@naver.com 으로 키로거 exe, dll좀 보내주실수있으시나요?

  20. cksdnf25 2011/01/21 23:39 댓글주소 | 수정 | 삭제 | 댓글

    저기 저도 좀 부탁드릴께요 cksdnf25@naver.com

    • reversecore 2011/01/25 23:11 댓글주소 | 수정 | 삭제

      안녕하세요.

      키로거 프로그램 말씀이신가요?

      본문과 위 댓글의 경로에서 받으시면 되구요.

      소스를 말씀하시는 거라면...
      제 프로그램이 아니라 소스는 없습니다..

      감사합니다.

  21. 코어님 2011/02/08 17:22 댓글주소 | 수정 | 삭제 | 댓글

    코어님 익스포트와 임포트가 뭐죠?/롤백이 뭐죠?

    • reversecore 2011/02/09 21:32 댓글주소 | 수정 | 삭제

      안녕하세요.

      PE 파일에서 Export 는...

      라이브러리 파일(DLL) 에서 함수를 제공하는 것을 말합니다. 그런 함수를 Export 함수라고 하지요.

      Import 는 Export 함수를 가져다 쓰는 것을 말하구요. EXE/DLL 에서 다른 DLL 이 제공하는 Export 함수를 사용할 때 그 함수를 Import 한다고 말합니다.

      감사합니다.

  22. 이은수 2011/02/11 22:10 댓글주소 | 수정 | 삭제 | 댓글

    네이트온 아이디 있으시면 gortlawjdfl1@naver.com << 여깃다가좀 보내주세요 ;;

    • reversecore 2011/02/18 22:45 댓글주소 | 수정 | 삭제

      안녕하세요.

      사용하는 메신저는 없습니다.

      문의사항 있으시면 블로그나 이메일(reversecore@gmail.com)을 이용해 주시기 바랍니다.

      감사합니다.

  23. 2011/03/04 01:51 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

  24. 리버서 2011/04/21 18:57 댓글주소 | 수정 | 삭제 | 댓글

    흠...콜백함수가 뭔가요??

    • reversecore 2011/05/03 01:06 댓글주소 | 수정 | 삭제

      안녕하세요.

      프로그래밍의 개념인데요...

      간단히 말해서 특정 이벤트에 호출해달라고 지정해 놓은 함수를 말합니다.

      윈도우 프로시저가 대표적인 콜백 함수입니다.
      (키보드, 마우스, 기타 이벤트가 발생하면 OS 에서 등록된 윈도우 프로시저를 호출해 줍니다.)

      감사합니다.

  25. 방금 치료 마친 사람 2011/07/06 03:29 댓글주소 | 수정 | 삭제 | 댓글

    무고한 피해자가 생기지 않게 해킹용 프로그램은 공유하지 않으셨으면 합니다.

    • 주황 2011/07/07 18:24 댓글주소 | 수정 | 삭제

      이 싸이트가 어떤 싸이트 인지도 모르면서 함부로 댓글 다는것부터 조심하셔야 겠습니다.

    • 방치마사 2011/07/10 12:31 댓글주소 | 수정 | 삭제

      주황 님이 말하고자 하는 바, 블로그 성격은 제목만 보아도 알 수 있지요. 그러니 더욱 의아합니다. 검색 한 번에 공유 가능한 주소가 뜬다는 건 위험한 일이 아닐런지요. 그를 모르는 분은 없으리라 생각합니다. 그러니 주인장이 사리분별을 하는 사람이라면 최소한, 요청하는 분의 저의를 살피고 메일로 주는 것이 맞겠지요. 물론 그리하여도 속이려 작정한 사람의 속내를 꿰뚫어 보기란 쉽지 않을 텝니다. 헌데 귀찮다는 이유로 주소를 유출, 아무나 받게 두다니요. 실제 저를 해킹한 아이는 아직 나이가 적은 아이로, 검색하여 나온 위 주소에서 받았다고 합니다. 주황 님의 덧글을 보자니 위 같은 글과 그것에 경각심을 갖지 않는 이가 더 있으리란 생각에 더욱 걱정스럽습니다. 블로그 지기께서 덧글을 달지 않고 주황 님이 덧글 다신 것을 보아 저라도 쪽지를 드려야 겠군요. 그리고 앞으로 이쪽 일을 공부하실 참이라면 무엇을 배워 누구에게 베풀 것인지 잊으면 안 되시겠습니다. 주황 님은 주황 님 같은 분께 프로그램 문의를 하고 싶을런지요. 위와 같은 프로그램이 유출 될 경우 악용사례로 인한 피해자가 있을 것이라 생각커나 게의치 않는 이에게 말입니다. 모든 일은 영향을 미치고 그 책임은 언행을 한 사람을 따르지요. 모쪼록 이 페이지에 다시 오셔서 깨달음이 있으시길 바랍니다.

    • SLinker 2011/07/12 16:04 댓글주소 | 수정 | 삭제

      방치마사님 이걸로 모든 키가 후킹 당한다 라고 보시는건가요 대부분 이렇게 알려지면 보안업체에서 막게되고 보안업체관련으로 일하려는 학생들은 이걸로 공부를 합니다. 이런게 없다면 우리나라 외국 바이러스 등에 다 털리겟죠 공개되었다는건 방어를 하기에도 쉬워집니다. 분석 한번 하는데 어느정도의 노력이 드는지 아시는지요
      요즘 악성코드 들도 안티 디버깅걸어놓는 마당에 말이죠^^; 이렇게 올려주셧으니 당연히 백신프로그램에 의한 방어는 갖추어 집니다;;

    • reversecore 2011/07/13 21:13 댓글주소 | 수정 | 삭제

      일반인(특히 사리 분별이 미숙한 어린 사용자)들을 고려하지 못한 제 잘못이 맞습니다.

      이 일로 더이상의 논쟁이 없었으면 좋겠습니다.

      제가 주황님과 SLinker님의 마음을 잘 알고 있습니다.

      우리 같은 리버서들은 일반 사용자들에 대한 배려가 좀 더 필요하다고 생각됩니다.

      감사합니다.

    • SLinker 2011/07/20 12:59 댓글주소 | 수정 | 삭제

      주인장께 죄송하네요 흠.... 하지만 이건 간단한 키로그 인데;; 저렇게 대응 하시니 뭐라 할말이 없네요;;
      전 리버서보다는 커널개발자 이기에 이런 자료들이 정말 중요하고 감사할 뿐입니다 ㅠ.ㅠ
      어떤식으로 접근하는지 알면 수월하게 막아낼수가 있으니.... 아직 분석은 잘 못하는편입니다.

    • reversecore 2011/07/22 19:24 댓글주소 | 수정 | 삭제

      안녕하세요.

      앗, 제가 공유한 정보에 의해서 불미스러운 일을 경험하셨나 봅니다.

      우선 사과드리겠습니다.

      향후에 혹시라도 발생할 피해를 예방하기 위해서라도 링크를 삭제하고 파일 공유 요청을 거절하겠습니다.

      좋은 지적 감사합니다.

  26. Matthew L.C.G 2011/07/27 00:59 댓글주소 | 수정 | 삭제 | 댓글

    이거......실습 못해보는건가요 ?

    뭐지.. 안티바이러스에 다걸리는걸 왜 저난리하신거지..

    악의적으로 적용하는놈이 있으면 막는놈도 있는거지 ㅡ.ㅡ;;

  27. biggy200 2011/08/26 09:26 댓글주소 | 수정 | 삭제 | 댓글

    학습용으로 사용하려고 하는데요, 파일 공유 부탁드려도 될까요?
    메일 주소는 zzangccnp@gmail.com입니다.

  28. biggy200 2011/09/22 17:05 댓글주소 | 수정 | 삭제 | 댓글

    매우 감사합니다.

  29. Stone 2011/09/22 17:06 댓글주소 | 수정 | 삭제 | 댓글

    reverscore님
    64bit에서 아즘실행시키는 법모르시나요.
    ㅋ ㅋ ㅋ

  30. soodo123 2011/10/29 18:57 댓글주소 | 수정 | 삭제 | 댓글

    soodo123@naver.com <<

    보내주시면감사하겟습니다