이전 강좌에서 이어지는 내용입니다. 

어셈블리 언어를 이용한 Code Injection (4)



# MessageBoxA() 파라미터 입력 1 – MB_OK

002D002C    6A 00         PUSH 0

PUSH 0 은 스택에 0 을 입력하는 명령입니다. 이 0 의 의미는 아래에서 호출될 MessageBoxA() API 의 네 번째 파라미터(uType)로 사용됩니다.

참고로 MessageBoxA() API 는 아래와 같이 4 개의 파라미터를 받습니다.

int WINAPI MessageBox(
  __in_opt  HWND hWnd,
  __in_opt  LPCTSTR lpText,
  __in_opt  LPCTSTR lpCaption,
  __in      UINT uType
);


* 참고
uType 값이 0 이면 MB_OK 를 의미하며, 단순히 OK(“확인”) 버튼 한 개만 보여주게 됩니다.


# MessageBoxA() 파라미터 입력 2 – "ReverseCore"

002D002E    E8 0C000000   CALL 002D003F
002D0033    52            PUSH EDX
002D0034    65:76 65      JBE SHORT 002D009C
002D0037    72 73         JB SHORT 002D00AC
002D0039    65:43         INC EBX
002D003B    6F            OUTS DX,DWORD PTR ES:[EDI]
002D003C    72 65         JB SHORT 002D00A3
002D003E    00E8          ADD AL,CH

이번에는 CALL 명령으로 코드 사이에 포함된 문자열 데이터 주소를 스택에 입력하는 기법을 소개하겠습니다. 이 역시 Assembly 프로그래밍 언어에서만 가능한 기법입니다.

위 2D0033 ~ 2D003E 주소 영역은 분명히 프로그램 코드 영역이지만 그 내용은 사실 "ReverseCore" 문자열 데이터 입니다. (붉은색 표시 부분) 즉, "ReverseCore" 문자열의 시작주소는 2D0033 입니다. 그리고 이 문자열은 MessageBoxA() API 의 세 번째 파라미터 (lpCaption)로 사용됩니다. 

함수 파라미터로 사용되려면 문자열 주소를 스택에 넣어줘야 하는데 과연 어떤 방식으로 입력할까요? 

2D002E 주소의 CALL 002D003F 명령을 디버깅으로 쫓아 들어가 보겠습니다. (StepIn [F7]) 그리고 아래 그림과 같이 스택 주소를 봐주시기 바랍니다.


<Fig. 13>

스택에 "ReverseCore" 문자열 시작 주소인 2D0033 이 입력되었습니다!!! MessageBoxA() 의 세 번째 파라미터가 입력된 셈이죠.

이 트릭은 CALL 명령어의 "동작원리"를 응용한 것입니다.

CALL 002D003F 명령을 수행하면 함수(2D003F) 가 종료된 후 돌아올 리턴 주소(2D0033)를 스택에 입력(PUSH)한 후 해당 함수 주소(2D003F)로 이동(JMP)합니다. 즉, CALL 명령어는 PUSH, JMP 명령어를 합쳐 놓은 것입니다. 

사실 2D003F 는 함수 형태가 아닙니다. RETN 명령어로 되돌아가는 형태가 아니란 얘기지요. 여기서의 CALL 002D003F 명령어는 바로 뒤에 이어지는 "ReverseCore" 문자열 주소를 스택에 입력하고 그 다음 코드 명령어로 가기 위해서 사용되고 있는 것입니다.

이해 되시나요? ^^ 재미있는 CALL 명령어 사용법입니다.


# MessageBoxA() 파라미터 입력 3 – "www.reversecore.com"

002D003F    E8 14000000   CALL 002D0058
002D0044    77 77         JA SHORT 002D00BD
002D0046    77 2E         JA SHORT 002D0076
002D0048    72 65         JB SHORT 002D00AF
002D004A    76 65         JBE SHORT 002D00B1
002D004C    72 73         JB SHORT 002D00C1
002D004E    65:636F 72    ARPL WORD PTR GS:[EDI+72],BP
002D0052    65:           PREFIX GS:
002D0053    2E:636F 6D    ARPL WORD PTR CS:[EDI+6D],BP
002D0057    006A 00       ADD BYTE PTR DS:[EDX],CH

역시 위 "ReverseCore" 문자열과 마찬가지로 MessageBoxA() API 의 두 번째 파라미터 lpText 문자열("www.reversecore.com")을 입력하는 명령입니다.

위 코드에서 빨간색으로 표시된 부분은 코드 명령어가 아니라 문자열 데이터("www.reversecore.com") 입니다. 

2D003F 주소의 CALL 002D0058 명령어는 (앞에서 설명 드린 바와 같이) 바로 뒤에 이어지는 문자열("www.reversecore.com") 데이터의 주소(2D0044)를 스택에 입력하고 그 다음 명령어 주소(2D0058)로 갑니다. (아래 그림 참고)


<Fig. 14>


# MessageBoxA() 파라미터 입력 4 – NULL

002D0058    6A 00         PUSH 0

MessageBoxA() API 의 첫 번째 파라미터인 hWnd 값을 입력합니다. 일반적으로는 메시지 박스가 소속된 윈도우 핸들을 입력하지만, 여기서는 NULL 을 입력하여 무소속(?) 메시지 박스가 출력되도록 만들겠습니다.


# MessageBoxA(NULL, "www.reversecore.com", "ReverseCore", MB_OK) 호출

002D005A    FFD0          CALL EAX

드디어 MessageBoxA() API 를 호출하는 CALL 명령어 입니다. 현재 EAX 레지스터에는 위에서 호출한 GetProcAddress() 에 의해서 리턴된 MessageBoxA() API 의 시작 주소(7793EA71)가 저장되어 있습니다. (<Fig. 12> 참고)

2D005A 주소의 CALL EAX 명령어까지 디버깅한 후 레지스터와 스택을 살펴보면 아래 그림과 같습니다.


<Fig. 15>

이 CALL EAX 명령어를 실행하면 메시지 박스가 나타날 것입니다.


<Fig. 16>


# ThreadProc() 리턴값 세팅

002D005C    33C0          XOR EAX,EAX

notepad.exe 프로세스에 인젝션된 코드(ThreadProc() 스레드 함수) 가 종료될 준비를 합니다. 스레드 함수의 리턴값을 0 으로 세팅하기 위해서 XOR EAX, EAX 명령어가 사용됩니다. 함수의 리턴값은 EAX 레지스터를 사용한다는 것을 기억 하시죠?

* 참고
XOR EAX, EAX 명령어는 EAX 레지스터를 0 으로 초기화하는 가장 쉽고 빠른 명령어 입니다. 디버깅하면서 많이 접하게 될 것입니다.


# Stack Frame 해제 및 함수 리턴

002D005E    8BE5          MOV ESP,EBP
002D0060    5D            POP EBP
002D0061    C3            RETN

ThreadProc() 함수 시작할 때 생성한 Stack Frame 을 해제 합니다. 그리고 RETN 명령으로 함수가 종료됩니다.

이 ThreadProc() 함수에서 Stack Frame 은 매우 중요합니다. 앞에서 설명 드린 "PUSH 를 이용한 스택에 문자열 넣는 기법" 에서 스택에 입력된 문자열을 일일이 POP 명령으로 힘들게 없앨 필요 없이 Stack Frame 해제 명령어 한방으로 가볍게 초기화 시킬 수 있습니다.

* 참고
Stack Frame 관련 내용은 아래 링크를 참고하세요~


+--+

Assembly 언어를 이용한 Code Injection 에 관한 설명을 마치도록 하겠습니다. C 언어 보다 더 자유로운 Assembly 언어를 사용하여 다양하고 창의적인 코드를 생성해 보시기 바랍니다. Assembly 초보자도 OllyDbg 의 "Assemble" 명령어를 이용하면 좀 더 쉽게 이용하실 수 있습니다.

위 실습을 다 끝내신 분께서는 한번 제 블로그 이미지에 있는 바이트 코드를 입력해서 실행해 보시기 바랍니다. ^^ 어떤 코드가 나타날까요? (OllyDbg 의 편집 기능과 New Origin Here 기능을 사용하면 되겠지요~)


ReverseCore

위 글이 도움이 되셨다면 추천(VIEW ON) 부탁 드려요~

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

  1. 철이 2010/07/06 15:44 댓글주소 | 수정 | 삭제 | 댓글

    정말로 잘보았습니다 ㅎ

  2. sonickaka 2010/07/10 14:23 댓글주소 | 수정 | 삭제 | 댓글

    정말 좋은 포스팅 감사합니다 ^^
    코드 인젝션을 체계적으로 정리해볼수 있어서 너무 갚지네요.. ^^
    감사합니다~!

  3. 땅콩 2010/07/24 11:35 댓글주소 | 수정 | 삭제 | 댓글

    한번 꼭 다 봐야지 봐야지..하면서...계속 미루다가..방학이라서..
    매일보고 있습니다..ㅎㅎ (analysis..부분만..거의 다 본듯..)

    이렇게 좋은 자료를..날로 먹는거 같아서....

    암튼..너무 감사합니다...

  4. 2010/07/31 00:11 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

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

      안녕하세요.

      비도덕적이고 불법적인 내용이 아니라면 제가 작은 도움을 드릴 수 도 있습니다.

      관련된 질문 있으시면 올려주세요~

      감사합니다.

  5. 2010/08/02 10:48 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2010/08/04 20:43 댓글주소 | 수정 | 삭제

      안녕하세요.

      PE Viewer 제작이야 말로 PE Header 공부의 완성판이라고 할 수 있지요. ^^

      제가 예전에 Assembly 로 개발한게 있긴 한데요... 소스를 공개할만한 수준은 아니라서요...

      그때 저는 Microsoft 의 Dumpbin 유틸리티를 참고해서 콘솔버전으로 만들었습니다.

      다른 소스는 가지고 있는게 없네요~

      요령이라고 할것도 없지만...
      그냥 편하게 Dumpbin 이나 PEView 같이 기본적인 정보를 나열하는 식으로 만드시면 됩니다.

      dbghelp.dll 에 기본적인 API 함수가 지원되기는 하지만 그보다는 파일 매핑을 사용해 직접 구현하시는 방법을 추천드립니다.
      (기본적인 구조체는 winnt.h 에 잘 정의되어 있구요...)

      감사합니다.

  6. gjuRlo 2010/08/04 10:06 댓글주소 | 수정 | 삭제 | 댓글

    8월 마소 강좌 코드 인젝션에 이끌러 이 홈페이지까지 왔다가 잘 보고 갑니다. 감사합니다~

  7. 땅콩 2010/08/06 13:19 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 ..공부하다가 막히는 부분이 있어서 .글을 올립니다.

    사실은 데브피아에 글도 올려봤는데..답변이..없어서...이렇게 실례를 무릅쓰고...ㅜㅜ

    요즘 디바이스를 공부중에 잇는 학생입니다.



    ssdt 후킹을 해서 계산기를 프로세스를 종료를 못하게 했습니다..

    (zwterminateprocess를 후킹했습니다..)



    그런데..sdtrestore라는 http://www.security.org.sg/code/sdtrestore.html 여기서 받은걸로는 탐지를 못하는데

    (sdtrestore의 원리는 ntoskrnl.exe에서 정보를 바로 읽어온다고 알고 있는데....)



    하지만....icesword는 탐지를 했습니다..요 방법이 아주 궁금해서 글을 올립니다..



    과연 icesword의 탐지 방법을 무엇인지......



    답변 부탁드립니다. ㅎㅎ



    (제 실행환경은 xp에 sp3 입니다.

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

      안녕하세요.

      후킹 탐지 방법은 후킹 방법처럼 무수히 많이 있답니다.

      저도 icesword 를 본적이 있습니다. 세부 동작 원리까지는 자세히 모르지만 매우 치졸한(?) 방법을 많이 사용했을 것입니다. ^^

      무슨 뜻이냐면... 후킹 탐지 되지 않기 위해서 별의별 기발한 기법들이 동원되는데요... 이를 탐지하는 입장에서도 똑같이 희안하고 무식하지만 확실한 (full scan 같은) 방법들을 동원하는 것입니다.

      커널 후킹은 고급주제이며 향후 제 블로그에 자세히 다룰 예정입니다.

      감사합니다.

  8. 땅콩 2010/08/09 14:26 댓글주소 | 수정 | 삭제 | 댓글

    옙..답변 감사드립니다..........원리를 분석하고 싶은데..아직 내공이 너무 부족하네요..ㅜㅜ

    빨리 블로그에서 글을 보았으면..좋겠습니다..ㅎㅎ

    수고하세요..ㅎ

  9. 봉이 2010/08/24 13:47 댓글주소 | 수정 | 삭제 | 댓글

    안녕 하세요!
    저두 마소에서 관련글보다 여기 왔는데, 정말 글을 잘쓰시는 거 같아요!
    코드인젝션편 보면서 그림과 올리디버거 사용방법등이 적절하게 나와 이해하기 좋았습니다.
    앞으로도 좋은 내용 기대할게요 ^^

  10. 엘군 2011/07/05 15:50 댓글주소 | 수정 | 삭제 | 댓글

    전 코드 인젝션에 대해 전혀 모르던 학생입니다ㅎㅎ
    dll 로딩 원리를 알고싶어서 구글링 하다가 우연히 봤는데 왜이렇게 재밌게 보이는지..!!
    요즘은 영상처리쪽 공부중인데요ㅎ 공부할 마음없었는데 기회되면 이분야도 해보고 싶네요!ㅋ
    책도내신것같은데 꼭 봐봐야겠습니다
    #덧, 좋은글 올려주셔서 감사합니다


◀ PREV : [1] : ... [4] : [5] : [6] : [7] : [8] : [9] : [10] : [11] : [12] : ... [91] : NEXT ▶