악성 코드 동적 분석기법

동적 분석은 해당 파일을 실행하면서, 분석을 진행하는 것을 말한다. 즉 프로그램 동작을 모니터링 하거나 디버깅 도구를 이용하여 실행 상태를 확인하는 작업이라 할 수 있다. 동적 분석을 진행할 때에는 반드시 분석 전용 머신에서 진행하는 것이 좋다. 그 이유는 해당 프로그램이 동작을 하면서 실제 악성코드가 사용자 머신을 감염시킬 수 있기 때문이다. 그리고 해당 바이러스 혹은 악성코드가 네트워크 감염을 진행한다면 분석하다가 오히려 당할 수 있다.

따라서 네트워크 환경을 격리하고, 시스템 감염을 막을 수 있는 격리된 머신에서 진행하기를 권장한다(이를 분석하기 용의한 머신이 가상화 머신이라 할 수 있다).

그리고 분석 전 미리 얘기하자면, 실제 프로그램 분석에 있어서는 PE Header만으로는 부족하다. 리버스 엔지니어링 기술을 사전에 이해하여야 실전 프로그램 분석을 원활히 진행할 수 있다.

여기서 분석할 프로그램은 Ntsec.exe으로, NT 보안 설정용으로 만든 간단한 C++ 프로그램이다. 따라서 초기 분석용도로 사용하기에 적합하다.

이 프로그램은 4가지 보안 설정 기능을 제공인데, 다음과 같다.

TCP SYN Protect

Root Share Disable

Login Banner Enable

Last Login User Disable

이 프로그램의 실행 구조를 우리가 배운 PE Header를 이용하여 역분석을 진행해보자.

앞서 다른 PE Header 값들에 대해서도 확인하여야 하지만, 그렇게 진행하기에는 지면을 많이 차지하므로, 개인의 몫으로 남기고, 여기서는 바로 해당 프로그램 분석을 위해 Ntsec.exe가 사용하는 API를 확인하기 위해, PEiD 확인 후, PEBrowse로 해당 파일을 열어, IAT를 확인해 보도록 하자.

그럼 이제 IAT를 확인해 보자. 아래와 같은 API가 IAT에 등록되어 있는 것을 알 수 있다.

Structure for IAT

ADVAPI32.DLL

(+0x0000) 0x00027396 (356, RegDeleteValueA)

(+0x0004) 0x00027366 (347, RegCloseKey)

(+0x0008) 0x00027374 (390, RegSetValueExA)

(+0x000C) 0x00027386 (370, RegOpenKeyExA)

KERNEL32.DLL

(+0x003C) 0x00027348 (282, GetLastError)

(+0x0040) 0x000276F0 (636, SetStdHandle)

(+0x0044) 0x000276DE (618, SetFilePointer)

(+0x0048) 0x000273D2 (202, GetCommandLineA)

…중략

SHELL32.DLL

(+0x014C) 0x000273B6 (114, ShellExecuteA)

[내용] PEBrowse로 확인한 Ntsec에서 사용하는 API

생각보다 많은 API가 사용되는 것을 확인할 수 있다.

이중 Kernel32.dll은 프로그램 실행에 기본적으로 필요한 함수들이 많다, 이를 전부 분석하기란 매우 큰 작업이 될 것이다. 이를 위해 분석할 API를 정해야 하는데, 여기서는 Advapi32.dll, Shell32.dll을 먼저 확인하도록 하겠다.

여기서는 아래 포인트에 브레이크 포인트를 설정할 예정이다.

ADVAPI32.DLL

(+0x0000) 0x00027396 (356, RegDeleteValueA)

(+0x0004) 0x00027366 (347, RegCloseKey)

(+0x0008) 0x00027374 (390, RegSetValueExA)

(+0x000C) 0x00027386 (370, RegOpenKeyExA)

SHELL32.DLL

(+0x014C) 0x000273B6 (114, ShellExecuteA)

위 API들을 MSDN에서 확인 하면 해당 API를 이해하는 데 큰 도움이 된다. 아래는 MSDN 정보를 확인한 내용이다.

(+0x0000) 0x00027396 (356, RegDeleteValueA)

Removes a named value from the specified registry key. Note that value names are not case sensitive.

LONG WINAPI RegDeleteValue(

__in HKEY hKey,

__in_opt LPCTSTR lpValueName

);

(+0x0008) 0x00027374 (390, RegSetValueExA)

Sets the data and type of a specified value under a registry key.

LONG WINAPI RegSetValueEx(

__in HKEY hKey,

__in_opt LPCTSTR lpValueName,

__reserved DWORD Reserved,

__in DWORD dwType,

__in_opt const BYTE *lpData,

__in DWORD cbData

);

(+0x014C) 0x000273B6 (114, ShellExecuteA)

Performs an operation on a specified file.

HINSTANCE ShellExecute(

__in_opt HWND hwnd,

__in_opt LPCTSTR lpOperation,

__in LPCTSTR lpFile,

__in_opt LPCTSTR lpParameters,

__in_opt LPCTSTR lpDirectory,

__in INT nShowCmd

);

RegDeleteValueA RegSetValueExA ShellExecuteA API 함수

물론 이름만으로도 용도를 확인이 가늠할 정도이다.

RegDeleteValueA는 Registry의 값을 지우는 것이고, RegSetValueExA은 Registry의 값을 설정하는 API 함 이다. 그리고 ShellExecuteA는 CLI에서처럼 특정 파일을 실행할 수 있는 API이다.

따라서 Ntsec.exe은 레지스트리의 값을 바꾸고, 특정 명령을 실행하는 기능이 들어있다고 추측할 수 있다. 이렇게 추측으로 분석을 완료할 수도 있지만, 확실히 어떤 값을 쓰고 실행하는지 확인할 필요가 있다. 그리고 이렇게 확인한 정보는 디버깅 시 등대와 같은 역할을 해줄 것이다.

이제 본격적으로 파일을 분석을 해보자, IDA, Ollydbg 등을 통해 동적 분석을 진행하고 Procmon등을 통한 실행상태를 확인하면서 분석을 진행하면 된다.

가장 먼저 진행할 부분은 바로 문자열 확인이다. 해당 프로그램이 어떤 문자열을 가지고 있는지 확인하여 동작 방식이나. 주요 처리 구분을 이해할 수 있다.

어셈블리창에서 마우스 오른쪽 버튼을 눌려 Search for à All referenced text strings 을 통해 해당 프로그램에서 사용되는 문자열을 확인하자.

[그림] Ntsec가 사용하는 모든 문자열을 확인할 수 있다

문자열을 통해, -set과 -unset이라는 문구 이외에도 프로그램에서 사용하는 모든 문자열을 확인할 수 있는데, 명령 및 동작방식들을 미리 추측할 수 있다. 이후 Ollydbg에서 프로그램을 Step over(F8키)를 눌러 진행하다 보면, 00402424 CALL ntsec.00401028가 실행될 때, 사전에 설정된 프로그램 도움말이 CLI 화면에 나타나며 –set, -unset의 내용과 프로그램의 기능 동작 코드들이 00402424에 위치함을 확인할 수 있다. 이제 프로그램을 다시 시작(Ctrl+F2키)하고 00402424 CALL ntsec.00401028에 브레이크 포인트(F2키)를 설정한 후 프로그램을 실행(F9키)하면 우리가 설정한 브레이크 포인트에서 프로그램이 멈추게 된다. 그럼 여기서, Step into(F7키)를 통해 프로시저 안으로 따라 들어가 보자.

[그림] 프로그램의 기능들이 동작하는 위치를 알 수 있다

Step into(F7키)로 들어가 보면 JMP 구문 실행 후 프로그램 메인 처리 구문을 만날 수 있는데 초기 명령을 함께 분석해 보도록 하겠다.

00401CA0 55 PUSH EBP

00401CA1 8BEC MOV EBP,ESP

00401CA3 83EC 44 SUB ESP,44

00401CA6 53 PUSH EBX

00401CA7 56 PUSH ESI

00401CA8 57 PUSH EDI

00401CA9 8D7D BC LEA EDI,DWORD PTR SS:[EBP-44]

00401CAC B9 11000000 MOV ECX,11

00401CB1 B8 CCCCCCCC MOV EAX,CCCCCCCC

00401CB6 F3:AB REP STOS DWORD PTR ES:[EDI]

// 3이상 되지 않으면 프로그램은 JMP 구문을 통해 종료 지점으로 이동된다. 기존에 입력 받은 EBP +8 값과 비교 한다.

00401CB8 837D 08 03 CMP DWORD PTR SS:[EBP+8],3

// 3이상인 경우 00401CD5로 이동한다.

00401CBC 7D 17 JGE SHORT ntsec.00401CD5

00401CBE 68 6C2A4200 PUSH ntsec.00422A6C ; ASCII “Windysoft NT Default Security Setup Tool\n Usage : ntsec.exe <-set|-unset> <1 2 3 4>\nThis program can be set to below list\n 1. Syn Attack Protect\n 2. Hidden Share Delete\n 3. Last Login User Protect\n 4. Display Business Use Notice\n”

// 위 ASCII 코드의 도스 명령프롬프트 창에 출력하라는 명령이다.

00401CC3 E8 F8050000 CALL ntsec.004022C0

00401CC8 83C4 04 ADD ESP,4

00401CCB B8 01000000 MOV EAX,1

00401CD0 E9 BF010000 JMP ntsec.00401E94 ß 프로그램 종료

// -set을 스택에 저장한다.

00401CD5 68 642A4200 PUSH ntsec.00422A64 ; ASCII “-set”

00401CDA 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]

00401CDD 8B48 04 MOV ECX,DWORD PTR DS:[EAX+4]

00401CE0 51 PUSH ECX

// 코드를 Step into로 확인해 보면, -set이 있는지 비교문이 실행되게 된다.

00401CE1 E8 4A050000 CALL ntsec.00402230

00401CE6 83C4 08 ADD ESP,8

00401CE9 85C0 TEST EAX,EAX

00401CEB 0F85 C4000000 JNZ ntsec.00401DB5 ß -unset 실행문으로 이동한다.

…중략

[내용] 실행 초기 분석

위와 같은 방식으로 각 코드를 분석해보는 방법도 있지만, 상당히 많은 시간이 걸리게 된다.

따라서 여기서는 –set 명령을 이용해 프로그램이 동작된다는 것을 이해했으므로, Ollydbg 실행시 추가 인자값으로 “-set 1 2 3 4″를 주어 프로그램을 실행하고 앞서 확인한 주요 함수인 여기서는 많은 API 중 RegSetValueExA, ShellExecuteA에 브레이크 포인트(F2키)를 걸어놓고, Ollydbg로 프로그램을 구동해 보도록 하자.

[그림] Ollydbg로 RegSetvalueExA, ShellExecuteA에 브레이크 포인트 설정

위 그림과 같이 설정 후 프로그램을 실행하면 아래와 같은 내용에서 브레이크 포인트가 걸리며, 실행 내용을 확인할 수 있다.

// SynAttackProtect 레지스트리를 설정함을 알 수 있다.

004010BB . 6A 04 PUSH 4                ; /BufSize = 4

004010BD . 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]        ; |

004010C0 . 51 PUSH ECX                ; |Buffer

004010C1 . 6A 04 PUSH 4                ; |ValueType = REG_DWORD

004010C3 . 6A 00 PUSH 0                ; |Reserved = 0

004010C5 . 68 1C214200 PUSH 42211C            ; |ValueName = “SynAttackProtect”

004010CA . 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-8]        ; |

004010CD . 52 PUSH EDX                ; |hKey

004010CE . FF15 D4714200 CALL DWORD PTR DS:[4271D4]    ; \RegSetValueExA

// AutoShareServer 레지스트리를 0으로 설정함을 알 수 있다.

0040120F . 6A 04 PUSH 4                ; /BufSize = 4

00401211 . 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4]        ; |

00401214 . 51 PUSH ECX                ; |Buffer

00401215 . 6A 04 PUSH 4                ; |ValueType = REG_DWORD

00401217 . 6A 00 PUSH 0                ; |Reserved = 0

00401219 . 68 C4224200 PUSH 4222C4            ; |ValueName = “AutoShareServer”

0040121E . 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-8]        ; |

00401221 . 52 PUSH EDX                ; |hKey

00401222 . FF15 D4714200 CALL DWORD PTR DS:[4271D4]    ; \RegSetValueExA

// AutoShareWrk 레지스트리를 0으로 설정함을 알 수 있다.

00401234 . 6A 04 PUSH 4                ; /BufSize = 4

00401236 . 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]        ; |

00401239 . 50 PUSH EAX                ; |Buffer

0040123A . 6A 04 PUSH 4                ; |ValueType = REG_DWORD

0040123C . 6A 00 PUSH 0                ; |Reserved = 0

0040123E . 68 B4224200 PUSH 4222B4            ; |ValueName = “AutoShareWrk”

00401243 . 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8]        ; |

00401246 . 51 PUSH ECX                ; |hKey

00401247 . FF15 D4714200 CALL DWORD PTR DS:[4271D4]    ; \RegSetValueExA

// 여기서부터는 실행부분으로, Parameters, FileName과 Operation으로 어떤 명령을 실행하는지 확인이 가능하다. .

00401263 . 6A 05 PUSH 5                ; /IsShown = 5

00401265 . 6A 00 PUSH 0                ; |DefDir = NULL

00401267 . 68 98224200 PUSH 422298            ; |Parameters = “share Admin$ /delete”

0040126C . 68 94224200 PUSH 422294            ; |FileName = “net”

00401271 . 68 8C224200 PUSH 42228C            ; |Operation = “open”

00401276 . 6A 00 PUSH 0                ; |hWnd = NULL

00401278 . FF15 18734200 CALL DWORD PTR DS:[427318]        ; \ShellExecuteA

…중략

[내용] RegSetvalueExA, ShellExecuteA의 진행 내용

이제 실제 실행하는 코드들을 확인하여, 프로그램이 어느 레지스트리를 쓰는지 어떤 실행 명령을 사용하는지 위 내용과 같이 파악하였다. 앞서 설명한 Ntsec의 동작 내용과 크게 다르지 않고, 위 내용에 대해서는 쉽게 이해할 수 있을 거라 생각되어, 추가로 설명하지는 않겠다.

여기에 각 API별 정확한 분석을 하고자 한다면, 앞서 확인한 MSDN을 통해 확인한 API 함수에 기재된 멤버들과, 위 내용에 나타난 각 값들을 비교하면 이해하는 데 큰 도움이 될 것 이다.

파일을 무작정 전체를 분석한다는 건 정말 많은 시간이 필요로 한다. 따라서 분석에 필요한 주요 API를 사전에 알아두어, 분석을 진행하는 것이 빠르게 핵심 내용을 분석할 수 있다.

그럼 분석 시 자주 접하게 되는 API에 대해 알아보도록 하자.

MSDN의 각 API별 파라미터도 표시할까 했으나, 지면 낭비인 것 같아 API만 표시하였다. 하지만 API의 파라미터들의 의미를 파악하는 건 역분석에서 중요한 작업이므로, 각 API들에 대해 MSDN을 통해 확인하는 습관을 기르기 바란다(http://msdn.microsoft.com).

DialogBox API

대화상자를 표시하거나 정보를 가져오는데 사용되는 API들로서 다음과 같은 API가 많이 사용된다.

대화상자는 사용자로 선택을 요구하는 상호 커뮤니케이션을 진행하기 때문에 이렇게 이름이 지어진 것 같다.

[그림] 대화상자는 보통 사용자의 선택을 요구한다

DialogBoxParamA // 대화상자를 생성한다.

GetDlgItem // 대화상자의 접근 주소를 얻는다.

GetDlgItemInt // 대화상자에 변수를 입력한다.

GetDlgItemTextA // 대화상자에 문자를 입력한다.

GetWindowsTextA // 대화상자의 제목을 얻는다.

Window API

윈도우는 운영체제의 모든 창틀을 애기한다. 이를 생성하거나 변경하는 API는 다음과 같다.

대화상자와 차이점이라면 내부에 따른 개체가 주어지는 메인 화면(부모 창)을 의미한다.

[그림] 창틀이 있는 개체들 전부 윈도우이다

CreateWindowEx // 윈도우를 생성한다.

ShowWindow // 윈도우를 표시한다. 이미 생성된 상황을 의미

UpdateWindow // 윈도우의 내용을 갱신한다.

MessageBox API

윈도우에서 알림과 같은 메시지를 표시하는데 사용되는 API들이다. 일반적으로 오류나 알림이 이에 속한다.

[그림] 알림용 메시지를 출력하는 용도로 사용된다

MessageBeep // 알림용도의 특정소리를 출력한다.

MessageBoxA // 메시지 박스를 출력한다.

MessageBoxExA // 메시지 박스를 출력한다. 확장형

SendMessageA // 다른 윈도우로 메시지를 전송한다.

SendDlgItemMessageA // 대화상자에 메시지를 전송한다.

SetDlgItemTextA // 대화상자 문자를 설정한다.

SetWindowTextA // 윈도우 문자를 설정한다.

Registry API

윈도우 레지스트리에 접근하여 삭제하거나, 생성 등을 할 수 있는 API들이다. 아래 API를 통해서 레지스트리에 접근하는 요청들을 확인 할 수 있다.

RegCreateKeyA // 레지스트리 키 생성

RegDeleteKeyA // 레지스트리 키 삭제

RegQueryValueExA // 레지스트리 키의 값 확인

RegCloseKeyA // 레지스트리 키 닫기, 레지스트리 키를 생성하거나 삭제, 값 확인을 하였다면, 닫기를 진행하여, 현재 잡고 있는 해당 레지스트리 핸들를 종료해줘야 한다.

RegOpenKeyA // 레지스트리 키 열기, 레지스트리 키를 생성하거나 삭제, 값 확인하고자 한다면 먼저 해당 위치의 레지스트리 핸들을 열어주어야 한다.

File API

파일을 읽거나 생성, 수정하는 작업은 진행하는 API들이다. 아래 API를 통해 파일을 생성하거나 읽어드리는 요청들을 확인할 수 있다.

ReadFile // 파일 읽기

WriteFile // 파일 쓰기

CreateFileA // 파일 생성

Data API

파일과 다른 그 외 데이터를 읽어 들이는 작업을 진행하는 API들이다. 아래 API를 통해서 데이터 비교 작업과 데이터 처리를 요청하는 내용을 확인할 수 있다.

lstrcmpA // 입력한 2개의 문자열을 1바이트씩 비교한다.

MulitByteToWideChar // 유니코드를 ANSI문자열로 변환한다.

WideCharToMultiByte // ANSI문자열은 유니코드로 변환한다.

wsprintfA // 문자열을 출력한다.

Time(Date) API

시간과 날짜를 설정하거나 확인하는 작업을 진행하는 API들이다. 시간에 따라 동작하는 프로세스이거나, 시간 정보를 확인하는 동작 내용을 확인할 수 있다.

GetFileTime // 파일의 시간 정보를 가져온다.

GetLocalTime // 현재 시스템의 지역 시간을 가져온다.

GetSystemTime // 현재 시스템의 시간을 가져온다. UTC 기준

GetSystemTimeAsFileTime // 날짜 및 시간을 가져온다.

SetTimer // 타이머를 지정한다.

SystemTimeToFileTime // 날짜 및 시간을 설정한다.

위 이외의 API들에 대해서도 사전에 알아둔다면, 파일 분석을 진행할 때 주요 분기에 브레이크 포인트를 걸어서 분석할 수 있어, 많은 시간을 단축할 수 있다.

그럼 이제 IDA를 통해 다른 코드들에 대해서도 확인해 보도록 하자, IDA를 통한 분석은 Ollydbg 이후 꼭 진행이 필요한 것은 아니다.

여기서는 소개를 위해 이렇게 진행하는 것이고 실제는 IDA 분석만 하여도 되고, Ollydbg로만 분석하여도 충분하다는 점을 알려드린다. 그럼 IDA를 통해 Ntsec.exe를 열어서, 우리가 분석하지 않은 부분을 확인해 보자.

[그림] IDA로 확인한 RegDeleteValueA의 흐름도

그림으로 잘 보이지는 않지만, 위 내용은 RegDeleteValueA를 IDA로 확인한 내용인데, 그림과 같이 IDA의 큰 장점은 전체적인 프로그램의 내용을 흐름도 처럼 확인이 가능하다는 것이다. 이를 통해 프로그램의 처리 흐름을 쉽게 이해할 수 있을 것이다.

이제 마지막으로 ProcMon등을 통해 시스템의 변화를 감시하여 기록하면, 실제적인 환경 변화에 대해 분석을 할 수 있다.

[그림] Process monitor를 통해 확인한 Registry 모니터링 값

위 그림과 같이 Ntsec.exe 프로그램이 접근하고, 변경한 Registry 내용을 모니터링 함으로써, 실제 디버깅 한 내용대로 작동됨을 검증할 수 있고, 코드상 확인하지 못한 변화되는 내용을 확인할 수 있게 된다.

추가로 네트워크 동작 내용을 자세히 알고 싶다면, 와이어샤크(Wire-shark)등을 추가로 실행하여 네트워크 패킷을 캡처하는 것도 좋은 방법이라 할 수 있겠다.

Facebook Comments

Leave A Reply

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.