악성코드 분석을 하는도중 커널디버깅을 해야 할일이 생겨서 windbg를 리모트로 가상 os와 붙여야 된다고 하더라구요.. 그래서 검색신공으로 마구마구 검색하여 해봤으나 대부분 안된다...ㅠ

그래서 결국 삽질끝에 해결은 했습니다.

 

 

 

windbg옵션에서 symbolpath를 설정해주는 옵션이 있습니다.

첫번째 옵션에 있으니 찾기 쉬우실거에요 ..

srv*.*http://msdl.microsoft.com/download/symbols 로 세팅해줍니다.

이 심볼패스를 설정해주는 이유는 커널디버깅할때 주소값들에대한 의미를 부여하는것입니다.

즉 심볼패스를 설정하지 않으면 특정 주소값의 의미를 사용자가 파악하기는 굉장히 불가능? 에 가깝습니다.

그러나 이 심볼패스를 지정해주면 특정 주소값이 어떤걸 의미하는지 함수인지 SSDT테이블인지 알려줍니다.

 

 

 

그리고 vmware에서 시리얼 포트를 추가해줍니다.

 

 

 

여기서 시리얼포트이름을 설절해줍니다.

나중에 windbg에서도 시리얼포트 이름을 설정해주는데 그거랑 같이 설정해 주면 됩니다.

이름은 자기 맘대로 만드시면되요.

저는 그냥 알기쉽게 com1포트를 사용하면 com_1, com_2이런식으로 사용합니다.

아차! 이거 세팅할때 가상os를 종료하고 진행하셔야합니다.

 

 

그리고 I/O mode에서 Yield CPU on poll 을 체크합니다.

 

 

그리고 vmware의 가상 os를 부팅합니다.

이것때메 삽질했었는데 이부분에대해 언급하는 자료가 없더라구요 ㅠㅠ

위에서 시리얼포트를 추가했을때 위와같이 관리도구를 보면 추가된 시리얼 포트가 있습니다.

저는 예전에 한번 추가한적이있어서 2개이고 보통은 1개일겁니다.

여기서 추가된 포트이름대로 뒤의 boot.ini를 세팅해 주시면 됩니다.

이것때메 거의 1시간 삽질했네요

 

 

실행 > msconfig

위와같이 세팅을 해줍니다.

 

 

그리고 가상 OS를 종료합니다.

 

 

그리고나서 Windbg의 시리얼 COM을 세팅해줍니다.

port는 vmware에서 세팅해준 이름 그대로 세팅해주시면 됩니다.

그리고 체크체크 하고 os를 부팅해주면...

 

 

 다음과 같이 빵~ 하고 windbg가 돌아 갑니다.

 

 

 

여기서 ctrl+break 버튼 이 브레이크버튼 키보드에서 정말 안쓰는 키인데 이거 찾을려고 또 키보드를 유심히 관찰했다는...;;;

아무튼 구지 저렇게 단축키 안써도 메뉴에 있으니까요 잘살펴보심 되구요

이거 누르면 윈도우가 디버깅 모드로 진입하게 되구요(멈춥니다.)

g를 누르면 윈도우가 running상태로 진입하게 됩니다.

 

posted by 미스터리 DKL

remotedebugging

실행이 안됬던 문제점
지금 버추어박스에 시스템에서 장치관리자에보면 세팅된 com포트가 com2로 세팅되어있으므로 디버기의 가상머신의 msconfig -> boot.ini -> 고급옵션 에서 com2로 세팅 이후
디버거의 머신에서 bcdedit /debug on -> becedit /dbgsettings serial debugpor:2 로 세팅
그리고 재부팅하면 디버기와 디버거 가 붙어버린다.

 

 

작성예정

posted by 미스터리 DKL

자주 쓰는 일반 명령어
r : 레지스터 상태 보기 / 변경
d : 메모리 내용 보기
e : 메모리 내용 변경
bp : 브레이크 포인트 걸기
p, t : 브레이크 포인트가 걸린 다음 명령들을 한 줄씩 수행
대표적 일반 명령어
A(Assemble), U(Unassemble)
BL(Breakpoint List), BC(Breakpoint Clear)
BD(Breakpoint Disable), BE(Breakpoint Enable)
BA(Break on Access)
BP, BU(Set Breakpoint)
D, DA, DB, DW, DD(Display Memory)
Dds(Display Words and Symbols)
DL(Display Linked List) LIST_ENTRY or SINGLE_LIST_ ...
DS, Ds(Display String)
DT(Display Type)
DV(Display Local Variable)
K, KB, KD, KP, KV (Display Stack Backtrace

E, EA, EB, Ed, EW, EU (Enter Values)
S(Search Memory)
R(Register)
LD(Load Symbol)
LM(List Loaded Symbols)
LN(List Nearest Symbols)
G(Go), P(Step), PC(Step to Next Call)
T(Trace), TB(Trace to Next Branch), TC(Trace to Next Call)
WT(Trace and Watch Data)


2. 메타 명령 (대부분 디버거 자체를 제어) (.으로 시작)

자주 쓰는 명령어
.attach : WinDbg를 특정 프로세스에 붙여서 그 프로세스를 디버깅할 때 사용
.cls : WinDbg 명령 창에 출력된 모든 내용을 지우기
.sympath : 현재 WinDbg에 설정된 심볼 경로 확인 / 설정
.reload : 심볼 경로를 설정한 후에 WinDbg가 심볼을 다시 로드하게 하기
대표적 메타 명령어
.bugcheck (Display Bug Check Data)
.cls (Clear Screen)
.ofilter (Filter Target Output)
.enable_unicode (Enable Unicode Display)
.crash (Force System Crash)
.dump (Create Dump File)
.reboot (Reboot Target Computer)
.cxr (Display Context Record)
.exr (Display Exception Record)
.ecxr (Display Exception Context Record)
.trap (Display Trap Frame)
.exepath (Set Executable Path)
.srcpath (Set Source Path)
.sympath (Set Symbol Store Path)
.symfix (Set Symbol Store Path)
.reload (Reload Module)
.context (Set User-Mode Address Context)
.process (Set Process Context)
.thread (Set Register Context)
.tss (Display Task State Segment)
.load (Load Extension DLL)


3. 확장 명령
온갖 종류의 명령을 많이 포함하고 있지만 굳이 성격을 구분하자면 윈도우 운영체제에 종속적인 정보를 다루는 명령들이 많다. 특정 정보를 자세히 보여주거나 해석해서 보여줘 디버깅을 편리하게 도와주는 기능을 가지는 것들도 있다.
확장 명령은 !로 시작하는 이름 형식을 가지고 있고 사용자가 직접 만들 수 있다. (자세한 것은 65page or 6장)

<DIV style="BORDER-BOTTOM: #9fd331 1px dashed; BORDER-LEFT: #9fd331 1px dashed; PADDING-BOTTOM: 10px; BACKGROUND-COLOR: #e7fdb5; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BORDER-TOP: #9fd331 1px dashed; BORDER-RIGHT: #9fd331 1px dashed; PADDING-TOP: 10px" class=txc-textbox>자주 쓰는 확장 명령어
!process : 현재 프로세스의 정보를 보거나 현재 윈도우 운영체제에서 실행 중인 모든 프로세스의 정보를 보여주는 명령
!gle : GetLastError의 약자로 Win32 API인 GetLastError()와 같이 마지막으로 설정된 Win32 Code를 보여주는 명령이다.
!error : Win32 Error Code나 NTSTATUS Code를 해석해 문자열로 보여주는 명령이다.
!analyze : 덤프 파일을 열었을 때 자동으로 분석을 하는 데 사용하는 명령이다.
대표적인 확장 명령어
!analyze : displays information about the current bug check
!cpuid : displays information about the processors on the system
!error : decodes and displays information about an error value
!gle : displays the last error value for the current thread
!obja : displays the attributes of an object in the object manager
!peb : displays a formatted view of the information in the process environment block(PEB)
!teb : displays a formatted view of the information in the thread environment block (TEB)
!token : displays a formatted view of a security token object
!process : displays information about the specified process or all
!stacks : displays information about the current kernel stacks
!thread : displays summary information about a thread
!zombies : displays all dead ("zombie") processes or threads
!drivers : displays a list of all drivers loaded
!devnode : displays information about a node in the device tree
!devobj : displays detailed information about a DEVICE_OBJECT
!devstack : displays a formatted view of the device stack
!drvobj : displays detailed information about a DRIVER_OBJECT


유저모드 명령줄
0:000
0은 프로세스 번호이고 000은 스레드 번호이다.
커널모드 명령줄

프로세스나 스레드 정보 표시가 없으므로 현재 어떤 프로세스의 어떤 스레드인지 확인하려면 !process, !thread 명령으로 직접 확인해봐야 한다.
멀티 프로세서는 현재 프로세서 번호를 포함해 아래처럼 나온다.

 

하이퍼스레딩을 지원하는 프로세서 하나는 두 개의 프로세서가 존재하는 것으로 나온다.
듀얼 코어 프로세서 하나는 2개의 프로세서로 나온다.
하이퍼스레딩을 지원하면서 듀얼 프로세서이면 4개로 나온다. 이러한 프로세서를 두 개 사용하면 8개의 프로세서로 나타난다.

 

출처

문헌 : WinDbg로 쉽게 배우는&nbsp;Windows Debugging
http://tiktoor.tistory.com/25

 

posted by 미스터리 DKL
srv*.*http://msdl.microsoft.com/download/symbols

 

posted by 미스터리 DKL

 

[그림.1]

 

분석전에 SSDT 테이블을 후킹을 하는 악성코드로 판단을 했었는데 다시 상세히 분석하니 이녀석은 SSDT 후킹을 하는 악성코드가 아니었습니다.

IceSword툴을 이용해서 SSDT 테이블에서 변경이된 Native Function이 있는지 확인결과 이상이 없었습니다.

 

 

[그림.2]

 

그런데 Process를 확인 결과 분명 PID 3720을 사용하고있는 svchost.exe는 태스크매니저 또는 ProcessExplorer로 확인했을때 없었는데 IceSword에서는 실행중인 프로세스로 확인이 되네요.

 

[그림.3]

 

올리디버거로 그림.3과 같이 0x151139F0와 0x15114D10함수로 진입하여 분석을 시도합니다.

위의 함수들은 Kernel모드로 진입하여 DKOM을 작업하는 함수들입니다.

DKOM은 간략히 설명하면 프로세스와 프로세스끼리 서로 링크드리스트를 이용하여 서로 포인터 메모리주소로 연결되어있는데 이 각각의 연결링크를 끊어 프로세스를 은닉하는 기법입니다.

 

[그림.4]

 

0x15114D10함수 내부로 진입하고 0x15114C80 함수로 진입후 실행하면 BlueScreen이 발생하여 더 이상 분석이 불가능합니다.

그 이유는 ollydbg는 UserMode 디버거이기 때문에 커널모드로 분석이 불가능 하기 때문입니다.

UserMode디버거는 커널로 진입하여 분석을 할 수 없기 때문에 더 이상 ollydbg로는 해당 악성코드 분석이 불가능했습니다.

그래서 해당 악성코드의 특정 코드 영역만 커널모드 디버거인 windbg를 이용하여 분석을 이어서 진행했습니다.

 

[그림.5]

 

우선 분석하기전에 현재 실행되고 있는 svchost.exe의 PID를 확인하면 3152 이고 이것을 16진수로 변경하면 C50이 됩니다.

16진수로 변경한 이유는 windbg에서 PID는 16진수로 보여주기 때문에 PID를 16진수로 변경 후 확인 해야합니다.

[그림.6]

 

현재 DKOM 코드 영역을 분석하기위해 vmware의 가상OS Windows XP와 windbg를 시리얼포트를 사용하여 리모트로 연결한 상태입니다.

즉 제가 사용하고 있는 로컬컴퓨터 Windows7 64비트에 windbg를 세팅하고, vmware의 가상OS의 Windows XP와 서로 연결해 놓은 상태입니다.

이와 같은 방법은 검색하면 쉽게 알 수 있으니 찾아보시면 금방 확인이 가능합니다.

분석을 위한 세팅이 완료된 상태이기 때문에 분석을 시작하겠습니다.

우선 위의 PrecessExplorer에서 확인했던 svchost.exe의 PID를 windbg에서 확인해 봅니다.

PID가 0c50인 svchost.exe가 존재하는걸로 확인이 됬습니다.

 

[그림.7]

 

그럼 이제 커널모드에서 특정 프로세스를 attach하여 분석을 해야합니다.

즉 커널 모드에서 유저모드디버깅이 가능하게 할 수 있는데 그림.7과같이 명령어를 입력하여 svchost.exe의 EPROCESS주소 0x89C182F8로 attach하면 svchost.exe를 분석 할 수 있게 됩니다.

정상적으로 붙었는지 확인을 !process 명령어로 확인 결과 0x89C182F8 svchost.exe로 세팅된 것을 확인 할 수 있습니다.

 

[그림.8]

 

그리고 svchost.exe는 실행시 은닉이되기 때문에 분석 편의상 제가 svchost.exe의 EP에 자기자신으로 점프하도록 바이너리를 수정해놨었는데요 이렇게되면 EP 0x151130F0 자기자신으로 점프하여 더이상 코드실행이 되지 않게 됩니다.

이상태에서 Attatch하여 분석하면 조금 편리하기 때문에 바이너리를 수정했었습니다.

 

[그림.9]

 

이 수정된 바이너리를 원래의 코드로 다시 수정을 해주고 해당 EP에 브레이크 포인트를 걸고 브레이포 인트까지 실행하여 분석을 이어갑니다.

 

 

[그림.10]

 

여기서 잠시 svchost.exe의 EPROCESS를 확인해봅니다.

DKOM 즉 프로세스와 프로세스간의 연결된 링크드리스트를 변경하려면 EPROCESS의 0x88의 ActiveProcessLinks ListEntry의 주소를 조작해야합니다.

그러나 주의해야 할 점이 현재 그림.10의 보이는 0x89B4DE54-0x89BA670C주소자체를 조작하는 것이아니라 이 주소가 가르키고 있는 주소를 변경해야 합니다.

즉 0x89B4DE54-0x89BA670C 주소는 포인터 역할을 한다고 보시면 됩니다.

 

[그림.11]

 

DKOM영역의 코드내부로 진입하면 sysenter 명령으로 커널로 진입하는 것을 확인 할 수있습니다.

 

[그림.12]

 

그림.12의 코드를 보면 0x89C182F8 svchost.exe의 EPROCESS의 주소를 얻어오고 이 주소값에다가 +0x88 을 더하여 0x89C18380 주소를 얻게 됩니다.

이 주소를 EPROCESS 구조체에서 확인해보면 ActiveProcessLinks ListEntry 주소인것을 확인 할 수 있습니다.

즉 링크드리스트를 조작하기 위한 첫번째 작업으로 ActiveProcessLinks ListEntry의 주소를 얻습니다.

[그림.13]

 

그리고 여기서 0x89B4DE28을 Flink(ForwardLink)라고 하고 0x89BA66E0를 Blink(BackLink)라고 하겠습니다.

그림.13의 0x151139CE코드를 보면 EAX가 ActiveProcessLinks ListEntry 의 Blink 0x89BA66E0를 가르키고 있고 이 가르키는 곳에 Flink 0x89B4DE28 주소로 변경합니다.

정리하면 원래 Blink는 다른 프로세스를 가르키고 있는 주소로 세팅되어 있었는데 이 주소를 자기자신의 Flink 0x89B4DE28 주소로 변경해 버린것 입니다.

 

[그림.14]

 

그리고 이어서 코드를 실행하면 0x151139DD 주소에 MOV DWORD PTR [EAX+4], EDX 코드를 볼 수 있습니다.

이 코드는 ActiveProcessLinks ListEntry Flink 0x89B4DE28 주소가 가르키는 곳에 EDX(0x89EA9E28)주소로 세팅합니다.

 

 

[그림.15]

 

변경된 Flink의주소 0x89EA9E28를 확인해보면 다른 프로세스의 링크드리스트 주소가 아닌 nt!PsActiveProcessHead의 엉뚱한 함수 주소를 가르키는 포인터임을 확인 할 수 있습니다.

이것으로 svchost.exe의 ActiveProcessLinks 의 ForwardLink와 BackLink가 모두 변경이 됬고 이로써 프로세스의 링크드 리스트는 끊어 젔기 때문에 svchost.exe는 은닉이 가능하게 되는 것이고 이 기법을 DKOM기법이라고 합니다.

 

[그림.16]

 

다시 ProcessExplorer로 확인결과 svchost.exe가 사라진 것을 확인 할 수 있습니다.

프로세스 은닉 이후의 행위에 대해 이어서 분석을 진행하겠습니다.

 

[그림.17]

 

windbg는 커널디버깅이 가능하다는 장점이 있고 기능들도 좋지만 유저모드 디버거들에 비해 불편한점이 많기 때문에 DKOM기법을 이용하여 은닉된 이후의 코드는 ollydbg로 분석 하였습니다.

그림.17을 보면 자기자신을 삭제하고 CreateFileA함수로 c:\windows\system32\mssrv32.exe 파일을 오픈합니다.

 

 

[그림.18]

 

그리고 레지스트리를 생성하고 값을 세팅하는데 다음과 같습니다.

 

[그림.19]

 

HKEY_LOCAL_MACHINE - SYSTEM\CurrentControlSet\Services\msupdate 의 레지스트리에 그림19와같이 레지스트리를 등록합니다.

해당 레지스트리는 자기자신의 코드를 복사한 mssrv32.exe를 서비스로 등록시킵니다.

 

[그림.20]

 

서비스로 등록된것을 확인하면 사용자들을 속이기 위해서 microsoft의 보안업데이트로 속이고 있는 것을 확인 할 수 있습니다.

 

[그림.21]

 

레지스트리 등록 과정이 끝나면 C드라이브의 볼륨 시리얼넘버를 구하고, 현재 사용중인 pc의 이름을 구해서 두 데이터를 결합 후 저장합니다.

 

 

[그림.22]

 

이 값을 다시 다음과 같이 만들어서 저장합니다.

id=xDKCOM_241CA3 B5&build_id=165BCFE

URL 파라미터 같네요

 

[그림.23]

 

HTTP Request패킷을 POST형식으로 다음과같이 세팅 하여 전송합니다.

www.crimeware.com/stat.php?id=xDKCOM_241CA3 B5&build_id=165BCFE

원래는 여기서 HttpSendRequestA함수로 URL 을 Requst하고 이에대한 Response응답데이터를 InetrnetReadFile함수로 받게됩니다.

하지만 해당 사이트는 현재 닫힌 상태이기 때문에 정상적인 Response를 받을 수 없는 상태입니다.

 

 [그림.24]

 

받은 데이터가 없을때는 위와같이 특정값 * 특정값 * 3E8와 같이 특정값과 3E8을 곱한 값을 millisecond로 만들어서 시간값을 세팅하고 그 시간동안 Sleep 함수를 호출하여 대기하게됩니다.

여기서 현재코드에서 Sleep을 하기전에 메모리주소 0x151135F2에 보면 CreateThread를 호출하는데 이 스레드의 코드 내용을 보면 아래와 같습니다.

 

[그림.25]

 

생성되는 스레드도 그림.11의 코드와 같이 특정값 * 특정값 * 3E8와 같이 특정값과 3E8을 곱한 값을 millisecond로 만들어서 시간값을 세팅하고 대기하게됩니다.

이렇게 대기한 이후에 코드는 이어서 그림.8부터의 과정을 반복하여 웹서버에 정보를 Request하고 Response받는 행위를 계속 하게됩니다.

여기까지 진행상황을 간략히 정리하면 감염자 대상의 PC정보와 이에해당하는 고유한 값의 정보를 웹서버에 전달하고 Response받은 데이터가 없을때 Sleep하여 감염된 PC의 봇은 잠을 자게됩니다. 그리고 또다시 Sleep함수에서 설정한 시간값만큼의 시간이 지나면 위의 과정을 반복적으로 하게되는 것입니다.

여기서 잠시 쿠쿠샌드박스에서 분석된 결과를 한번 확인해보겠습니다.

 

[그림.26]

 

쿠쿠샌드박스에서도 3개의 프로세스가 탐지되고 분석을 해서 리포트로 만들어 줬는데요

그중 마지막 자식프로세스인 svchost.exe를 분석한 결과를 리포트해줍니다.

쿠쿠의 결과를 봐도 그림.12와같이 각각 메인스레드와 호출된 스레드가 600000, 1800000으로 Sleep까지만 분석을 하고 종료한것을 확인 할 수 있습니다.

더이상의 특별한 과정이 없나봅니다.

SSDT 테이블의 주소값을 건드리길래 멋지게 후킹을 해줄것으로 기대했는데 굉장히 아쉽네요 ㅠ

이부분은 후킹이 됬는지 추후에 확인해보겠습니다.

일단은 분석은 이렇게 종료가 됬습니다.

하지만 위에서 잠시 언급한대로 감염된 PC이름과 고유한 값의 정보를 웹서버에 전달하고 Response받은 데이터가 없을때 Sleep한다고 했습니다.

여기서 잠깐 왜 Response가 없을때 Sleep함수를 호출할까요? 라고 생각해 볼 수 있겠습니다.

왜그럴까? 한번 고민해보면 간단합니다.

만약 이러한 프로그램들로 수백만대의 감염된 PC들이 있다고 생각했을때를 볼 수 있습니다.

수백만대의 컴퓨터가 각자의 정보들을 공격자의 웹서버에 Request를 보냅니다. 여기서 Sleep이 없다면?

Request데이터가 없는 봇들 전부다 공격자 웹서버에 패킷을 보내게됩니다.

역으로 DDOS를 받는 셈이겠네요..(나중에 저러한 봇들 찾아서 이와같이 컨트롤하는 부분들 전부다 찾아서 코드패치후 역으로 공격하고싶은 상상을 하게되네요 ㅋ)

그렇다면 Response받은 데이터가 있다면? 분명 이에 대한 어떤 특정 루틴을 탈것 같은 예감이 듭니다.

이부분은 간단히 웹서버를 만들고 감염된 pc의 호스트주소에서  WWW.CRIMEW***.COM를 내 웹서버로 세팅해놓고 조건에 맞게끔 하나

씩 멋지게 분석 하려고했으나.. 엄청난 삽질이 예상이되네요..

차라리 제대로 동작하는 샘플을 하나더 분석하는게 정신건강에 더 이로울듯 싶다고 생각해서 그냥 끼워맞추기식 코드패치를 해서 몇개의 명령어들을 알아냈습니다.

그럼 간단히 알아보겠습니다.

 

[그림.27]

 

우선 InternetReadFile함수가 호출될때 PUSH ECX 이 부분의 파라미터에 호출이후 데이터가 버퍼의 시작주소가 됩니다.

그래서 InternetReadFile 함수 호출이후 이 버퍼에 제가 임의로 "gogo" 문자열을 입력을 함으로써 Response데이터 있는것 처럼 만든것 입니다.

 

[그림.28]

 

역시 예상대로 Response데이터를 파싱해서 특정위치에있는 데이터와 연산을해서 4바이트짜리 코드를 2바이트로 만들어서 저장합니다.

 

[그림.29]

 

이어서 실행되는 루틴을 따라가보니 특정 명령어(stop/die/open)에 대한 분기를 하여 실행을 하게됩니다.

여기서 die는 자기자신(_bot.exe)을 복사했던 파일 c:\windows\system32\mssrv32.exe을 삭제하도록 하는 명령어입니다.

그리고 open명령어는 iexplorer.exe "? " 와 같은 형식으로 실행을 하는 명령입니다.

즉 공격자가 _bot.exe감염된 pc에서 원하는 주소로 웹브라우저를 실행하는 명령입니다.

 

[그림.30]

 

이어서 루틴을 따라가면 공격자가 실행한 명령어가 맞는지 검증합니다.

검증하는 곳의 주소로 가보니 몇개의 명령어들이 있네요..

 

[그림.31]

 

계속 코드를 따라가보면 "open"에 대한 명령을 위와 같이 실행합니다.

제가 코드를 강제로 패치해서 진행하다보니  iexplorer.exe "비어있는 버퍼" 로 세팅이 됬습니다.

코드에서 비어있는 버퍼부분에 값을 wsprinf함수로 두개의 문자를 결합하는 것으로 보아 빈 버퍼에는 공격자가 입력해서 사용할 URL 주소일거라고 생각이 듭니다.

 

[그림.32]

 

실제 웹브라우저가 실행이 됬는지 확인해보면 정상적으로 실행이 됬네요.. 

 

[그림.33]

 

웹브라우저가 실행이 되고나면 스레드를 실행합니다.

 

[그림.34]

 

스레드  내부로 들어가서 보면 웹브라우저를 실행해놓고 2710(10000ms)의 시간만큼 Sleep을 한 후에 실행시켰던 웹브라우저를 종료합니다.

이와 같은 방식으로 웹브라우저에 있는 문자(명령어)들로 _bot.exe에게 명령을 내리는것 같네요..

이것으로 분석을 마치겠습니다~~

posted by 미스터리 DKL

 

첫번째 _bot.exe프로세스가 CreateProcess로 자기자신을 자식프로세스로 생성하게 되는데요 그 생성된 프로세스를 분석해 보겠습니다.

우선 이번에도 분석하기전에 분석대상 실행파일에서 대략 코드의 흐름을 보겠습니다.

 

[그림.1]

 

이번에도 툴을 돌려서 확인하겠습니다.ㅋ

이번 분석대상은 CreateProcess로 호출된 _bot.exe의 자식프로세스 입니다. 

 

[그림.2]

분석이 완료되고 Report결과를 보면 함수가 별로 호출이 안되네요.

윗부분 몇줄은 _bot.exe의 부모프로세스와 동일하고 메모리주소 0x1511267F lstrcpy부터 코드가 틀린것을 확인 할 수 있습니다.

지금부터 코드는 _bot.exe가 아닌 _bot.exe의 자식프로세스의 이름을 _bot_petch.exe라고 하겠습니다.

_bot_petch.exe라고 지은 이유는 자식프로세스로 생성되는 녀석에 EP에 INT 3(0xCC)로 소프트웨어 브레이크 포인트를 설정하여 코드 패치를 하고 분석을 진행 했기 때문입니다.

 

 [그림.3]

 

 

이 자식 프로세스에서의 흐름은 부모 프로세스가 뮤텍스를 갖고 있기 때문에 GetLastError에서 리턴으로 0x0B7을 리턴합니다.

이 값의 의미는 위에서 설명을 했기 때문에 생략하겠습니다.

CMP EAX(0x0B7),0B7은 JNZ분기문의 조건에 맞지 않으므로 점프하지 않고 아래의 함수 0x15112650을 호출하게 됩니다.

그럼 함수 내부로 진입하여 분석을 이어가겠습니다.

 

[그림.4]

함수내부로 들어가면 위의그림과 같습니다.

svchost.exe의 경로를 만들고 그 경로에있는 svchost.exe를 자식프로세스로 생성을합니다.

벌써 두번째 자식프로세스 생성입니다.(자식좀 그만 낳았으면 좋겠지만 어떤 녀석들인지 알아야겠지요-0-)

여기서 중요한점은 생성할때 CreationFlags를 CREATE_SUSPENED로 호출한다는 점입니다.

이렇게 호출 될 경우 프로세스가 생성될때 정지 상태로 생성하게 됩니다.(느낌에 svchost.exe가 수술당할 것 같은 느낌이 드네요.. -0-)

 

 

 [그림.5]

 

그리고 _bot_petch.exe의 힙 영역에 자기자신의 코드를 복사하기 위해 VirtualAlloc함수를 호출하여 코드사이즈만큼 메모리 공간을 만듭니다.

호출이후 리턴된 메모리주소는 0x00B70000입니다.

그리고 GetCurrentProcess함수로 현재 실행중인 _bot_petch.exe의 HANDLE을 구하고 할당된 힙 영역에 자기자신의 코드를 그대로 복사합니다.

 

 [그림.6]

 

현재 생성된 프로세스를 확인하겠습니다.

첫번째 부모프로세스 _bot_fetch.exe(_bot.exe) 와 첫번째 자식 프로세스인 _bot_petch.exe 그리고 이 녀석이 낳은 또하나의 자식 프로세스인 svchost.exe가 메모리에 올라와있습니다.

이중에서 svchost.exe에 무언가 작업(수술-0-)을 하기위해 Suspended로 호출된 상태이구요.

 

[그림.7]

본격적으로 svchost.exe에 작업을 하는 코드입니다.

우선 _bot_petch.exe 의 힙 영역에 자기자신의 코드를 저장한 상태에서 VirtualAllocEx 함수로 svchost.exe의 HANDLE 0x88 과 _bot_petch.exe 의 Sizeofimage 0xA0000(코드크기) svchost.exe에 생성할 힙 주소를 0x15110000으로 세팅하고 PAGE_EXECUETE_READWRITE의 메모리 권한으로 생성합니다.

결국 이렇게 되면 svchost.exe에 0x15110000 주소의 힙 영역이 생성이 되고, 그 생성된 사이즈는 0xA0000이 됩니다.

하지만 아직까진 svchost.exe에 생성된 힙 영역은 빈공간 입니다.

이것을 아래 그림을 통해 확인해 보겠습니다.

 

[그림.8]

 

메모리 영역의 확인은 sysinternals에서 제공하는 VMMap.exe 툴을 사용하여 확인이 가능합니다.

왼쪽이 VirtualAllocEx 함수를 호출 하기 전 이고 오른쪽이 함수 호출 후 입니다.

오른쪽을 보면 0x15110000 svchost.exe 프로세스의 힙 메모리 영역에 0x15110000이 생성된 것을 확인 할 수 있습니다.

 

[그림.9]

 

그리고 svchost.exe의 힙 영역에 생성된 코드 부분으로 코드의 실행 제어권을 넘기게되는데 이부분을 보기전에 잠시 확인하고 가야될 부분이 있습니다.

그림.4에서 CreateProcess 함수로 실행됬던 svchost.exe파일의 pProcessInfo에 LPPROCESS_INFORMATION 구조체가 들어 가게 됩니다.

그부분을 확인해보면 svchost.exe정보를 확인 할 수 있는데 그 정보들은 그림.9와 같고 Thread Handle이 0x98인것으로 확인이 가능합니다.

 

[그림.10]

 

여기서 svchost.exe의 EIP를 변경함으로써 코드의 흐름을 변경하게 되는데요.

우선 CONTEXT 구조체를 세팅하고 GetThreadContext로 svchost.exe의 CONTEXT를 가져옵니다.

이 구조체안에는 EDI,ESI,EBX,EDX,ECX,EAX,EIP,EBP,ESP와 EPLAGS 또는 CONTEXTFLAGS 등의 정보들로 채워지고 레지스터에 세팅되는 값들은 현재 EIP에 해당하는 값들로 세팅이 됩니다.

그림.10에서 왼쪽 그림에 GetThreadContext 호출했을때 HEX창을 확인 했을때 보여지는 값이 svchost.exe의 현제 EIP레지스터 값이며 0x7C7E0735로 세팅되어 있습니다.

그리고 오른쪽 그림의 코드상 메모리주소 0x15112700에 보면 다음과 같은 코드가 있습니다.

MOV DWORD PTR SS:[EBP-378],EDX

이 콛드는 CONTEXT.EIP = 0x151130F0로 해석할 수 있으며, 이 의미는 svchost.exe의 EIP주소를 0x151130F0 세팅하겠다는 의미입니다.

즉 svchost.exe의 힙 영역에 붙여넣은 코드 0x15110000에서 정확히 .text section 코드섹션의 시작지점으로 EIP를 변경하여 실행하게 되는 것입니다.

이제 마지막으로 SetThreadContext 함수로 svchost.exe 프로세스의 CONTEXT 정보를 수정한 CONTEXT로 세팅하게 되고, 마지막으로 ResumeThread 함수로 suspended된 svchost.exe프로세스를 실행하게됩니다.

 

 

[그림.11]

 

이번에도 스레드에 IN3 소프트웨어 브레이크 포인터를 걸어서 실행했더니 JIT(just...?)실행이 안되더라구요..(Attach List 목록에 svchost가 없습니다. ㅠ)

그래서 스레드의 코드실행 첫라인 2바이트에 현재 라인으로 점프하는 코드 EBFE 를 걸어 현재의 명령어 라인에 무한루프롤 돌게끔 걸어놓고 분석을 진행했습니다.

그랬더니 CPU 사용량이 막올라가네요.

이상태에서 Attach List 목록을 확인해 보면 svchost.exe가 올라와 있는것을 볼 수 있습니다.

 

[그림.12]

svchost.exe와 attach 해서 코드 내로 진입했습니다.

대충 보면 소켓함수와 wininet.dll 라이브러리를 쓰네요 인터넷연결과 소켓통신을 하려고 하는 모양인데 자세한건 분석해 봐야겠지만 재미있을것 같네요..ㅎㅎ;

이것으로 두번째 프로세스 분석을 마치겠습니다~

 

posted by 미스터리 DKL

 

 

 

 

 

[쿠쿠 분석결과]

 

요즘 모 교육을 받고 있는데 샘플 상세분석은 안하더라구요.ㅠ

궁금하기도 하고 해서 한번 상세 분석을 진행해 보았습니다...

 

 [그림.1]

 

그림.1을 보면 알수 없는 문자열이 스택에 저장되는 것을 볼 수있습니다.

악성코드 제작자가 분석을 방해하거나 백신우회? 를하기위해 저렇게 알 수 없는 문자열로 저장해놓고 0x15117430 .data section에 디코딩을 합니다.

디코딩을 하면 URL이 나오는데 이 정보를 숨기고 싶었나봅니다.

 

 [그림.2]

 

.bdata section 입니다. 이 데이터의 아스키 코드값을 메모리의 offset으로 사용합니다.

 

 [그림.3]

 

그리고 .data section의 데이터입니다. 그림.2에서 Memory + Ascii offset 한 위치의 값을 .data section에서 찾아오는 역할을 합니다.

 

 [그림.4]

 

.data section의 문자를 offset으로 사용하여 Memory + Ascii Offset 와 같이 연산하여 메모리 주소를 구합니다.

그리고 그 메모리 주소에있는값으로 쉬프트 연산과 or연산등을 이용하여 디코딩을 진행합니다.

그림.4의 메모리 주소(0x15111960)를 보면 이렇게 진행한 1바이트의 문자를 0x15117430으로 저장하는 것을 확인 할 수있습니다.

 

 [그림.5]

 

ollydump로 덤프떠서 확인해보니 디코딩된 문자열을 확인 할 수 있었습니다.

원본과 비교하면 확실히 다른점을 찾을 수 있습니다.

그리고 맨아래 섹션이 추가되었는데 이것은 악성코드에서 추가한 섹션이아니고 ollydump로 덤프뜨면 생기는 섹션이더라구요 참고하세요 ㅋ

 

[그림.6]

 

첫 OEP부터 코드를 보면 정말 분석하기 친절하게 함수들이 나열되어있네요..

처음 윈도우 시스템 디렉토리 경로를 구하고 GetModuleHandleA함수로 현재 실행중인 프로세스의 핸들을 얻어옵니다.

사실 정확히는  GetModuleHandleA함수로 얻어오는값은 현재 실행중인 프로세스의 ImageBase값 입니다.

리턴된 메모리주소값(0x15110000)으로 가서 확인해보면 실행파일의 처음부분인 ImageBase주소임을 확인 할 수 있습니다.

그리고 이 ImageBase값을 인자로 GetModuleFile함수를 호출하여 현재 실행중인 프로세스의 이름을 구해옵니다.

 

[그림.7]

 

여기서 뮤텍스를 이용해서 조건에 맞게끔 코드제작자가 원하는 함수를 호출 할 수 있도록 제어를 합니다.

우선 이름있는 뮤텍스를 생성하고, GetLastError 함수를 호출해서 리턴값이 0x0B7 인지 체크합니다.

이 0x0B7은 MSDN을 참고해보면 다음과 같은 값임을 알 수 있습니다.

 

0x0B7(183)

ERROR_ALREADY_EXISTS183

Cannot create a file when that file already exists.

 

위의 뮤텍스 이후의 JNZ 분기문을 해석해보면 현재 뮤텍스가 생성이 되고 에러코드가 0x0B7인지 체크하는데 첫1회 실행시 0x0B7이 아니므로 바로 아래루틴으로 이동하게 됩니다.

하지만 이 악성코드 뒷부분을 확인 해보면 같은 코드로 CreateProcess를 함수를 호출 하게되고 그때는 이부분에서 조건이 만족(0x0B7 = GetLastError 리턴값)되어 점프를 하게됩니다.

이렇게 각각의 뮤텍스를 이용하여 각각 실행되는 함수가 서로 다르게 됩니다.

 

[그림.8]

 

그리고 GetCurrentProcess로 현재 실행중인 프로세스의 모조핸들을 구하고 이 핸들을 이용하여 OpenProcessToken 현재 프로세스의 AccessToken을 구합니다.

 

[그림.9]

 

그리고 얻어온 AccessToken에서 SeDebugPrivilege권한이 있는지 확인하기 위해 LookupPrivilieageValueA함수를 호출합니다.

이 AccessToken은 프로세스의 보안속성에 관련해서 명시한 리스트들 이며, 간단히 다음과 같이 확인 할 수있습니다.

 

[그림.10]

 

ProcessExplorer로 확인이 가능합니다.

현재 BlackEnergy2의 AccessToken에서 SeDebugPriviege는 Disabled되어있습니다.

아마도 이부분을 활성화해서 타 프로세스를 디버깅모드로 접근하여 프로세스를 수정하거나 Context값들의 획득이 목적인것으로 추측합니다.

 

[그림.11]

 

그리고 여기서 AdjustTokenPrivileges 함수호출로 권한상승을 하게됩니다.

즉 위에서 확인했던 SeDebugPriviege = Disable권한을 여기서 Enable로 활성화 시킵니다.

이 함수에서 중요한 파라미터는 pNewState입니다.

pNewState = 0x0012FF20 이며 해당 버퍼에 저장된 구조체의 값들이 순차적으로 있습니다.

여기서 4번째값이  TOKEN_PRIVILEGES 구조체이며, 간단히 확인해 보겠습니다.

 

 

[그림.12]

 

MSDN에서 확인해보면 여러 값들이 있는데 여기서 사용된 값은 SE_PRIVILEGE_ENABLED입니다.

 

[그림.13]

 

이값을 WinNt.h파일에서 확인해보면 0x00000002 인것을 확인이 됩니다.

위의 HEX DUMP창의 4번째 구조체의 값과 비교해보면 정확히 일치하는것을 알 수 있습니다.

이것으로 SE_PRIVILEGE_ENABLE값으로 함수를 호출했다는것을 확인 할 수있습니다.

 

여기서 제가 분석할때 쓰려고 만든 허접하고 허접한 코드분석프로그램으로 분석이 어디까지 진행됬는지 확인한번하고 가겠습니다.ㅋ;;

 

[그림.14]

 

한 4분의1정도 분석이 완료됬네요 나머지는 lstrlen함수 호출을 반복하는 부분이 대부분이라 현재의 코드상 분석진행은 조금 남았다고 볼 수 있겠습니다. ㅎㅎㅎ

 

 

[그림.15]

 

다시 ProcessExplorer로 확인해보면 SeDebugPrivilege값이 Enabled된것을 확인 할 수있습니다.

제대로 변경 되었네요..

 

[그림.16]

 

그리고 이어서 GetModuleHandleA함수로 ntdll.dll의 ImageBase의 주소값을 얻어오고 이 값으로 GetProceAddress함수의 인자로 넘겨서 NtSystemDebugControl 함수에대한 함수포인터를 얻어옵니다.

참고로 얻어온 값은 0x7C93DE4E인데 이값은 ntdll.dll에서 EXPORT_ADDRESS_TABLE(EAT에서 얻어온 값입니다.)

PEVIEW로 해당값을 EAT에서 찾아보면 0xDE4E이며 현재 로드된 ntdll.dll의 ImageBase값과 0xDE4E값을 더하게되면 0x7C93DE4E값이 됩니다.

 

 

[그림.17]

 

그리고 ZwQuerySystemInformation 함수로 SystemModuleInfo를 인자로 함수를 호출하게 됩니다.

이것은 현제 시스템의 커널이미지에 대한 정보를 얻기위해 호출하는 함수입니다.

호출하여 저장된 버퍼주소 0x00970000부터 보면 각종정보들이 담겨져 있습니다.

현재 로드된 커널이미지는 ntkrnlpa.exe인데요 이것은 운영체제별로 로드되는 이미지가 틀리다고합니다.

 

ntoskrnl.exe (1 CPU)                    One CPU

ntkrnlmp.exe (N CPU, SMP)          MultiProcessCPU , Symmetric multiprocessing

ntkrnlpa.exe (1 CPU, PAE)            One CPU, Physical addresss extention

ntkrpamp.exe (N CPU, SMP, PAE)  MultiProcessCPU, Symmetric multiprocessing, Physical addresss extention


 

여기서 현재 로드된 커널이미지는 ntkrnlpa.exe이며 PAE(Physical addresss extention)가 있습니다.

이것은 기본적으로 Process는 4G의 메모리를 필요로 하는데 그 이상의 메모리를 사용하려고 할때 확장이 가능하다는 뜻입니다.

 

[그림.18]

 

ZwQuerySystemInformation 함수로 호출하여 리턴된 구조체의 정보를 보면 ntkrnlpa.exe의 ImageBase의 주소를 확인 할 수 있으며, 해당되는 주소값은 0x804d9000으로 커널 메모리 영역의 주소입니다.

windbg를 사용하여 해당 메모리위치로 가보면 로드된 ntkrnlpa.exe 커널이미지를 확인 할 수 있습니다.

 

[그림.19]

 

이어서 ZwQuerySystemInformation 함수로 얻어온 정보를 기반으로 ntkrnlpa문자열을 파싱하기 위한 작업을 진행하고,

LoadLibraryExA함수로 ntkrnlpa.exe 커널이미지의 메모리상에 로드된 ImageBase값을 얻어옵니다.

 

 

[그림.20]

 

그리고 ntkrnlpa.exe의 ImageBase값 0x00A60000 값을 인자로 ntkrnlpa.exe내에서 KeServiceDescriptorTable값의 주소를 EAT테이블에서 찾습니다.

KeServiceDescriptorTable은 다음과 같은 구성으로 되어있습니다.

 

KeServiceDescriptorTable 

KiServiceTable                    // SSDT테이블의 시작주소를 가르킨다.(이 테이블의 시작주소로부터 함수들의 진입주소가 리스트 되어있다.)

CounterTable                      // 각 함수들이 몇번 호출되어 있는지 나타낸다.

ServiceNum                        // SSDT테이블에 포함된 함수의 갯수를 저장한다.

KiArgumentTable                 // 각 함수별 파라미터의 크기를 나타내는 값을 저장하고 있는 테이블의 포인터가 저장되어 있다.

 

그럼 실제 KeServiceDescriptorTable의 주소를 ntkrnlpa.exe에서 찾아보겠습니다.

 

[그림.21]

 

현재 메모리상에 로드된 ntkrnlpa.exe의 ImageBase값은 0x006A0000 이었고, 이 값에다 EAT의 KeServiceDescriptorTable의 함수 진입주소를 더하면 그림.20과 같이 0x00ADC0A0값이 됩니다.

 

[그림.22]

 

그리고 ntkrnlpa.exe 의 ImageBase값(0x006A0000)과 KeServiceDescriptorTable의 VA값(0x00ADC0A0)을 빼서 RVA값(0x0007C0A0)로 만들어서 스택에 저장하고 다시 이 값을 ECX레지스터를 이용하여 함수 0x15114930을 호출하게됩니다.

함수내부로 들어가 보겠습니다.

 

[그림.23]

 

함수내부로 들어갔더니 내부에서 한개의 함수를 하나더 호출합니다.

함수이름은 0x151148E0이며 이 함수의 역할은 커널이미지 ntkrnlpa,exe 에서 PE signature offset, Machine offset , Optional header magic offset, .text section start offset을 0x12FEF4에 순차적으로 저장하고 빠저나옵니다.

그리고 다음과 같은 코드를 실행 합니다.

 

[그림.24]

 

그림.24에서 핵심은 박스안의 코드들입니다.

굉장히 복잡하네요.. 그러나 차근차근 한라인한라인 분석해보면 뭘 하려는지 파악이 됩니다.(수련을 한다고 생각하시고...-0-;)

 

우선 여기서 0xC49300 주소가 ntkrnlpa.exe의 .reloc section의 시작주소입니다.

박스 윗부분은 이 섹션에서 특정값을 파싱하고 아래의 박스안의 코드를 실행하기 위한 사전작업을 진행하는 것입니다.

별로 중요하지  않으므로 윗부분의 코드는 생략하고 박스 안의 코드부터 분석을 하겠습니다.

처음 0xC49308 주소 즉 .reloc section의 시작주소에서부터 +8 offset의 위치에 있는 WORD값 0x3784을 저장해두고, 다시 0x3784를 0x0FFF 와 AND 연산하여 0x0784값으로 만듭니다.

그리고 메모리주소 0x151149F8의 코드에서 ADD EDX,DWORD PTR DS:[EAX] 연산을 합니다.

여기서 각각 들어있는 값이 EDX에 0x0784이고, [EAX]의 값은 [0xC493000]입니다.

즉 이코드를 계산하면 0x0784과 0xC493000이 가르키는값 0을 더하는 코드입니다.

0과 더하기 때문에 당연이 연산결과는 0x0784가 되겠습니다.

그리고 이 연산결과 값과 ntkrnlpa.exe의 ImageBase값 0xA60000을 서로 더합니다.

그 결과는 0xA60784가 되며, 이 값은 ntkrnlpa.exe의 .text section(코드가 실행되는 영역)을 가르키는 주소값으로 세팅이 됩니다.

0xA60784가 가르키는 주값을 따라가 보면 0x4874FC 와같이 또다른 VA(VirtualAddress)값이 들어있습니다.(메모리주소 0x15114A06부터의 설명)

이값으로 다음과 같은 연산을 진행합니다.

SUB EAX,DWORD PTR DS:[EDX+1C]

여기서 EAX값은 0x4874FC이며, [EDX+1C] 가르키는 값은 ntkrnlpa.exe 실제 파일상의 ImageBase주소인 0x400000으로 세팅되어있습니다.

0x4874FC(VA) - 0x400000(ImageBase) = 0x874FC(RVA)와 같이 RVA를 구하기 위한 연산임을 파악 할 수 있습니다.

그리고 첫번째 블록의 마지막 CMP ECX,DWORD PTR SS:[EBP+C]의 비교연산을 진행하는데 이것을 해석하면 다음과 같습니다.

0x874FC(RVA) == 0x7C0A0(KeServiceDescriptorTable 그림.22 참조)와 비교하여 같지 않을 경우 루프의 처음으로 돌아가고 같을 경우 이어서 코드가 실행이 됩니다.

 

다음으로 두번째 블록의 코드입니다.

메모리주소 0x15114A19를 보면 ADD EDX,DWORD PTR SS:[EBP-20]의 코드가 있습니다.

여기서 EDX는 0xA60000(ImageBase)값 입니다. 그리고 :[EBP-20]의 값은  0x7C0A0(KeServiceDescriptorTable RVA)의 값을 가르키는 RVA값 1BA48E입니다.

이 두값을 더하면 C1A48E값이 되며, 이값을 EDX레지스터에 저장합니다.

그리고 [EDX-2]와같이 C1A48E에서 -2의 위치의 데이터를 가리켜 EAX에 저장을하고, 이 EAX값과 5C7값과 비교연산을 진행합니다.

즉 여기서 5C7의 값의 의미를보면 PE상의 RVA값을 구하고 그 구한 위치에서 앞으로 2바이트 이동후 그 바이트 값과 비교한다면 이것은 OPCODE로 의심해 볼 수 있습니다.

한번 확인해 보겠습니다.

 

 [그림.25]

확인 결과 0x1BA48E 주소에 0xA0C047이 있으며, 0x1BA48E- 0x2 =  0x1BA48C에는 0xC705(Little Endian표기 변환시 0x5C7) 인것을 확인 할 수있습니다.

그럼 이 OPCODE가 어떤것을 의미하는지 알아야합니다.

다음과 같이 Olly에서 빈공간에 코드를 붙여다 놓습니다.

 

[그림.26]

 

코드를 확인결과 MOV DWORD PTR DS:[47C0A0], 42AB8C의 코드로 0x47C0A0가 가르키는 곳에 0x42AB8C주소값으로 세팅하는 OPCODE입니다.

음.. 뭔가 냄새가 나기 시작하네요 ㅎㅎ

어쨌든 이어서 그림.24의 메모리주소 0x15114A30 부터분석을 이어서 진행하겠습니다.

마지막 코드 부분의 메모리 주소 0x15114A30에 보면 MOV EAX,DWORD PTR DS:[ECX+4]와 같은 코드가 있습니다.

현재의 ECX값은 0xC1A48E이며 여기서 +4의 값은 C1A492값이 되고, 이 주소값이 가르키는 값은 0x42AB8C값이 됩니다.

그리고 그 아래 SUB연산은 역시 VA값을 RVA로 변환하는 코드가 되겠습니다.

그렇게되면 0x2AB8C가 되며, 추가적인 작업없이 함수를 빠저나갑니다.

 

[그림.27]

 

그 다음 작업으로 SSDT 테이블에 함수포인터 주소들이 저장되어 있는데 이 주소들을 자기자신의 .data section에다 가져오는 파싱하는 작업을 진행합니다.
그림.27에서 분석을 하다보면 그림.26에서 MOV DWORD PTR DS:[47C0A0],42AB8C 코드의 의미를 알 수있습니다.
먼저 답을 말하자면 0x47C0A0(VA)는 KeServiceDescriptorTable의 시작주소이며, 0x42AB8C(VA)는 위에서 설명한 KeServiceDescriptorTable의 첫번째 멤버인 KiServiceTable(SSDT)의 시작주소 입니다.
즉 KeServiceDescriptorTable 의 첫번째 멤버에 SSDT 테이블의 시작주소로 세팅하는것으로 볼 수 있습니다.
박스위의 코드먼저 분석하면 다음과 같습니다.
우선 0x42AB8C(SSDT VA) - 0x400000(IMAGEBASE) = 0x2AB8C(SSDT RVA) SSDT의 RVA값을 획득하고,
0x2AB8C(SSDT RVA) >= 0x1F9900(ntkrnlpa.exe의 Optional_Header의 SizeOfImage)의 조건이 만족할때까지 루프를 돕니다.
그리고 0xA60000(LoadLibray로 로드된 krnlpna.exe의 IMAGEBASE) + 0x2AB8C(SSDT RVA) = 0xA8AB8C(SSDT VA)값으로 세팅합니다.
그리고 0xA8AB8C(SSDT VA)가 가르키는 첫번째 주소에는 함수에 대한 포인터주소가 VA형식으로 저장되어 있습니다.(그림.28참조)

0xA8AB8C(SSDT VA)가 가르키는 첫번째 주소에는 0x4C2A5E가 값이 있으며, 이 주소값이 VA이기 때문에 RVA로 변환하는 작업을하여 0x2CA5E로 만듭니다.
그리고 그림.17 과 그림.18을 보면 현재 커널영역에 로드된 커널이미지의 주소를 얻어왔었습니다.
그 주소는 0x804D9000이고 이 주소값역시 IMAGEBASE주소인것을 확인 했었습니다.
이제 이 주소에다 SSDT 테이블의 첫번째 함수주소의 RVA값을 더해서 커널영역에 로드된 커널이미지의 SSDT테이블의 첫번째 함수의 주소값으로 세팅을 합니다.
0x804D9000 + 0x2AB9C(SSDT RVA) = 0x8059BA5E(Kenel영역에 로드된 커널이미지의 SSDT 테이블의 첫번째 함수포인터 nt!NtConnectPort)

 

 

[그림.28 합쳐야함]

 

여기서 확인해보겠습니다.

커널에 영역에 로드된 커널이미지의 IMAGEBASE인 0x804D9000 주소값과 0x2AB8C(SSDT RVA) 를 더하면 0x80503B8C주소로 KeServiceDescriptorTable의 첫번째 멤버인 KiServiceTable(SSDT)의 주소인것을 확인 할 수 있으며, 다시 0x80503B8C 확인해보면 함수포인터주소로 세팅되어진 SSDT를 확인 할 수 있습니다.

 

[그림.29]

 

그리고 그림.27에서 메모리주소 0x15114BDE에서 작업한대로 현재 실행중인 악성코드의 .data section에 SSDT 테이블의 함수포인터들이 저장되어있는것을 확인 할 수 있습니다.

 

 [그림.30]

 

그리고 자기자신(_bot.exe)을 c:\windows\system32\mssrv32.exe 파일로 이름을 변경하여 시스템 디렉토리에 복사합니다.

그리고 CreateProcessA함수로 _bot.exe를 또다시 실행하게 됩니다.

 

[그림.31]

 

시스템 디렉토리에 생성된 mssrv32.exe파일과 현재 실행중인 코드와 같은지 MD5로 비교했고 결과는 동일한 파일임이 밝혀졌습니다.

이제 그림.30에서 CreateProcessA함수로 자기 자신을 또다시 실행하게 됩니다.

이때 실행되는 코드는 지금까지 실행됬던 코드 그대로 실행되는것이 아닙니다.

분석초반부 그림.7번에서 뮤텍스를 생성했었고 부모 프로세스인 _bot.exe가 뮤텍스를 갖고 있습니다.

이상태에서 자식프로세스로 자기자신을 생성했을시에는 그림.7번과 같이 0x0B7(183) ERROR_ALREADY_EXISTS 에러코드가 발생을 하고 분기문을 무시하고 순차적으로 코드가 실행이 됩니다.

간단하게 부모프로세스가 뮤텍스를 갖고있고 자식프로세스가 동일한 이름의 뮤텍스를 생성하려고 시도하자 0x0B7(183) ERROR_ALREADY_EXISTS 에러가 발생하게되어 분기의 흐름을 변경시키는 것입니다.

그럼 여기서 CreateProcess로 실행되는 코드분석은 어떻게 해야할까요?

코드를 수정해서 서스펜드 시킨 후 분석해도 되지만 뮤텍스로 코드의 흐름을 제어하는것을 알았기 때문에 그냥 현재의 디버거창은 그대로 둔 상태에서 새로운 Ollydbg로 _bot.exe파일을 올려서 분석을 진행하면 이어서 분석을 할 수가 있습니다.

 

 

posted by 미스터리 DKL
2014.06.27 11:15 [Forensic]/메모리분석

요즘 악성코드 분석을 하면서 흥미로운 분야를 발견했습니다.

바로 포렌식에서 메모리분석 기법인데요 툴자체가 굉장하네요 정말 후덜덜 합니다. 그만큼 강력하단 얘기죠 ㅎㅎ

대략 이툴의 기능들을 소개하자면 현재  프로세스 생성소멸 정보, 레지스트리정보, 후킹탐지기능, 네트워크 연결정보, MFT정보, 실행파일 덤프기능, 그리고 디버거와같은 디버거기능 등의 강력한 플러그인들이 존재합니다.

이 도구는 명령어 기능이 잘되있기 때문에 명령어를 사용하는데 있어서 외우거나 어려움을 느낄필요가 전혀~ 없습니다.

volatility -h 하면 이 도구의 사용법에대해 친절히 알려줍니다. 또는 volatility -f [메모리덤프경로] [사용할 명령어] -h 할 경우 사용하는 명령어에대한 Help기능을 제공합니다.

그렇기 떄문에 전혀 문제가 되지않지요 ..하지만 사용방법은 이렇다고 해도 이 명령어가 뭐에대한 명령어인가 무엇을 나에게 보여주는것인가에 대한 설명은 아쉽게도 부족합니다.

예로 vadinfo 는 프로세스별로 갖고있는 vad영역의 정보를 나에게 보여주는것입니다. 그리고 mftparser는 메모리덤프에서 mft를 추출해서 그 내용을 보여줍니다.

이렇게 봤을때 이해가시나요? 이해가시는 분들은 vad와 mft가 무엇인지 이미 알고계신분들입니다. 이렇기 때문에 운영체제에대해 공부를 안할 수 가 없네요 ㅋㅋ(반성하고 날잡고 파야할듯해요.ㅠ)

뭐 어쨌든 샘플하나구해서 분석을 한번 해보겠습니다.ㅎㅎ

유투브에서 분석하시는분들 보면 분석을 굉장히 빠르고 정확하며 현란?하게 사용하시는데 저는 그렇지 못하다는점 ㅋㅋ 아직 간만 봤다는점~! 이해하고 봐주시기 바랄게요~

 

 

 

 

pslist 명령어로 현재 실행중인 프로세스를 확인합니다.

의심되는 프로세스가 4개네요 근데 딱봐도 의심되는게 1e0...으로 시작하는 프로세스네요. 이게 학습용 샘플이라 그렇지 실제에서는 악성코드를 저런식에 이름으로 만들어 배포할 바보는 없겠죠 ㅎㅎ

그렇기 떄문에 저 한개의 프로세스만 확인해야할것이 아니라 저 4개의 프로세스들에대해 다 조사를 해봐야 됩니다.(의심가는건 전부다 -0-)

일단 4개의 프로세스가 2010년 8월 15일 19시 21분대에 시작해서 대략 2초정도 후에 종료되네요.

정상적인 프로그램이라면 보통은 저렇게 짧게 죽지 않겠죠 이런류는 실행하자마자 자기 볼일 다보고 사라지거나 하는 악성코드로 볼수있습니다.

아닌경우도 있지만요.. 그렇기 떄문에 확인을 해봐야겠지요.

1e0...시작하는 녀석의 부모프로세스 id는 1724로 explorer.exe이네요.

explorer.exe는 윈도우의 쉘을 관장하는 녀석이라고 알고있습니다. 쉘을 통해서 명령을 내리듯이 윈도우에서 마우스를 클릭하든 명령어를 입력하든 발생되는 이벤트를 처리해줍니다.

근데 이런녀석이 이상한 자식프로세스로 1e0...의 알수없는 이름을 가진녀석을 낳았다는것은 백빵 악성코드라고 의심할 수 있습니다.

 

 

 

psscan은 이전에 종료되었던 프로세스들까지 모두 보여줍니다. 확인결과 pslist와 psscan에서 차이가 별로없네요

역시 의심되는 ie0f...녀석은 존재하구요

 

 

 

 

다음으로 userassist를 확인해봅니다.

시스템에서 실행되었던 응용 프로그램의 목록과 실행횟수 등의 정보를 레지스트리에 저장하는데요

경로는 다음과 같습니다.

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist

분석해보면 실행횟수는 1번이고 실행경로는 c:\Documents and Settings/Administrator\Desktop 이네요. 바탕화면에서 실행한것으로 확인이 가능합니다.

 

 

 

 

mftparser을 이용하여 메모리 덤프에서 mft여역을 추출하는 과정입니다.

MFT는 다음과 같습니다.

NTFS 시스템 파일로 NTFS로 포맷 된 볼륨에 볼륨에 각 파일 및 폴더에 대 한 정보(파일크기, 작성일자,사용권한,데이터내용 등)를 포함하며 NTFS볼륨에서 첫번째 영역입니다.

라고하네요.

확인해보니 바탕화면에서 실행했고 이후에 프리패치 파일에 기록하는것을 확인 할 수 있습니다.

 

 

psxview로 은닉되었는지 확인합니다.

여기서 pslist에는 있고 psscan에는 없을경우와 이와반대인경우에 각각 의심 할 수 있는 경우가 있었는데 생각이...ㅠ DKOM탐지 관련해서 였던거같은데..흠..;

찾아서 다시정리해야겠어요..;;

 

 

 

thrscan으로 프로세스에서 생성된 스레드의 시간과 종료시간을 파악할 수 있습니다.

확인결과 svchost.exe, explorer.exe system.exe등의 프로세스에서 악성코드가 실행됬던 시간에 스레드가 생성되고 바로죽는 현상이 발생됬었네요.

 

 

 

callbacks 명령으로 프로세스 및 스레드를 감시할 수 있습니다.

pssetcreatethreadnotifyroutine는 스레드의 생성을 통지하는 함수입니다.

확인결과 GenerickernelCallback,pssetcreatethreadnotifyroutine에보면 0004A2A 값으로 수상한 이름이 있습니다. 

 

 

thread 명령어에 -F옵션으로 HookedSSDT 가들어있는 섹션만 뽑도록 했습니다.

그 결과 00004A2A 수상한값의 이름이 SSDT테이블을 후킹한것으로 확인되네요..

간단하게 SSDT 테이블의 특정 함수를 00004A2A로 바꿔치기한것입니다.

 

 

 

 vadinfo로 프로세스에 존재하는 vad영역 확인합니다.

여기서 중욯나건 tag가 vads이고 Protection이 PAGE_EXECUTE_READWRITE경우인경우 의심하고 확인해 봐야합니다.

 

 

 

malfind 명령어로 검색한 결과 위의 vad영역에서 의심되는 부분을 찾아냈네요

헛 ~!!  MZ 실행파일입니다...

 

 

volshell로 확인결과 확실하네요 이젠 빼도박도 못합니다.

이부분을 추출해야겠지요? svchost vad영역에 MZ 실행파일이 발견되었습니다.

DLL형태로 올라가있을수 있기떄문에 DLL형태로 덤프를 떠보도록 하겠습니다.

 

 

 

 

덤프가 제대로 생성이 됬네요.

여기서 dlldump사용시 프로세스명과 이미지베이스를 줘야 정확히 가저옵니다.

 

 

peview로 확인결과 사용하는 함수에 DownloadFile이 있네요 뭘 다운받나 봅니다.

이것으로 초간단 volitility를 활용한 악성코드 메모리 분석후기였습니다.

 

'[Forensic] > 메모리분석' 카테고리의 다른 글

volatility를 이용한 blackenergy2 샘플 분석  (0) 2014.06.27
posted by 미스터리 DKL
2014.06.19 16:43 [OS]/▶Windows


윈도의 기본 프로세스
⊙ 사용자가 강제로 종료할 수 있는 시스템 프로세스
Explorer.exe
- 윈도의 기본 쉘 입니다. 윈도의 대부분의 명령 처리를 관리 합니다.
예를 들어 사용자가 아이콘을 클릭하면 Explorer.exe 가 받아서 해당 명령을 수행합니다. 꼭 필요한 프로세
스이지만, 프로세스 강제 종료가 가능하고, 일반적으로 강제 종료 후 다시 실행 되지만 실행되지 않을 경우
작업관리자의 파일->새작업 에다가 Explorer.exe 를 입력하시면 다시 실행이 가능합니다.


Internat.exe
- 사용자의 키보드 입력 로케일을 로드 하는 프로세스 입니다.
트레이에 보면, 현재 입력 상태를 표시할수 있는 아이콘이 있는데 (EN 혹은 KO 라고 적힌 파란색 아이콘(숨
겨져 있는 경우도 있음)) 이 입력 로케일을 관리하는 프로세스 입니다.


Taskmgr.exe
- 이놈은 작업 관리자의 실행파일 입니다.
⊙ 사용자가 강제로 종료할 수 없는 시스템 프로세스


Csrss.exe
- 윈도 생성, 쓰레드, 16비트 가상 MS-DOS 모드 (시작->실행 command.com 을 입력하면 뜨는거.. cmd
를 입력하면 뜨는 창과는 다릅니다.) 를 관장합니다.


Winlogon.exe
- 실제로 로그온 작업을 하는 프로세스 입니다.


Lsass.exe
- Winlogon.exe 의 요청에 의해 인증과 관련된 부분을 처리하는 프로세스 입니다.


Mstask.exe
- 예약 작업을 처리하는 스케쥴러 입니다.


System Idle Process
- CPU 의 Idle 프로세스로서.. CPU 에 남은 리소스를 관리하는 프로세스 입니다. 일반적으로 이놈이 CPU
점유율이 상당히 높게 나오는데 이 수치는 쓰고있는 리소스가 아닌 놀고있는 리소스 입니다. 그러니.. 높을수
록 CPU의 리소스가 많이 남은게 됩니다.


Smss.exe
- 사용자 세션을 시작시키는 세션 관리자 로서 Winlogxon 및 Csrss.exe 프로세스를 구동, 시스템 변수를
설정는 등을 합니다.
쉽게 말해.. 시스템이 구동되면 초기화 작업을 하고나서.. 다른 프로세스가 도는상황을 감시 합니다. 그러다
가 Csrss.exe 등이 정상적인 종료 신호를 주면 시스템을 정상적으로 종료하고, 걔네가 비정상적으로 멈추면
얘가 시스템을 정지 시키고 신호가 있을때까지 기다리게 하는 역할을 담당합니다.


System
- 쓰레드를 생성후 실행하는 윈도의 기본 프로세스입니다.
1/2
Spoolsrv.exe


- 프린터의 스풀링을 담당하는 프로세스 입니다.


Winmgmt.exe
- 윈도의 클라이언트를 관리하는놈 입니다. 클라이언트의 요청에 따라, 초기화등의 작업을 관장 합니다.


Svchost.exe
- DLL 형식으로 실행되는 다른 프로세스들의 host 역할을 합니다.
해당 형식의 프로세스가 여러개면.. 이놈도 여러개씩 뜹니다.
요즘 이 프로세스와 관련된 웜이 있어.. 대부분 유저들이 알고 있는 프로세스
일텐데요.. 이 프로세스에 관한 자세한 설명은 이곳을 참고 하시면 되겠습니다.
- 참고사항으로.. Svchost 에 어떤 DLL 들이 작동중인지를 살펴보려면 tlist.exe 파일이 필요 한데요.. 이
파일은 윈도 CD 의 SUPPORTTOOLS 폴더 안에 보면 SUPPORT.CAB 라는 캐비넷 파일이 있는데 이 파일
안에 들어있으니.. 압축을 해제 하셔서 사용하시면 되고.. tlist -s 라고 입력하시면 호스트 사용 목록이 나타
납니다.


Services.exe
- 윈도의 서비스를 관리하는 역할을 합니다. 여기서 서비스라는건.. Messenger 서비스같은 서비스들을 얘
기 합니다.
2/2

'[OS] > ▶Windows' 카테고리의 다른 글

윈도우의 기본 프로세스  (0) 2014.06.19
posted by 미스터리 DKL
2014.06.08 15:06 [PROGRAMMING]

코드분석/악성코드분석/코드 분석/악성 코드 분석/리버싱/바이러스 분석/CODE 분석/익스플로잇 분석

코드를 분석할때 복잡한 코드를 현재 내가 진행중인 위치와 코드의 얼만큼을 분석했고 얼만큼의 함수를 더 분석해야 하는지 파악하며, 코드의 전체적인 흐름과 빠른 동적분석 진행을 위해 제작하게 되었습니다.

 

<동작 구성도 및 동작원리>

 

 

 

동작 원리에 대해 간단히 설명하겠습니다.

우선 디버깅 할 대상의 프로세스를 CreateProcess 함수에 DEBUG_PROCESS를 인자로 호출하게 되면 프로세스가 생성될때 디버깅 이벤트를 발생할 수 있는 프로세스로 생성이 됩니다.

이때 디버깅 루프를 돌면서 각 이벤트에 맞춰 프로그래밍 해주면 됩니다.

디버깅 루프코드만 다음을 참조했습니다.(http://blogs.msdn.com/b/reiley/archive/2011/07/21/data-breakpoint.aspx)

 

 

 

 

 

처음 발생되는 이벤트들은 DLL이벤트 입니다.

한개의 프로세스(프로그램)가 실행이 되면 해당 프로세스에서 필요한 DLL을 호출하게 됩니다.

이때 디버깅 이벤트는 DEBUG_EVENT 구조체를 사용하여 현재 발생된 디버깅 이벤트와 호출된 DLL 핸들 및 ImageBase 또는 발생된 DebugEventCode들의 정보를 담고있습니다.

 

 

 

DLL의 디버깅 이벤트가 발생하면 우선 제가 만든 함수인 PeParse함수에 디버깅 이벤트가 발생된 DLL의 핸들과 ImageBase 등의 기타 정보들을 인자로 호출합니다.

 

 

 

 

PeParse의 함수구조에대해 간단히 설명하겠습니다.

우선 함수에 "DLL", "EXE"문자열을 대상으로 DLL이벤트인지 EXE이벤트인지 IF문으로 분류하여 분석을 합니다.

그 이유는 EXE인경우 IAT테이블 대상으로 PE 구조를 파싱하고 DLL인경우 EAT테이블을 대상으로 파싱하기 위함입니다.

처음 함수에 진입하면 VirtualAlloc 함수로 65525의 크기 만큼의 동적메모리를 생성합니다.

여기서 동적메모리를 생성하는 이유는 현재 호출된 DLL또는 EXE파일의 바이너리 데이터를 그대로 메모리에 올리고 이 상태에서 PE구조 값과 .TEXT섹션의 OPCODE의 데이터들을 얻기 위해서입니다.

위에서 말씀드린대로 호출된 DLL또는 EXE핸들로 CreateFileMapping함수를 호출하고 MapViewOfFile을 호출하게되면 현재 호출된 DLL/EXE파일이 그대로 메모리에 올라가게 됩니다.

위의 그림에서 확인해보면 LPVOID Mapview 변수에 MapViewOfFile이 호출되면 메모리의 시작주소를 반환하게 되는데 직접 메모리를 확인해보면 호출된 DLL파일의 바이너리가 메모리에 올라가 있는것을 확인 할 수있습니다.

이 메모리의 시작주소를 대상으로 각각의 PE구조를 파싱하게 됩니다.

 

 

그리고 호출된 DLL의 이름을 찾는 방법에 대해 설명드리겠습니다.

실행파일의 이름을 찾는 방법은 간단하게 GetModuleFileName함수를 사용하는 방법 그리고 로드된 PE를 가상메모리에 올린후 직접 OFFSET을 계산하여 이름을 알아내는 방법이 있습니다.

GetModuleFileName의 함수는 프로세스의 핸들(IMAGE BASE)을 첫번째 인자로 전달해주면 호출된 파일의 경로와 이름을 쉽게 알아낼 수 있습니다.

저는 PE구조에 대해 공부하기 위해 직접 OFFSET을 계산하여 이름을 얻을수 있도록 구현했습니다. 

 

 

우선 IMAGE_IMPORT_DATA_DIRECTORY를 보면 EXPORT Table의 값이 있는지 없는지 체크합니다.

그리고 값이 있을경우 RVA이기 때문에 FILE OFFSET으로 변경해 줘야합니다.

(상세 계산법은 시중에 책이나 인터넷에 많이 알려져있으니 참고하시기 바랍니다.)

첫번째 작업으로 EXPORT Table의 주소값이 포함된 영역의 섹션을 찾습니다.

찾은경우 해당 섹션의 RVA값과 Pointer to Raw Data값을 뺍니다.

ntdll.dll에서 해당 값을 연산했을때 결과는 0x1000 - 0x400h = 0xC00 가 됩니다.

그럼 이제 여기서 EXPORT Table의 RVA값과 섹션에서의 연산값 0xC00를 다시 빼줍니다.

결과는 0x361B0 - 0xC00 = 0x355B0 값이 됩니다.

  

 

 

그림에서 확인해보면 IMAGE_EXPORT_DIRECTORY(IMAGE_EXPORT_DESCRIPTOR)의 시작주소 인것으로 확인할 수 있습니다.

이상태에서 Name RVA값을 얻고 해당 값 역시 RVA이기 때문에 OFFSET값을 구해 줘야합니다.

 

 

 

 

위에서 설명한 방식대로 구하게 되면 0x3AF6E - 0xC00 = 0X3A36E값이 되고 해당 파일 OFFSET으로 가보면 실제 ntdll.dll 문자열이 있는것을 확인 할 수있습니다.

처음에 MapViewOfFile함수로 로드되는 모듈의 핸들을 인자로 넘겨 호출된 모듈의 PE를 가상메모리에 로드 했었는데요 실제 이 주소로 가보면 PE첫 시작임을 확인 할 수 있었습니다.

해당 가상메모리의 주소는 0x6400000 였습니다. (할당되는 가상메모리의 주소는 유동적인 주소입니다.)

이 값에다 ntdll.dll의 OFFSET값 0x0X3A36E을 더하면 해당 가상메모리상태에서의 OFFSET값을 구할 수 있게됩니다.

 

다시한번 확인해보겠습니다.

이번 테스트의 경우  MapViewOfFile함수로 메모리에 로드했을때의 주소값은 0x2610000값 입니다. 

 


실행파일이 실제 로더에의해 실행되어질때 메모리에 매핑되는기준이 PE포맷의 IMAGE BASE주소값입니다.

MapViewOfFile 함수로 가상메모리에 매핑된 주소는 0x2610000 값입니다.

즉 MapViewOfFile로 리턴되는 가상메모리의 주소값이 IMAGE BASE의 값으로 보면됩니다.



이제 가상메모리주소값(IMAGE BASE값) 0x2610000 에다 RVA(Relative Virtual Address)을 더해주기만 하면됩니다.

RVA(Relative Virtual Address) 상대 가상 주소 로 해석할 수 있습니다.

즉 메모리에 매핑된 주소값으로부터 얼마만큼 떨어져 있느냐? 다시해석해서 IMAGE BASE에서 얼마만큼 떨어져 있는 주소값이냐?

이것을 공식으로 만든것이 IMAGE BASE + RVA = VA 가 되는것입니다.



이제 해당 0x2610000 + 0x3A36E 더한값을 포인터변수에 저장하고 출력해주기만하면 호출된 모듈의 이름을 구할 수 있습니다.

이런식으로 로드된 모듈의 핸들로 가상메모리에 매핑하고 코드를 작성해 나가면 됩니다.

물론 코드분석 대상의 프로세스 핸들(EX)notepad.exe)로 코드를 작성해야합니다.

 

<바운딩 처리된 PE에서의 함수호출 처리>

처음 코드를 작성하면서 E8과 FF15 부분에대한 OPCODE값을 계산해서 IAT테이블에서 해당되는 함수를 찾도록 구현했을때 두가지 처리방식에 대한 문제점이 발생했습니다.

첫번째로 바운딩 처리되었을때와 레지스터를 이용하여 함수호출을 하는경우입니다.

그 첫번째로 바운딩 처리된 PE구조를 확인해보겠습니다.

 

 c:\windows\system32\notepad.exe를 PEVIEW로 오픈해보면 다음과 같이 섹션헤더 아래에 BOUND IMPORT Directory Table, BOUND IMPORT DLL Names 섹션이 추가되어있는것을 확인할 수 있습니다.

 

 

IMPORT ADDRESS TABLE을 확인해보면 주소값이 RVA가 아닌 VA로 되어있는것을 확인 할 수 있습니다.

이것은 로딩시간을 단축하기위해 IAT에 메모리에 로드될 주소들을 고정시켜 버린것입니다.

 

 

실제 notepad.exe를 디버거에 올려서 확인해보겠습니다.

CALL GetStartupInfoA 부분의 OPCODE를 확인해보면 FF15 FC100C00이고 이것은 함수를 호출하는 코드입니다.

이것을 Big Endian으로 바꿔주면 FF15 000C10FC 가 됩니다. 즉 호출하는 주소는 0xC10FC가되는데 이 주소는 바운딩된 PE의 IAT테이블에서 찾을 수 없습니다.

 

 

그렇다면 일단 함수 내부로 진입해 보겠습니다.

GetStartInfoA함수의 첫 시작주소는 0x75BD1E10입니다. 참고로 GetStartInfoA함수는 KERNEL32.DLL에 있는 함수입니다.

 

 

현재 로드된 SYSTEM DLL들의 IMAGE BASE값을 확인해보겠습니다.

로드된 Kernel32.dll의 IMAGE BASE값은 0x75BD0000값입니다. 이 IMAGE BASE값은 운영체제가 재부팅될때마다 주소가 바뀝니다.

그 이유는 WINDOWS VISTA이후로 보안차원에서 WINDOWS시스템에서 ASLR(Address Space Layout Randomization)적용으로 메모리에 로딩되는 주소(IMAGE BASE)를 유동적으로 변경하여 사용하도록 했습니다.

그리고 실제 Visual Studio에서 컴파일옵션으로 설정해서 컴파일이 가능합니다.(ASLR이 적용된 PE는 재부팅이 될때마다 변경되는것이 아닌 프로세스가 재 실행될때마다 IMAGE BASE주소값이 변경되도록 컴파일이 가능합니다.)

이어서 함수의 진입 주소값과 로드된 IMAGE BASE값을 빼보겠습니다.

0x75BD1E10 -  0x75BD0000 = 0x1E10

 

 

PEVIEW로 Kernel32.dll을 로드시킨후 EXPORT ADDRESS TABLE에서 해당값을 찾아보면 GetStartupInfoA함수의 RVA주소 라는 것을 확인 할 수있습니다.

정리하면 호출된 함수 내부로 들어가 함수의 시작주소를 구하고 해당 시작주소의 IMAGE BASE를 빼주면 실제 로드된 모듈의 RVA값입니다.

이것을 코드로 옮기면 됩니다.


<레지스터로 호출하는 함수 처리>

 

 

테스트를 위해서 c:\windows\system32\calc.exe를 대상으로 디버거에 로드했습니다.

빨간밑줄의 OPCODE는 FFD7입니다. 이 OPCODE도 함수를 호출할때 사용하는 코드입니다.

근데 기존 호출과 다른점이 레지스터를 이용하여 함수를 호출합니다.

레지스터를 이용하여 호출하는경우 OPCODE는 FFD0 ~ FFD7까지 사용합니다. 각각 D0 = EAX 레지스터를 뜻하며 순차적으로 나열하면 다음과 같습니다.

D0 = EAX / D1 = ECX / D2 = EDX / D3 = EBX / D4 = ESP / D5 = EBP / D6 = ESI / D7 = EDI 

그럼 FFD7 이므로 호출되는 레지스터는 EDI가 될것이고 EDI가 가르키는 주소값은0x75C1CD5C입니다.

이또한 calc.exe가 갖고있는 IAT의 주소값이 아닙니다. 이값또한 Kernel32.dll에서 로드된 IMAGE BASE값과 EXPORT ADDRESS TABLE의 RVA값을 더한 값입니다.

 

 

실제 함수내부로 들어가면 0x75C1CD5C가 함수 시작주소인것을 확인 할 수 있습니다.

 

 

IMAGE BASE주소를 확인하고 연산하면0x75C1CD5C - 0x75BD0000 = 0x4CD5C가 됩니다.

 

 

확인결과 정확히 일치합니다.

정리하면 레지스터를 사용하여 호출하는 함수인경우 bounding 처리된 PE에서 처리된 방식과 같이 함수내부로 진입하여 처음 함수 시작주소를 구하고 로드된 모듈에서 IMAGE BASE를 빼고 연산결과로 나온 RVA값을 로드된 모듈의 EAT테이블에서 찾는다. 로 정리할 수 있겠네요

바운딩 처리되었을때 함수호출과 레지스터를 사용하여 호출하는 함수에 대한 처리는 이와 같은 방법으로 해결 했습니다.

 

<로드되는 모듈(DLL) 관리>

 

위의 방법으로 각각의 상황을 처리하려면 우선 로드되는 DLL마다 각각의 PE의 정보들을 파싱해서 동적메모리를 생성한 후 저장해야 합니다.

처음 Kernel32.dll, User32.dll, ntdll.dll등의 로드되는 모듈의 파싱된 정보가 들어있는 구조체의 주소를 담기위한 2차원배열의 동적메모리를 할당합니다.

그리고 이 동적메모리는 모듈이 로드될때마다 이 코드가 포함된 함수를 호출하여 메모리를 할당합니다.

함수가 호출될때마다 기존에 있던 주소들이 새로생성된 주소로 변경됩니다. 하지만 이렇게 주소가 변경되었다 하더라도 이전에 있던 주소들은 해제를 하지 않았기 때문에 유효한 상태가됩니다.

그래서 이전에 생성 됬던 주소값들을 관리하기위한 변수가 위에서 선언된 2차원 동적메모리 배열변수입니다.

 

 

첫번째 그림에서 ExportAddressFunctionManagerArr 2차원 동적메모리 변수에 주소값들을 관리하기 위한 변수를 생성했다면, 다음으로 모듈의 PE정보를 갖고있는 구조체의 주소를 담기위한 1차원 동적메모리 변수 ExportAddressFunction선언 합니다.

그리고 ExportAddressFunctionManagerArr[0] = ExportAddressFunction으로 연결시킵니다.

그리고 제일 밑에 ExportAddressFunctionManagerArr을 의주소를 관리하기 위한 목적으로 _PDLLPtrBridge구조체의 PTR_ExportAddressFunctionManagerArr변수에 연결시킵니다.

이렇게 연결시키는 이유는 함수의 인자값으로 넘겨줄때 ExportAddressFunctionManagerArr,ExportOrdinalTableManagerArr,ExportNameTableManagerARR을 각각 넘겨줄 필요없이 _PDllPtrBridge 구조체 하나만 넘겨주고 필요한 정보는 또 이구조체에 넣어서 사용하면 편리하기 때문에 이렇게 여러번 연결시켜서 사용했습니다.

 

이렇게 각 정보들을 연결시켜서 최종적으로 완성된 _PDllPtrBridge 구조체는 아래와 같습니다.

 

이 구조체가 담고있는 정보는 함수이름을 파싱하기위한 핵심정보들로 구성되어있습니다.

맨위의 DllHandle,LoadDllImageBase,DllName은 로드된 dll의 핸들정보 dll의 IMAGEBASE정보 그리고 dll의 이름정보를 저장하는 멤버변수입니다.

이변수도 동적메모리 주소공간에 순차적으로 각 값들이 저장되며 멤버변수가 가르키는 주소값은 각값들이 저장되어있는 메모리주소의 시작번지 주소를 담고있습니다.

그리고 아래로 내려오면 SaveCount라는 멤버변수가 있는데 이 함수의 역할은 현재 로드된 dll의 정보를 저장한 횟수를 카운트하는 멤버변수입니다.

예로 ntdll.dll,Kernel32.dll,User32.dll,KERNELBASE.dll이 로드되서 각값들이 이 구조체에 저장이 됬다고 하면 SaveCount의 값은 4가 됩니다.

이렇게 카운트를 하는이유는 추후에 이러한 데이터들을 대상으로 PDllPtrBridge구조체를 배열형식으로 사용해서 값을 찾기 위함입니다.

그리고 이외의 로드된 모듈의 EXPORT DESCRIPTION TABLE 정보, Ordinal Talbe정보, Name Table정보,Address Function Table정보등등의 많은 정보를 담고있습니다.

 

 

<PDllPtrBridge 구조체에서 함수이름 탐색>

 

 

 

내용을 정리하면 위의 그림과 같습니다.

우선 로드되는 모듈에 따라 각각의 PE정보를 파싱하는데 DLL의 EXPORT DESCRIPTION TABLE의 정보를 기반으로 각각의 값을 파싱하며, 테이블에서 파싱되는 값은 다음과 같습니다.

 

Name Rva                          000B8C54        Kernel32.dll

Ordinal Base                      00000001        

Number of Function             00000554         

Number of Name                 00000554

Address Table RVA             B579C

Address Name Table RVA    B6C5C

Ordinal Table RVA               B81AC

 

실제 함수의 진입점 포인터를 저장하기위해 위에서 1차원 동적메모리 배열 ExportAddressFunction을 선언했었고 선언할때 EXPORT DESCRIPTION TABLE의  Number of Function 값만큼 할당을 하고 저장을 합니다.

그리고 나머지 ExportOrdinalTable 함수와 ExportNameTalbe 함수는 EXPORT DESCRIPTION TABLE의 Number of Name 값으로 세팅합니다.

ordinal과 name table의 크기를 같게 정하는 이유는 두개 테이블안의 데이터 갯수는 서로 동일하기 때문입니다.

하지만 Number of Function값은 오직 Address table의 함수갯수와 같은점을 고려해서 해당테이블의 특성에 맞게 사이즈를 할당해서 메모리를 할당하면 됩니다.

 

 

 

그리고 로드된 DLL의 PE데이터들을 관리적 편의를 위해 PDllPtrBridge구조체를 선언해서 관리를 해줍니다.

PDllPtrBridge 구조체의 구성변수들은 위에서 설명한대로 동적할당한 메모리 주소포인터, IMAGE BASE주소,로드된 모듈의 이름, EXPORT Table 정보등을 저장하는 역할을 합니다.

이런식으로 디버깅 루프를 돌면서 DLL 모듈을 호출하는 이벤트가 발생될 경우 PEPARSE함수 내부로 진입하여 동적메모리를 할당하고 PDllPtrBridge 구조체에 관련정보들로 채우게 됩니다.

즉 디버깅 대상의 소프트웨어 브레이크포인트(0xCC) 이벤트가 발생되기 전까지 PDllPtrBridge 구조체에는 ntdll.dll, Kernel32.dll, KERNELBASE.dll, COMDLG32.dll,msvcrt.dll 등의 프로세스가 실행될때 필요한 dll들의 각 PE정보들로 채우게 되는것 입니다.

 

 

<PDllPtrBridge 구조체에서 함수이름을 찾는 과정>

 

처음단계에서는 디버깅당하는 대상의 프로세스에서 소프트웨어 브레이크포인트(0xCC)가 발생되기 전까지 로드되는 DLL 모듈의 PE정보들을 수집하게되고 이 수집된 데이터들이 있는 주소값 및 정보들을 PDllPtrBridge 구조체에 저장하여 관리를 한다고 했습니다.

그럼 이제 실제 함수가 호출되고 해당함수의 DLL에서 함수이름을 찾는 과정에대해 설명하겠습니다.

 

위에서 그림을 가져왔습니다 그림 넘버링 예정

 

 

다시 위의 몇번 그림을 가지고 설명하겠습니다.

우선 현재의 EIP가 0x000C36A6 이고 OPCODE FF15 FC100C00입니다. 하지만 이 주소값은 디버깅 되고있는 프로세스 IAT테이블에서 찾을수 없습니다.

그럼 일단 EIP를 한단게 더진행하여 GetStartupInfoA함수 내부로 진입합니다.

 

 

진입하면 EIP는 함수 시작주소인 0x75BD1E10으로 세팅이 됩니다.

여기서 처리되는게 가장중요한데. 함수 내부로 진입시 내부가 시스템 DLL영역인 경우가 있고 다음과 같이 시스템DLL영역이 아닌경우가 있습니다.

 

시스템 DLL이 아닌경우 함수가 호출되기 전과 호출되고 난 후의 주소값의 거리가 비슷합니다.

그러나 위의 그림에서와 같이 시스템 DLL인 경우 주소영역대가 0x7xxxxxxxx 번대로 할당되어 사용되어집니다

다시 설명드리지만 메모리에 로드되는 주소값(VA)은 IMAGEBASE + AddressFunctionRVA 값으로 구성되어있습니다.

 

그럼 다시 로드된 IMAGE BASE의 주소를 확인해 보겠습니다.

msvcrt.dll, Kernel32.dll, OLEAUT32.dll 모두 시스템 DLL들이며 주소영역대가 0x7xxxxxxxx대인것을 확인할 수 있습니다.

구글에서 이와관련된 자료를 겨우 찾았는데 다음과 같은 정보를 찾았습니다.

 

The system DLLs for Windows are currently based in memory from 0x70000000 to 0x78000000 on the Intel processors and from 0x68000000 to 0x78000000 on the MIPS processors.(http://www.codeproject.com/Articles/35829/Modify-the-Base-Addresses-for-a-DLL-Files-Series)

 

intel processor에서 사용되는 시스템 DLL의 영역은 0x70000000 to 0x78000000 이라고 하네요.

ASLR이 적용이 된다 하더라도 저 범위안에서 주소값이 결정되는것으로 생각이 됩니다.

하지만 이러한 정보들 대부분이 작성된시기가 2006년정도라서 신뢰되는 정보인지 의심이 되지만 그냥 믿고 저대로 코드를 작성했습니다.

현재의 EIP가 0x70000000 to 0x78000000 이면 Context 구조체에서 ESP레지스터 함수시작주소에 진입하는 순간 ESP는 리턴되는 주소로 세팅되어집니다.

그래서 ESP주소에 브레이크포인터(0xCC)를 걸고 탈출하게끔 작성했더니 윈도우7기준으로 아무이상 없이 함수를 잘 찾아줬습니다.

그러나 윈도우XP에서 문제가 발견되었는데 함수를 찾지 못하는 현상이 발견되었습니다.

그이유는 윈도우 XP에서 시스템 DLL이 로드되는 주소가 0x7Cxxxxxx 번대로 로드가 되는겁니다.

제가 예외처리한 영역의 주소는 0x70000000 to 0x78000000 이기때문에 시스템 DLL이라고 하더라도 저 주소 영역에 포함되지 않기때문에 그냥 진행해서 시스템DLL의 내부에서 호출되는 함수들까지 다 처리를 해버리는 경우가 발생되었습니다.

그래서 저부분을 그냥 0x70000000 to 0x80000000 번대로 바꿔주니 간단히 해결되긴했는데 조금더 테스트를 해봐야 정상적으로 작동하는지 정확히 알수 있을듯 싶습니다.

 

 

자 그럼 이제 0xC36A6 GetStartupInfoA 의 함수 내부로 들어와서 함수의 시작주소인 0x75BD1E10로 왔다고 가정합니다.

그럼 0x75BD1E10이 0x70000000 to 0x80000000 내에 포함되니까 if문으로 들어오게 되고 CONTEXT구조체의 ESP가 가르키는 주소에 리턴주소가 들어있기 때문에 해당 주소에 브레이크 포인터를 세팅합니다.

그리고나서 ParsingLoopDllExportName이라는 함수에 진입하게 되는데 이 함수의 역할은 루프를 돌면서 PDllPtrBridge 구조체에서 0x75BD1E10대상의 ExportAddressTable의 주소값과 비교하고 비교후 해당값에 맞는 데이터를 ExportOrdinalTable값에서 조사후 ExportNameTable에서 해당주소값의 함수이름을 찾아주는 역할을 합니다.

 

이 과정들을 좀 더 자세히 보겠습니다.

우선 GetStartupInfoA  함수의 진입 주소가 0x75BD1E10 이며 EIP의 주소값도 동일한 주소값으로 세팅된 상태입니다.

 

 

 

SaveCount의 값으로 로드된 dll의 갯수만큼 루프를 돌면서 LoadDllImageBase(0x75BD0000)라고 가정하고 0x75BD1E10 - LoadDllImageBase(0x75BD0000) = 0x1E10 값을 빼줍니다.

그리고 다시 루프내의 루프를 돌면서 PTR_ExportAddressFunctionManageArr[DllList][EATLocation] 저장된 모든 DLL들의 ExportAddressFunction RVA값과 비교를합니다.

 

 

 

루프를 돌면서 동일한 RVA값을 찾고 0x1E10까지 카운트한 값은 263입니다.

PEVIEW에는 264라고 표기되어있는데 PEVIEW값과 다른 이유는 PEVIEW에서는 COUNT를 1부터시작해서 0x1E10이 있는곳까지의 카운터가 264가된것이고 제가만든 프로그램에서는 0부터 카운터 했기때문에 264 - 1 = 263이 되는것입니다.

 

 

이제 263 값을 ExportOrdinalTable에서 0부터 1씩증가하면서 263이란 Ordinal 값을 갖고있는 녀석을 찾습니다.

Ordinal값 263이 있는곳까지 0부터 1씩증가해서 최종적으로 카운트된 값은 610 값입니다.

 

 

 

 

Export Ordinal Table에서 카운트된 610의 값을 그대로 ExportNameTable에 매치시키면 해당 함수명을 구할 수 있습니다.

이게 가능한 이유는 위에서 설명했었는데 ExportDescriptorTable에는 Address Table이 위치하는 주소값, Ordinal Table이 위치하는 주소값,NameTable이 위치하는 주소값들을 갖고있습니다.

그리고 각 값들이 몇개가 있는지 명시하는 부분이 있었는데요.

그부분이 NumberOfFunction과 NumberOName값이었습니다.

위에서도 언급한대로 NumberOfFunction의 갯수는 AddressTable의 RVA갯수와 같고 NumberOfName의값은 OrdinalTable과 NameTable의 RVA갯수로 사용합니다.

즉 NumberOfName = 8 이면 NameTable의 RVA갯수가 8개라는 얘기고 동시에 OrdinalTable의 RVA갯수도 8이라는 뜻이됩니다.

이러한 방식으로 DLL에서 함수네임을 파싱해서 출력해줍니다.

 

 

 

<프로그램 테스트 분석결과>

 

 

C:>dbg.exe -auto -debugreg c:\windows\system32\notepad.exe 커맨드로 노트패드 프로그램을 분석한 결과입니다.

노트패드 및 계산기,지뢰찾기 등등의 GUI 프로그램은 이벤트 드리븐 방식으로 동작하기 때문에 노트패드 프로그램 영역에 마우스를 움직이거나 클릭하거나 글을쓰거나 할때마다 이벤트가 발생되며 관련된 함수들을 호출하며 루프형식으로 이벤트를 처리합니다.

이렇게 실시간으로 발생되는 이벤트들도 AnalysisReport.txt에 함수가 호출된 순서대로 실시간 로그를 남깁니다.

 

 

 

C:>dbg.exe -auto -debugreg c:\windows\system32\winmine.exe를 커맨드로 지뢰찾기 프로그램을 분석한 결과입니다.

 

 

마찬가지로 C:>dbg.exe -auto -debugreg c:\windows\system32\mspaint.exe를 분석한 결과 입니다.

 

 

 

위와같이 분석이 진행되어 함수가 호출될때마다 실시간으로 txt파일에에 로그를 기록하도록 만들었습니다.

텍스트파일이 생성되는 파일명 규칙을 설명드리겠습니다.

우선 프로그램을 실행시켰을때 첫번째로 파일이 생성된 요일/월/시/분/초/년도/분석대상 실행파일이름/분석모드/부분분석 순으로 저장이 됩니다.

ex)notepad_auto.txt 인경우

dbg.exe -auto c:\windows\system32\notepade.exe로 명령어를 입력하여 분석시 로그파일이름은 날짜_notepad_auto.txt와 같이 생성이되며

부분분석인경우(특정 메모리주소의 시작과 끝을 정하여 분석하는 방법 자세한 내용은 뒤에 설명드리겠습니다.)

dbg.exe -auto 0x400345d 0x400348d c:\windows\system32\notepade.exe로 명령어를 입력하여 부분분석을 진행하여 분석시 로그파일 이름은 _notepad_auto_part.txt 로 분류되어 저장이됩니다.

 

 

AnalysisReport.txt의 내용을 확인해보면 호출된 함수의 EIP주소 그리고 함수명을 확인 할 수 있습니다.

결과 내용을 살펴보면 루프를 돌면서 반복적인 패턴으로 함수들을 중복호출하여 사용하는것을 확인할 수 있습니다.

로그 내용에서 the called funtion name is found in the EXE IAT table과 the called function name is found in the DLL EAT table 두가지 로그로 확인할 수 있는데요

이차이점을 간략히 말씀드리면 우선 함수 호출에 해당하는 OPCODE를 분석하고 LittleEndian을 BigEndian으로 변경해주면 VA주소값이 되는데 이값을 RVA값으로 변경하여 디버깅중인 프로세스의 IAT 함수를 뒤저서 해당되는 값의 함수 이름을 찾게 됩니다.

여기서 MapviewofFile 함수로 메모리에 올려진 PE의 바이너리 데이터들은 로더가 올린 프로세스가 아니기 때문에 IAT의 주소값을 계산해보면 INT를 가르키는 것을 확인 할 수있게 됩니다.

이런점을 이용하여 IAT테이블의 값을 가르켜 사용되는 함수 이름을 가저올수 있게 했습니다.

그리고 두번째 the called function name is found in the DLL EAT table 는 IAT테이블에서 함수이름을 찾지 못하는 경우가 간혹있습니다.

그런경우는 Bounding 처리된 PE구조(IAT주소를 지정된 주소로 고정시키는방식)을 사용할때 Step in 함수 내부로 들어갈경우 RAW파일에서의 IAT테이블에 고정된 주소값과 로더에의해 실행 되어질때의 주소값이 서로 다르게 처리되어 OPCODE를 분석후 IAT테이블에서 함수주소를 찾기에 어려움이 있었습니다.

그리고 또한가지의 어려움은 레지스터를 사용하여 CALL 요청을 하는 부분 EX)CALL EDI or CALL EAX 레지스터를 이용하여 함수를 호출하는 경우 OPCODE자체가 FFD1 ~ FFD7을 사용하게됩니다.

여기서 D1~D7은 각각 D1 = EAX, D2 = ECX, D3 = EDX.. 와 같이 레지스터와 1:1매칭되어 사용되어 지는데 OPCODE자체가 FF15 38130004 인경우 FF15 40001338로 VA이기 때문에 IAT테이블에서 값을 찾을수있지만 FFD1 같은 OPCODE인경우 RVA값이 아니기 때문에 IAT테이블에서 값을 찾을수 없습니다.

그래서 프로그램을 대폭 수정하였습니다.

먼저 디버깅 대상프로세스가 로드되면서 프로세스에서 사용될 DLL들이 호출되는데 호출되는 각 DLL들마다 PE구조를 분석하고 메모리를 할당하여 저장합니다.

예로 KERNEL32.DLL/NTDLL.DLL/USER32.DLL 등등 3개인경우 각각의 PE를 분석하는데 EXPORT ADDRESS TABLE/EXPORT NAME TABLE/ EXPORT ORDINAL 테이블을 모두 저장하도록 해놓고 만약 FFD1인 경우 Step in 함수내부로 진입하여 진입된 EIP주소값과 로드된 DLL들의 PE의 Image Base 값들을 순차적으로 빼고 RVA를 구하여 각각 순차적으로 EXPORT ADDRESS TABLE/EXPORT NAME TABLE/ EXPORT ORDINAL TABLE 들을 뒤저서 함수 이름을 찾도록 구현했습니다.

 

 

<기능 및 옵션 설명>

 

1.help 기능

 

 

 

프로그램을 사용하면서 분석대상 실행파일 위치를 잘못지정 했거나 옵션을 잘못지정 했을경우 메뉴를 확인할 수 있게 만들었습니다.

한글로 되어있기 때문에 한국인?이라면 쉽게 사용법을 확인하실수 있습니다.

 

2. auto mode

 

auto mode는 말그대로 프로그램의 시작부터 끝까지 자동으로 분석을 하고 그 결과를 리포트 하는 기능입니다.

사용 예)c:\> xxx.exe -auto c:\windows\system32\notepad.exe

 

<Auto mode 분석 예시>

 

3.Step mode

 

 

Step Mode는 제가 디버깅 하기위한 목적으로 만들었습니다.

Step Mode Original Entry Point에 브레이크 포인트를 걸고 순차적으로 디버깅을 진행합니다.(그냥 보통 디버거의 기능이라고 보시면됩니다.)

수동으로 직접 Enter를 입력하여 한라인씩 분석을 해야하므로 제가 개발한목적인 함수의 흐름을 파악하기에는 그닥 좋은 기능이 아닙니다.

즉 이 기능은  분석시간이 오래걸립니다.

사용 예)c:\> xxx.exe -step c:\windows\system32\notepad.exe


4.Manual Mode

 

 

Manual Mode모드는 함수가 호출되는 시점에 브레이크포인트를 걸어서 분석을 진행하게 됩니다.

OEP와 함수호출이 아닌 어셈코드를 모두 건너뛰고 함수를 호출하는 부분만 분석을 해서 결과를 리포트해 주는 기능입니다.

Console로 리포트해주는 결과는 EXE PE상의 IAT에 있는 함수이름을 호출해주는 결과와 함수내부로 진입하여 주소값을 계산하고 주고값이 시스템 DLL인경우 해당 DLL의 EXPORT TABLE값에서 함수를 찾기때문에 두가지의결과 FOUND IN DLL IMPORT/ FOUND IN DLL EXPORT 두가지로 나보여주게 됩니다.

 사용 예)c:\> xxx.exe -manual c:\windows\system32\notepad.exe


5.Partial Analysis Mode 




Partial Analysis Mode 는 분석대상의 메모리주소를 지정하면 지정된 영역만 분석을 진행하고 그 결과를 리포트 해주는 기능입니다.

예로 코드를 분석중 전체 분석이 필요없는 부분인 경우나 분석중에 특정영역에서 호출되는 함수를 파악하고 싶을때 사용할 수 있는 기능입니다.



부분분석을 진행한 결과입니다.

테스트시 주소를 지정한 영역은 0x34308d ~ 0x34309d 까지입니다. 첫번째 0x0으로 나오는 이유는 버그수정을 해야하는데..... 넘흐 귀찮네요 .. 뭐사용하는데 지장은 없으니 나중에 정 불편하면 그때 고처야겠습니다. ㅋ

사용 예)c:\> xxx.exe -auto 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -step 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -manual 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -auto -debugreg 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -step -debugreg  0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -manual -debugreg 0x34308d 0x34309d c:\windows\system32\notepad.exe


6. DebugRegisterView Mode



DebugRegisterView Mode는 명령어 두번째 옵션중 -debugreg 옵션을 주게 되면 사용할 수 있습니다.

해당 옵션의 기능은 그림에서와같이 현재 EIP의 레지스터와 스택상태를 보여주는 옵션입니다.

이 기능 또한 제가 디버깅용으로 만든거기 때문에 수동으로 디버깅하지 않는이상 안쓰시는게 분석속도를 향상시키는데 도움이 됩니다.

 

 

<악성코드 샘플분석 결과>

 

요즘 제가 K사교육을 듣고있는데 거기서 수집한 악성코드 샘플입니다. 실제 악성코드라고 하네요..

아직 수업중에는 분석하지 않았는데 어떤 악성코드인지 궁금하네요 그래서 겸사겸사 프로그램도 테스트해볼겸 한번 돌려 보겠습니다.

 

우선 분석대상은 malware.exe이름의 악성코드입니다.

 

 

 

 

virustotal 확인결과입니다.

총 3개의 섹션으로 구성되어있구요. PE확인결과 패킹은 되어있지 않았습니다.

 

 

역시 VirusTotal 에서 확인한 결과입니다.

PE IMPORT 정보입니다.

해당 파일에서 사용되는 함수들 리스트 입니다.

PE IMPORT정보만 보면 대략 어떤행위를 하는지 예측할 수는있지만 저 리스트가 순차적으로 실행되는게 아니기 때문에 어떤함수를 호출하는지 알수는 없습니다.

그래서 저 함수들을 실행하는 순서대로 리스트화 해주는게 제가 제작한 프로그램의 목적입니다.

그럼 이제 돌려보도록 하겠습니다(두근두근ㅋㅋ)

 

 

 

옵션은 간단히 -auto 로해서 OEP에서부터 프로그램의 끝까지 자동으로 돌렸습니다.

한 1분정도 뒤에 종료가 되네요.. 결과를 확인해보겠습니다.

 

 

흠.....@_@ 결과를 뽑는건 만족하지만 리포트가 조금 그렇네요..(좀 많이 그런가..ㅋㅋ)

Cuckoo샌드박스 보고서처럼 만들어 주면 쓸만할것 같은데......

 

 

GetModuleFileName으로 호출된 모듈의 파일이름을 얻어오는 함수인데요.

이 함수 써서 만들려다가 어떤 헤더파일 선언해서 써야하는데 msdn찾아보니 뭐또 설치하라고해서 그냥 제가 만들어서 썼습니다.

대략 저 함수는 호출된 모듈의 핸들을 인자로 넘겨주면 호출된 모듈의 파일이름 ex)Kernel.dll, Ntdll.dll등 의 이름을 가저오는 역활을합니다.

이 함수 호출이후 중앙에 보면 0x402943 에서 402180함수를 반복적으로 호출해서 사용하는것을 확인할 수 있습니다.

이 부분은 루프문으로 보시면 됩니다.

그리고 하단에 보면 3개의 블록이 있는데 이 역시 4025CB, 4025D0, 402609, 40345C를 순차적으로 호출하고 3번 반복하는것으로 볼수 있습니다.

여기서 4개의 함수(4025CB, 4025D0, 402609, 40345C)가 한세트 인것으로 볼수 있습니다.

아마도 이 4개의 함수 호출이후 분기문에 의해 호출되는 함수가 틀려지는것으로 예상됩니다.

 

 

그리고 여기서부터 함수호출은 파일관련해서 조작하는것으로 볼 수 있습니다.

우선 GetWindowsDirecotryA함수로 윈도우 디렉토리경로를 얻고 GetModuleFileName으로 파일이름을 얻고난 후에 CreateFileA함수 파일을 읽던지 쓰기던지 하기 위해 파일스트림을 열고

SetFilePointer로 파일포인터를 세팅한 다음 그 부분부터 ReadFile함수로 데이터를 읽고있는것을 확인할 수 있습니다.

ReadFile함수부터는 루프문으로 반복적으로 특정데이터의 값을 읽고 있는것을 확인할 수 있습니다.

 

다음으로 다시 SetFilePointer함수로 파일포인터를 세팅하고 ReadFile 파일을 읽고 WriteFile 파일을쓰고를 반복하는것을 확인할 수 있습니다.

 

 

이 부분은 RegCreateKeyExA,RegSetvalueExA함수를 사용하는것으로보아 레스트리에 특정값을 쓰는것으로 추측할 수 있습니다.

뭐 그리고 GetSystemDirectoryA함수로 윈도우 시스템디렉토리 경로를 얻고  또 파일을  WriteFile로 데이터를 쓰고 있습니다.

 

 

여기서는 CreateToolhelp32Snapshot 함수로 프로세스의 상태정보를 얻는?(아마도 프로세스 관련 구조체를 여기에 넣고 이 함수 호출해서 Process32First로 처음 프로세스찾고 Process32Next로 다음 프로세스 찾는걸로 알고있는데 다시한번 봐야겠네요 오래되서 가물가물) 아무튼 Process32First,Process32Next함수로 특정 프로세스를 찾는듯 합니다.

 

여기는 OpenProcess가 호출되는거보니 위에서 Process32Next함수를 반복 호출해서 프로세스를 탐색해서 찾은것 같습니다.

여기는 일단 OpenProcess로 찾은 프로세스를 여는것같네요 그리고 VirtualAllocEx로 동적메모리를 생성하고 그 영역에 WriteProcessMemory로 값을 쓰는것 같습니다.

 

 

계속해서 프로세스를 찾아서 VirtualAllocEx함수와 WriteProcessMemory,GetmoduleHandleA,GetProceAddress,CreateRemoteThread,WaitForSingleObject를 사용하는것으로보아 코드를 인젝션하는것으로 예상되네요.

프로세스를 탐색하고 해당 프로세스영역에 가상메모리를 할당하고 그 영역에 특정 데이터를 쓰는것같은데요

상세한건 OLLYDBG나 IMMUNITY DBG와같은 전용 디버거로 분석하면 단번에 알수 있겠네요

이상 허접한 개발후기였습니다.


 

혹시 사용해 보고싶은 신분들은 비밀댓글로 이메일주소 남겨주시면 보내드립니다으리!!

 

'[PROGRAMMING]' 카테고리의 다른 글

코드분석 도구 개발하기(C언어)  (10) 2014.06.08
#1 DLL(묵시적 링킹)  (0) 2012.09.20
posted by 미스터리 DKL