Microsoft Word - 과목1.Reverse Engineering.doc

Similar documents
목 차 1. 개요 2. PE(Portable Executable) 이란? 3. IMAGE_DOS_HEADER 4. IMAGE_NT_HEADER 1) IMAGE_FILE_HEADER 2) IMAGE_OPTIONAL_HEADER 3) IMAGE_DATA_DIRECTORY

Deok9_PE Structure

<4D F736F F D20B0ADB5BFC7F65FB1E2BCFAB9AEBCAD5F4645B1B8C1B620B1E2BCFAB9AEBCAD5F66726F6D E6B5F66696E F2E646F63>

Deok9_Exploit Technique

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

No Slide Title

Microsoft Word - FunctionCall

hlogin2

=

슬라이드 1

Microsoft PowerPoint - ch07 - 포인터 pm0415

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

Microsoft PowerPoint - hy2-12.pptx

금오공대 컴퓨터공학전공 강의자료

버퍼오버플로우-왕기초편 10. 메모리를 Hex dump 뜨기 앞서우리는버퍼오버플로우로인해리턴어드레스 (return address) 가변조될수있음을알았습니다. 이제곧리턴어드레스를원하는값으로변경하는실습을해볼것인데요, 그전에앞서, 메모리에저장된값들을살펴보는방법에대해배워보겠습

< C6520B1B8C1B6BFCD20BEF0C6D0C5B7C0C720BFF8B8AE2E687770>

Microsoft PowerPoint - chap01-C언어개요.pptx

IDA 5.x Manual hwp

BMP 파일 처리

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

금오공대 컴퓨터공학전공 강의자료

INTRO Basic architecture of modern computers Basic and most used assembly instructions on x86 Installing an assembly compiler and RE tools Practice co

PowerPoint 프레젠테이션

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202839C1D6C2F7207E203135C1D6C2F >

연재순서 실행파읷속으로 필자소개 싞영짂 웰비아닶컴에서보안프로그래머로읷하고있다. 시스템프로그래밍에관심이많으며다수의 PC 보안프로그램개발에참여했다. 현재데브피아 Visual C++ 섹션시

chap 5: Trees

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>

Microsoft PowerPoint - chap06-2pointer.ppt

Microsoft PowerPoint - o8.pptx

Microsoft PowerPoint - a6.ppt [호환 모드]

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

Microsoft Word - ntasFrameBuilderInstallGuide2.5.doc

목차 ⓵ VA and RVA 4p. ⓶ RVA to RAW 5p. ⓷ PE 7p. PE의개념. PE Header가생기는과정. PE의필요성. ⓷ DOS Header 8p. e_magic e_lfanew ⓸ DOS Stub 9p. 1

untitled

Microsoft Word - PE_structure.docx

Microsoft PowerPoint - additional01.ppt [호환 모드]

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

Microsoft PowerPoint - chap10-함수의활용.pptx

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>

Chapter ...

11장 포인터

11장 포인터

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

Win32 실행파일 (PE) 의구조 Windows 운영체제실행파일의구조에대하여알아보자 Kali-KM

Microsoft PowerPoint - a10.ppt [호환 모드]

C++ Programming

PowerPoint 프레젠테이션

슬라이드 1

Microsoft PowerPoint - a8a.ppt [호환 모드]

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

02.Create a shellcode that executes "/bin/sh" Excuse the ads! We need some help to keep our site up. List Create a shellcode that executes "/bin/sh" C

Chapter 4. LISTS

PowerPoint 프레젠테이션

비트와바이트 비트와바이트 비트 (Bit) : 2진수값하나 (0 또는 1) 를저장할수있는최소메모리공간 1비트 2비트 3비트... n비트 2^1 = 2개 2^2 = 4개 2^3 = 8개... 2^n 개 1 바이트는 8 비트 2 2

vi 사용법

제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

<B1E2BCFAB9AEBCAD5FB9DABAB4B1D45F F F64746F72732E687770>

Secure Programming Lecture1 : Introduction

PowerPoint 프레젠테이션

OCW_C언어 기초

설계란 무엇인가?

PowerPoint Presentation

03_queue

Microsoft Word - PE Infection ? How to Inject a dll.doc

Microsoft PowerPoint - chap06-5 [호환 모드]

OCW_C언어 기초

이번장에서학습할내용 동적메모리란? malloc() 와 calloc() 연결리스트 파일을이용하면보다많은데이터를유용하고지속적으로사용및관리할수있습니다. 2

CKKeyPro 적용가이드

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2

adfasdfasfdasfasfadf

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

C# Programming Guide - Types

Microsoft PowerPoint - chap03-변수와데이터형.pptx

Microsoft PowerPoint - [2009] 02.pptx

Chapter #01 Subject

Frama-C/JESSIS 사용법 소개

Microsoft Word - building the win32 shellcode 01.doc

Microsoft PowerPoint - a2.ppt [호환 모드]

Microsoft PowerPoint - chap05-제어문.pptx

<4D F736F F F696E74202D20BBB7BBB7C7D15F FBEDFB0A3B1B3C0B05FC1A638C0CFC2F72E BC8A3C8AF20B8F0B5E55D>

PowerPoint Template

ISP and CodeVisionAVR C Compiler.hwp

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070>

API 매뉴얼

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

악성코드분석을위한 실행압축 해제기법 1. 개요 이제목을보고 실행압축이뭐야? 하는이도있을테고, 실행하면자동으로압축이풀리는 ZIP 파일과비슷한거아냐? 하고떠올리는이도있을것이다. 그러나여기서설명하는실행압축은그대상이다르다. 흔히말하는 ZIP, RAR처럼데이터들을하나로묶어놓는압

Microsoft PowerPoint - chap06-1Array.ppt

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

Microsoft PowerPoint - chap-11.pptx

PowerPoint 프레젠테이션

제 11 장포인터 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

Visual Basic 반복문

Microsoft PowerPoint - CSharp-10-예외처리

Microsoft PowerPoint - Lecture_Note_7.ppt [Compatibility Mode]

학습목차 2.1 다차원배열이란 차원배열의주소와값의참조

JVM 메모리구조

다른 JSP 페이지호출 forward() 메서드 - 하나의 JSP 페이지실행이끝나고다른 JSP 페이지를호출할때사용한다. 예 ) <% RequestDispatcher dispatcher = request.getrequestdispatcher(" 실행할페이지.jsp");

강의10

Transcription:

Table of Content Module 1 RCE 란?...1 1-1. RCE 란?...2 1-2. RCE 관련법규...4 1-3. RCE 응용분야...6 Module 2 RCE 기초...9 2-1. CPU 동작방식...10 2-2. CPU 레지스터...12 2-3. Assembly...17 2-4. STACK 구조...21 2-5. 함수호출규약 (Calling Convention)...23 2-6. SEH(Structured Exception Handling)...26 2-7. C 코드컴파일후어셈코드변환모습...34 2-8. 특정루틴에서사용되는 API 함수...40 Module 3 PE 파일구조...43 3-1. PE 파일포맷...44 3-2. DOS 헤더및 DOS Stub Code...49 3-3. PE File Header...51 3-4. Optional Header...54 3-5. Section Table...59 3-6. Import Table...62 3-7. Export Table...69 Module 4 분석기초...75 4-1. 분석도구설정...76 4-2. CrackMe 분석실습...84 4-3. KeygenMe 분석실습...94 Module 5 MUP(Manual UnPack)...101 5-1. Packing / Unpacking...102 5-2. Packing 종류...103 5-3. MUP 실습...105 5-4. MUP 를위한 Ollydbg script 작성...114 Module 6 Anti-Reverse 기법...121 6-1. 디버거탐지기법...122 6-2. BreakPoint 탐지기법...133 6-3. TLS Callback...145 6-4. Process Attach 기법...150 Module 7 분석실전...155 7-1. 온라인해킹대회문제분석...156 7-2. 악성코드분석 - shadowbot...173 - i -

- ii -

Module 1 RCE 란? Objectives 리버스엔지니어링의기본개념 리버스엔지니어링의법적문제 리버스엔지니어링응용분야 - 1 -

1-1. RCE 란? RCE 란? Reverse Code Engineering 어떠한장치나객체혹은시스템들의구조나기능, 그리고어떻게작동되는지등의기술적인원리를분석함으로써그것들이작동되는과정을알아내는것 작동하는모든순간순간의세세한것들까지분석하는것을포함 기계적인장치 전자적인부품 소프트웨어적인프로그램 Student Notes 리버스엔지니어링은여러분야에서사용되는용어로써인공물로부터설계사상이나지식을추출하는 행위를말한다. 본교재에서는소프트웨어에대한리버스엔지니어링을이야기할것이다. 통상적으로컴파일된바이너리 (EXE, DLL, SYS 등 ) 를디스어셈블러라는도구를이용하여어셈블리코 드를출력한후그것을 C 언어소스형태로다시옮겨적고적당한수정을통해리버스하고있는파일과 동일한동작을하는프로그램을만드는것이있다. 모든어셈블리코드를소스형태로옮기지않고그냥동작방식만을알아낸다거나일정부분만수정하 는것들도리버스엔지니어링이라고할수있는데예를들면바이러스를분석하는일은모든코드를알 아낼필요가없기때문에동작방식만알아내면된다. 그리고크랙처럼일정부분만수정하여사용제한 - 2 -

을푸는것등도이에해당된다. 실행파일을디스어셈블하지않고도그실행파일이만들어내는데이터파일이나패킷등을분석하여똑같이재구현하는것도리버스엔지니어링이다. 예를들면오래전 PC 게임에서많이하던일인데, HEX 에디터등으로세이브파일을분석하여에디트를만들거나게임자체를조작하는것이있고, 당나귀와호환되는이뮬같은프로그램은당나귀프로토콜의패킷을분석하여동일한동작을하도록만들어낸것이다. 리버스엔지니어링에서가장많이사용되는방식은첫번째로이야기했던바이너리를디스어셈블하여 코드를얻어내는것이다. 이것을하기위해서는먼저인텔어셈블리를배워야하고, 물론 C 언어도알아 야된다. 그런데여기서컴파일된바이너리가 VC 나 gcc 등으로컴파일한것이대부분이지만비주얼베이직으로컴파일한것도있고델파이 ( 파스칼 ) 로컴파일한것도있을것이다. 이바이너리들은모두 CPU 에서직접실행되는것들이기때문에디스어셈블해보면모두똑같은방식으로되어있다. 그래서 VC, VB, 델파이 ( 파스칼 ) 등으로컴파일된것도디스어셈블한뒤 C 소스코드로옮길수있다. 물론리버스하는사람이 VB 나파스칼로옮겨적을수도있을것이다. 대부분리버스해서얻어내는것들은프로그램의로직 ( 알고리즘 ) 이기때문에어느언어로표현하든결과는똑같기때문이다. - 3 -

1-2. RCE 관련법규 RCE 관련법규 상호운용성 경쟁 (Competition) 저작권법 (Copyrightlaw) The Digital Millennium Copyright Act (DMCA) RIAA의 Felten 교수연구팀에대한위협 드미트리스킬리아로프의 ebook 사건 SunnComm사의 CD 복제방지시스템 Student Notes 컴퓨터프로그램보호법제 12 조의 2 ( 프로그램코드역분석 ) 1정당한권원에의하여프로그램을사용하는자또는그의허락을받은자가호환에필요한정보를쉽게얻을수없고그획득이불가피한경우당해프로그램의호환에필요한부분에한하여프로그램저작권자의허락을받지아니하고프로그램코드역분석을할수있다. 2제 1 항의규정에의한프로그램코드역분석을통하여얻은정보는다음각호의 1 에해당하는경우에는이를사용할수없다. 1. 호환목적외의다른목적을위하여이용하거나제 3 자에게제공하는경우 2. 프로그램코드역분석의대상이되는프로그램과표현이실질적으로유사한프로그램을개발 제작 판매하거나기타의프로그램저작권을침해하는행위에이용하는경우 [ 본조신설 2001.1.16][[ 시행일 2001.7.17]] - 4 -

위컴퓨터프로그램보호법은 2001 년 1 월 16 일개정되어 7 월 17 일부터시행되는데, 이번개정에서가장중요하게다루어진것이컴퓨터프로그램역분석의허용한계를규정한것이다. 컴퓨터프로그램의역분석은이미 2000 년개정된컴퓨터프로그램보호법에서도규정하고있었는데, 2001 년개정법에서는저작권제한의일종으로리버스엔지니어링을추가하고, 프로그램코드의역분석을규정하고있다. 상호운용성을위한리버스엔지니어링은특정범위안에서는대부분합법적이긴하다. 하지만경쟁사의 코드를사용하는것은분명한저작권위반이될수있다. DMCA(The Digital Millennium Copyright Act) 은인터넷에서의저작권침해행위를단속하기위한미국법이다. DMCA 의보호를받는대상에는 HTML 소스코드, 본문내용, 사진, 이미지, 해킹된코드, 불법복제소프트웨어, 음악, 영화, 웹페이지및이메일등이있다. DMCA 에의해서제재를받은대표적인사례들몇가지를예로들어보겠다. DMCA 가 Sony-BMG 의 " 루트킷 " 취약성의공개를늦추다 Sony-BMG 의 CD 복제방지기술인 " 루트킷 (root kit)" 이이용자의컴퓨터에보안문제를야기하는것을발견한프린스턴의대학원생알렉스해더맨은저작권침해여부를판단하기위해서수주간발표를연기할수밖에없었고때문에많은이용자가위험에노출된상태로오랫동안있었음 펠튼교수연구팀위협을받다 Secure Digital Music Initiative(SDMI) 라는다산업집단이디지털음악을보호하는특정한워터마킹기술을깨는경연대회를공개적으로열었고, 프린스턴대학의에드워드펠튼과프린스턴과라이스대학그리고제록스사의연구자들이깼다. 이결과를학회에서발표하려는것을 SDMI 가저작권법을근거로책임을묻겠다는협박으로막았다. 연구자들이소송을제기하고나서야위협을철회했다. 썬콤사대학원생위협하다 SunnComm 사가개발한 CD 복제방지시스템을단지컴퓨터의쉬프트키만누르고있으면무력화된다는사실을발견한프린스턴대학원생알렉스핼더만이발표한보고서에대해서 SunnComm 사가소송을제기하겠다는위협을함. 후에 SunnComm 사는위협을포기함. 드미트리스킬리아로프체포되다 러시아프로그래머인드미트리스킬리아로프는어도비사의이북포맷의전자책을 pdf 파일로바꾸는소프트웨어를개발했다는이유로수주간감옥살이를하고다섯달동안미국에서유치장생활을했다. 전자책을 pdf 로바꾸는과정에서이북포맷에포함된이용제한조치가깨졌다는이유에서다. 스킬라로프는직접적으로어떤저작권침해를하지도않았고저작권침해를돕지도않았지만미국라스베가스에서열린학회에서그소프트웨어를발표한것으로이런생활을한것이다. 결국은검찰의기소는각하되었다. < 출처 : http://www.eff.org/ip/dmca/unintended_consequences.php> - 5 -

1-3. RCE 응용분야 RCE 응용분야 악성코드분석 DRM 등보호알고리즘분석 개발효율성증가 안전성테스트 응용프로그램패치 RCE 의핵심은 사용자가원하는대로 프로그램이동작하게만드는것 Student Notes 리버스엔지니어링은흔히불법이라는생각에응용분야또한불법인것이아닌가하는생각을할수있다. 하지만실제는정반대의상황이발생할수있다. 현재인터넷을사용하는모든사용자가가장신경을많이쓰는부분은바이러스 / 웜이나악성코드일거라생각한다. 몰론스팸도만만치않겠지만바이러스 / 웜이나악성코드 ( 이하 악성코드 라함 ) 의경우직접적으로내가사용하고있는컴퓨터에영향을미치기때문에더많은신경을쓰고백신프로그램도설치하고할것이다. 이러한악성코드는대부분확장자가 exe 인실행파일의형태로배포가되고감염자 PC 에서실행되면서악성코드제작자의의도대로악의적인행동을하게된다. 그렇다면이러한악성코드가내 PC 에서실행이되면어떤행동들을하는지정확하게파악할수있어야지만악성코드를찾아내서내 PC 를안전하게할수있다. 이러한일련의행동들을우리는리버스엔지니어링기법을통해서파악할수있다. 물론 - 6 -

악의적인목적으로운영체제나소프트웨어들의취약점을찾아서공격하기위해리버스엔지니어링기법을사용할수도있겠지만좋은의미에서리버스엔지니어링을사용할수도있다는것이다. 대표적인예로시그내처기반으로동작하는백신의경우새로운악성코드발견시전문가들에의해서악성코드의일련의행동들을알아낸후백신데이터베이스를업데이트한다. 이때도리버스엔지니어링기법을사용하게된다. 혹은최근배포되고있는악성코드들이암호알고리즘을사용하여악성코드자체를보호하는경우가있 는데이러한경우에도리버스엔지니어링기법을사용한다. DRM 의경우소프트웨어복제방지기술과유사한데차이점이라면소프트웨어복제방지기술은소프트웨어자체를보호하는것이고 DRM 은컨텐츠를보호하는것이라는점이다. 예를들어온라인에서음악을들을수있는사이트가있고이사이트는클라이언트들이음악을듣기위한프로그램설치를요구할수있다. 한악의적인사용자가이클라이언트프로그램을리버스엔지니어링을통해서음원을다운받을수있게할수있다는것이다. 상용소프트웨어와상호호환이되는소프트웨어를개발하는경우소프트웨어라이브러리나 API 에대한매뉴얼의내용이부족한경우가많은데이러한문제점을해소하기위해서리버스엔지니어링기법을사용하거나소프트웨어개발시아무것도없는상태에서개발하는것은힘들기때문에잘만들어진소프트웨어에대해서리버스엔지니어링기법을사용하여재사용을하기도한다. 물론공개소프트웨어의경우는소스코드를활용할수있겠지만공개되지않은소프트웨어의경우는리버스엔지니어링과정을거쳐야할것이다. 마지막으로소프트웨어의안전성과취약점을평가하기위해리버스엔지니어링기법을사용할수있다. 개발된소프트웨어에대해리버스엔지니어링기법을적용하여소프트웨어에결함이있지는않은지품 질평가를할수있다. 그리고응용프로그램에대한패치도리버스엔지니어링의응용분야이다. 취약점이알려진프로그램이 있는데소스코드가없어서재컴파일할수없을경우바이너리를패치해야하고이때리버스엔지니어 링을하게된다. 물론이런점을악용하면크랙을하는것이될수도있다. 하지만리버스엔지니어링에서가장중요한건개발자의의도가아닌사용자의의도대로프로그램이동 작하게하려고한다는것이다. - 7 -

- 8 -

Module 2 RCE 기초 Objectives CPU 동작방식 CPU 레지스터 어셈블리 / 디스어셈블리 STACK 구조 함수호출규약 (Calling Convention) SEH(Structured Exception Handling) C 코드컴파일후어셈코드변환모습 특정루틴에서사용되는 API 함수 - 9 -

2-1. CPU 동작방식 CPU 동작방식 CPU (Central Processing Unit) ALU (Arithmetic Logic Unit) Register Set Control Unit BUS Interface Memory I/O BUS Keyboard Monitor NIC HDD Student Notes CPU 는소프트웨어명령의실행이이루어지는컴퓨터의부분, 혹은그기능을내장한칩을말한다. CPU 는기계어로쓰여진컴퓨터프로그램의명령어를해석하여실행한다. CPU 는프로그램에따라외부에서정보를입력하고, 기억하고, 연산하고, 외부로출력한다. CPU 는컴퓨터부품과정보를교환하면서컴퓨터전체의동작을제어한다. 기본구성으로는레지스터, 산술논리연산장치 (ALU: arithmetic logic unit), 제어부 (control unit) 와내부버스등이있다. 각종전자부품과반도체칩을하나의작은칩에내장한전자부품을마이크로프로세서라고한다. 마이크로프로세서는전기밥통에쓰이는낮은성능의제품부터컴퓨터에쓰이는높은성능의제품까지매우다양하다. 마이크로프로세서들가운데가장복잡하고성능이높은제품은컴퓨터의연산장치로쓰인다. 이것을중앙처리장치라고한다. - 위키백과사전 - - 10 -

프로그램이실행되면실행프로그램이메모리에적재되고메모리의내용을 CPU 에서가져와레지스터 에저장한후 Control Unit 을통해 ALU 에전달된후 ALU 에서산술연산을하게된다. ALU( 산술연산장치 ) : 두숫자의 ( 덧셈, 뺄셈같은 ) 산술연산과 ( 배타적논리합, 논리곱, 논리합같은 ) 논리연산을계산하는디지털회로이다. 산술논리장치는컴퓨터중앙처리장치의기본설계블럭이다. Register : 컴퓨터의프로세서내에서자료를보관하는아주빠른기억장소이다. 일반적으로현재계산을수행중인값을저장하는데사용된다. 대부분의현대프로세서는메인메모리에서레지스터로데이터를옮겨와데이터를처리한후그내용을다시레지스터에서메인메모리로저장하는로드-스토어설계를사용하고있다. - 11 -

2-2. CPU 레지스터 CPU Register Student Notes CPU 에서사용되는레지스터는크게범용레지스터, 세그먼트레지스터, EFLAGS 레지스터, CIP 레지스 터네가지로구분된다. 기본적으로 32 비트 (4 바이트 ) 의크기를갖고있으며 16 비트나 8 비트형태로나 누어서사용되기도한다. - 12 -

레지스터에저장되는데이터들은부호가없는 00000000 부터 FFFFFFFF 까지이다. Reverse Engineering 범용레지스터 (General-Purpose Register) 범용레지스터는논리, 산술연산에사용되는오퍼랜드나주소계산을위한오펀랜드, 메모리포인터에 사용된다. EAX(Extended Accumulator Register) : 산술연산에사용 EBX(Extended Base Register) : DS 세그먼트에데이터를가리키는역할 ( 주소지정을확대하기위한인덱스로사용 ) ECX(Extended Counter Register) : 루프의반복횟수나좌우방향시프트비트수기억 EDX(Extended Data Register) : 입출력동작에서사용 ESI(Extended Source Index) : 출발지인덱스에대한값저장 EDI(Extended Destination Index) : 다음목적지주소에대한값저장 ESP(Extended Stack Pointer) : 스택포인터, 스택의 TOP 을가리킴 EBP(Extended Base Pointer) : 스택내의변수값을읽는데사용, 스택프레임의시작점을가리킴 세그먼트레지스터 (Segment Register) 세그먼트레지스터는 16 비트세그먼트선택자를가지고있다. 세그먼트선택자는메모리에서세그먼트 를확인하는특별한포인터다. - 13 -

CS : 코드세그먼트의시작주소를저장 - 프로그램에서명시적으로로드될수는없으나프로그램제어를변경하는명령또는내부프로세서조작에의해묵시적으로로드될수있다. SS : 스택으로사용할메모리영역의시작주소를저장 - 명시적으로로드될수있기때문에프로그램에서이레지스터를통해여러개의스택을셋업하여그들간을상화전환할수있다. DS : 데이터세그먼트의시작주소를저장 ES : 여분의데이터세그먼트용, 문자열처리명령시목적지데이터저장 FS/GS : 여분의세그먼트용, IA32 프로세서에서만사용가능 세그먼트레지스터가어떻게사용되는지는사용하는메모리관리모델에따라달라질수있다. 메모리 관리모델은크게 Flat 메모리모델과 Segmented 메모리모델이있다. 그중윈도우환경에서사용되며일 반적으로많이사용되는메모리모델은 Segmented 메모리모델이다. EFLAGS 레지스터 EFLAGS 레지스터는상태플래그, 제어플래그, 시스템플래그그룹으로구성되어있으며 1, 3, 5, 15, 22 ~ 31 비트는예약되어있으므로사용할수없다. - 14 -

LAHF, SAHF, PUSHF, PUSHFD, POPF, POPPFD 등의명령으로프로시저스택혹은 EAX 레지스터로플래그의그룹을이동할수있다. EFLAGS 레지스터의내용은비트조작명령 (BT, BTS, BTR, BTC) 을사용해플래그를검사및변경할수있다. 상태플래그 (Status Flags) : 산술연산 (ADD, SUB, MUL, DIV) 을하는명령의결과를가리킨다. CF(bit 0) Carry flag : 부호없는정수끼리의산술연산후오버플로우가발생했는지확인 - 0 : 오버플로우가발생하지않은경우 - 1 : 오버플로우가발생한경우 PF(bit 2) Parity flag AF(bit 4) Adjust flag ZF(bit 6) Zero flag : 산술연산또는비교동작의결과를나타냄 - 0 : 결과가 0 이아닌경우 - 1 : 결과가 0 인경우 SF(bit 7) Sign flag OF(bit 11) Overflow flag : 부호있는수끼리의산술연산후오버플로우가발생했는지확인 - 0 : 오버플로우가발생하지않은경우 - 1 : 오버플로우가발생한경우 ( 너무큰정수나너무작은음수인경우 ) 제어플래그 (Control Flags) DF(bit 10) Direction flag 시스템플래그 (System Flags) TF(bit 8) Trap flag IF(bit 9) Interrupt enable flag IOPL(bit 12, 13) I/O privilege level field NT(bit 14) Nested task flag RF(bit 16) Resume flag VM(bit 17) Virtual-8086 mode flag AC(bit 18) Alignment check flag VIF(bit 19) Virtual interrupt flag VIP(bit 20) Virtual interrupt pending flag ID(bit 21) Identification flag 리버스엔지니어링에서필요한플래그는 ZF, OF, CF 세가지플래그이다. - 15 -

EIP 레지스터현재프로세서가실행하고있는명령바로다음에실행할명령어의오프셋을저장한다. 수행한명령어길이만큼증가된 EIP 는메모리내의다음실행할명령어를가리킨다. 실제모드로동작할때는 16 비트 EIP( 상위 16 비트는 0) 로, 보호모드로동작할때는 32 비트 EIP 로동작한다. EIP 는소프트웨어의의해조작할수없고 CALL, JMP, RET 와같은 control-transfer 명령에의해서만영 향을받는다. - 16 -

2-3. Assembly Assembly Arithmetic Instruction Data Transfer Instruction Logical Instruction String Instruction Control Transfer Instruction Processor Control Instruction Student Notes 어셈블리어는리버스엔지니어링을하는데중요한기초지식중하나이다. 어셈블리어명령어들이어떤 역할을하는지에대해서이해하고한줄한줄의의미보다는여러라인이어떠한하나의의미를가지는 지를파악하는것이중요하다. 이번장에서는여러가지어셈블리명령어에대해서알아보도록하겠다. - 17 -

Arithmetic Instruction 명령 ADD SUB ADC SBB CMP INC DEC NEG AAA DAA AAS DAS MUL IMUL AAM DIV IDIV AAD CBW CWD 설명캐리를포함하지않은덧셈캐리를포함하지않은뺄셈캐리를포함한덧셈캐리를포함한뺄셈두개의오퍼랜드비교오퍼랜드내용을 1 증가오퍼랜드내용을 1 감소오퍼랜드의 2 의보수, 즉부호반전덧셈결과 AL 값을 UNPACK 10 진수로보정덧셈결과의 AL 값을 PACK 10 진수로보정뺄셈결과 AL 값을 UNPCAK 10 진수로보정뺄셈결과의 AL 값을 PCAK 10 진수로보정 AX 와오퍼랜드를곱셈하여결과를 AX 또는 DX:AX 에저장부호화된곱셈곱셈결과 AX 값을 UNPACK 10 진수로보정 AX 또는 DX:AX 내용을오퍼랜드로나눔. 몫은 AL, AX 나머지는 AH, DX 로저장부호화된나눗셈나눗셈결과 AX 값을 UNPACK 10 진수로보정 AL 의바이트데이터를부호비트를포함하여 AX 워드로확장 AX 의워드데이터를부호를포함하여 DX:AX 의더블워드로변환 Data Transfer Instruction 명령 설명 MOV 데이터이동 ( 전송 ) PUSH 오퍼랜드내용을스택에쌓음 POP 스택으로부터값을가져옴 XCHG 첫번째오퍼랜드와두번째오퍼랜드교환 XLAT BX:AL 이지시한테이블의내용을 AL 로로드 LEA 메모리오프셋값을레지스터로로드 LDS REG (MEM), DS (MEM+2) LES REG (MEM), ES (MEM+2) LAHF 플래그의내용을 AH 의특정비트로로드 SAHF AH 의특정비트가플래그레지스터로전송 PUSHF 플래그레지스터의내용을스택에쌓음 POPF 스택으로부터플래그레지스터로가져옴 - 18 -

Logical Instruction 명령 설명 NOT 오퍼랜드의 1 의보수, 즉비트반전 SHL/SAL 왼쪽으로오퍼랜드만큼자리이동 ( 최하위비트는 0) SHR 오른쪽으로오퍼랜드만큼자리이동 ( 최상위비트는 0) SAR ROL/ROR RCL/RCR AND TEST OR XOR 오른쪽자리이동, 최상위비트는유지왼쪽 / 오른쪽으로오퍼랜드만큼회전이동캐리를포함하여왼쪽 / 오른쪽으로오퍼랜드만큼회전이동논리 AND 첫번째오퍼랜드와두번째오퍼랜드를 AND 하여그결과로플래그세트논리 OR 배타논리합 (OR) String Instruction 명령 REP MOVS COMPS SCAS LODS STOS 설명 REP 뒤에오는스트링명령을 CS 가 0 이될때까지반복 DS:DI 가지시한메모리데이터를 ES:DI 가지시한메모리로전송 DS:DI 와 ES:DI 의내용을비교하고결과에따라플래그설정 AL 또는 AX 와 ES:DI 가지시한메모리내용비교하고결과에따라플래그설정 SI 내용을 AL 또는 AX 로로드 AL 또는 AX 를 ES:DI 가지시하는메모리에저장 Control Transfer Instruction 명령 설명 플래그의변화 CALL 프로시저호출 JMP 무조건분기 RET CALL 로스택에 PUSH 된주소로복귀 JE/JZ 결과가 0 이면분기 ZF=1 JL/JNGE 결과가작으면분기 ( 부호화된수 ) SF!= OF JB/JNAE 결과가작으면분기 ( 부호화안된수 ) CF=1 JLE/JNG 결과가작거나같으면분기 ( 부호화된수 ) ZF=1 or SF!= OF JBE/JNA 결과가작거나같으면분기 ( 부호화안된수 ) CF=1 or ZF=1 JP/JPE 패리티플래그가 1 이면분기 PF=1 JO 오버플로우가발생하면분기 OF=1 JS 부호플래그가 1 이면분기 SF=1-19 -

JC 캐리가발생하면분기 CF=1 JNE/JNZ 결과가 0 이아니면분기 ZF=0 JNL/JGE 결과가크거나같으면분기 ( 부호화된수 ) SF=OF JNB/JAE 결과가크거나같으면분기 ( 부호화안된수 ) CF=0 JNLE/JG 결과가크면분기 ( 부호화된수 ) ZF=0 and SF=OF JNBE/JA 결과가크면분기 ( 부호화안된수 ) CF=0 and ZF=0 JNP/JPO 패리티플래그가 0 이면분기 PF=0 JNO 오버플로우가아닌경우분기 OF=0 JNS 부호플래그가 0 이면분기 SF=0 JNC 캐리가아닌경우분기 CF=0 LOOP CX 를 1 감소, 0 이될때까지지정된라벨로분기 LOOPZ/LOOPE CX 가 0 이아니면지정된라벨로분기 ZF=1 LOOPNZ/LOOPNE CX 가 0 이아니면지정된라벨로분기 ZF=0 JCXZ CX 가 0 이면분기 CX=0 INT 인터럽트실행 INTO 오버플로우가발생하면인터럽트실행 IRET 인터럽트복귀 ( 리턴 ) Processor Control Instruction 명령 CLC CMC CLD CLI HLT STC NOP STD STI WAIT ESC 캐리플래그클리어캐리플래그를반전디렉션플래그를클리어인터럽트플래그를클리어정지캐리플래그셋아무동작하지않음디렉션플래그셋인터럽트플래그셋프로세서를일시정지상태로한다이스케이프명령 설명 - 20 -

2-4. STACK 구조 STACK 구조 Student Notes 스택은한쪽끝에서만데이터를넣거나뺄수있는선형구조로되어있다. 자료를넣는것을 PUSH 라 고하고데이터를꺼내는것을 POP 이라고하는데이때꺼내지는데이터는가장최근에넣은데이터가 된다. 이처럼나중에넣은값이먼저나오는것을 LIFO(Last In First Out) 구조라고한다. 스택은구현방법에따라네가지로구분할수있다. Full Stack : TOP 이마지막으로 PUSH 된데이터를가리킴 Empty Stack : TOP 이다음데이터가들어올곳을가리킴 Ascending Stack : 낮은메모리주소에서시작하여높은메모리주소방향으로자람 Descending Stack : 높은메모리주소에서시작하여낮은메모리주소방향으로자람 - 21 -

인텔아키텍처의스택은 Full Descending Stack 의형태를가지고있다. 즉, 스택의 TOP 이마지막으로들어온데이터를가리키고있으며높은메모리주소에서시작하여낮은메모리주소방향으로자란다는뜻이다. 스택에서주로사용되는세개의레지스터에는 EBP, ESP, EIP 가있다. EBP 는스택프레임의시작점을가리키는데함수내에서 ESP 를통해스택의크기를늘리고줄일때기존의 ESP 값을백업하는용도로사용된다. 그리고 ESP 는스택의 TOP 을가리키는데스택포인터이동시에사용된다. EIP 는다음에실행할명령의오프셋을가지고있는데보통함수호출후복귀주소를나타내는레지스터로써소프트웨어에의해조작될수없다. 다음그림은시스템에서프로그램이실행되고함수가호출될때스택의변화를나타낸그림이다. 그림에서보는것처럼메인프로그램이실행되는도중에 F1 이라는함수가호출이되면새로운 func1 함수에게새로운스택을할당한다. 이때 func1 함수의실행이끝나면다시메인함수로돌아오기위해서복귀주소를먼저스택에넣어둔다. 이것은스택의 func1 함수의실행이끝나면 func1 함수가사용하던스택에있던데이터를전부비운후마지막으로복귀주소를보고메인함수로돌아오기위함이다. 그리고 func1 함수내에서 func2 함수를호출하는경우도같은방법을사용한다. - 22 -

2-5. 함수호출규약 (Calling Convention) 함수호출규약 (Calling Convention) 함수호출방법에대한규약 Argument 전달방법 Stack : C 표준라이브러리, cdecl, stdcall, Pascal Register : fastcall Argument 전달순서 Right to Lest : cdecl, stdcall Left to Right Stack Clearing 방법 Caller : cdecl Callee : stdcall Return Value 주요함수호출규약 stdcall, cdecl, fastcall Student Notes 함수호출규약은프로그램이함수에게파라미터를전달하고결과값을다시받는일련의표준화된방법 이다. 즉, 함수호출을위해서밟는절차를정해둔것이라고생각하면되겠다. 함수의호출을구현하기위해서는플랫폼이나프로그래밍언어에따라다를수있겠지만기본적으로실행할함수의코드위치는갖고있는포인터나그값을가져올수있는함수이름, 현재처리하고있는함수의정보를저장할공간, 함수의실행이끝난뒤리턴값을돌려받을공간, 함수의실행에필요한인자를넘겨줄공간등이필요하다. 함수호출규약을이해하기위해서먼저스택프레임 (Stack Frame) 에대해서알아보도록하겠다. 스택프 레임은호출된함수가실행되는동안필요한지역변수나호출한함수의실행에필요한정보가손실되 - 23 -

지않도록스택에저장할때사용하는구조를의미한다. 이렇게저장해야하는정보에는다음과같은것 들이있다. 함수의실행이종료된후에리턴할주소 지역변수 자신을호출한함수의스택프레임위치 레지스터같은기계상태 예외처리리스트등 스택프레임은기본적으로함수가호출될때마다새로설정이되고 Fame Pointer( 또는 Base Pointer) 를통 해서참조할수있다. 프레임포인터는현재실행되고있는함수의스택프레임이스택에서어느주소위 치에있는지가리키는값으로인텔아키텍처에서는 EBP 레지스터에저장된다. 함수호출규약에는총 5 가지규약이있지만 (stdcall, cdecl, thiscall, fastcall, naked) 여기서는자주사용되 는 stdcall 과 cdecl 호출규약에대해서만알아보도록하겠다. stdcall stdcall 호출규약은윈도우플랫폼에서시스템 API 를호출할때사용하는규약이다. stdcall 호출규약에서컴파일러는멤버함수의오른쪽인자부터왼쪽인자순으로스택에집어넣고, 함수호출이종료되면호출된함수 (Callee) 가인자를스택에서제거하며, 리턴값이있는경우레지스터 eax 를통해서리턴값을돌려받는다. 호출된함수가스택에서제거해야할인자의개수를정확히알아야하기때문에가변개수의인자를전달할수없다. 보통 call 문다음에 ret n 을이용하여스택을정리한다. - 24 -

cdecl cdecl 호출규약은 C 컴파일러혹은 C++ 컴파일러에서전역함수나전역스태틱함수, 스태틱멤버함수호출에기본적으로사용하는규약이다. cdecl 호출규약에서컴파일러는멤버함수의오른쪽인자부터왼쪽인자순으로스택에집어넣고, 함수호출이종료된뒤함수호출자 (Caller) 는스택에집어넣은인자를제거하며, 리턴값이있는경우레지스터 eax 를통해서리턴값을돌려받는다. cdecl 호출규약에서는호출자가넣은인자를제거하게되어있으므로, 함수가가변개수의인자를가져도안전하게스택을정리할수있다. 보통 call 문다음에 add esp, n 을이용하여스택을정리한다. 가장자주사용되는함수호출규약인 stdcall 과 cdecl 의가장큰차이점은누가스택을정리하냐하는것 이다. stdcall 호출규약의경우호출된함수 (Callee) 가스택을정리하기때문에호출하는함수 (Caller) 와 Callee 모두파라미터의크기를알고있어야정상적인처리가가능하지만 cdecl 호출규약의경우 Caller 가스 택을정리하기때문에 Callee 는파리미터의크기를정확히몰라도된다. - 25 -

2-6. SEH(Structured Exception Handling) SEH (Structured Exception Handling) FS TIB FS:[0] FS:[1] FS:[2]... ERR NEXT SEH ERR FFFFFF SEH TIB (Thread Information Block) : Thread에대한중요한정보를가지고있는구조체 ERR 구조체 Next ERR 구조체 Exception Handler 시작주소 Student Notes SEH(Structured Exception Handling) 은윈도우에서제공하는예외처리방식이다. 예외 란예기치못하거나해당프로세스의정상적인실행을방해하는이벤트이다. 예외를발생시키는상황은여러가지가있는데대표적인것들을 0 으로나누기나숫자형식의오버플로우, 잘못된메모리주소에대한읽기나쓰기를시도하는것들이있다. 예외발생시운영체제는 TIB 에서가리키고있는주소로이동하여예외를처리한다. TIB 는쓰레드에대한중요한정보를가지고있는구조체인데 TIB 의첫번째멤버인 FS:[0] 은 ERR(Exception Registration Record) 구조체를가리키고있는포인터이다. ERR 구조체는다음 ERR 구조체의주소와 Exception Handler 시작주소의두개의멤버를가지고있다. - 26 -

Exception Handler 의프로토타입은다음과같다. cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame, struct _CONTEXT *ContextRecord, void * DispatcherContext ); _CONTEXT 는예외발생시모든레지스터의값을저장한다. 그리고 EAX 0, ret 시사용하게된다. +0 context 플래그세그먼트레지스터 (GetThreadContext API 를이용할때사용 ) 디버그레지스터 +4 debug register #0 +8 debug register #1 +C debug register #2 +10 debug register #3 +14 debug register #6 +18 debug register #7 +8C gs register +90 fs register +94 es register +98 ds register 일반레지스터 +9C edi register +A0 esi register +A4 ebx register +A8 edx register +AC ecx register +B0 eax register - 27 -

FPU / MMX 레지스터 +1C ControlWord +20 StatusWord +24 TagWord +28 ErrorOffset +2C ErrorSelector +30 DataOffset +34 DataSelector +38 FP registers * 8 ( 각각 10 바이트차지 ) +88 Cr0NpxState 제어레지스터 +B4 ebp register +B8 eip register +BC cs register +C0 eflags register +C4 esp register +C8 ss register Winnt.h 에정의되어있는 EXCEPTION_RECORD 는다음과같다. typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; UINT_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; ExceptionCode : 발생한예외의종류 C0000005h : 접근할수없는메모리를 Read/Write 하려할때 C0000094h : 0 으로나누려할때 C0000095h : DIV 할때발생하는오버플로 C00000FDh : 스택의최대크기를넘을때 80000001h : Guard Page 플래그가셋팅된메모리주소를접근하려할때 C0000025h : 더이상처리할수없는 Exception C0000026h : 예외처리도중시스템에의해사용되어지는 ExceptionCode 80000003h : INT3 80000004h : Trap 플래그세트 ExceptionFlags : 발생한예외에대한몇가지부가적인정보를포함 0 : 처리할수있는 Exception 1 : 처리할수없는 Exception 2 : 스택이 Unwinding 하고있을때 예제를통해서 SEH 에대해서알아보도록하겠다. - 28 -

먼저예제프로그램을 Ollydbg 에서열고실행을시켜보면아래쪽상태바에서다음과같은메시지를만 나게된다. 예외가발생한것을볼수있다. 이때 SEH chain 을확인하거나 Stack 정보를확인하면예외를처리할부 분이어느곳인지확인할수있다. 현재 ERR 구조체를스택에서확인해보면 Next SEH 는 0007FFE0 에위치해있고현재예외를처리하는 곳은 00B13313 에있다. 00B13313 에브레이크포인트를걸고 SEH 에서어떠한행동을하는지확인해보 겠다. 브레이크포인트설정후 Shift + F9 를눌러진행한다. 브레이크포인트설정후 Shift + F9 를눌러진행한다. 00B13313 에서멈춘것을확인할수있다. 이부분부터가예외발생시처리하는부분이다. 스택의내용을확인해보도록하자. 예외처리루틴으로들어가게되면앞서살펴본것과같이 ExceptionRecord, EstablishedFrame, Context, DispatchContext 순서로스택에저장된다. - 29 -

0007FBC8 7C968752 RETURN to ntdll.7c968752 // Return 주소 0007FBCC 0007FCA8 // ExceptionRecord, 환경저장 0007FBD0 0007FF90 // Frame, ERR 구조체 0007FBD4 0007FCC4 // Context 0007FBD8 0007FC84 // DispatchContext 현재위치에서부터아래쪽으로코드들을확인해보면뭔가잘못되었다는것을알수있다. 00B13321 에서 JMP SHORT 00B13324 로되어있으므로 00B13324 로점프를해야하는데 00B13324 가 보이지않는데이것은디버거가해석을잘못했기때문이다. 이렇게실제실행되는내용들이쓰레기코 드때문에보이지않는데이런부분들을찾아서필요없는코드들을전부 NOP(0x90) 처리해준다. 코드를수정할부분에서바이너리에디트를해서 ( 단축키 CTRL + E) 다음과같이수정한다. - 30 -

위와같이수정하면해당부분의코드가다음과같이변경된다. 이런코드들을전부수정하면다음과같다. 수정된코드를분석해보면 SEH 발생시어떤행동들을하는지확인할수있다. /*B13313*/ PUSH 0B1331C /*B13318*/ INC DWORD PTR SS:[ESP] /*B1331B*/ RETN /*B1331C*/ NOP 스택에 0B1331C 를넣은후 ESP 에있는값을 1 증가시킨다. 이것은 JMP 0B1331D 와같은의미이다. /*B1331D*/ MOV EAX,DWORD PTR SS:[ESP+C] ESP+C 의주소에있는값을 EAX 에저장한다. ESP+C 는 0007FD4 가되고이주소에있는값은 0007FCC4 이다. 이곳은 CONTEXT 내용있는곳이다. - 31 -

/*B13321*/ JMP SHORT 00B13324 /*B13324*/ ADD DWORD PTR DS:[EAX+B8],2 EAX+B8 은 CONTEXT 구조체를확인해보면 EIP 레지스터를나타낸다. 즉, EIP 를 2 만큼증가한다. /*B1332B*/ JMP SHORT 00B13347 00B13347 로점프 /*B1332D*/ MOV ESP,EBE817EB /*B13332*/ ADC AL,0E8 /*B13334*/ JMP SHORT 00B13347 /*B13336*/ CALL EC994226 /*B1333B*/ OR EBP,EAX /*B1333D*/ JMP SHORT 00B13347 /*B1333F*/ INT 20 /*B13341*/ JMP SHORT 00B13347 앞에서 00B13347 로점프하기때문에의미없는코드들 /*B13347*/ XOR EAX,EAX /*B13349*/ RETN EAX 값을초기화하고 SEH 처리완료한다. 다음 SEH 를호출할필요없이바로복귀 SEH 처리루틴이끝나면다음과같은코드를만나게된다. 이것은 SEH 체인에서상위 SEH 를제거하는과정이다. 그런후 F9 를눌러계속진행하면된다. 지금까지의과정은 SEH 에서어떤행동들을하는지확인한것이었다. 이예제와같이다른행동을하지 - 32 -

않고 EIP 만변경하는것이라면문제가없겠지만 SEH 처리중간에다른행동들을하게끔작성된코드들 도있을수있다. SEH 를하나씩처리해나가다보면마지막 SEH 처리에서다음과같은모습을볼수있다. 마지막 SEH 는 EIP 를 2 증가하고 SEH 내부에서하드웨어브레이크포인트를클리어하는작업을하는 것을볼수있다. XOR EAX, EAX 는 EAX 값을 0 으로설정하는것이고이것은현재 SEH 가발생한익셉 션을정상적으로처리했다는것을의미한다. - 33 -

2-7. C 코드컴파일후어셈코드변환모습 C 코드컴파일후어셈코드변환모습 for, while, if String 관련함수 strcpy strcmp strlen 컴파일러는자동화된도구이기때문에규칙을가지고컴파일을하기때문에특정프로그램을컴파일한후어셈블리어코드는일정한패턴을갖는다. Student Notes C 코드를컴파일한후실행파일을디버거로분석하면어셈블리어로표현이되는것을볼수있고특정함수의경우동일한또는비슷한패턴의어셈블리어로표현되는것을볼수있다. 우리는이런어셈블리어로된코드들을보고분석을하게된다. 숙련된리버서들은여러패턴의코드들을보기때문에어셈블리어코드만보고도금방어떤행동들을하는코드인지식별하는것이가능하지만덜숙련된리버서들은조금힘들수있다. 이번장에서는몇가지함수들을예제로하여어셈블리어코드와비교하면서 C 코드가컴파일된후에는 어떤어셈블리어코드로변하는지알아보도록하겠다. - 34 -

Example #1. for #include <stdio.h> main(int argc, char *argv[]) { int i=0; int sum=0; for(i=0; i<10; i++) { sum+=i; } printf("%d, %d\n", i, sum); } 위코드를컴파일하여실행파일을만든후실행파일을디버거로확인하면다음과같은코드들을볼수 있다. 루프문을확인하는가장확실한방법은 v 표시와 ^ 표시, 그리고 > 표시를확인하는것이다. 0040103D 에서 00401048 로분기하고조건이만족할경우 (CMP 결과 ) 00401059 로그렇지않을경우계속코드를 진행하다가 00401057 에서 0040103F 로분기한다. - 35 -

Example #2. if, strcpy, strcmp, strlen 이번예제는 if 문과함께스트링관련함수들 (strcpy, strcmp, strlen) 이있는예제를살펴보겠다. #include <stdio.h> void main( int argc, char *argv[] ) { char src[] = "ForEducationbydemantos"; char *dst=(char *)malloc( 30 * sizeof(char)); int cnt=0; printf("input the password : "); fgets(dst, 30, stdin); dst[strlen(dst)-1] = '\0'; if( strlen(dst)!= strlen(src) ) { printf("not match!!\n"); exit(0); } else { if(!strcmp(dst, src) ) { printf("good\n"); exit(0); } else printf("not match!!\n"); exit(0); } } 위예제는비교할패스워드를위한메모리공간을할당한후입력된값의길이를비교한후맞을경우입 력된값과실제패스워드의문자열을비교하는프로그램이다. 먼저문자의길이를구하는부분이다. - 36 -

4 글자씩잘라내면서문자의길이를구한다. 함수의실행결과는 EAX 레지스터에저장이되고 F8 로한스텝씩진행하면서 EAX 레지스터를살펴보면길이가저장되는것을확인하수있다. 아래코드는입력된문자와비교할문자의길이를구하는코드들이다. 0040109E 에서입력된문자의길이와비교할문자의길이를비교해서같을경우 004010B9 으로분기하 고그렇지않을경우 Not match!! 라는메시지를출력하는루틴으로분기하게된다. 문자의길이가같 을경우입력한문자와비교할문자가같은지비교하는루틴으로분기한다.(004010B9) 사용자가입력한문자를 EAX 에저장하고비교할문자를 EDX 에저장한후 strcmp 함수를호출하고있 다. strcmp 함수내부로들어가보면 (F7 을눌러진행 ) 다음과같은코드를볼수있다. 함수내부에서는 4 글자씩분할한후비교를하고있다. 먼저첫번째글자를비교하는부분이다. - 37 -

두번째글자를비교하는부분이다. 두글자비교후 SHR 을이용해서쉬프트한후다시두문자를비교하는부분이다. 0x10 은 10 진수로 16 이고이것은 16 비트를의미한다. 즉, 2 바이트만큼오른쪽으로쉬프트하므로 4 글자중앞에서비교했던 두글자를없애고나머지두글자로다음글자를비교하는것이다. 처음 4 글자가같을경우 ECX 와 EDX 값을 4 씩더해서다음 4 글자를비교하게된다. 위코드를 F8 진행하면서확인해보아야할부분은레지스터창과상태창이다. RETN 전코드에브레이크 포인트를설정하고 CTRL+F8 로실행을해보면레지스터창에서 4 글자씩감소하는것을확인할수있고 상태창에서는현재비교할문자들을보여주는것을확인할수있다. 이런반복문이나기타여러함수들에대한어셈블리어코드들은자주보면서눈에익숙하게하는것이 가장좋은방법이다. 자주이런코드들을접하다보면자연스럽게코드들이눈에들어오게된다. - 38 -

Example #3. while #include <stdio.h> #include <conio.h> int main(void) { char ch; ch = getche(); } while(ch!='q') { ch=getche(); } printf("found the q"); return 0; getche 함수에의해서사용자의입력을받아들이고 q 라는글자가입력이되면 found the q 라는메시 지를출력하면서끝난다. while 에의해서생성된어셈코드는아래와같다. 00401034 에서비교를하고같을경우 00401043 으로분기하여루프문을빠져나오지만그렇지않을경 우계속입력을받아들인다. Hex dump 부분에보이는 v 표시와 ^ 표시, 그리고 > 표시를잘보도록하자. 빨간색으로화살표가그려질경우분기를한다는것이고회색으로표시되면분기되지않는다는것이다. - 39 -

2-8. 특정루틴에서사용되는 API 함수 특정루틴에서사용되는 API 함수 파일및디렉토리관련 CreateFile, ReadFile, WriteFile, SetFilePointer, CopyFile, GetFileAttribute, SetFileAttribute, FindFirstFile, FindNextFile, GetModuleFileName, GetSystemDirectory, GetWindowsDirectory, GetCommandLine, SetCurrentDirectory 레지스트리관련 RegCreateKey, RegOpenKeyEx, RegSetValueEx, RegQueryValueEx 네트워크관련 WSAStartup, WSAAPI socket, recv, send, listen, accept, gethostbyname, ntohs, inet_addr, WNetOpenEnum, WNetEnumResource, InternetGetConnectedState, ioctlsocket 메모리관련 RtlZeroMemory, GlobalAlloc 기타 ShellExecute, GetProcAddress, CreateThread Student Notes 윈도우환경에서 exe 파일을분석할경우 API 함수들을많이알고있는것이분석에도움이된다. 디버거를사용하여현재프로그램에서사용되는 API 함수들을나열할수있는데함수들의기능이나리턴값을알아야만프로그램을디버깅하는과정에서브레이크포인트를지정할곳을쉽게찾아낼수있다. 이번장에서는악성코드에서자주사용되거나필히알아두어야할 API 함수들에대해서살펴보도록하겠다. 파일, 디렉토리관련함수, 레지스트리관련함수, 네트워크관련함수, 메모리관련함수들로구분지어 서보도록하겠고기타함수들에대해서도간단히살펴보도록하겠다. - 40 -

파일및디렉토리관련함수 CreateFile : 파일을생성하거나연다. ReadFile : 파일의내용을읽는다. WriteFile : 파일에내용을쓴다. SetFilePointer : 파일포인터를이동시킨다. CopyFile : 파일을복사한다. GetFileAttribute : 파일이나디렉토리의속성을저장한다. SetFileAttribute : 파일이나디렉토리의속성을설정한다. FindFirstFile : 디렉토리에서파일을찾는다. FindNextFile : 파일검색을계속한다. FindFirstFile 함수의호출에이어서검색을계속한다. GetModuleFileName : 파일의절대경로를저장한다. GetSystemDirectory : 윈도우가설치된디렉토리를저장한다. GetWindowsDirectory : 윈도우가설치된디렉토리를알아낸다. GetCommandLine : 현재프로세스의 commandline 문자열을저장한다. SetCurrentDirectory : 현재프로세스에대해현재디렉토리를바꾼다. 레지스트리관련함수 RegCreateKey : 레지스트리키를생성하거나연다. RegOpenKeyEx : 레지스트리키를연다. RegSetValueEx : 레지스트리값을설정한다. RegQueryValueEx : 타입과데이터를저장한다. 네트워크관련함수 WSAStartup : WS2_32.DLL 을사용하기위해초기화한다. socket : 소켓을생성한다. recv : 연결되거나바운드된소켓으로부터데이터를받는다. send : 연결된소켓으로데이터를보낸다. listen : 연결요청이들어오는것을기다린다. accept : 소켓을통해들어오는연결을허용한다. gethostbyname : 호스트정보를저장한다. - 41 -

ntohs : network byte order 를 host byte order 로변환한다. inet_addr : IN_ADDR 구조체로주소를변환한다. WNetOpenEnum : 열거할네트워크자원에대해목록화한다. WNetEnumResource : WNetOpenEnum 에서나열된데이터를반복적으로가져온다. InternetGetConnectedState : 로컬시스템의연결상태를저장한다. ioctlsocket : 소켓의입출력모드를제어한다. 메모리관련함수 RtlZeroMemory : 한메모리블록을 0 으로채운다. GlobalAlloc : Heap 메모리를할당한다. 기타함수 ShellExecute : 프로그램내에서다른프로그램을실행시키기위해사용된다. GetProcAddress : SLL 로부터익스포트된함수의주소를저장 CreateThread : 실행할쓰레드를생성한다. 이외에도여러가지함수들을알고있어야하며함수들에대한자세한내용은 WIN32.HLP 파일을참조 하면된다. - 42 -

Module 3 PE 파일구조 Objectives PE 파일포맷 DOS 헤더및 DOS Stub Code PE File Header Optional Header Section Tables Import Table Export Table - 43 -

3-1. PE 파일포맷 PE 파일포맷 DOS Header DOS Stub Code ImageBase DOS Header DOS Stub Code PE Header 248byte 24 + optional header( 기본 224) PE Header Section Table 40byte x 섹션개수 Section Table Section #1 Section #2... Section #n DISK Section #1 Section #2... Section #n MEMORY Student Notes PE 파일형식 (Portable Executable File Format) 이란파일 (File) 에담겨다른곳에옮겨져 (Portable) 도실행시킬수있도록 (Executable) 규정한형식 (Format) 이란뜻이다. Win32 의기본적인파일형식이며윈도우운영체제에서실행되는프로그램이모두 PE 파일형식을가지고있다. 이런실행프로그램또는응용프로그램은 EXE 파일확장자를가지는데이런 EXE 파일이 PE 파일의대표적인예이다. 그리고동적링크라이브러리인 DLL 파일도 PE 파일형식을가지고있다. PE 파일은운영체제와상관없이 Win32 플랫폼에서는공통으로사용할수있다. 즉, 인텔 CPU 를사용하 지않는경우에도어떠한 Win32 플랫폼의 PE 로더도이파일형식을사용할수있다는것이다. Win32 Platform SDK 의 Winnt.h 헤더파일에보면 PE 관련구조체들이선언되어있다. 이구조체에서파 - 44 -

일이라는명칭대신이미지 (Image) 라는단어를사용하는데이것은 PE 파일이하드디스크에파일로존재하지만실행되기위해메모리로로드되기때문이다. 이번모듈을학습하는과정에서꼭기억해야할것은 PE 파일은디스크에서의모습과메모리에서의모습이거의같다 는것이다. PE 구성요소 DOS Header DOS Stub Code PE Header Section Table Section DOS Header 디스크상에서 PE 파일은 DOS 헤더로시작한다. 메모리상에서는 ImageBase 에서시작하기때문에 DOS 헤더는 ImageBase 에서찾을수있다. 파일의정보는 Stub_PE 프로그램을이용하였다. 파일상의가장첫부분과메모리상에서는 ImageBase 의위치인 0x01000000 과일치하는것을확인할수 있다. DOS 헤더는 4D 5A(MZ) 로시작한다. MZ 는 DOS 개발자인 Mark Zbikowski 라는사람의이니셜로 DOS 시그내처로사용된다. - 45 -

PE Header PE 헤더는 PE 파일의정보를가지고있다. 섹션의개수나파일의속성, optional 헤더의크기를비롯하여 ImageBase, 엔트리포인트주소, 메모리상에각섹션이차지하는크기, 디스크상에서의섹션정렬, PE 파 일의총사이즈, 디스크상에서의헤더의총사이즈등과같은정보를가지고있다. PE 헤더의위치는 DOS 헤더에있는 e_lfanew 값을이용하여확인할수있다. e_lfanew 는 DOS 헤더 의마지막 4byte 에위치하고있다. Section Table 섹션테이블은섹션의이름, 섹션의파일상에서의위치및사이즈, 메모리상에서의위치및사이즈, 속성값에대한정보를가지고있다. 섹션테이블은 PE 헤더바로뒤에위치하며섹션테이블의위치는 PE 헤더시작주소에서 PE 헤더의사이즈를더해주면된다. PE 헤더는기본적으로 248bytes 의크기를가지면경우에따라서는변할수있다. 섹션테이블은각섹션별로 40bytes 의크기를갖는다. 따라서섹션의개수가 4 개일경우섹션테이블의크기는 40bytes * 4 = 160bytes 가된다. Section 섹션은실제데이터가위치하는공간이다. 각섹션의위치는섹션테이블에있는섹션헤더에서확인할 수있다. 섹션헤더에는 VirtualAddress 값과 PointerToRawData 라는값이저장되어있다. 둘다 offset 값 - 46 -

으로메모리상에서의위치와디스크상에서의위치를가리키는값이다. 이렇게두개의값이별도로존 재하는이유는디스크상에서의위치와메모리상에서의위치가다를수있다는의미이다..text 섹션의디스크상에서의 offset 이 0x400 임을확인할수있고.text 섹션이파일의시작점부터 0x400 만큼떨어진곳에있다는것을의미한다. Winhex 에서 0x400 의위치를확인하면다음과같다. 위정보를통해섹션헤더의 PointerToRawData 값은해당섹션의파일상에서의시작위치를확인하였다. 이번에는메모리상에서는어디에위치하는지확인해보도록하겠다. 메모리상에서의.text 섹션의위치는 VirtualAddress 에서확인할수있다. VirtualAddress 의값이 0x1000 인것을확인할수있고이값은 ImageBase 로부터의 offset 값이다. 이런 offset 을 RVA(Relative Virtual Address) 라고부른다. 따라서.text 섹션의메모리상에서의위치는 ImageBase 와 VirtualAddress 을합한값인 0x01001000 이된다. 0x01001000 의위치로이동하면다음과같은 HEX 값을확인할수있다. - 47 -

파일상에서.text 섹션의시작점의데이터는 493AD377 이었는데메모리상에서.text 섹션의시작점데이 터는 FAF4F377 로서로다른값인것을확인할수있다. 이렇게파일상에서의섹션의내용과메모리상 에서의섹션의내용이다를수있다는것을확인하였다. PE 파일은메모리상에서와파일상에서거의같다는것을확인했다. 차이가발생하는부분은섹션인데 이것은 SectionAlignment 와 FileAlignment 와연관이있다. 이것은디스크상에서의정렬단위와메모리 상에서의정렬단위가다르다는것을의미한다. 정렬단위는뒤에서나오는 Optional Header 에서자세히보도록하고간단하게두가지만알아보겠다. 디스크상의섹션은 FileAlignment 의배수가되는주소에서시작 메모리상의섹션은 SectionAlignment 의배수가되는주소에서시작 - 48 -

3-2. DOS 헤더및 DOS Stub Code DOS 헤더및 DOS Stub Code DOS Header MZ DOS Stub Code PE signature PE File Header DOS Header e_lfanew IMAGE_DOS_HEADER (64byte) Optional Header DOS Stub Code Section Table Section #1 Section #2... Section #n PE signature Student Notes DOS 헤더는 DOS 와호환을위해사용되는헤더이다. PE 파일은 DOS 헤더로시작하고 DOS 헤더는항 상 64bytes 의크기를갖는다. 우리가알아야할부분은가장처음 2byte 를차지하는 e_magic 과마지막 4byte 를차지하는 e_lfanew 이다. Winnt.h 에선언되어있는 IMAGE_DOS_HEADER 는다음과같다. typedef struct _IMAGE_DOS_HEADER { // DOS.EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed - 49 -

WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; e_magic 은 DOS 헤더의시그내처로 4D 5A(MZ) 값을가진다. e_lfanew 는 PE 헤더의시작점을가리 키는오프셋값이다. DOS Stub Code 는크게중요하지않는부분이다. 이부분이없더라도프로그램이실행되는데는아무런문제가없다. 이부분에있는내용은윈도우용응용프로그램을도스모드에서실행시켰을경우 This program cannot be run in DOS mode 라는메시지를출력하고프로그램이종료되는코드가삽입되어있다. 그리고응용프로그램을제작할경우오브젝트파일을링킹할때 STUB 옵션을사용하여원하는스텁코드를삽입할수도있다. - 50 -

3-3. PE File Header PE File Header DOS Header DOS Stub Code PE signature PE File Header Optional Header Section Table PE\0\0 Machine NumberOfSections TimeDateStamp PointerToSymbolTable NumberOfSymbols SizeOfOptionalHeader Characteristics 4byte IMAGE_FILE_HEADER (20byte) Section #1 Section #2... Section #n Student Notes Winnt.h 에선언되어있는 IMAGE_NT_HEADERS 는다음과같다. typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; IMAGE_NT_HEADERS 에는 PE 시그내처와 IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER 로구성되어있다. 첫번째로 PE 시그내처는 50 45 00 00 으로고정되어있고두번째나오는 IMAGE_FILE_HEADER 도 20bytes 의고정사이즈를갖는다. 마지막에오는 IMAGE_OPTIONAL_HEADER 는 224bytes 의사이즈를갖지만원칙적으로는가변사이즈이다. 이는 - 51 -

Data Directory 때문인데 Data Directory 는 Optional 헤더부분에서보도록하겠다. Winnt.h 에선언되어있는 IMAGE_FILE_HEADER 는다음과같다. typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; Winhex 에서보면다음과같다. PE 시그내처는 DOS 헤더의가장마지막에있는 e_lfanew 가가리키고있는주소에있다. File 헤더에서꼭알아두어야할필드는 4 가지이다. Machine : CPU ID 를나타낸다. IA32 의경우 0x14C 가되고 IA64 일경우 0x200 이되어야한다. NumberOfSections : 섹션의개수를의미한다. SizeOfOptionalHeader : 파일헤더뒤에오는 optional 헤더의크기를나타낸다. 기본적으로 224bytes 의사이즈를갖지만 data directory 가생략될경우 96bytes 의사이즈를갖게된다. - 52 -

Characteristics : 파일의속성을나타낸다. 일반적인실행파일의경우 0x10F 의값을가진 다. Winnt.h 에다음과같은속성이정의되어있다. #define IMAGE_FILE_RELOCS_STRIPPED #define IMAGE_FILE_EXECUTABLE_IMAGE #define IMAGE_FILE_LINE_NUMS_STRIPPED #define IMAGE_FILE_LOCAL_SYMS_STRIPPED #define IMAGE_FILE_AGGRESIVE_WS_TRIM #define IMAGE_FILE_LARGE_ADDRESS_AWARE #define IMAGE_FILE_BYTES_REVERSED_LO #define IMAGE_FILE_32BIT_MACHINE #define IMAGE_FILE_DEBUG_STRIPPED #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP #define IMAGE_FILE_NET_RUN_FROM_SWAP #define IMAGE_FILE_SYSTEM #define IMAGE_FILE_DLL #define IMAGE_FILE_UP_SYSTEM_ONLY #define IMAGE_FILE_BYTES_REVERSED_HI 0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0080 0x0100 0x0200 0x0400 0x0800 0x1000 0x2000 0x4000 0x8000 일반적으로 0x10F 의값을가진다고했는데이는 IMAGE_FILE_RELOCS_STRIPPED(0x0001), IMAGE_FILE_EXECUTABLE_IMAGE(0x0002), IMAGE_FILE_LINE_NUMS_STRIPPED(0x0004), IMAGE_FILE_LOCAL_SYMS_STRIPPED(0x0008), IMAGE_FILE_32BIT_MACHINE(0x0100) 의속성이설정되어있는것이다. - 53 -

3-4. Optional Header Optional Header DOS Header DOS Stub Code PE signature PE File Header Optional Header Section Table Section #1 Section #2... Section #n Magic AddressOfEntryPoint ImageBase SectionAlignment FileAlignment SizeOfImage SizeOfHeader NumberOfRvaAndSizes... IMAGE_DATA_DIRECTORY #0 IMAGE_DATA_DIRECTORY #1... IMAGE_DATA_DIRECTORY #15 IMAGE_OPTIONAL _HEADER (224byte) Data Directory (128byte) Student Notes Optional 헤더는 PE 파일에서가장중요한부분이라고할수있다. 명칭은옵션이지만절대옵션의성격을갖고있지않고프로그램이메모리상에로드되었을때시작할주소라든지메모리상에서의정렬단위와같이중요한정보들을다수포함하고있다. optional 헤더는 30 개의필드와 1 개의데이터디렉토리를가지고있다. 하지만모든필드를알아야할필요는없다. 여기서는중요한필드에대해서만알아보도록하겠다. 먼저 Winnt.h 에선언되어있는 IMAGE_OPTIONAL_HEADER 를보도록하자. typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; - 54 -

DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; Winhex 로보면다음과같다. - 55 -

Machine : optional 헤더의시작위치에존재하는필드로 optional 헤더를구분하는시그내처로사용된다. 0x10B 로고정되어있다. AddressOfEntryPoint : 엔트리포인트는 PE 파일이메모리에로드된후맨처음으로실행되는코드의주소를가지고있다. 이부분에지정된주소값은가상주소가아닌 RVA 값이다. 즉, ImageBase 에서부터의오프셋이다. ImageBase : PE 파일이로더에의해서로드되는위치이다. EXE 파일의경우가상메모리공간에가장처음로드되므로항상 ImageBase 에로드되지만 DLL 의경우 ImageBase 로지정된주소공간이다른모듈에의해서이미사용중인상황이발생할수있다. 이런경우해당 DLL 을다른곳에로드하고재배치작업을수행하게된다. 대부분의링커는 EXE 파일일경우 0x00400000 으로, DLL 일경우 0x10000000 으로설정한다. SectionAlignment : 각섹션이메모리상에서차지하는최소단위이다. 각섹션의시작주소는언제나이 SectionAlignment 에서지정된값의배수가되어야한다. 디폴트값은 0x1000(4096byte) 이다. FileAlignment : 디스크상에서섹션이차지하는최소단위이다. 각섹션의시작주소는 FileAlignment 필드에지정된값의배수가되어야한다. 디폴트값은 0x200(512byte) 이고 2 의 n 승의형태의값을사용해야한다. SizeOfImage : 메모리상에로드된 PE 파일의총사이즈이고 SectionAlignment 의배수가되어야한다. SizeOfHeader : : 디스크상에서의헤더의총사이즈이고 FileAlignment 의배수가되어야한다. - 56 -

Data Directory 데이터디렉토리는 Optional 헤더다음에오는 128bytes 사이즈의 IMAGE_DATA_DIRECTORY 구조체 의배열이다. Winnt.h 에다음과같이선언되어있다. typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 마지막에정의된이배열의엔트리의개수는 Optional 헤더의마지막필드인 NumberOfRvaAndSize 에정의되어있던값과같은값으로총 16 개의엔트리를가지고있다. 데이터디렉토리의각엘리먼트들은 Export table, Import table 등 PE 파일에서중요한역할을하는개체들의주소 (VirtualAddresS) 와크기 (Size) 에대한정보를가지고있다. 총 16 개의엘리먼트중 15 개가실제정보를가지고있고마지막은 0x00 으로채워진비어있는엔트리 이다. 15 개의엘리먼트중몇가지엘리먼트에대해서만간단히알아보도록하겠다. IMAGE_DIRECTORY_ENTRY_EXPORT : EXPORT 테이블의메모리상에서의시작점과크기에 대한정보를가지고있다. EXPORT 테이블은대부분 DLL 에존재한다. - 57 -

IMAGE_DIRECTORY_ENTRY_IMPORT : IMPORT 테이블의메모리상에서의시작점과크기에대한정보를가지고있다. IMAGE_DIRECTORY_ENTRY_BASERELOC : 기준재배치정보를가리킨다. 재배치란로더가실행모듈을원하는위치에위치시키지못했을때코드상의포인터연산과관련된주소를다시갱신해야하는경우를말한다. IMAGE_DIRECTORY_ENTRY_TLS : Thread Local Storage 초기화섹션에대한포인터이다. 안티리버싱기법중 TLS Callback 에서사용되기때문에알아두는것이좋다. IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT : DLL 바인딩과관련된정보를가지고있다. IMAGE_DIRECTORY_ENTRY_IAT : 첫번째임포트주소테이블 (IAT) 의시작주소를가리킨다. 임포트된각각의 DLL 에대한 IAT 는메모리상에서연속적으로나타난다. IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT : 지연로딩정보에대한포인터이다. - 58 -

3-5. Section Table Section Table DOS Header DOS Stub Code PE signature PE File Header Optional Header Section Table Section #1 Section #2... Section #n Name VirtualAddress SizeOfRawData PointerToRawData Characteristics Name VirtualAddress SizeOfRawData PointerToRawData Characteristics... Name VirtualAddress SizeOfRawData PointerToRawData Characteristics Section #1 Section #2 Section #n Student Notes 섹션테이블은 IMAGE_SECTION_HEADER 타입의엘리먼트로구성된배열이다. 섹션헤더는로더가 각섹션을메모리에로드하고속성을설정하는데필요한정보들을가지고있다. 섹션은동일한성질의데이터가저장되어있는영역이다. 섹션은윈도우에서사용하는메모리프로텍션매커니즘과연관이있는데윈도우의경우메모리프로텍션의최소단위가페이지이고페이지단위로여러속성을설정해두고속성에위배되는행동을시도할때 access violation 을발생시켜메모리를보호한다. 즉, 페이지의일부는읽기만가능하고, 페이지의일부는읽고, 쓰기가가능하도록설정하는방식의프로텍션은허용하지않는다는말이다. 이것은성질이다른데이터들을하나의페이지에담을수없다는것을의미한다. 그렇다보니프로그램 - 59 -

에포함된데이터들중읽기와실행이가능해야하는데이터인실행코드와읽고쓰기가가능한데이터, 읽기만가능한데이터들을별도의페이지에두어야하는데로더의입장에서는이를구분할방법이없으므로섹션이라는개념을두어실행파일생성단계에서구분해놓도록하는것이다. Winnt.h 에선언되어있는 IMAGE_SECTION_HEADER 는다음과같다. typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; Winhex 에서보면다음과같다. Name : 섹션의이름이다. 8byte 의크기를가지고있지만이름이 NULL 이거나 8byte 보다길어도상관없다. 8byte 보다길경우엔 8byte 만큼만이름으로사용하고나머지는버리게된다. VirtualAddress : 섹션이로드될가상주소 (RVA 값 ) 이다. SizeOfRawData : 파일상에서의섹션의사이즈이다. FileAlignment 의배수이어야한다. PointerToRawData : 파일상에서의섹션의시작위치를나타낸다. Characteristics : 섹션의속성값이다. 로더는 PointerToRawData 가지정한곳에서부터 SizeOfRawData 만큼데이터를읽어들여 VirtualAddress - 60 -

에맵핑한후 Characteristics 에설정된속성을이용하여페이지프로텍션을적용한다. Reverse Engineering - 61 -

3-6. Import Table Import Table IMPORT TABLE IMAGE_THUNK_DATA VirtualAddress Size IMAGE_DIRECTORY_ENTRY_IMPORT OriginalFirstThunk TimeDateStamp ForwarderChain ILT #1 IMAGE_IMPORT_BY_NAME hint func1 Name FirstThunk IMAGE_THUNK_DATA Binding 전 OriginalFirstThunk ILT #2 TimeDateStamp IAT #1 USER32.DLL IMAGE_IMPORT_DESCRIPTOR ForwarderChain Binding 후 Name FirstThunk KERNEL32.DLL IAT #2 func1 NULL Student Notes Import 테이블에는 PE 파일이실행될때외부로부터가져와서사용하는함수들 (DLL) 의목록이들어있 다. 위그림에서는 USER32.DLL 과 KERNEL32.DLL 을임포트한모습을예로한것이다. DLL 들은함수를익스포트하고 EXE 가그 DLL 로부터함수를임포트한다. 익스포트된함수를가져온다는것은결국해당 DLL 과사용하는함수에대한정보를어딘가에저장한다는것을의미하고사용하고자하는익스포트함수들과그 DLL 에대한정보를저장하고있는곳이 Import Table 이다. 일반적으로 PE 파일의섹션테이블에는.idata 라는이름으로지정된다. 각 IMAGE_IMPORT_DESCRIPTOR 엔트리는임포트한 DLL 의정보를가지고있으며마지막은임포트 테이블의끝을알리기위해 NULL 로채워져있다. - 62 -

Winnt.h 에정의되어있는 IMAGE_IMPORT_DESCRIPTOR 와관련된구조체는다음과같다. typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; OriginalFirstThunk : ILT(Import Lookup Table) 을가리키는 RVA 값이다. 앞의그림에서보이는것처럼 ILT 는 IMAGE_THUNK_DATA 로구성된배열이다. IMAGE_THUNK_DATA 는상황에따라서 IMAGE_IMPORT_BY_NAME 을가리키기도하고함수의주소를가리키기도하며오디널값으로사용되거나포워더로사용되기도한다. TimeDateStamp : 바인딩전에는 0 으로설정되며바인딩후에는 -1 로설정된다. ForwarderChain : 바인딩전에는 0 으로설정되며바인딩후에는 -1 로설정된다. Name : 임포트한 DLL 의이름을가리키는 RVA 값이다. FirstThunk : IAT(Import Address Table) 의 RVA 주소값을가지고있다. IAT 역시 ILT 처럼 IMAGE_THUNK_DATA 배열이고바인딩전에는 ILT 와동일한모습을갖는다. 하지만 PE 파일이메모리에로드된후에는로더가임포트테이블의각엔트리의이름정보를확인한후해당 DLL 의익스포트테이블을참조하여함수의실제주소를알아낸다. 그리고나서 IAT 를실제함수주소로업데이트한다. IMAGE_THUNK_DATA 는다음과같이정의되어있다. typedef struct _IMAGE_THUNK_DATA32 { union { PBYTE ForwarderString; PDWORD Function; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; } u1; } IMAGE_THUNK_DATA32; IMAGE_THUNK_DATA 와 ILT, IAT 의관계를정리해보면다음과같다. ILT - OriginalFirstThunk 가가리키고있다. - 바인딩전후의모습이변경되지않는다. - 63 -

- IMAGE_IMPORT_BY_NAME 구조체를가리키거나 Ordinal 로사용되기도한다. - 임포트한함수에대한 Ordinal 값을저장하고있는배열이거나임포트한함수에대한이름을저장하고있는 IMAGE_IMPRT_BY_NAME 구조체의 RVA 값으로이루어진배열이다. - 함수의의미로사용되지않는다. IAT - FirstThunk 가가리키고있다. - 바인딩전에는 AddressOfData 의의미로사용되거나 Ordinal 의의미로사용된다. - IMAGE_THUNK_DATA 가 IMAGE_IMPORT_BY_NAME 을가리키고있으면 AddressOfData 의의미로사용된것이다. IMAGE_IMPORT_BY_NAME 은임포트할수있는함수의이름을저장하고있는구조체이다. - 최상위비트값이 1 이면 ordinal 로사용된것이고, 0 이면 AddressOfData 로사용된것이다. - 바인딩후에는함수의실제주소를가리킨다. 다음그림은 IMAGE_THUNK_DATA 가 AddressOfData 로사용될경우를도식화한것이다. 다음그림은 IMAGE_THUNK_DATA 가 Ordinal 로사용될경우를도식화한것이다. - 64 -

Winhex 에서임포트테이블을확인해보면다음과같다. 앞서살펴보았듯이임포트테이블의마지막은 NULL 로채워져있다. 현재이파일은 6 개의 DLL 을임 포트한다는것을알수있다. Import Table 의위치 임포트테이블을메모리상에서찾을때는 IMAGE_DIRECTORY_ENTRY_IMPORT 에지정된 - 65 -

VirtualAddress 를찾아보면확인할수있다. 하지만파일상에서즉, 메모리에로드되기전에임포트테이블이어디에있는지에대한정보는 PE 파일에저장되어있지않다. Stud_PE 로확인해보면임포트테이블에대한정보를확인할수있다. 재미있는건실제 PE 파일에존재하지않는정보까지확인할수있다는것이다. Import Table 에서 Raw 라는항목은파일상에서의임포트테이블의위치를나타내는것이다. 실제 PE 파일에서데이터디렉토 리에는 RVA 값인 VirtualAddress 와 Size 만가지고있다. 임포트테이블의 RVA 값은 0x12FD4 이므로.text 섹션에있다는것을알수있다..text 섹션의 RawOffset 이 0x400 이므로파일상에서의위치는 0x123D4 가된다. 간단하게공식을만들자면파일상에서임포트 테이블의위치는 ( 임포트테이블의 RVA) ( 임포트테이블이위치한섹션의 VirtualOffset) + RawOffset - 66 -

보통임포트테이블은임포트섹션의시작점에위치하지만항상그런것은아니다. 앞의예제인 calc.exe 에서도임포트테이블이임포트섹션이아닌.text 섹션에위치하고있는것을확인할수있다. 섹션헤더에포함된임포트섹션에대한정보는파일을메모리에로딩하는과정에서파일상에서임포트섹션을구분하고메모리에로딩하기위해서사용하는것뿐이다. 그리고 PE 파일이메모리에로딩되고나면 IAT(Import Address Table) 을수정해주어야한다. 따라서로 더는메모리상에서임포트테이블의위치를알수있어야하는데이정보는데이터디렉토리중두번째 인 IMAGE_DIRECTORY_ENTRY_IMPORT 에저장되어있다. 임포트된 DLL 확인 앞서살펴보았던 IMAGE_IMPORT_DESCRIPTOR 구조체에임포트한 DLL 의정보를가지고있었다. 먼저임포트테이블의파일상에서의위치를찾아보도록하자. 임포트테이블의 RVA 가 0x12FD4 이고임포트테이블의위치는.text 섹션에속해있었다. 즉, ( 0x12FD4 0x1000 ) + 0x400 = 0x123D4 가된다. Winhex 를통해파일상에서확인해보도록하겠다. - 67 -

calc.exe 프로그램이임포트할 DLL 의이름을가리키고있는 RVA 값은 0x134D6 이다. 이값은파일상에서의오프셋이아니라메모리에로드된후에찾아갈 RVA 값이므로파일상에서는다른위치에있을것이다. Stud_PE 를이용하여섹션테이블을살펴보면 0x134D6 는.text 섹션안에있는것을확인할수있다. 결국 Name 은 0x134D6 0x1000 + 0x400 = 0x128D6 에서확인할수있다. 임포트디렉토리의첫번째엔트리는 KERNEL32.dll 인것을알수있다. - 68 -

3-7. Export Table Export Table EXPORT TABLE VirtualAddress Size IMAGE_DIRECTORY_ENTRY_EXPORT Name Base NumberOfFunctions NumberOfNames AddressOfFunctions AddressOfNames AddressOfNameOrdinals... MYDLL.DLL RVA RVA RVA code code Kernel32.OpenProcess Name Base NumberOfFunctions NumberOfNames RVA RVA MyFunc1 Myfunc2 AddressOfFunctions AddressOfNames AddressOfNameOrdinals... ordinal ordinal Student Notes Import 테이블이외부로부터가져오는함수들의목록이들어있다면 Export 테이블은다른프로그램을위한기능을제공하기위해노출하는함수들의목록이들어있다. Export 테이블은 Data Directory 중에서 0 번째인덱스인 IMAGE_DIRECTORY_ENTRY_EXPORT 에서 VirtualAddress 와 Size 를확인할수있다. Export 테이블은 Import 테이블에비해비교적직관적이고간단한구조로되어있다. Winnt.h 에는다음과같이정의되어있다. typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; - 69 -

DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; Name : DLL 이름을나타내는 ASCII 문자열의위치를지시하는 RVA 값 Base : 익스포트된함수들에대한서수의시작번호 NumberOfFunctions : AddressOfFunctions 가가리키는 RVA 배열의개수 NumberOfNames : AddressOfNames 가가리키는 RVA 배열의개수 AddressOfFunctions : 함수의실제주소가담긴배열 (RVA 값 ) AddressOfNames : 함수의심볼을나타내는문자열배열 (RVA 값 ) AddressOfNameOrdinals : 함수의서수값의배열위치. 실제해당함수의서수는 Base 값을더해야한다. 구조체에선언된것과같이익스포트테이블은내보내지는 DLL 이가지고있는함수들의이름과위치 (RVA 값 ), 그리고서수를가지고있다. Export Table 의위치 파일상에서익스포트데이블을찾는것은임포트테이블을찾는방법과동일하다. 1. IMAGE_DIRECTORY_ENTRY_EXPORT 에지정된 VirtualAddress 확인 VirtualAddress 는 0x00016A1C 이고 Size 는 0x00004B9A 이다. 2. VirtualAddress 를이용하여 RawOffset 계산 ( 익스포트테이블의 RVA) ( 익스포트테이블이위치한섹션의 VirtualOffset) + RawOffset 0x00016A1C 0x00001000 + 0x00000400 = 0x00015E1C - 70 -

익스포트테이블의 RVA 값은 0x16A1C 이므로.text 섹션에있다는것을알수있다..text 섹션의 RawOffset 이 0x400 이므로파일상에서의위치는 0x15E1C 가된다. Winhex 에서보면다음과같다. 그렇다면함수의이름과서수를가지고함수주소를찾는방법에대해서알아보도록하겠다. Name : 0x000186D2 Base : 1 NumberOfFuntions : 0x000002DB NumberOfNames : 0x000002DB AddressOfFunctions : 0x00016A44 AddressOfNames : 0x000175B0 AddressOfNameOrdinals : 0x0001811C 여기에서사용되는 offset 값은전부 RVA 이므로파일상에서찾을때는앞서했던계산과정을거쳐야 한다. - 71 -

Name : 0x000186D2 0x1000 + 0x400 = 0x00017AD2 이름이 USER32.dll 이라는것을알수있다. 이름의마지막은 NULL ( \0 ) 로끝난다. 서수의시작번호는 1 이고 (Base : 1), 함수의개수는 731 개이고 (NumberOfFuntions : 0x2DB), 함수이름 의개수도 731 개이다. (NumberOfNames : 0x2DB) 실제함수의이름이있는곳은 AddressOfNames 이다. AddressOfNames : 0x000175B0 0x1000 + 0x400 = 0x000169B0 0x000186DD 은 RVA 값이므로다시파일상에서의 offset 으로변환하면 0x00017ADD 이다. 첫번째실제함수의이름은 ActivateKeyboardLayout 이라는것을확인할수있다. 그리고이함수의구현 코드가있는곳인 AddressOfFunctions 의파일상의위치는 0x00016A44 0x1000 + 0x400 = 0x00015E44 이다. - 72 -

AddressOfNameOrdinals : 0x0001811C 0x1000 + 0x400 = 0x0001751C 0x1751C 의위치에서부터 NumberOfNameOrdinals 가시작된다. 모든함수들의서수를가지고있는데이서수배열의최초원소의값은항상 1 이고그리고이배열값에 Base 의값을더하면정확한서수값을획득할수있다. 현재우리가보고있는 USER32.dll 파일은총 731(0x2DB) 개의함수이름을가지고있기때문에 AddressOfNameOrdinals 또한 731(0x2DB) 개를가지고있다. 위과정을도식화하면다음과같다. - 73 -

- 74 -

Module 4 분석기초 Objectives 분석도구설정 CrackMe / KeygenMe 실습 - 75 -

4-1. 분석도구설정 분석도구설정 Debugger Ollydbg Disassembler IDA W32DASM File Analyzer Stud PE PEiD ImportREConstruction HEX Editor WinHex Student Notes 분석도구에는디버거, 디스어셈블러, 파일분석기, 헥스에디터등다양한도구들이존재한다. 도구들의 사용은분석을좀더용이하게해주기때문에자주사용이되며본인에게가장잘맞는도구를선택하는 것도중요하다. 여기서는대표적인도구들의사용법을위주로소개하고자한다. - 76 -

Ollydbg http://www.ollydbg.de/ 에서무료로배포하고있고, 바이너리분석에사용된다. File : 디버깅할파일혹은프로세스를지정합니다. View : 디버깅대상에대한각종정보를출력합니다. Debug : 디버깅에관련된기능들입니다. Plugins : 디버깅에유용한각종유틸리티를플러그인형태로제공합니다. Options : 각종옵션을변경합니다. Window, Help : 화면 ( 창 ) 설정과도움말입니다. 프로그램을 restart, close, run 시키거나실행과정을조작할수있는기능들을제공한다. 어셈코드를한줄씩실행하거나혹은특정상황이발생할때가지실행하거나할수있다. 디버깅을하는데필요한중요한정보들을보여준다. 자주사용하는것들은 E : Executable modules, 모듈정보 M : Memory map, 현재메모리정보 H : Handles, 핸들정보 C : CPU main htread, 메인디버깅화면 / : Patches, 기계어코드가변경된내용을출력 K : Call stack of main thread B : Breakpoints, 설정된브레이크포인트 R : References, 프로그램에사용된함수나문자열등을참조할때사용... : Run trace - 77 -

다음은메인화면에대해서알아보도록하자. A 부분은디스어셈블링된코드와 OP 코드들을나타낸다. 네부분으로나뉘어져있는데차례대로메모리주소, OP 코드, 디스어셈블된코드, 코멘트의내용을가지고있다. 이곳에서프로그램의흐름이나실행을제어해서분석한다. B 부분은상태바이다. A 부분에서실행되고있는각해당위치의 offset 값과변경된메모리주소, 레지스터내용들을나타낸다. C 부분은메모리의값들을헥사코드와아스키코드로보여주는부분이다. 그외다른다양한형태로값들을확인할수있다. D 부분은 CPU 레지스터들의정보를나타낸다. 레지스터값이나플래그등을직접조작할수도있다. E 부분은 Stack 의내용을보여준다. Ollydbg 를이용하여분석하기전몇가지설정을해주어야한다. - 78 -

먼저 Option Appearance 에서 udd 폴더와 plugins 폴더를지정해준다. Option Debugging Option CPU : jump 구문이있을경우어느곳으로점프하는지보여준다. - 79 -

Exception 발생시어떻게처리할것인지와범위를지정한다. 프로그램을디버거로오픈할때어느곳에서멈출것인지지정한다. - 80 -

Stud PE PE 파일의구조를분석해주는프로그램으로 http://www.cgsoftlabs.ro/studpe.html 에서다운받을수있으 며프리웨어이다. PEiD PE 파일의정보를볼수있는것은 Stud PE 와비슷하고패킹여부를확인할수있는기능도있다. http://www.peid.info/ 에서다운로드받을수있다. - 81 -

ImportREC IAT 를복구시켜주는프로그램이다. 패킹이되어있는파일을언패킹한후 IAT 를맞춰주지않으면정상 적으로실행되지않기때문에 IAT 를복구시켜주어야한다. 이때 ImportREC 을사용한다. http://vault.reversers.org/imprecdef 에서다운로드받을수있다. 하지만모든상황에서 IAT 가정상적으로복구되는것은아니다. 패커가의도적으로 IAT 를꼬이게했을경우정상적으로찾지못할수있다. 이것은임포트테이블의마지막이 0000 0000 이라는점을이용하여자동으로 IAT 를복구하는데정상적인함수들의목록중간에 0000 0000 을삽입하게되면그곳이마지막이라고 ImportREC 이인식하기때문이다. 이런경우수작업으로 IAT 의주소와사이즈를맞춰주어야한다. - 82 -

Winhex 파일을 HEX 형태로볼수있다. 바이너리파일을직접수정할수도있다. - 83 -

4-2. CrackMe 분석실습 CrackMe 실습 인증우회하기 Key 값찾기 Keyfile 만들기 등등 Student Notes 이번장에서는온라인상에공개되어있는 Crackme 예제를통해몇가지기법들에대해서익히도록하겠다. 온라인상에공개된 Crackme 프로그램들의특징을살펴보면인증을우회하거나고정된키값을찾아서입력하는방법, 또는키파일을만들어야하는방법등여러가지방법들을있다. 물론 Keygen 을만들어서입력된이름이나아이디에의해변경되는키값을알아내는방법도있는데 Keygen 에관한내용은다음장에서보도록하겠다. 여기서는먼저고정된키값을찾아내는크랙미를이용하여실습을할것이고두번째는우리가입력한 값에의해키값이변하는크랙미에대해서분석을해보도록하겠다. - 84 -

Crackme #1 이파일을실행하면다음과같이패스워드를입력하라고나오고아무런값이나입력해보도록하겠다. 이프로그램에서요구하는패스워드와일치하지않기때문에 Invalid Password 라는메시지를출력하 고프로그램이종료를하게된다. 여기서우리가확인해야할부분은정확한패스워드를입력하지않았 을때출력되는 Invalid Password 라는문구이다. 디버거를통해서프로그램을실행시켜보자. 코드창에서마우스오른쪽을클릭하여 Search for All reference text strings 를클릭하면다음과같이이 프로그램에서사용되는스트링들을확인할수있다. 앞에서보았던문구가두번째줄에보이고아래쪽에 The password is %s 라는스트링이보인다. 아마도정확한패스워드를입력할경우 The password is xxxxxxxxxx 라고콘솔에출력이될것이고그렇지않을경우 Invalid Password 라는메시지를출력하는것일거라예상할수있다. Invalid Password 라는스트링을더블클릭하면해당코드로이동하게된다. - 85 -

Invalid Password 라는메시지바로위라인에비교하는부분이있고같을경우 004010DA 로분기하는것을볼수있다. 004010DA 는다시 0040109C 로분기하기때문에아래쪽에보이는 The password is %s 부분으로이동하지는않는다. 위쪽으로조금더올라가면 JNB 에의해서분기하는코드가보이는데조건에만족하면 004010DC 로분기하여 The password is %s 라는메시지를출력하게된다. 즉, 현재보이는코드보다윗부분에서우리가입력한패스워드와프로그램이요구하는패스워드를비교하는부분이있을것이다. 그렇다면화면을조금위로올려서패스워드를입력받는부분을확인해보도록하겠다. PUSH Crackme#.00407030 은 Please enter the password: 라는출력할메시지를스택에넣어둔후바로아래에있는 CALL 에의해서화면에출력이된다. 항상 CALL 에의해서함수호출이끝나면스택을잘보아야한다. 함수내부에서어떠한연산작업후스택에데이터들이저장되기때문이다. 그리고이데이터들은다음코드에영향을미칠수있기때문이다. - 86 -

현재스택의값들을확인하고다음코드를보도록하겠다. /*40102C*/ ADD ESP,4 /*40102F*/ PUSH 10 // 스택클리어 // 스택에 10 을저장 /*401031*/ PUSH 0 // 스택에 0 을저장 /*401033*/ LEA EAX,DWORD PTR SS:[EBP-34] // EBP-34 의위치 ( 주소 ) 를 EAX 에저장 /*401036*/ PUSH EAX // EAX 의값을스택에저장 /*401037*/ CALL Crackme#.00401150 // 00401150 의코드를호출 /*40103C*/ ADD ESP,0C /*40103F*/ MOV DWORD PTR SS:[EBP-1C],0 // 스택클리어 // EBP-1C 의위치에 0 을저장 /*401046*/ MOV DWORD PTR SS:[EBP-20],0 // EBP-20 의위치에 0 을저장 /*40104D*/ MOV DWORD PTR SS:[EBP-24],0 // EBP-24 의위치에 0 을저장 위부분은우리가코드를분석하는데큰영향을미치지는않는부분들이고단지우리가입력한패스워 드와프로그램이가지고있는패스워드를비교하는연산을위해서공간을만드는역할을하는코드이다. 다음코드로진행을하면패스워드를입력할수있게된다. 임의의패스워드를입력하고다음코드를살펴보자. 00401054 에서 00401140 에위치한함수를호출한다. 이함수는사용자의입력값을받아들이는함수이 다. 그리고함수를호출하고연산을수행한결과는 EAX 에저장된다. - 87 -

Disassembly 부분에서굵은선으로표기된부분은루프문을나타내는것이다. 즉, 특정조건을만족할때 까지 00401054 부터 00401084 까지계속루프를돌게된다. 그렇다면현재위에보이는코드가어떠한역 할을하는지알아보도록하겠다. /*401059*/ MOV BYTE PTR SS:[EBP-4],AL // AL 에있는값을 EBP-4 에 1 바이트만저장 /*40105C*/ MOV ECX,DWORD PTR SS:[EBP-1C] // EBP-1C 에있는값을 ECX 에저장 /*40105F*/ MOV DL,BYTE PTR SS:[EBP-4] // EBP-4 에있는값을 DL 에저장 /*401062*/ MOV BYTE PTR SS:[EBP+ECX-34],DL // DL 에있는값을 EBP+ECX-34 에저장 /*401066*/ MOV EAX,DWORD PTR SS:[EBP-1C] // EBP-1C 에있는값을 EAX 에저장 /*401069*/ ADD EAX,1 // EAX 값을 1 증가 /*40106C*/ MOV DWORD PTR SS:[EBP-1C],EAX // EAX 에있는값을 EBP-1C 에저장 /*40106F*/ MOVSX ECX,BYTE PTR SS:[EBP-4] // EBP-4 에있는값을 ECX 에저장 /*401073*/ CMP ECX,0A // ECX와 0x0A 를비교 /*401076*/ JE SHORT Crackme#.00401086 // 같으면 00401086 으로분기 /*401078*/ MOVSX EDX,BYTE PTR SS:[EBP-4] // EBP-4 에있는값을 EDX 에저장 /*40107C*/ TEST EDX,EDX /*40107E*/ JE SHORT Crackme#.00401086 // EDX 와 EDX 를 AND 연산 // 같으면 00401086 으로분기 /*401080*/ CMP DWORD PTR SS:[EBP-1C],10 // EBP-1C 에있는값과 0x10 을비교 /*401084*/ JB SHORT Crackme#.00401054 // EBP-1C 의값이 0x10 보다적으면 00401054 로분기 - 88 -

계속해서루프를돌면서뭔가를계산하고있다. 4 번정도루프를돈후헥사값이있는부분을확인해보 도록하자. CALL 00401140 에의해우리가입력한값을한글자씩가져와서 EAX 에저장하고글자수를세고있다. 입력한글자의개수는 [EBP-1C] 에저장이되고현재까지읽어들인글자들은 [EBP+ECX-34] 에저장이된다. 그리고현재읽어들인글자는 [EBP-4] 에저장이된다. 코드에서보면 BYTE PTR SS 라는부분이자주나오는데이것이바로한글자씩 (1 바이트 ) 읽어온다는증거이다. 그리고 ECX 에는위에보는것과같이 [EBP-4] 에있는값을저장한후 0x0A 와비교하고있다. [EBP-4] 는현재읽어들인글자하나를의미하고 0x72( r ) 와 0x0A 를비교하는것이다. 0x0A 는 LF(Line Feed) 로 써사용자가입력한글자의끝을찾기위해사용된비교구문이다. 위부분은 [EBP-1C] 에있는값과 0x10 을비교하는부분인데 [EBP-1C] 에는현재까지읽어들인글자의개수를나타내므로사용자가입력한값의길이가 16 글자 (0x10) 보다작은지확인하는부분이다. 16 글자보다많은경우는 00401054 로분기하지않고루프문을빠져나오게된다. 즉, 우리가입력한글자를 16 글자까지만인식을하게된다. 루프를빠져나온후레지스터에저장된값과헥사코드정보에대한부분을확인해보도록하겠다. - 89 -

레지스터에저장된값은다음과같다. EAX 에입력한글자수 (0x0B) 가저장되어있고 ESP 에는사용자가입력한글자를가지고있는주소가 저장되어있다. 여기까지알아낸 Crackme#1 을풀기위한조건은 16 글자이하라는것과 EAX 에사용자가입력한글자 의개수를저장해두었다는것이다. 물론 EAX 에있는이값은어디론가옮겨져사용될것이다. 왜냐하 면다음루틴에서도 EAX 를써야하기때문이다. 루프문을빠져나오면다음과같은코드를볼수있다. EBP-34 의주소값을 EAX 에로드하고 EAX 에있는값을 [EBP-8] 의위치로옮긴다. 그리고 [EBP-20] 와 [EBP-24] 는각각 0 과 3 이라는값을저장한다. 아마도다음작업을위한준비단계일것이다. [EBP-34] 의주소값을확인해보니 0013FF4C 이고이주소에는사용자가입력했던값이저장되어있다. 위코드실행후다음코드는 004010AE 로분기하는코드이다. 004010AE 로이동해보도록하겠다. [EBP-20] 에있는값과 0x0D 를비교하여 [EBP-20] 의값이 0x0D 보다적지않으면 004010DC 로분기한 다. 즉, [EBP-20] 에있는값이 0x0D 보다크거나같으면분기한다는의미이다. 그런데바로앞에서 - 90 -

[EBP-20] 에 0 을저장하였기때문에분기하지않고다음코드를실행하게된다. 다음코드는 004010B4 부터시작한다. /*4010B4*/ MOV EAX,DWORD PTR SS:[EBP-20] // EBP-20 에있는값을 EAX 로저장 /*4010B7*/ SHR EAX,2 /*4010BA*/ MOV ECX,DWORD PTR SS:[EBP-8] // EAX 의값을오른쪽으로 2 바이트쉬프트 // EBP-8 에있는값을 ECX 에저장 /*4010BD*/ MOV EDX,DWORD PTR SS:[EBP-24] // EBP-24 에있는값을 EDX 에저장 /*4010C0*/ MOV EAX,DWORD PTR DS:[ECX+EAX*4] // ECX+EAX*4 에있는값을 EAX 에저장 /*4010C3*/ CMP EAX,DWORD PTR SS:[EBP+EDX*4-18] // EBP+EDX*4-18 의값과 EAX 의값비교 위코드는사용자가입력한패스워드와프로그램이가지고있는패스워드의처음 4 글자를비교하는부 분이다. 헥사코드부분을보도록하자. - 91 -

004010B4 에서부터 004010C3 까지의코드가처음실행될때 ECX 값은사용자가입력한값이저장되어있는주소를가지고있으며 EAX 는 0 을가지고있다.([EBP-20] 의값이 0 이므로 ) 그리고 EDX 는 [EBP-24] 에있는값을가지고있는데이전코드에서 3 이라는값을저장했었다. 즉, EBP+EDX*4-18 을하면 0013FF80 + 3 * 4 18 = 0x0013FF80 + 0xC 0x18 = 0x0013FF74 가된다. 즉, qwer 과 powe 를비교한다. 그리고다음코드에서같으면 004010DA 로분기하고그렇지않으면 004010C9 을실행한다. 004010C9 은 Invalid Password 라는메시지를출력하면서종료되는루틴이있는곳이다. 만약사용자가첫번째 4 글자를 powe 라고입력했다고가정하고다음코드를살펴보도록하겠다. 눈치가빠른리버서라면지금패스워드를알아보았을수도있다. 하지만좀더정확한루틴을파악하기 위해다음코드를실행할때어떻게되는지살펴보자. 첫번째 4 글자가같을경우 004010DA 로분기하 는데여기서다시 0040109C 로분기한다. /*40109C*/ MOV ECX,DWORD PTR SS:[EBP-20] // EBP-20 에있는값을 ECX 에저장 /*40109F*/ ADD ECX,4 // ECX 의값을 4 증가 /*4010A2*/ MOV DWORD PTR SS:[EBP-20],ECX // ECX 의값을 EBP-20 에저장 /*4010A5*/ MOV EDX,DWORD PTR SS:[EBP-24] // EBP-24 에있는값을 EDX 에저장 /*4010A8*/ SUB EDX,1 // EDX 에서 1 감소 /*4010AB*/ MOV DWORD PTR SS:[EBP-24],EDX // EDX 의값을 EBP-24 로저장 /*4010AE*/ CMP DWORD PTR SS:[EBP-20],0D // EBP-20 의값과 0x0D 비교 - 92 -

앞의코드를보면 [EBP-20] 에는 0 이들어있기때문에 ECX 를 0 으로초기화한후 4 를더하고다시그값을 [EBP-20] 에저장한다. 그리고 [EBP-24] 에있는값을 EDX 에저장한후 1 을빼고다시이값을 [EBP-24] 에저장한다. 결국 [EBP-20] 에는 ECX 값인 4 가들어가고 [EBP-24] 에는 EDX 값인 2 가들어간다. 그리고 [EBP-20] 은계속 4 씩증가하는데 0x0D 와비교하는것은프로그램이가지고있는패스워드가 13 글자이기때문에 0x0D(13) 보다크면 004010DC 로분기하여정상적인패스워드를출력하게된다. 그리고앞서보았던 004010B4 부터 004010C3 까지코드를실행하는데이때 [EBP-20] 에있던 4 를 EAX 에넣은후 SHR 을통해서 1 로변경이된다. 그리고 [EBP-8] 에있는주소를 ECX 에넣고 [EBP-24] 에있 는값을다시 EDX 에넣는다. 이제비교할두개의값을계산을해보자. ECX + EAX*4 = 0x0013FF4C + 1 * 4 = 0x0013FF50 EBP + EDX*4 18 = 0x0013FF80 + 2 * 4 0x18 = 0x0013FF80 + 0x08 0x18 = 0x0013FF70 헥사코드가있는부분을통해서어떤값인지확인해보겠다. 패턴을확인해보니프로그램에서요구하는패스워드는 4 글자씩분할되어거꾸로저장이되어있고비 교할때뒤에서부터 4 글자씩가져와서사용자가입력한값과비교를한다. 이렇게알아낸패스워드를 입력해보도록하자. - 93 -

4-3. KeygenMe 분석실습 KeygenMe 실습 인증을위한 Key 값요구시Key 값찾기 Key 값을생성하는루틴확인 Keygen작성 Student Notes 이번에는단순히키값을찾는것뿐만아니라키값을만들어내는 Keygenme 에대해서분석해보도록하겠다. keygenme 는말그대로키를생성하는프로그램으로직접 C 나 C++ 과같은프로그래밍언어를이용하여작성할수도있고내부키젠이라고해서프로그램내부에키값을생성하는루틴을찾아서사용자가잘못입력하면정상적인키값을보여줄수있도록프로그램을패치하는방법을사용하기도한다. 본교재에서는내부키젠을만드는방법에대해서알아보도록하겠다. - 94 -

keygenme#1 이번에분석할예제는사용자이름과인증코드를입력하는문제이다. 본교재에서는키젠을따로만드는것이아닌내부키젠을만드는방법에대해서알아보겠다. 내부키젠이라는것은키젠프로그램을별도로만들지않고잘못된인증코드를입력할경우올바른인증코드를출력하게해주게끔해당프로그램을패치하는것을말한다. 그럼프로그램을실행해보도록하겠다. 프로그램을실행하고사용자이름과인증코드를입력한다. 위와같이정상적인인증코드를입력하지않으면 Nope, that s not it! Try again 이라는메시지를출력 한다. 그럼디버거를통해서파일을오픈해보겠다. 먼저위에보이는에러메시지가있는곳을찾아보겠 다. Search for All referenced text strings 를클릭하면다음과같다. 우리가보았던메시지뿐만아니라성공메시지 (Good boy...) 도보인다. 성공메시지가있는부분을더블 클릭하여해당코드로이동하겠다. - 95 -

성공메시지가있는코드로분기할것인지에러메시지가있는코드로분기할것인지를결정하는부분은 0040128A 에있는 OR EAX, EAX 이다. 그리고그윗쪽으로 lstrcmpa 라는함수가보이고이함수에의해서우리가입력한값과프로그램이가지고있는 ( 또는계산한 ) 값을비교할것이라고예상할수있다. 그렇다면우리가입력한값은어디에저장되어있으며프로그램이가지고있는값은어디에저장되어있는지찾아보도록하자. 위코드에서조금더올라가면다음과같은부분을만날수있다. - 96 -

00401248 에브레이크포인트를걸고 F9 를눌러실행해보자. 그리고사용자이름과인증코드를입력하고 Check It 을누르면 00401248 에서멈추게된다. 그리고 F8 을눌러서코드를한줄씩실행하다가 00401252 주소에있는코드를실행하면스택에서다음과같은값을볼수가있다. /*401252*/ PUSH keygenme.00406584 우리는이것을인증코드라고예상할수있다. 그리고어디선가이값을만들어냈을것이다. 일단앞에서 진행했던코드들을계속실행해보겠다. 우리의예상이정확히맞았다는것을알수있다. 00401285 에서스트링을비교하는 lstrcmp 함수를호출하는데인자값으로우리가입력한값인 1234567890 과스택에서보았던 JXCS-USIQ-XNUI- CPRU 을받아들여서비교하고있다. 그리고 OR EAX, EAX 에의해서두개의인자값이같으지확인한다. 같다면 0040128E 를실행할것이고같지않다면 004012A8 을실행할것이다. 여기서우리의목적을다시한번떠올려보면잘못된인증코드를입력하더라도올바른키값을알아낼 수있도록키값을메시지박스에띄워주면된다. 따라서아래에보이는에러메시지코드를키값이출력 될수있는코드로패치하면된다. 그렇다면어느부분을고쳐야할것인가? 처음에보았던화면을떠올려보면 Nope, that s not it! Try again 메시지가출력이되었었는데이메시지대신올바른키값을출력해주면된다. MessageBoxA 함수는총 4 개의인자를받아들인다. API 함수를찾아보면다음과같다. - 97 -

int MessageBox( HWND hwnd, // handle of owner window LPCTSTR lptext, // address of text in message box LPCTSTR lpcaption, // address of title of message box UINT utype // style of message box ); 두번째와세번째인자가실제메시지박스에출력된다는것을알았다. 그럼코드를살펴보자. /*4012A8*/ PUSH 10 /*4012AA*/ PUSH keygenme.00406337 /*4012AF*/ PUSH keygenme.00406318 /*4012B4*/ PUSH DWORD PTR SS:[EBP+8] /*4012B7*/ CALL <JMP.&user32.MessageBoxA> 00406337 과 00406318 에있는문자열들을가져와서출력을해준다. 그럼 00406318 에있는문자열을키 값을가지고있는주소로변경하면에러메시지대신키값을출력해줄것이다. 키값을가지고있는주소 는 lstrcmpa 에서확인할수있다. 00406B84 에키값이들어있는것을확인할수있다. 그럼이제코드를수정해보자. 해당코드를클릭한후스페이스를누르면어셈블리어코드를수정할수있다. 변경전 /*4012AA*/ PUSH keygenme.00406337 /*4012AF*/ PUSH keygenme.00406318 변경후 /*4012AA*/ PUSH keygenme.0040630c /*4012AF*/ PUSH keygenme.00406b84 마우스오른쪽버튼을클릭하고 Copy to executable All modifications 를클릭하여수정된코드를저장 - 98 -

한다. 이때 Copy all 을선택한다. 그리고창이하나새로뜨면마찬가지로마우스오른쪽클릭해서 Save file 을한후변경된파일을다른 이름으로저장한다. 새로저장한파일을실행한후사용자이름을입력하고인증코드는아무값이나입력한다. 그리고에러 메시지대신키값이출력이되는지확인해보자. 정확하게패치가되었는지확인하려면사용자이름을 계속바꿔가면서입력해보면된다. 이렇게내부키젠을생성할수도있고또는프로그램내에서키값을생성하는알고리즘을파악한후실 제 C 와같은프로그래밍언어나인라인어셈을이용하여키젠을별도로작성할수도있다. 키젠을생성 하는방법은아래 URL 을참고하기바란다. http://dual5651.hacktizen.com/new/87-99 -

- 100 -

Module 5 MUP(Manual UnPack) Objectives Packgin / Unpacking Packing 종류 MUP 실습 MUP 를위한 Ollydbg script 작성 - 101 -

5-1. Packing / Unpacking Packing / Unpacking Packing 실행파일을실행할수있는형식그대로압축또는암호화하는과정 (EXE, DLL) Unpacking 압축또는암호화되어있는실행파일 ( 패킹된파일 ) 을원상태로해제하는과정 Entry Point PE Header Unpack Stub OEP Entry Point PE Header Code Section...... Student Notes Pack 이라는단어의사전적의미를살펴보면 포장하다, 묶다 의의미를가지고있다. 패킹은실행파 일을특정루틴에의해서압축을하거나암호화하는과정을말한다. 반대로압축되어있거나암호화되 어있는실행파일을원래의상태로푸는과정을언패킹이라고한다. 보통패킹은특정섹션을추가하여그추가된섹션에언패킹루틴을만들고파일을메모리에로드할때메모리에그대로매핑을한다. 패킹된상태에서의 Entry Point 는언패킹루틴이있는섹션을가리키고있고파일을실행하면추가된섹션에서언패킹루틴을실행하고코드섹션으로점프해서원래의 Entry Point(OEP) 로진입하게된다. - 102 -

5-2. Packing 종류 Packing 종류 UPX, ASPack, FSG 기타등등매우많은 Packer들이존재종류는다르지만 Packer의기본적인동작원리는대부분비슷하다. 대상파일을패킹 ( 암호화 ) 한후프로그램의시작점을변경하여그부분부터진행 ( 복호화 ) 을함 그런후본래의실행파일의데이타를메모리상에복원해프로그램을실행되게함 Student Notes 현재알려져있는패커 ( 패킹을하는프로그램 ) 는무수히많지만대부분동작원리는비슷하다. 경우에따 라서는이중으로패킹이되어있는경우도있다. 대상프로그램이패킹이되었는지안되어있는지는 PEiD 나 EXEInfo 와같은프로그램을사용하면확인할수있다. 하지만이프로그램을통해서나온결과가 100% 정확한것은아니다. 이유는패킹여부를확인하는프로그램들이시그내처기반으로검사를하기때문이다. 그리고간혹같은파일을다르게보여주는경우도있다. - 103 -

위에서보는것과같이같은파일임에도불구하고 EXEInfo 는 SVK-Protector v1.32 demo 라고이야기하 고있고 PEiD 는 yoda s cryptor 1.x / modified 라고이야기하고있다. Stud_PE 로확인해보면 yoda s cryptor 1.x / modified 라고나온다. 그렇다고해서이파일이 yoda s cryptor 로패킹이되었다고장담할수는없다. 시그내처기반으로패커를판단하기때문에 A 라는패킹프로그램의특징을그대로살려서패킹을하는데 B 라는패킹프로그램을사용했다면위와같은프로그램들은 A 라는패커라고알려줄것이다. 하지만실제로는 B 라는패커를사용해서프로그램을패킹한것이라는의미이다. 그렇기때문에직접수작업으로패킹을푸는작업 ( 언패킹 ) 을통해서실제 OEP 를찾는방법을사용하기 도한다. - 104 -

5-3. MUP 실습 MUP 실습 1. Packing 여부확인 PEiD난 EXEInfo와같은프로그램사용 2. OEP를찾는다. 3. 메모리덤프메모리에로드된후에는 unpack이되므로 4. IAT 복구 Student Notes MUP 는직접패킹을풀고 OEP 를찾은후덤프를뜬후 IAT 를복구시키는과정을거친다. 패킹된프로그램을실행하면패킹되지않은프로그램과동일하게동작하기때문에프로그램실행후에는정상적인코드들이메모리에로드가된다. 메모리에로드된후덤프를하게되면정상적인프로그램이덤프가되지만 IAT 는정상적이지않다. 따라서 IAT 를복구하는과정을거쳐야한다. 이번장에서실습할파일은 UPX 로패킹되어있는파일이다. - 105 -

보통패킹이되었다는것은디버거로파일을오픈할때알수있다. 위와같이 Compressed code? 라는팝업창뜨면패킹이되어있다는것을의심할수있다. 그리고디버 거로패킹된프로그램을오픈하면엔트리포인트에서다음과같은코드를확인할수있다. PUSHAD 는범용레지스터에들어있는값들을전부스택에저장하는명령이다. 이것이바로패킹을했다는증거이기도하다. 패킹이된프로그램은패킹이되지않은프로그램과동일하게동작을한다. 정상적으로프로그램이동작하기위해서는언패킹과정을거치고 OEP 로가서실제코드를실행해야하기때문에프로그램시작당시모든레지스터의값들을스택에저장하고언패킹루틴을거친후다시 POPAD 에의해스택에저장되어있던범용레지스터들을가져와서실제코드를실행하게된다. PUSHAD 를했을때스택의변화를살펴보면범용레지스터값들이스택에저장되는것을확인할수있 다. - 106 -

POPAD 를통해저장되어있던범용레지스터들을꺼내온후 JMP 구문을통해서어디론가점프하는것 을볼수있다. JMP 구문을따라 F8 로진행을계속하면다음과같이 OEP 를찾을수있다. - 107 -

패킹되지않은프로그램을디버거에서오픈한후앞의화면과비교해보면같다는것을알수있다. OEP 를찾았으니덤프를하도록하겠다. 마우스오른쪽클릭후 Dump debugged process 라는메뉴를선 택한다. 이메뉴는 Olly Dump 플러그인이설치되어있어야한다. 현재메모리에로드된상태를덤프한것이다. Entry Point 가 20C40 에서 12475 로바뀐것을확인할수있다. 이 OEP 를잘기억해두자. 그리고아래쪽에보면 Rebuild Import 메뉴는체크를해제한다. 이메뉴는디버거에서 IAT 를복구한다는의미인데정상적으로복구가안되는경우가있기때문에체크를해제한다. IAT 를복구해야만정상적으로프로그램이실행될텐데 IAT 복구메뉴를체크를해제하면어떻게 IAT 를복구할것인가? 걱정하지마시길바란다. IAT 를복구는 ImportREConstructor 라는프로그램을이용할것이다. - 108 -

ImportREC 을실행한후 Attach to an Active Process 에서현재 MUP 작업중인파일을선택한다. calc_upx.exe 는패킹되어있는파일이고이파일에정상적인 IAT 를가지고있을것이다. 따라서이파일 을통해서 IAT 정보를획득한후조금전에덤프한파일에적용시켜주는과정을거쳐 IAT 를복구할것 이다. 먼저덤프할때보았던 OEP 를 1 번부분에적어넣고 AuthoSearch 를눌러서검색을한후 Get Imports 를 눌러인포트테이블을가져온다. - 109 -

마지막으로오른쪽아래에있는 Fix Dump 를눌러서덤프된파일의 IAT 정보를복구할것이다. 저장을하면다음과같이 calc_dump_.exe 라는파일명으로언패킹되고 IAT 가복구된파일이생성된다. 파일의크기도다른것을확인할수있다. calc_dump.exe 는언패킹만하고 IAT 를복구하지않은파일이 기때문에실행이되지않는다. IAT 가정상적으로복구되지않은경우 PE 포맷중임포트테이블을상기시켜보자. 임포트테이블의마지막은 NULL 값 (00000000) 이들어있다. 이것을보고임포트테이블의끝이라는것을알수있다. ImportREC 은 IAT 중에서 00000000 값이나오 면 IAT 의끝으로판단하고복구시크기를 00000000 이나오는곳까지만잡아서자동으로계산한다. 이럴경우어떤문제가발생할까? 정상적으로임포트되어서실행되어야할함수들이임포트되지않게 되고결국프로그램이정상적으로실행되지않을것이다. - 110 -

이번에는이렇게 IAT 가제대로복구되지않았을경우수작업으로 IAT 를복구하는과정에대해서알아 보도록하겠다. 이런경우 IAT 가제대로복구되지않아서관련함수를 read 할수없어서발생하는에러메시지이다. 먼저디버거를통해 IAT 가어디서부터어디까지인지확인해보도록하자. ImportREC 을보면 IAT 에대한정보중 RVA 값은임포트테이블의시작주소를나타내는것이고 Size 가 임포트테이블의크기를의미한다. 임포트테이블의 RVA 값이 00025190 이므로실제로메모리상에서는 00425190 이된다. 이위치를확인 해보자. 함수들의이름이보인다. ImportREC 은여기서부터 0xCC 크기만큼임포트테이블으로인식한것이다. - 111 -

0xCC 가떨어진곳을임포트테이블의마지막으로인식한것이다. 임포트테이블의마지막을알리는 00000000 이보인다. 아래로계속내려가보자. 또다른함수가보인다. IAT 를쪼개서자동복구를방해하고있는것이다. 따라서 ImportREC 에서 IAT 를복구할때사이즈를다시계산해서정확한사이즈를입력해주어야한다. IAT 가 0x425190 부터 0x4252C0 까지이므로사이즈는 0x130 이된다. OEP 와사이즈를다시지정한후 Get Import 해서 IAT 를다시불러온다. - 112 -

Fix Dump 를눌러언패킹한파일을지정하여 IAT 를복구시켜준후파일을실행하면정상적으로실행되 는것을볼수있다. 정리를해보면언패킹을하게되면원래의엔트리포인트 (OEP) 가변경된다. 그리고정상적으로 IAT 를 복구시켜줘야하는데 ImportREC 과같은프로그램에서자동으로검색을하기때문에정확하지않을수 있다. 마지막으로패킹전프로그램과패킹후프로그램이어떤차이를보이는지확인해보도록하자. Before Packing After Packing - 113 -

5-4. MUP 를위한 Ollydbg script 작성 MUP 를위한 Ollydbg script 작성 MUP 과정을스크립트로작성 Ollydbgscript 어셈블리어와비슷한형식으로작성 접두사, 접미사없이모든숫자는 16진수로인식함 (10진수로인식시키고자할경우. 을붙임ex. 10=16.) 변수를사용하기위해서는먼저선언해야함 메모리주소를가리키기위해서는 [] 를사용함 플래그비트는반드시앞에! 을붙임 연속된바이트를하나로묶어서사용하기위해서는 # 사이에헥사값으로입력해야함 DWORD 값에대해 +, -, *, /, &,, ^, >, < 를사용할수있음 문자열에대해 + 를사용할수있음 Student Notes 앞서살펴보았던 MUP 는비교적간단한과정을거쳐서작업을할수있었다. 하지만더복잡한경우도많을것이고패커에따라서언패킹을하는과정이다르기도하다. MUP 작업을좀더편하게하기위해서스크립트를작성한후실행할수있다. 물론꼭 MUP 에서만스크립트를사용하는것이아니고특정작업을하기위한스크립트를작성하기도한다. Olly Script 를사용하기위해서는플러그인이필요하다. 인터넷을통해검색을하면여러가지스크립트 를획득할수있고사용이가능하다. 여기서는스크립트를사용하기위한몇가지명령어들에대해서알 아보고앞서실습했던 MUP 를수행할수있는스크립트를작성해보도록하겠다. - 114 -

Olly Script 내부변수 $result 몇몇스크립트명령어들은결과값을리턴함 EAX 레지스트리대신에임시변수를이용함 $version Ollyscript 의버전정보를나타냄 ex) cmp, $version, 1.47 //1.47 버전보다큰가? 명령어 # INC filename 다른스크립트파일의명령어복사 ex) # INC text.txt # LOG 스크립트결과를 LOG 창에기록 OllyDbg Log 와구분짓기위해 로시작 ADD dst, src 문자열을합쳐서 dst 에저장 ex) add y, Times Alloc size size 만큼메모리를확보한뒤시작주소를 $result 에저장 ex) alloc 1000, free $result 1000 AI / AO Animate into / animate over 와동일 STI / STO Step into / step over 와동일 ESTI / ESTO Shift + F7 / Shift + F8 과동일 - 115 -

TI / TO Trace into / trace over 와동일 TC Close Run Trace 와동일 TICND <condition> / TOCND <condition> Trace into / trace over 와동일하나조건을만족하면트레이스를중지 ex) TICND eip>40100a BC Address / BP Address Breakpoint Clear / BreakPoint BPCND Address, Condition 조건에만족할경우 BP 활성화 ex) bpcnd 40100A, EAX==1 BPHWC Address / BPHWCALL BP HW Clear / BP HW Clear All BPHWS Address, pattern BP HW Set r ( 읽기 ) w ( 쓰기 ) x ( 실행 ) /BPHWS Address ex) BPHWS 40100A, r BPL Address, Expression BP of Logging ex) bpl 40100A, EAX BPLCND Address, Expression, Condition BP of Logging Condition ex) bplcnd 40100A, EAX, ECX==0 BPMC BP Memory Clear - 116 -

BPRM Address, size / BPWM Address, size BP on Read Memory / BP on Write Memory ex) BRPM 40100A, FF ex) BPWM 40100A, FF AN Address Ollydbg 에서 analyse 와동일 ASM Address, Instruction Ollydbg 에서 assemble 명령과동일어셈블한명령어의길이가바이트단위로 $result 에저장 ex) asm 40100A, xor eax, eax CMT address, comment(string of character) Ollydbg 에서주석추가와동일 ex) cmt eip, <== OE RUN / PAUSE Ollydbg 에서 run / pause 와동일 RTR / RTU Ollydbg 에서 Execute till Return (Ctrl + F9) / Run till user code (Alt + F9) 와동일 AND dst, src / SUB dst, src 어셈블리명령어 and/sub 와동일 ex) and eax, -1 CMP dst, src / SCMP dst, src 어셈블리명령어 cmp / scmp 와동일 ex) cmp eax, 1 MOV dst, src 어셈블리명령어 mov 와동일 MUL dst, src / DIV dst, src 어셈블리명령어 mul/div 와동일 - 117 -

OR dst, src / XOR dst, src 어셈블리명령어 Or/xor 과동일 EOB Label / EOE Label Excute On Breakpoint / Excute On Exception ex) EOB Label / EOE Label1 FILL Address, Length, Value 주소에서길이만큼입력한값으로채움 ex) fill 40100A,10,90 Find Address, Content 메모리주소부터특정값을검색한후일치한값이있을경우 $result 에주소값을, 없을경우 0 을리턴 ex) find eip, # 6A??E8 # FINDOP Address, Content 주소부터특정명령어바이트로시작하는코드를검색한후결과를 $result 에저장 ex) findop 401000, #61# // find next POPAD findop 401000, #6A??# // find next PUSH FINDMEM what [, Start Address] 전체메모리에서특정값을검색한후일치한값이있을경우 $result 에주소값을, 없을경우 0 을리턴 ex) findmem #6A00E8# findmem #6A00E8#, 00400000 REPL Address, Find, Replace, Length 주소의명령어가 find 한값과같은지비교한후같다면길이만큼바이트를대체 ( 와일드카드사용가능 ) ex) repl eip, #??00 #, #??01 #, 10 RET 스크립트의끝을나타낸다 REV val Val 값을역바이트로 $result 에저장 ex) rev 01020304 // $result == 04030201-118 -

EXEC/ENDE EXECute / END Excute, {} 는치환 ex) var x var y mov x, "eax mov y, "0DEADBEEF exec mov {x}, {y} mov ecx, {x} ende ex) exec push 0 call ExitProcess ende ret JA LABEL / JAE LABEL Cmp 이후에사용하여야하며, 어셈블러 ja / jae 와동일 JB LABEL / JNB LABEL Cmp 이후에사용하여야하며, 어셈블러 ja / jae 와동일 JE LABEL / JNE LABEL Cmp 이후에사용하여야하며, 어셈블러 ja / jae 와동일 JMP LABEL 어셈블러 JMP 와동일 LEN str Str 의길이를획득함 ex) len NiceJump msg $RESULT - 119 -

MUP 를위한스크립트작성 언패킹을위한스크립트를작성할때는수작업의과정에해당하는명령을찾아서작성해주면된다. 앞서보았던 UPX 언패킹을생각해보자. PUSHAD 를통해레지스터를백업한후 POPAD 로레지스터를 원상복귀시킨후 OEP 로진입하는것을볼수있었다. OEP 에진입하는바로그시점에서브레이크포인트를걸어서멈추게해야한다. 또는 OEP 진입바로전 에브레이크포인트를걸어서멈추게한후 Step Over(F8) 해서 OEP 에진입할수도있다. 그렇다면 OEP 진입바로전에행해지는행동은어떤것인가? 바로스택에백업되어있던레지스터들을다시가져오기위해서 ESP 에접근하게된다. 즉스택에접근 해서백업되었던레지스터들을복구한후 OEP 에접근한다는것이다. 그렇다면이런일련의행동들을 앞에서봤던스크립트명령어들로구성해보도록하겠다. STI // Step into(f7), PUSHAD 를한번실행시킨다. BPHWS esp, r // PUSHAD 후 POPAD 할때 BP 설정 RUN // 프로그램실행 (F9) BPHWC // 설정했던 BP 에서멈춘후하드웨어 BP 제거 STO // Step over(f8), 멈춘후한번더진행해서 OEP 진입 CMT eip, <- this is OEP // 현재위치에코멘트 RET // 종료 위예제는비교적간단한예제이다. UPX 에의해패킹이되어있을경우다른패커에비해패턴이간단 하기때문이다. 위예제와똑같이 OEP 를찾는스크립트인데약간다르게작성된스크립트를하나더보 겠다. EOB Break // RUN 이후브레이크발생시 Break: 에서부터스크립트실행 FINDOP eip, #61# // OP 코드가 61 인명령어검색. 즉 POPAD 를검색 BPHWS $RESULT, "x" // 위명령의결과가 $RESULT 에저장되고그메모리주소에 RUN // 있는명령어가실행되면브레이크한다. Break: STO STO BPHWC $RESULT RET // Step over(f8) // BP 해제 // 종료 - 120 -

Module 6 Anti-Reverse 기법 Objectives 디버거탐지기법 Breakpoint 탐지기법 TLS Callback Process Attach 기법 - 121 -

6-1. 디버거탐지기법 디버거탐지기법 kernel32!isdebuggerpresent PEB!IsDebugged PEB!NtGlobalFlags Heap flags Vista anti-debug (no name) NtQueryInformationProcess kernel32!checkremotedebuggerpresent UnhandledExceptionFilter NtSetInformationThread kernel32!closehandle and NtClose Self-debugging Kernel-mode timers User-mode timers kernel32!outputdebugstringa Ctrl-C Rogue Int3 "Ice" Breakpoint Interrupt 2Dh Timestamp counters Popf and the trap flag Stack Segment register Debug registers manipulation Context modification TLS-callback CC scanning EntryPoint RVA set to 0 출처 : http://www.securityfocus.com/infocus/1893 Student Notes 자신의프로그램이디버거에의해어떻게동작하는지디버거들에의해서속속들이들춰진다면그리기분이썩좋지는않을것이다. 혹은상용프로그램이시리얼을생성하는방법들을디버거를통해알아낸후키젠을만들게되면이윤을추구하는회사로써는큰손실이될수있다. 악성코드제작자들은자신이만든악성코드를누군가디버깅하여악성코드가동작하는과정이나방식등을알아낸다면제작자의의도와부합되지않을것이다. 이러한이유로자신이만든프로그램이정상적으로실행되는것이아니라디버거를통해서실행되는것을탐지하여프로그램을종료하는기법들을디버거탐지기법이라고한다. 디버거를탐지하는기법들을구현하는것은생각보다간단하다. 디버거들은위에보이는기법들을눈에 익히고디버깅도중디버거를탐지하는루틴이보이면그것을우회하면된다. 여기서는몇가지기법들 에대해서알아보도록하겠다. - 122 -

kernel32!isdebuggerpresent 가장기본적인디버거를탐지하는기법은 PEB(Process Environment Block) 에서 BeingDebugged 플래그가 포함되어있는지확인하는것이다. IsDebuggerPresent() 는디버거가프로세스를디버깅하고있는지플래 그값으로확인한다. IsDebuggerPresent 에함수를사용한프로그램을그냥실행시키면위와같이 Debugger not found! 라는메 시지를보여준다. 하지만디버거를통해서프로그램을실행시키면다음과같이 Debugger found! 라는메 시지를보여주게된다. 디버거를통해서확인을해보면다음과같다. IsDebuggerPresent() 함수가수행되고그결과값은 EAX 에저장이된다. 만약디버거가탐지가되면 IsDebuggerPresent() 함수가 1 을반환하고디버거가탐지되지않으면 0 을반환한다. CMP 에의해비교를한후같으면디버거가탐지되었다는메시지를출력해주는루틴으로이동한다. (IsDebugg.0040101F) 위코드는다음과같이작성할수도있다. - 123 -

call IsDebuggerPresent test eax, eax jne @DebuggerDetected CALL 문을 F7 로따라서들어가보면다음과같은코드를볼수있다. 이것은 TEB(Thread Environment Block) 에접근하여 PEB 의주소를얻은다음 PEB 를검사하고 PEB + 2 위치에서 BeingDebugged 플래그를확인하는것이다. 이 BeingDebugged 플래그를 0 으로설정하면 IsDebuggerPresent 탐지기법을우회할수있다. 변경후 F9 로실행을하면다음과같이 Debugger not found! 메시지를확인할수있다. PEB!IsDebugged PEB 의두번째바이트를참조해서디버거를탐지한다. 이것은앞서보았던 IsDebuggerPresent 와내부적 으로동작하는방식이같다. 간단한예제만하나보도록하겠다. mov eax, fs:[30h] mov eax, byte [eax+2] test eax, eax jne @DebuggerDetected - 124 -

PEB!NtGlobalFlags PEB 의 68 번째비트에있는 NtGlobalFlags 를이용하여디버거를탐지한다. 프로세스가디버깅중이면 NtGlobalFlags 의값이 0x70 이고정상적인실행중이라면 0x00 이다. 그리고프로세스가디버깅중이라면 ntdll 의힙조작루틴을제어하는몇몇플래그들이설정된다. 이플래그들은 Heap 과관련된디버깅에필요한옵션들이다. FLG_HEAP_ENABLE_TAIL_CHECK (Heap Tail Checking) FLG_HEAP_ENABLE_FREE_CHECK (Heap Free Checking) FLG_HEAP_VALIDATE_PARAMETERS (Heap Parameter Checking) : 0x10 : 0x20 : 0x40 NtGlobalFlags 를이용한디버거탐지기법의사용한코드를보도록하겠다. FS:[30] 은 PEB 를가리키고있다. PEB 에서 0x68 만큼떨어진곳에 NtGlobalFlags 값이있다. - 125 -

PEB 에서 0x68 만큼떨어진곳의값이 70 인지아닌지를확인하는것이다. 우회하는방법은당연히이값 이 70 이아닌다른값으로설정하면된다. NtQueryInformationProcess 이구조체의프로토타입은다음과같다. NTSYSAPI NTSTATUS NTAPI NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESS_INFORMATION_CLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength ); ProcessDebugPort 가 7 로설정된 ProcessInformationClass 와함께호출이될때프로세스가디버깅중이라면시스템은 ProcessInformation 을 -1 로설정한다. 즉, 디버거가아닌정상적으로프로그램을실행시키면 ProcessInformation 의값이 0 이고디버거에의해실행이될경우에는 -1 값이설정이된다. 다음은 NtQueryInformationProcess 를이용하여디버깅을탐지하는코드이다. - 126 -

NtQueryInformationProcess 의구조체에들어갈값들을설정하는코드가보이고 CALL DWORD PTR DS:[40306C] 를통해서 NtQueryInformationProcess 함수를호출하는것을볼수있다. 함수호출바로직전의스택을살펴보자. 함수가호출이되고리턴값이 eax 에저장이되고이값을꺼내서비교하는것을볼수있다. 그렇다면호출된함수의결과값을 0 으로바꿔주면 TEST 의결과가 0 이되고점프를하지않게되어디 버거를탐지루틴을우회할수있다. 0x00401035 의함수호출부분에서 Step into(f7) 을이용해서함수 내부로들어가보겠다. 함수내부로들어가야만리턴값을정확히알수있기때문이다. 함수내부로진입하면 0x7FFE0300 주소에있는내용을호출을하고리턴을한다. 리턴후 0x0013FFC0 에 ProcessInformation 의값이들어있고값이 FFFFFFFF 로되어있는걸볼수있다. 이것은디버거를탐지하는 ProcessInformation 값을조작하지못했다는의미이다. 이값을조작을해보도록하겠다. 그리고스택을살펴보면스택의가장위에있는값을 POP EAX 에의해서가져오게되는데 FFFFFFFF 로되어있는걸볼수있다. - 127 -

NtQueryinformationProcess 함수진입후보이는아래코드를 ProcessInformation 의값을 0 으로바꿀수 있는코드로수정하겠다. 총 7 바이트를차지하고있다. 그런데우리가작성하고자하는코드는 9 바이트이다. 따라서 RETN 14 의 일부를덮어쓰게될것이다. ESP+C 에는 ProcessInformation 의주소가들어있는곳이고이주소에 0 이라는값을넣는다. 그리고앞 에서보았던큰의미를갖기않는루틴을지나리턴하게된다. 이전과는다르게스택의가장위에있는값이 00000000 으로되어있다. 이값을가져와 TEST 로비교한 후디버거가탐지되지않았다는메시지를보여주게된다. - 128 -

kernel32!checkremotedebuggerpresent CheckRemoteDebuggerPresent 함수는자신의프로세스뿐만아니라원하는프로세스가디버깅당하는중 인지까지확인이가능한함수이다. BOOL CheckDebugger(HANDLE hprocess) { } BOOL Retval = 0; CheckRemoteDebuggerPresent(hProcess,&Retval); return Retval; 이함수는 NtQueryInformationProcess 로연결된다. 이함수를이용한전형적인코드는다음과같다. CALL EAX 를 Step into(f7) 로들어가보면다음과같은코드가나온다. EBP+8 에있는값과 0 을비교하고같으면아래쪽에나오는 NtQueryInformationProcess 함수호출을뛰어넘어서나갈수있고같지않으면 NtQueryInformationProcess 함수를호출해야한다. NtQueryInformationProcess 함수가호출이되면 ProcessInformation 값을 0 으로패치해야한다. NtQueryInformationProcess 에서봤던코드와비슷한코드들이보인다. 함수데코레이션덕분에함수명 - 129 -

이보여금방알수있었지만데코레이션이안되었더라도 0, 4, 7 을 PUSH 하고 EAX 와 EBP+8 등을 PUSH 하는것을봐서 NtQueryInformationProcess 라는것을의심해볼수있다. ProcessInformation 값을패키하는방법은앞에서보았으니여기서는 NtQueryInformationProcess 함수를거치지않는방법을사용하는것을보도록하자. /*7C85C09C*/ CMP DWORD PTR SS:[EBP+8],0 이코드를실행하기전에다음과같이 EBP+8 에있는 값을수정한다. 수정한후 F9 를눌러실행하면 Debugger not found! 라는메시지를확인할수있다. ProcessInformation 값을패치하는것이아니라 NtQueryInformationProcess 함수자체를호출하지않는방 법을사용하여디버거를탐지하는루틴을우회하였다. Timing Check 시간을이용한디버거탐지기법은아주단순한개념을사용한다. 일반적으로실행하는데걸리는시간 보다사용시간이더길다면디버거에서프로세스를실행중이라고판단한다. 이때 RDTSC(Read Time Stamp Counter) 명령을사용해서시간의차이를계산한다. 간단한예제를통해서확인해보도록하겠다. - 130 -

이기법은시간체크하는코드를트레이싱하여우회하는방법을사용하면된다. 증가량을비교하는코드 가나오기전에브레이크포인트를걸고브레이크포인트가걸릴때까지트레이싱한다. 또는 GetTickCount 함수를불러올때브레이크포인트를설정하여리턴값을수정할수도있다. 근본적인우회방법은커널드라이버를작성해야하는데다행스럽게도 Olly Advanced 플러그인을사용 하여우회할수있다. Olly Advanced 에서사용하는방법은다음과같다. 1. 4 번컴트롤레지스터 (CR4) 의 TSD(Time Stamp Disable) bit 값을 1 로설정한다. TSD bit 가 1 로설정되면 Ring0 이외의모드에서 RDTSC 가실행될때마다 GP(General Protection) 익셉션이발생한다. 2. GP 익셉션이발생하면 GPF 핸들러가동작한다. GPF 익셉션넘 버는 0xD 이므로 GPF 핸들러의주소는 IDT 베이스주소로부터 0xD * 4byte 만큼떨어진곳에저장되어있을것이다. 3. Olly Advanced 플러그인은바로 IDT 에등록되어있는 GPF 핸들러를후킹하여 GPF 가발생한경우 RDTSC 가원인인지조사하고 RDTSC 실행이원인이라면리턴값을조작하는방법으로 timing check 를우회한다. - 131 -

kernel32!outputdebugstringa 이기법은유효한 ASCII 스트링과함께 OutputDebugStringA 를호출한다. 프로그램이만약디버깅중이 라면반환값은매개변수로서전달된스트링의주소가될것이다. 정상적인조건에서는반환값이 1 이 되어야한다. 그리고이기법은 Ollydbg 에서만가능한포맷스트링공격에사용될수있다. 올리디버거의취약점을공격할수있으며현재사용중인 1.10 에서는패치가되지않은상태이다. 이외에도다양한디버거탐지기법들이있는데모든기법들을수작업으로우회한다는것은상당히힘든작업이될것이다. 그래서우리는올리플러그인을이용해서비교적간단히디버거탐지기법들을우회할수있다. 대표적인플러그인으로는앞에서잠깐나왔던 Olly Advanced 가있다. 그리고 HideDebugger 나 Olly Invisible, IsDebuggerPresent 와같은플러그인도있다. - 132 -

6-2. BreakPoint 탐지기법 Break Point 탐지기법 Hardware Breakpoint DR0 ~ DR3 디버그레지스터이용 DR7 디버그레지스터는브레이크포인트제어에사용 Software Breakpoint 디버거가직접관리하는브레이크포인트 0xCC (INT 3) 을사용 Instruction Breakpoint 특정주소번지의명령에설정되는브레이크포인트 Memory Breakpoint 특정메모리영역에위치한데이터를읽거나변경할때사용 Student Notes 브레이크포인트는디버거를통해실행중인프로그램을특정위치나조건에서멈추게하고싶을때사용하는기법이다. 이런브레이크포인트를무력화하는것도안티리버싱기법중하나이다. 여러분들이만든프로그램을누군가가디버거로분석을하면서특정위치에브레이크포인트를걸어서중요한정보를획득하려고시도한다고생각해보자. 당연히그브레이크포인트를삭제해버리고싶을것이다. 먼저브레이크포인트의특징에대해서알아보고이런브레이크포인트를탐지하고무력화시키는방법 과우회기법에대해서알아보도록하겠다. - 133 -

브레이크포인트종류 Hardware Breakpoint Software Breakpoint Instruction Breakpoint Memory Breakpoint DR0 ~ DR3 디버그레지스터이용 DR7 디버그레지스터를이용하여브레이크포인트제어아키텍처에서직접지원하므로빠르고정확하다. 레지스터의개수가제한적이라는단점이있다. 디버거가직접관리한다. 소프트웨어방식으로구현된인스트럭션브레이크포인트는명령어의첫번째바이트를 0xCC(INT 3) 으로변경한다. 특정주소번지에설정되는브레이크포인트이다. 하드웨어 / 스포트웨어방식으로구현된다. 특정메모리주소의데이터를읽거나변경할때디버거를멈출때사용한다. 하드웨어 / 소프트웨어방식으로구현 Hardware Breakpoint 탐지및우회기법 - 134 -

IA32 에서제공하는디버그레지스터를이용하는방법이하드웨어브레이크포인트이다. DR0 ~ DR7 까지모두 8 개의디버그레지스터를제공한다. DR0 ~ DR3 : 브레이크포인트를설정할수있는주소를지정 DR4 ~ DR5 : 예약된공간으로사용하지않음 DR6 : 디버그상태레지스터, 디버그나브레이크포인트예외가발생했을때영향받는상태를알려준다. DR7 : 디버그제어레지스터, 하드웨어브레이크포인트의활성화여부와유형에대한정보를가지고있다. 주의깊게봐야할부분은브레이크포인트유형에서 R/W0 ~ R/W3 까지이다. 이곳에 00 으로설정되어 있으면인스트럭션브레이크포인트이고 01 이나 11 이면메모리브레이크포인트를의미한다는것이다. DR7 디버그레지스터는위그림과같이각비트들이특별한의미를가지고있다. 프로그램들이위내용을확인할수있다면하드웨어브레이크포인트를금방탐지할수있을것이다. 하지만 IA32 매뉴얼에의하면디버그레지스터는일반적으로 Ring0 모드에서만접근할수있다고되어있다. 그런데윈도우의경우 SEH 를이용하면 Ring3 에서도디버그레지스터의값을읽거나변경하는것이가능하다. 모듈 2-6 에서보았던 SEH 의프로토타입을떠올려익셉션핸들러의세번째아규먼트는 ContextRecord 로써익셉션이발생했을당시의레지스터들의값에접근하거나변경하는것이가능하다. 그리고 _CONTEXT 구조체안에는범용레지스터를비롯하여디버그레지스터가포함되어있다. 즉, 익셉션이 - 135 -

발생하면디버그레지스터를 Ring3 모드에서도접근하거나변경이가능하다는것이다. 방법은비교적 간단하다. 1. SEH 핸들러에하드웨어브레이크포인트탐지루틴을등록 2. 익셉션을발생 3. 미리등록해놓은 SEH 핸들러가실행 4. 핸들러는 ContextRecord 를이용하여디버그레지스터값을변경하여하드웨어브레이크포인트를무력화시킨다. 먼저하드웨어브레이크포인트를탐지하는예제를보도록하겠다. 익셉션을발생시켜 SEH 안으로들어 오게해서하드웨어브레이크포인트가설정되어있는지검사를한다. 먼저적당한곳에하드웨어브레이크포인트를건다. 하드웨어브레이크포인트설정유무를확인하려면메뉴에서 Debug Hardware breakpoints 에서확인할 수있다. - 136 -

그리고 F9 로실행한다. 화살표로표시된부분이익셉션을발생시킨부분이다. 디버거의하단에있는상태바를확인해보니 Access violation 이발생한것을볼수있다. 익셉션이발생하면 Shift + F7/F8/F9 셋중하나를누르면된다. Shift + F9 를누르면익셉션을처리하고계속실행이되고 Shift + F8 을누르면익셉션을처리하는루틴으로들어가서한단계씩실행하게된다. 일단 Shift + F9 를눌러보자. 역시나익셉션을핸들러내부에하드웨어브레이크포인트를탐지하는루틴이들어있는것이다. 다시처음부터똑같이진행하되이번에는 Shift + F9 대신 Shift + F7 을눌러서진행해보도록하자. 하드웨어브레이크포인트는지워지지않았으니다시지정하지않아도상관은없다. 그런데언제나올지모르는디버거탐지루틴을마냥 F7 을눌러가면서찾을수는없는노릇이다. Shift + F7 을눌러익셉션처리루틴으로들어와서 SEH Chain 을살펴보자. 상단메뉴에서 View SEH chain 에서 SEH 처리부분을찾을수있다. - 137 -

현재익셉션을처리하는곳이 0x0040103E 라는것을알수있다. 이주소에서하드웨어브레이크가설정 되어있는지확인하는루틴이들어있을거라예상을하고브레이크포인트를설정한다. F9 를눌러실행한다. 우리가원하던곳을찾았다! 화살표로표시된부분이바로 DR0 ~ DR3 까지하드웨어브레이크포인트가설정되어있는지확인하는부분이다. 모두결과가 0 과같이않을경우 0x0040109A 로점프를한다. 0x0040109A 에는앞에서보았던하드웨어브레이크포인트가설정되었다는메시지박스를띄워주는함수가있다. 만약여기서하드웨어브레이크포인트를무력화시키고자한다면 SEH 핸들러내부에서 DR0 ~ DR3 까 지의값을 0 으로만들어버리면된다. 앞서보았던예제의어셈코드중디버그레지스터의설정유무를확인하는루틴은다음과같다. CMP DWORD PTR DS:[EAX+4h],0 JNE @hardware_bpx_found CMP DWORD PTR DS:[EAX+8h],0 JNE @hardware_bpx_found CMP DWORD PTR DS:[EAX+0Ch],0 JNE @hardware_bpx_found CMP DWORD PTR DS:[EAX+10h],0 JNE @hardware_bpx_found - 138 -

모두비교만하는데비교하는부분을 MOV 를이용하여 0 으로바꿔주면하드웨어브레이크포인트를 무력화시킬수있다. MOV DWORD PTR DS:[EAX+4h],0 MOV DWORD PTR DS:[EAX+8h],0 MOV DWORD PTR DS:[EAX+0Ch],0 MOV DWORD PTR DS:[EAX+10h],0 이렇게하드웨어브레이크포인트를무력화시키거나탐지하는경우는어떻게우회할것인가? 먼저익셉션을발생시키는코드를 NOP 로처리하는방법이있다. 하드웨어브레이크포인트탐지를위 해익셉션을발생시켰다면익셉션을발생시키는코드는전체코드가실행되는데큰문제가없을것이다. 본예제에서는익셉션을발생시키는코드를 NOP 처리하고바로다음메시지박스를띄우는루틴으로 점프하는구문을추가하였다. 두번째방법은어느한시점에서브레이크포인트를탐지하거나제거한다는단점을역으로이용하면된다. 이렇게브레이크포인트를탐지하거나제거하는코드를찾아낼수있다면탐지및제거코드이후에브레이크포인트를설정하는것이다. 유저모드어플리케이션에서하드웨어브레이크포인트를탐지하거나제가하려면반드시 SEH 를호출해야한다는사실을이용하는방법도있다. 익셉션핸들러에서스택을사용하면 EBP+0x10 은아규먼트로전달받은 ContextRecord 값이다. 이러한코드가발견될경우 ContextRecord 를전달받아서어떤행동들을하는지살펴볼필요가있다. 만약익셉션핸들러가스택을사용하지않는다면 ESP+0x0C 가 ContextRecord 를의미한다. 그리고리버싱을하는과정에서 SEH chain 을자주확인하는것이좋다. - 139 -

Software Breakpoint 탐지및우회기법 소프트웨어브레이크포인트는명령의처음 1 바이트를 0xCC(INT 3) 으로변경하여프로그램의진행을 멈춘다. 소프트웨어브레이크포인트를탐지하는방법은크게두가지정도있다. 보호하려는코드의처음 1byte 또는처음몇바이트정도를검사하여 0xCC 가있는지확인한다. 보호하려는코드전체의체크섬값을계산하여비교한다. 먼저특정위치에소프트웨어브레이크포인트가설정될경우프로그램이비정상적으로종료되는예제 를보도록하겠다. 정상적인프로그램의실행은다음과같은결과를보여준다. 바로이 good job 이라는메시지를출력해주는함수에브레이크포인트를걸경우프로그램이비정상적 으로종료된다. 디버거로프로그램을오픈하고 Step over(f8) 로계속진행하다보면다음과같은코드를볼수있다. 프로그램이종료 (CALL softbp.exit) 되기바로직전에스택클리어링 (ADD ESP, 0C) 을볼수있다. 00401005 의주소를호출하는데따라가보도록하겠다. 메인함수를호출하는부분과우리가보호하고자하는함수모두보인다. 먼저 ProtectedFunc 에 F2 를눌 - 140 -

러브레이크포인트를걸고메인함수를따라이동해보겠다. 메인함수에서는특정위치에 0xCC 가있는지확인을할것이다. 그런데 0xCC 는어디에도보이지않는 다. 이것은 code permutation 이라는기법이적용되어있는것인데다음코드를보도록하자. ECX 에조사할바이트를설정한다. 그리고 EAX 에 0x660 을넣는다. SHR 은오른쪽으로쉬프트하는명령이고 EAX 의값을오른쪽으로 3 비트쉬프트하게되면 0xCC 가된다. 이렇게변경되는이유는 0x660 을이진수로표현하면 0110 0110 0000 이되고이값을오른쪽으로 3 비트쉬프트하면 000011001100 이된다. 이값을다시 16 진수형태로변환하면 0xCC 가되는것이다. (4 비트씩분할하면 1100 이되고이값은 10 진수로 12 가된다. 즉, 16 진수로는 C 가된다 ) 즉, 위코드부분이 0xCC(INT 3) 인소프트웨어브레이크포인트가설정되어있는지검사하기위한준비하고보면된다. 위코드가의미하는것은 EDI 가가리키는곳에 EAX 에저장되어있는값이있는지찾으라는것이다. 만약 0xCC 를찾게되면 ECX 값은 0 이되지않고찾지못하면 0 이된다. 따라서 TEST ECX, ECX 에서 - 141 -

ECX 의값이 0 이면 004010A4 로점프하고그렇지않을경우아래의코드를실행하게된다. RETN 은현재스택의가장윗부분에있는주소로복귀를하게된다. RETN 을실행하기전스택에는다 음과같은데이터들이저장되어있다. RETN 은 POP EIP 와같은의미이다. 물론 POP EIP 와같은명령은존재하지않지만이것은스택의가장윗부분에있는주소값을가져와서그주소로복귀한다는의미이다. 그런데현재스택의가장윗부분에는 B58C7BB6 이라는엉뚱한값이저장되어있고결국예외가발생하여프로그램이비정상종료를하게된다. 보통함수가끝나고복귀할때 RETN 명령을만나게되고다음과같이복귀주소가스택에탑에있는것이정상적이다. 이렇게복귀주소를이상한곳으로설정되게끔프로그래밍을한다면브레이크포인트가탐지되고엉뚱 한곳으로복귀하게되어정상적으로분석하는것이힘들어진다. 소프트웨어브레이크포인트탐지기법을우회하는방법은하드웨어브레이크포인트를사용하는방법 이있고메모리브레이크포인트를사용하는방법이있다. 이두가지방법모두실제코드를변경하는것 이아니기때문에우회가가능하다. ProtectedFunc 함수에인스트럭션브레이크포인트를설정하는것이아니라메모리에접근할때멈출수 있게끔메모리브레이크포인트를설정하면앞에서사용한탐지기법을우회할수있다. - 142 -

ProtectedFunc 에메모리브레이크포인트를설정하고 F9 로실행했더니다음그림에서보는것과같은위 치에서멈추었다. 앞에서봤던코드들이다. 바로 0xCC 를찾는코드인데 F8 로계속진행을해보면엉뚱한곳으로점프하 는구문을벗어나정상적인루틴을실행한다. 또한가지의소프트웨어브레이크포인트를우회하는방법은 Import 테이블을이용하는것이다. 즉, 코드 에브레이크포인트를설정하는것이아니라임포트되는함수에브레이크포인트를설정하는것이다. 코드에브레이크포인트를설정하고 F9 로실행하면익셉션이발생해서프로세스가종료된다. - 143 -

다음과같이현재모듈에서의이름을검색한다.( 단축키 CTRL+N) 그런후아래와같이 MessageBoxA 함수에브레이크포인트를설정한다. 그리고 F9 로실행을하면정상적으로실행이된다. - 144 -

6-3. TLS Callback TLS Callback TLS (Thread Local Storage) Callback 프로세스가초기화되기전과종료된후에호출 프로그램의엔트리포인트이전에실행 디버거탐지또는언패킹루틴을 TLS Callback에등록시켜서디버거로오픈할때프로그램을종료시킨다. PE 헤더중 Data Directory 엔트리중 10번째인 IMAGE_DIRECTORY_ENTRY_TLS에정보가들어있다. Student Notes TLS Callback 기법은악성코드나패커와같으프로그램에서안티리버싱기법으로자주사용되는기법 이다. TLS Callback 은쓰레드가생성될때로더에의해서실행되는코드이다. 즉, 엔트리포인트에도달 하기전에실행되는것이다. 만약 TLS Callback 에대한지식이없다면상당히당황스러울것이다. TLS Callback 부분에디버거탐지 루틴이나언패킹루틴이들어있다면디버거로파일을열면죽고, OEP 를찾아야하는데어느지점에서 언패킹이이루어지는지를찾지못하기때문이다. - 145 -

디버거에서예제파일을열어보도록하겠다. 코드가전혀보이지않고바로디버거가발견되어프로그램을종료한다는메시지창이뜨고프로세스가종료된다. 이것은메인프로그램이시작하기전에어떠한함수에의해서디버거를탐지했다는것을알수있다. TLS 가무엇인지알았다면 TLS Callback 에의해서디버거가탐지되었다는것을알수있다. 즉, 우리는 TLS Callback 함수가실행하는위치를찾아서디버거탐지루틴을우회하면된다. 먼저 PE 파일에서 Data Directory 에있는 TLS 에대해서알아보도록하자. TLS 는데이터디렉토리테이 블에서 10 번째에위치하고있다. Stud_PE 를이용하여예제파일을열어보자. Data Dir 에서 IMAGE_DIR_ENTRY_TLS 를선택하면 TLS 에대한정보를확인할수있습니다. 앞에서부 터 RVA, Size, Raw 이다. PE 파일구조에서보았던것처럼 Raw 값은 Stud-PE 프로그램이계산해준값으 로실제 PE 헤더에는존재하지않는값이다. RVA 값을이용하여파일상에서의위치를구할수있다. TLS 의 RVA TLS 가위치하고있는섹션의 VirtualOffset + RawOffset = 3060 3000 + 800 = 860-146 -

TLS 테이블의위치는파일상에서 0x860 이다. Winnt.h 에정의되어있는 IMAGE_TLS_DIRECTORY 는다음과같다. typedef struct _IMAGE_TLS_DIRECTORY32 { DWORD StartAddressOfRawData; DWORD EndAddressOfRawData; PDWORD AddressOfIndex; PIMAGE_TLS_CALLBACK *AddressOfCallBacks; DWORD SizeOfZeroFill; DWORD Characteristics; } IMAGE_TLS_DIRECTORY32; 네번째멤버인 AddressOfCallBacks 에 TLS Callback 의주소가들어있다. Winhex 로파일을열어보자. TLS Callback 의주소는 0x00403084 이다. 이주소는 RVA 값이아닌가상주소이다. 파일상에서 TLS Callback 의내용을보려면한번더계산을해야한다. 가상주소가 0x00403084 이므로 RVA 값은 ImageBase 를뺀 0x3084 가된다. (3084 3000 + 800 = 884) TLS 의파일상에서의위치는 884 가된다. Winhex 에서확인해보자. TLS Callback 의주소가 0040101B 이다. 디버거가프로그램을시작하는위치를메인모듈의 Entry Point 가아닌 System breakpoint 로설정한다. 이것은프로그램이로더에의해서메모리에적재되기전위치에 서부터디버깅을시작하겠다는의미이다. - 147 -

디버거에서프로그램을시작하면다음과같이 System breakpoint 에서멈춘다. TLS Callback 의주소인 0040101B 로이동해보자. CTRL+G 를눌러이동할주소값을적는다. 0040101B 의위치로가면디버거탐지루틴이바로보인다. 0040101B 에브레이크포인트를걸고 F9 를눌러실행을시킨다. 그리고 IsDebuggerPresent 를우회하면 된다. 이예제의경우 TLS Callback 에비교적간단하고눈에잘보이는디버거탐지루틴이있지만다른방식 으로디버거탐지루틴을복잡하게구성하거나쓰레기코드들을많이추가하고, 발견되었을때도단순 종료가아닌엉뚱한주소로점프하게만들어디버거가코드를잘못해석하게만들수도있다. TLS Callback 을우회하는방법은 IMAGE_DIRECTORY_ENTRY_TLS 에주소값이들어있는지확인하 고있을경우 TLS 테이블내에있는 AddressOfCallbacks 의값을 00000000 으로바꿔주면된다. TLS Callback 함수주소를알아내는방법은위의방법을사용해도되고 IDA 를이용하면쉽게찾아낼수 있다. IDA 로분석할파일을오픈한후 CTRL+E 를눌러보면다음과같이쉽게찾을수있다. - 148 -

- 149 -

6-4. Process Attach 기법 Process Attach 기법 일반적으로혼자서실행이불가능한파일분석시사용 dll이나 ocx 파일분석시사용 다른프로그램에인젝션되어정상적인프로세스를봐야하는경우에도사용 Student Notes 프로세스어태치기법은일반적으로단독으로실행이불가능한 DLL 이나 OCX 와같은파일을분석할 때사용되는기법이다. 악성코드중 DLL 로배포되면서특정프로세스에인젝션이되는경우가있는데 이런파일들을분석할때프로세스어태치기법을사용한다. 대표적인예로 DLL Injection 기법을사용하는악성코드의경우해당 dll 이인젝션된프로세스를찾고 그프로세스를통해서 DLL 을 attach 시켜서분석을하게된다. 간단한예제를통해서분석기법에대해 서알아보도록하겠다. - 150 -

디버거를실행시키고메뉴에서 File Attach 를클릭한다. Attach 를클릭하면현재실행되고있는프로세스들의목록을보여주고어태치할프로세스를선택하면 된다. 여기서는 explorer.exe 파일을어태치해보도록하겠다. 어태치할파일을선택하면디버거에서해당프로 세스의코드를읽어오고 Pause 상태에서멈추게된다. 이때다시한번 Start 를눌러서시작시켜준다. 프로세스를시작시킨후현재이프로세스와함께동작하고있는모듈들을확인한다. Executable modules 를클릭하면해당프로세스가사용하고있는파일들의목록이나온다. 대부분 dll 파 일이다. 이파일들의목록중분석하고자하는 dll 이있다면해당 dll 을마우스오른쪽클릭하여 View names 로함수들의이름을확인할수있다. - 151 -

만약특정함수가하는행동들을살펴보고싶다면해당함수에로그브레이크포인트를설정한다. Conditioinal log breakpoint on import 는임포트되는함수에서특정행동이발생시브레이크포인트를설 정하는데코드의실행이멈추는브레이크포인트가아닌로그만남기는브레이크포인트이다. - 152 -

위와같이프로그램을멈추지말고아규먼트에대해서만로그를남기게끔설정하여분석하고자하는 dll 이어떤행동을하는지살펴보면된다. 확인할때는 View Log 에서확인하면된다. 좀더원활한분석을원한다면 Log 창에서는지금까지남아있던로그들을지우고보는것이좋다. 일부악성코드들은이런어태치기법을통해서분석되지않게하기위해어태치가발생하면디버거를종료시키는루틴을가지고있다. 디버거가실행중인프로그램을어태치하려고하면반드시 ntdll.dll 내의 ZwContinue 를 CALL 해야한다. Anti-Attach 의기법들중 ZwContinue 를후킹해서다른루틴 (Anti- Debugging 루틴 ) 으로점프시키는기법이존재하는데이럴경우디버거에서어태치해서분석하는것이힘들어지게된다. Ollydbg 의플러그인인 AttachAnyway 는현재열려있는모든프로세스들의가상메모리공간에서 ZwContinue 의시작주소를확인함으로써후킹여부를판단하여해당프로그램존재시프로그램명을알 려준다. 그리고후킹코드를 ZwContinue 원래의코드로다시덮어씌워서어태치가가능하게해준다. - 153 -

- 154 -

Module 7 분석실전 Objectives 온라인해킹대회문제분석 악성코드분석 - shadowbot - 155 -

7-1. 온라인해킹대회문제분석 온라인해킹대회문제분석 KISA 제4회해킹방어대회리버스엔지니어링문제 Unpacking Steganography Student Notes 이번모듈에서는 KISA 에서주관하고있는해킹방어대회문제중리버스엔지니어링기법이적용되는 문제를풀어보도록하겠다. 2007 년에있었던제 4 회해킹방어대회예선문제중스테이지 8 을보도록 하자. 스테이지 8 8 번문제 : 평소업무시간에메신저를즐겼던하모씨는최근사내에필수소프트웨어가설치된이후로메신저를사용할수없었다. 해당프로그램을크랙하여그프로그램의패스워드를획득하라. ID: stage8, PASSWORD: 획득한패스워드 - 156 -

이문제는언패킹을한후추출된파일에서스테가노그라피기법을이용하여푸는문제이다. 먼저파일을실행시켜보겠다. Reverse Engineering 패스워드를입력해보면다음과같이패스워드가일치하지않는다는메시지를볼수있다. 먼저패킹이되어있는지확인해보겠다. PEiD 를이용해서확인한결과다음과같이 Nothing found 라는 메시지를볼수있다. 그렇다면패킹이되어있지않은것일까? 좀더정확한확인을위해오른쪽하단에보이는 버튼을크 릭해서 Hardcore Scan 을클릭해보자. - 157 -

이번에는 nspack 2.2 North Star/Liu Xing Ping 이라고나오는것을볼수있다. 물론 PEiD 는시그내처 기반이기때문에 userdb.txt 가업데이트되어야만 nspack 로패킹되었다는메시지가보일것이다. 그럼디버거를통해파일을오픈해보겠다. 지금까지보았던화면과는좀다른화면이보인다. 이것은디버거가정상적으로코드를해석하지못하 기때문이다. 그런데위에조금내려와보면!packed by nspack$@ 라는문자열이보이는것으로봐서 - 158 -

nspack 으로패킹이되어있다는것을알수있다. 프로그램을실행하면패킹이풀릴것이고메모리에로드될것이다. 어느부분에서언패킹이되는지확 인하기위해 Memory map 을열어보자. 메모리맵은메뉴에서 View Memory 를선택하거나 ALT+M 을눌러서확인할수있다. PE 헤더가 006E0000 에위치한것으로보아 ImageBase 가이부분이라는것을알수있다. 물론패킹된 프로그램의코드가이부분에서부터시작하는것이다. 그럼 F9 를눌러실행을시켜서메모리에실제코 드가로드가되는지확인해보자. - 159 -

실행을해보니 ImageBase 로자주볼수있는 00400000 이생겼다. 아마도패킹되었던부분이풀리면서이주소에코드가로드된것이라예상해볼수있습니다. 그러면메모리에공간을할당하고이주소에쓰기를할때를포착하면언패킹된정상적인코드를볼수있을것이다. CTRL+F2 로다시시작을시키고메모리에공간을할당할때쓰이는 VirtualAlloc 함수에브레이크포인 트를설정하여메모리에공간을할당하는순간을찾아서앞에서보았던 00400000 의주소가할당되는지 확인해보겠다. 먼저 VirtualAlloc 함수가어떻게생겼는지확인해보도록하자. LPVOID VirtualAlloc( LPVOID lpaddress, DWORD dwsize, DWORD flallocationtype, DWORD flprotect ); // address of region to reserve or commit // size of region // type of allocation // type of access protection 첫번째인자가메모리에할당되는주소이다. 이것을확인하도록하자. 하단에 Command bar 에서다음 과같이브레이크포인트를설정한다. VirtualAlloc 함수에브레이크포인트를설정하니 7C8245A9 라는주소에브레이크포인트가설정되었다. F9 를눌러실행을하고스택창을확인하여 00400000 이라는주소에메모리공간을할당하는코드를확인하도록한다. F9 를한번실행하고 VirtualAlloc 함수가호출될때브레이크포인트가걸릴때스택은다음과같다. 위와같이 Address 가 NULL 이다. 이 Address 가 00400000 인곳을찾아야한다. F9 를눌러몇번더실행 하다보면다음과같이 Address 가 00400000 인곳을찾을수있다. - 160 -

F8 을눌러 VirtualAlloc 함수를빠져나오면다음과같이이상한문자열들만보이는코드부분에도달할 수있다. 이렇게보이는이유는디버거에서제대로해석을하지못했기때문이다. CTRL+A 를누르면다시해석 을해서화면에실제코드들을보여주게된다. 그리고메모리맵을확인해보니아직공간만할당한상태 이고코드가메모리에로드되기전이라는것을알수있다. 패커가프로그램실행을위해언패킹을하면이위치에코드를언팩을하게된다. 그래서이주소에하드 웨어브레이크포인트를건다. 이때하드웨어브레이크포인트는 write 로한다. 왜냐하면이주소에코드 를쓰기 (write) 를하는시점을잡기위함이다. - 161 -

하드웨어브레이크포인트가설정되었으면 F9 를눌러실행해본다. 실행을했더니방금전에할당받았던주소인 00400000 에내용이생겼다. 이주소에 write 를하고있기 때문에브레이크포인트에의해서멈추게된것이다. 아래쪽으로계속내려가서 RETN 이있는곳에브 레이크포인트를설정한다. ( 하드웨어브레이크포인트는제거한다 ) 그리고 F9 를눌러실행한다. RETN 0C 에서멈출것이고 F8 을눌러계속진행한다. F8 로진행을하니레지스터들을스택에서꺼내는것이보이고 0045972C 로점프하는것이보인다. 보통 패킹된프로그램들은 PUSHAD 를통해레지스터들을스택에백업한후언패킹루틴이나복호화루틴를 거쳐서실제코드를풀어낸다음 POPAD 를통해서레지스터들을복구한다. - 162 -

앞의코드는 POPAD 는아니지만 POP 명령어로레지스터들을복구하는것이보이고 JMP EAX 에의해 서 0045972C 로점프한다. 바로이점프한후나오는부분이 OEP 이다. 일반적으로 OEP 를찾으면덤프를뜨고 ImportREC 같은프로그램으로 IAT 를복구하면된다. 하지만이 프로그램은가상으로메모리를할당받았기때문에직접 IAT 를복구해주어야한다. 그리고실행을하 게되면 IAT 는다른값으로채워지기때문에채워지기전값으로바꿔준뒤덤프를하면된다. 현재위코드에서덤프를뜨기위해서코드창에서 Dump debugged process 를클릭하면아래보이는것과 같이이상한주소가 OEP 로되어있는것을볼수있다. - 163 -

IAT 를직접복구하려면아무함수나하나를찾아서확인하면된다. 00459737 주소에서엔터를눌러함수안으로들어가보면 00406120 주소에 CALL 00406050 이있다. 다시한번이부분에서엔터를눌러들어가보면다음처럼함수들을찾을수있다. 함수가있는코드에서마우스오른쪽클릭한후 Follow in Dump Memory address 를눌러서해당함수 가있는곳으로따라가보자. 헥사코드덤프창에서마우스오른쪽을클릭한후 Long Address 를눌러서 IAT 를확인한다. 자, 그럼 IAT 가어디서부터어디까지인지확인해보도록하자. 임포트테이블의마지막은 0000 0000 으로채워져있다. 하지만간혹임포트테이블중간 0000 0000 을삽입하여리버서들을혼돈시키거나귀찮게하기도한다. ImportREC 과같은프로그램은자동으로 IAT 를복구시켜주는데이렇게 0000 0000 이임포트테이블중간에있을경우정상적으로 IAT 를복구하지못하는경우가발생하기도한다. - 164 -

IAT 의시작주소는 0045D118 이고마지막주소는 0045D6D0 이다. 그리고 IAT 를복구하기위한또하 나의정보는 OEP 인데 OEP 는앞에서보았듯이 0045972C 이다. 그럼덮어씌워지기전의 IAT 를복사하기위해 CTRL+F2 를눌러프로그램을다시실행시키고 VirtualAlloc 에브레이크포인트를설정한다. 그리고앞에서했던과정들을그대로진행하면서가상메 모리에주소공간을할당한후까지인아래보이는코드까지진행한다. 그리고헥사덤프창에서앞에서구했던 IAT 의시작주소로이동한다. 현재는아무런값도들어있지않다. IAT 의시작주소인 0045D118 에하드웨어브레이크포인트를설정 한다. (Breakpoint Hardware, on write DWROD) 그리고 F9 를한번눌러진행한다. 그러면헥사덤프 창에내용이채워지는것을볼수있다. - 165 -

F8 을한번눌러계속진행시켜나머지 IAT 도채운다. IAT 가모두채워졌다. 이부분이원래의 IAT 이다. 0045D118 부터 0045D6D0 까지복사를한다. 그런데앞에서분명 0045D6D0 까지였는데지금확인해보니 0045D730 까지라고되어있다. 앞에서확인 했던 IAT 는덮어씌워진후이기때문에실제와약간다를수도있다. 0045D730 까지바이너리를복사한 다. ( Binary Binary copy) 그리고조금전에설정했던하드웨어브레이크포인트를제거한다. 상단메뉴에서 View Hardware breakpoint 에서제거하면된다. 이제원래 IAT 가준비가되었으니 OEP 까지가보도록하자. OEP 로이동한후 IAT 를확인해보니원래 의 IAT 대신새로구해진주소값들이들어가있는것을확인할수있다. - 166 -

조금전에복사했던바이너리를이곳에붙여넣는다. ( Binary Binary paste) IAT 를복구했으니덤프를뜹니다. 현재프로그램이가상메모리에로드되어있기때문에메모리맵으로 이동해서덤프를떠야한다. Dump 창이한뜰것이다. 여기서 Backup Save data to file 을선택해서저장한다. 이제마지막으로섹션헤더들의 RVA 값을맞춰주어야한다. PEvoyeur 을이용해서섹션헤더들의 RVA 값을수정하겠다. - 167 -

PEvoyeur 에서오른쪽아래에보면 More 옵션에서 Plugins Sections Fixer 을클릭하면섹션을픽스시켜 준다. 그리고간단한정보창이뜨고섹션헤더들의정보의 FIX 가끝난다. 픽스된파일은 fixed-sections.exe 라는파일명으로저장된다. 실행해보면원래파일과동일한화면과동 일한결과가나온다. 언패킹된파일은 Borland Delphi 6.0 으로작성된프로그램이었다. - 168 -

그럼이제디버거를통해언패킹된파일을불러와서패스워드를입력받은패스워드를프로그램가지고 있는프로그램과비교한후성공 / 실패메시지를뿌려줄것이다. 하지만이파일은디버거를통해서확인 해도올바른패스워드를구할수없다. Resource Hacker 를이용해서언패킹된파일을오픈해서확인해본결과 RCData 에서 TFORM1 에서다음 과같은내용을확인할수있었다. TabVisible 이 False 로되어있는것으로보아어떤탭이숨겨져있다는것을예상할수있다. 그리고아래 쪽으로 Picture.Data 에 16 진수값이들어있다. 아래로계속내리면 Picture.Data 를하나더만날수있다. - 169 -

TabVisible 을 True 로바꾼후 Compile Script 를하고새파일로저장한다. 그리고실행하면다음과같이 화면이바뀌어있는것을볼수있다. 그림이보고유추할수있는것은스테가노그라피이다. 육안으로식별하기에는두개의그림이똑같다. 따라서앞에서보았던 Picture.Data 에있는 16 진수값을두개모두가져와 Winmerge 를통해비교해서 서로다른곳이있는지찾아보았다. ( 리눅스에서는 diff 명령을사용하면된다 ) 010101010100000001010000000001010101000101010000010100000101010101010001010 000000100000001010001010100010100000101000000010001000101010001000101010100 000001000001010000010101000100000001000100010101000101000101010000000001010 101000000010000010100000101010101010000010101010101000101000000010100000000 010101010000000100000101000001010001010101010001000101010100000001010101010 101000001010101010001000101010100010000000101010001000101010101010100000101 0101000101010101010001000100000100000001000101-170 -

얼핏보기에는 2 진데이터처럼보이지만리소스해커에서확인했을때 Picture.Data 의처음부분에알파 벳이있었으므로 16 진수데이터라는것을알수있다. 이내용을 Winhex 를통해서저장을해보겠다. 두개의이미지파일은좋 0xF7(247) 바이트만큼차이가있었다. 01 로되어있는바이트는 1 로, 00 으로 되어있는바이트는 0 으로고쳐서다시확인해보면다음과같다. 11111000 11000011 11011100 11001111 11011000 10001101 11011001 10001010 11101011 11000100 11001110 10001010 11101101 11000011 11000100 11001111 11001111 11011000 11000011 11000100 11001101 11110101 11100011 11111001 11110101 11101000 11101011 11111001 11101111 11010100 10001011 첫번째비트가 1 이기때문에이것을다시 10 진수형태로변환한후 ASCII 테이블에서해당되는문자를찾으면이상한문자만보일뿐이다. 그리고만약위에보이는 16 진수가패스워드라면 31 글자를갖는패스워드라는것을유추해볼수있다. 그래서위 16 진수데이터를기반으로간단한프로그램을작성하여실행시켜다음과같은결과를얻을수있었다. - 171 -

대부분알수없는문자들이었고 River s And Gineering_IS_BASE~! 가패스워드라고예상할수있다. 그 럼이문자열을입력하면성공메시지를볼수있다. - 172 -

7-2. 악성코드분석 - shadowbot 악성코드분석 통제된환경에서분석 별도의네트워크구축 가상머신활용 분석툴준비 Filemon, Regmon, TDImon, TCPView, Wireshark Process Explorer Winalysis PEiD, LordPE Ollydbg, Windbg, IDA ImportREC, Revival Strings, BinText Student Notes 악성코드를분석하는것은쉽지않은일이다. 노력과시간이동시에필요하며인내력또한필요하다. 그리고악성코드를분석하는과정에서조그마한실수로인해서악성코드가네트워크를통해서유포되는일이발생할수도있다. 따라서악성코드를분석할때는항상통제된환경에서분석을해야한다. 가장좋은통제된환경은네트워크와연결되지않은머신에서악성코드를분석하는것이고좀더원활한분석을위해서는 VMware 나 VirtualPC 와같은가상머신에서악성코드를분석하는것이다. 본교재에서는 VMware 를이용하여통제된환경을구성하고 shadowbot 이라는악성코드를분석해보도록하겠다. shadowbot 에대한정보는다음 URL 에서확인하기바란다. http://kr.ahnlab.com/info/smart2u/virus_detail_7404.html http://kr.ahnlab.com/info/smart2u/virus_detail_7405.html - 173 -

통제된환경구성 먼저 shadowbot 을분석하기위한통제된환경을구성하는방법에대해서알아보도록하겠다. 본교재에서는 VMware 를이용할것이며다른가상머신이어도상관없다. 단, 악성코드가실행될머신은외부와연결이되지않아야한다. 여기서는분석자가사용하는운영체제를호스트운영체제, VMware 에설치된운영체제를게스트운영체제라부르겠다. VMware 의메뉴에서 VM Settings 에서 Ethernet 을 Host-only 로설정한다. 그리고게스트운영체제에서네트워크설정을다음과같이설정한다. VMware 에서 Host-only 모드는게스트운영체제가호스트운영체제하고만통신이가능한모드이다. 게 스트운영체제에서 shadowbot 이실행되고어떤패킷을보내게된다면이것을캡쳐하기위해게스트운 영체제의게이트웨이와 DNS 를호스트운영체제로설정하는것이다. - 174 -

모니터링및분석툴준비 shadowbot 실행시프로세스는어떠한것들이생기는지, 파일을어떤것을생성하고접근하는지, 네트워 크를통해어떤패킷을보내는지를확인하기위한모니터링툴이필요하다. 호스트운영체제준비툴 Wireshark 게스트운영체제준비툴 Filemon : 실시간으로현재사용되는파일들에대한정보를볼수있다. Regmon : 실시간으로현재레지스트리에대한정보를볼수있다. TDImon : 실시간으로현재모든 TCP/UDP 입출력상황을보여준다. TCPView : 현재네트워크연결정보를확인할수있다. Process Explorer : 현재실행중인프로세스정보와해당프로세스에관련된 DLL 이나핸들링정보제공 Winalysis : 현재시스템의상황을스냅샷을뜬다. BinText : 바이너리파일에서스트링을추출해준다. Ollydbg, PEiD, ImportREC 모니터링툴들은동적분석시사용된다. 악성코드를실행하고어떤행동들을하는지확인하기위함이다. 그리고호스트운영체제는게스트운영체제에서혹시외부로패킷을보내게되면이패킷을캡쳐하기 위해 Wireshark 나 Ethereal 같은패킷캡쳐프로그램을실행할것이다. 초기분석 초기분석단계에서는패킹여부를확인하고만약패킹이되어있다면언패킹을해야한다. 물론동적분 석시에는언패킹을하지않아도상관없지만정적분석시에는언패킹을한후에디버거를통해서분석 을해야한다. 패킹여부는 PEiD 를통해서확인할수있다. 또는 IDA 를이용하여확인할수있는데패킹된파일을 IDA 로열면경고창이뜨고코드의사이즈가작고데이터사이즈가크다는점을통해서패킹여부를확 인할수있다. 여기서는 PEiD 를이용하여패킹여부를확인해보겠다. - 175 -

PEiD 로 shadowbot 을확인하니 UPX 로패킹되어있는것을확인할수있다. 그럼언패킹을해보자. UPX 로패킹된파일은 upx 프로그램을이용하여패킹여부를확인할수도있고언패킹하는것이가능하다. 만약 UPX 가아닌다른패커로패킹되었다면그에맞는언패커로패킹을풀어야할것이고또는직접매뉴얼언패킹을해도상관없다. 언패킹을해서 shadowbot_unpack.exe 라는파일로저장을하였다. 동적분석을마친후정적분석시이 언패킹된파일을가지고분석을하도록할것이다. - 176 -

이번에는 BinText 를이용하여읽을수있는스트링추출한다. 여기서주의할것은패킹된파일을 BinText 에읽어와서스트링을추출하면모든스트링을다확인할수없다는것이다. 따라서언패킹했던파일을대상으로스트링을추출한다. Browse 를눌러파일을선택하고 Go 를누르면파일내에있는읽을수있는스트링을보여준다. 위에보이는것처럼함수들의이름을확인할수있는데이렇게임포트되는함수들의이름을보고이파일이실행이되면어떠한행동들을하는지짐작할수있다. 오른쪽하단에 Save 를눌러서텍스트파일로저장한후확인하도록하자. 확인결과다음과같은함수들과특이한문자열들을확인할수있었다. WriteProcessMemory // Load 할 DLL 이름 CreateToolhelp32Snapshot // snapshot 을찍는다. CreateRemoteThread // DLL Injection 4 번째파라미터가 LoadLibrary 인가? RegSetValueExA RegCreateKeyExA CoInitialize CloseClipboard OpenClipboard // 레지스트리등록 // 아마도재부팅후에도적용받게설정 // com 오브젝트와관련된것들 // 클립보드관련 // 열고닫고복사와관련된것들 - 177 -

CloseServiceHandle // 서비스닫기. 혹바이러스백신을닫을수도있다. EnumServicesStatusA OpenSCManagerA InternetReadFile InternetOpenUrlA InternetOpenA CreateMutexA Explorer.exe // 서비스관련 // 서비스관련무슨작업을기대 // 인터넷에서파일다운후실행 // http://~~, ftp://~~ // 반복실행방지 // 인젝션을할정상적인파일일수도 PRIVMSG %s :pstore %s %s:%s \n // private message 의약자 darkjester.xplosionirc.net // irc server 일수도있다. lol lol lol :shadowbot // 이름의유래가된스트링 PING, PONG, JOIN, PRIVMSG // IRCBot 일가능성이높다. rdshost.dll // 인젝션되는 dll 일까? \\photo album.zip // 압축파일은뭘까? 위내용을토대로 shadowbot 에대한가정은 IRCBot 으로 DLL Injection 을할것이다. DLL Injection 은아마도 explorer.exe 에시도를할것이고 COM Object 관련작업과서비스매니저를이용해서어떤행동들을할것이다. 그리고이때사용되는파일은 rdshost.dll 일것이다. 아직까지는이런가설만세울수있다. 좀더세부적인분석을통해서이가설을확인할것이다. 그리고초기분석의마지막으로실행파일인 shadowbot.exe 와 shadowbot_unpack.exe 에대한해쉬값을 계산한다. 이것은악성코드중실행이되면자기자신을변경하는악성코드들도있기때문이다. 해쉬값 을계산해놓은후실행하고후에이해쉬값을비교해보면변경이되었는지확인할수있다. md5sum.exe 프로그램을이용해서해쉬값을계산해서파일에저장해둔다. 비교할때는 c 옵션을사용 하면된다. 자, 이제초기분석이끝났고동적분석에들어가보도록하자. - 178 -

동적분석 Filemon 이나 Regmon 은실행직후갑자기많은정보들이출력되는것을볼수있는데원활한분석을위 해서는 Exclude Process 를이용해서필요없는프로세스를지워주는것이좋다. Filemon 을실행한다. 그리고분석에필요한프로세스는아직없으니전부 Exclude Process 로없앤다. Regmon 을실행한다. 그리고 Filemon 에서와동일하게모든프로세스를 Exclude Process 로없앤다. - 179 -

Winalysis 를실행하고 Snapshot 을누른다.. Start 버튼을눌러 File 부터시작해서 Service 나 System 등여러가지정보에대한스냅샷을찍는다. 여러가지정보들이스냅샷이찍혔다. 이제동적분석을위한준비가끝났다. shadowbot 을실행해서어떤행동들을하는지확인하면되는데 shadowbot 을실행하기전에 VMware 에서 Snapshot 을만들도록하자. VMware 에서의스냅샷은일종의백업의의미로후에어떤작업을하다가잘못될경우다시이전상태로되돌릴수있다. 분석을하는데걸리는시간을단축시킬수있는방법중하나이다. - 180 -

적당한이름과설명을적어둔다. 이것은사용자가어느상황에서스냅샷을만들었는지확인하기위함 이다. 마지막으로호스트운영체제에서패킷캡쳐프로그램을실행시킨다. 이것은악성코드 (shadowbot) 가외 부로패킷을보내는지확인하기위함이다. 여기서는 Wireshark 를사용하겠다. 캡쳐는처음에설정했던게스트운영체제의게이트웨이롤사용하면된다. 만약 shadowbot 이패킷을외 부로보낸다면분명게이트웨이를통해서보낼것이고여러분은그패킷을잡을수있을것이다. 그럼 Start 를눌러캡쳐를시작한다. 이제 shadowbot 을실행해보도록하자. 예상치못했던일이발생했다. 실행중이던모니터링프로그램들 이전부사라졌다. 이것은 shadowbot 이 explore.exe 의자식프로세스를죽이는것이다. 이렇게되면동적 인분석이어려울수있지만방법은있다. 조금전에만들어두었던 VMware 스냅샷으로돌아가도록하자. 모니터링툴을모두실행한상태에서 Process Explorer 에서 explorer.exe 프로세스를죽이고새롭게 explorer.exe 를실행하면모니터링툴들이 explorer.exe 의자식프로세스가아니기때문에정상적으로모니터링이가능하다. - 181 -

explorer.exe 프로세스를죽인후메뉴에서 File Run 을통해 explore.exe 를실행한다. explorer.exe 를실행하면다음과같이 procexp.exe 프로세스의자식프로세스로써 explorer.exe 가실행된다. 이제 shadowbot 이실행되더라도모니터링툴들이죽는일은없을것이다. 이상태에서 VMware 스냅샷을만들어두는것을추천한다. ( 악성코드분석작업은한치앞을내다볼수없기때문에항상최악의상황을고려하는것이좋다 ) 다시 shadowbot 을실행시켜보겠다. 실행시키자 shadowbot.exe 가프로세스익스플로러에보였다가사라 졌다. 먼저 Winalysis 의내용을확인해보도록하겠다. 동적분석준비를하면서스냅샷을떠놓았고 shadowbot 을실행한후에는 Test 를눌러스냅샷과현재를비교해야한다. - 182 -

그리고 Start 를누르면스냅샷과비교하여변경된부분이있는지확인한다. 확인결과다음에보이는것 과같이 File 부분과 Registry 부분이변경된것을볼수있다. Filemon 과 Regmon 의결과도살펴보자. 일단새롭게생성하거나변경한파일이나레지스트리가있는 지확인해보도록하겠다. Filemon 의결과중파일을생성하거나 (CREATE) 파일에쓰기 (WRITE) 하는부분을찾아보았다. CREATE C:\WINDOWS\photo album.zip SUCCESS Options: OverwriteIf Access: All WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 0 Length: 30 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 30 Length: 19 READ C:\malware\shadowbot.exe SUCCESS Offset: 0 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 49 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 1024 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 1073 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 2048 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 2097 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 3072 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 3121 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 4096 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 4145 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 5120 Length: 1024-183 -

WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 5169 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 6144 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 6193 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 7168 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 7217 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 8192 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 8241 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 9216 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 9265 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 10240 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 10289 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 11264 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 11313 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 12288 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 12337 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 13312 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 13361 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 14336 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 14385 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 15360 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 15409 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 16384 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 16433 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 17408 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 17457 Length: 1024 READ C:\malware\shadowbot.exe SUCCESS Offset: 18432 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 18481 Length: 512 READ C:\malware\shadowbot.exe END OF FILE Offset: 18944 Length: 1024 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 18993 Length: 46 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 19039 Length: 19 WRITE C:\WINDOWS\photo album.zip SUCCESS Offset: 19058 Length: 22 CLOSE C:\WINDOWS\photo album.zip SUCCESS CLOSE C:\malware\shadowbot.exe SUCCESS CREATE C:\WINDOWS\system32\rdshost.dll SUCCESS Options: OverwriteIf Access: All OPEN C:\WINDOWS\system32\SUCCESS Options: Open Directory Access: 00000000 WRITE C:\WINDOWS\system32\rdshost.dll SUCCESS Offset: 0 Length: 14848 CLOSE C:\WINDOWS\system32\rdshost.dll SUCCESS QUERY INFORMATION C:\WINDOWS\system32\rdshost.dll SUCCESS Attributes: A OPEN C:\WINDOWS\system32\rdshost.dll SUCCESS Options: Open Access: Execute QUERY INFORMATION C:\WINDOWS\system32\rdshost.dll SUCCESS Length: 14848 CLOSE C:\WINDOWS\system32\rdshost.dll SUCCESS QUERY INFORMATION C:\WINDOWS\system32\rdshost.dll SUCCESS Attributes: A OPEN C:\WINDOWS\system32\rdshost.dll SUCCESS Options: Open Access: Execute CLOSE C:\WINDOWS\system32\rdshost.dll SUCCESS C:\WINDOWS 에 photo album.zip 이라는파일을생성한후 shadowbot.exe 파일에서조금씩읽어와 photo album.zip 파일에쓰는것을볼수있다. 그리고 C:\WINDOWS\system32 에 rdshost.dll 이라는파일 을생성했다. 이파일들은초기분석당시스트링을추출했을때보였던파일명들이다. - 184 -

이번에는 Regmon 의결과에서 Filemon 에서찾아낸 rdshost.dll 과 photo album.zip 파일을중심으로찾아 보니다음과같은내용을찾았다. SetValue HKLM\Software\Microsoft\Windows\CurrentVersion\ ShellServiceObjectDelayLoad\rdshost SUCCESS rdshost.dll 을레지스트리에등록하고있다. Filemon 과 Regmon 의결과를통해 shadowbot.exe 가 C:\WINDOWS\photo album.zip 과 C:\WINDOWS\system32\rdshost.dll 을생성한다는것을알수있었다. 그리고호스트운영체제의 Wireshark 에서패킷이캡쳐되었다. darkjester.xplosionirc.net 도메인에대한질의를하고있는것을알수있다. DNS 쿼리를보낸다는것은해당서버에접속하기위해 IP 를받아오기위함이다. 따라서이 URL 로접속을하게되면좀더구체적인행동들을할수도있다. DNS 설치대신게스트운영체제의 hosts 파일에이도메인을추가해서다시실행시켜보겠다. 게스트운영체제의 hosts 파일을수정하고다시호스트운영체제에서 Wireshark 로패킷을캡쳐해보니 이번에는 8080 포트로접속하려고하는것을확인할수있다. - 185 -

8080 포트로접속을시도하였지만현재호스트운영체제는 8080 포트를열고있지않기때문에계속 RST 되는것을볼수있다. 그렇다면만약호스트운영체제가 8080 포트를열고연결을기다리면뭔가또다른행동들을볼수있을것이다. 8080 포트를열고연결을기다려야하기때문에 netcat 을사용하도록하겠다. nc 를통해포트를열고기다렸더니위와같은메시지를만날수있었다. IRC 에서사용하는명령어가보 인다. 패킷캡쳐를통한결론은 shadowbot 에감염이되면 darkjester.xplosionirc.net 이라는 IRC 서버에 test 라는방에접속하려고한다는것이다. 그렇다면실제 IRC 서버를구축해서실제로접속을하는지확인해보자. IRC 서버는 UnrealIRCD 를이용 하여설치한다. 설치후 unrealircd.conf 라는파일을 unrealircd 가설치된디렉토리에복사한다. 그리고 분석자도 IRC 에접속해보자. - 186 -

shadowbot 에감염된에이전트가 IRC 서버에접속되어있는것이보인다. shadowbot 에감염된호스트들은모두이 IRC 서버에접속을하게될것이고공격자는특정명령을실행하여감염된좀비컴퓨터들을제어하는것이가능할것이다. 앞서 Filemon 과 Regmon 을통해확인했던 rdshost.dll 는스스로동작하지않고어떤프로그램과함께실 행되는파일이다. 초기분석에서스트링추출후내렸던가설이 rdshost.dll 이라는파일이 explorer.exe 에 DLL Injection 을시도한다는것이었다. 이가설이점점명확해지고있다. dll 파일들이어떤프로세스와함께사용되고있는지 Process Explorer 에서검색하면찾을수있다. 메뉴 에서 Find Find Handle or DLL 을클릭한다. 그리고 rdshost.dll 을검색하면다음과같다. rdshost.dll 은 explorer.exe 프로세스와함께동작하고있다. 원래 explorer.exe 는 rdshost.dll 을임포트해서 실행하지않는다. 그런데현재 explorer.exe 가 rdshost.dll 을임포트해서사용하고있다. 즉, shadownot 이 rdshost.dll 파일을생성하고 explorer.exe 에 DLL Injection 을하고있다는의미이다. DLL Injection 은몇가지특정함수들을사용한다. CreateRemoteThread WriteProcessMemory VirtualAllocEx OpenProcess LoadLibraryA 이런함수들이사용이되면항상그런것은아니지만 DLL Injection 의가능성이있다는것을의미한다. 따라서정적분석을할때위함수들을주시하면서분석을해야할것이다. 그럼이제정적분석에들어가도록하겠다. - 187 -

정적분석 초기분석과동적분석의결과를토대로 shadowbot.exe 과 rdshost.dll 파일을디버거를통해분석해보도 록하자. 아마도분석을해보면 IRCBot 의기능들을알수있을것이다. 먼저 shadowbot.exe 부터분석해보자. shadowbot.exe 를분석할때패킹되어있던원본이아닌언패킹한 파일을가지고하는것이좋다. 앞에서확인했던 DLL Injection 에사용되는함수들과파일을생성하는 CreateFileA 함수정도에브레이크포인트를설정한다. F9 를눌러실행해보자. BP 에처음으로멈춘부분은 shadowbot.exe 파일을오픈하는 CreateFileA 함수이 다. - 188 -

그다음에멈추는 BP 는 C:\WINDOWS 에 photo album.zip 이라는파일을생성하는 CreateFileA 함수이다. 그리고 BP 를설정하지는않았지만 photo album.zip 파일을 CreateFileA 함수를통해서파일을만든후내 용을쓰는 (WriteFile) 부분이있는데다음과같다. - 189 -

그리고 rdshost.dll 파일을생성하는부분이다. 그리고바로아래쪽에 rdshost.dll 파일에내용을쓰는부분이있다. 계속 F9 로진행하면 OpenProcess 함수에서멈춘다. OpenProcess 를통해서앞에서생성한 rdshost.dll 파 일을 DLL Injection 하기위한타겟을확인한다. (3 번째인자 ) - 190 -

타겟의 ProcessId 가 0x258 이다. 10 진수로변환하면 600 이다. 확인해보니 explorer.exe 였다. Reverse Engineering 계속진행을하면가상메모리공간을할당하고 WriteProcessMemory 를통하여 DLL Injection 에이용할 DLL 파일을확인한다. 세번째인자가인젝션이되는 DLL 파일의이름을가지고있는주소이다. 0012FA84 를확인해보면다 음과같다. 인젝션되는파일은앞에서생성되었던 C:\WINDOWS\system32\rdshost.dll 이라는것을확인할수있다. 또는레지스터창에서 EAX 레지스터를확인하면같은내용을확인할수있다. 역시나우리가앞에서예 상했던일들이하나씩일어나고있다. - 191 -

F9 를눌러계속진행하면 CreateRemoteThread 를통해서 DLL Injection 공격이이루어지는것을볼수있 다. 이함수가호출이된후에 rdshost.dll 파일이 explorer.exe 에인젝션이된다. 그리고계속실행을시키면디버거가죽는다. 앞에서도봤던현상인데 explorer.exe 의자식프로세스로 써디버거를동작시켜서그렇다.( 디버거를 explorer.exe 의자식프로세스로동작시키지않았다면디버 거는아직그대로살아있을것이다!) 그럼 explorer.exe 에 rdshost.dll 파일이삽입되었는지확인해보도록하겠다. Process Explorer 에서 explorer.exe 를더블클릭하고 Threads 에서다음과같은정보를확인할수있다. 이렇게 rdshost.dll 이 DLL Injection 을통해서 explorer.exe 의쓰레드에삽입되는것을확인하였고이번에 는이 rdshost.dll 의실질적인행동인 IRCBot 으로써의기능을분석해보도록하겠다. - 192 -

IRCBot 기능분석 이제정적분석중 shadowbot.exe 에대한분석을마치고실제공격을담당하는 rdshost.dll 파일을분석해 보도록하겠다. DLL 의경우혼자서독립적으로실행되지않는파일이기때문에 rdshost.dll 이인젝션되 어있는 explorer.exe 파일을 attach 시켜서분석하도록하겠다. 디버거메뉴에서 File Attach 를하면어태치할파일을선택하는데 explorer.exe 를선택한다. 어태치후 CPU 창에는별다른내용이나오지않고상태도 Pause 로되어있다. 일단시작을시키고메뉴 에서 View Executable modules 을선택한다. ( 단축키 ALT+E) rdshost.dll 을찾아서마우스오른쪽을클릭한후 View names 를해서 ( 단축키 CTRL+N) rdshost 내에존재 하는함수들중문자열과관련이있는함수를찾아서 conditional log breakpoint 를설정한다. 함수이름에 서마우스오른쪽을클릭해서 Conditional log breakpoint on import 를선택한다. - 193 -

상단메뉴에서 View Log 를선택하거나 ALT+L 을눌러로그창을연다. 현재로그가남아잇는것을 볼수있을것이다. IRCBot 에의한로그만을확인하여보다정확한분석을위해전부클리어한다. IRC 창에서아무런글자나입력을한다. 그러면다음과같은로그를확인할수있다. 이것은 IRC 를통해서메시지를전달하면 IRCBot 이가지고있는명령어와비교를한다는것을의미한 다. 따라서 shadowbot 에서는 imstart, pstore, msnfuck, dlexec 와같은명령어들이사용된다는것을알수 있다. dlexec 명령은지정한 URL 에서파일을다운로드해서실행시키는명령이다. - 194 -

그리고 imstart 는 shadowbot 을전파하기위한명령인데 MSN 메신저를통해서 photo album.zip 이라는파 일을전파된다. photo album.zip 파일은 photo album2007.pif 파일을가지고있는데이파일은 shadowbot.exe 파일과동일 한크기를가지고있다. 디버거로열러보면 shadowbot.exe 와동일한파일이라는것을알수있다. 디버거를통해언패킹을하고 IAT 를복구했더니 shadowbot.exe 를실행했을때와동일한증상이나타났다. - 195 -