안녕하세요. Message 입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 9-1을 분석합니다.

점점 분량이 늘어나고 있습니다...

스크롤 압박이 다소 있지만, 중간중간 필요한 서브루틴을 적절히 검색하시면 편리합니다~

 

※ 분석환경 : WindowsXP, Windows7, Vmwre 12.1.0 build, IDA 6.1 and 6.6

 

 

 

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

0x00 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1. PE구조 & 패킹

별도의 패킹이 되어 있지 않습니다.

 

2. DLL

KERNEL32, ADVAPI32, WS2_32 등은 자주 보아왔던 DLL이었지만

SHELL32 ShellExecuteA 함수가 눈에 띄었습니다.

어디선가 보았다 싶어서 찾아봤더니 OLE 취약점 분석 포스팅에서 VBScript에 있던 함수명이네요.

해당 취약점에서는 ShellExecute 함수를 이용하여 cmd.exe 파일을 실행시키는 용도로 사용하였는데

이번 케이스는 어떻게 사용될지 궁금합니다.

 

3. String

1) .data 섹션

레지스트리 경로, URL, system32 폴더, CMD 명령어, Service 관련 문자열들이 눈에 띕니다.

네트워크 통신과 더불어 시스템에 영향을 미치는 기능을 수행하는 악성코드로 예상됩니다. 

 

2) .rdata 섹션

대부분이 프로그램 생성시 포함되는 소스코드 또는 Import DLL 목록으로 판단됩니다.

R6016~R6028로 시작하는 문자열들이 많이 등장하고 있습니다.

프로그램 로드 또는 실행시 CRT에 의해서 발생하는 C Runtime Error 관련 에러 문자열입니다.

이번 문제 역시 Lab06-01 문제와 동일하게 printf 를 WIndows API로 명명해주지 않기 때문에

내부에서 사용되는 함수/문자열들이 Hex-ray를 통해 소스코드로 변경되면서 수면 밖으로 나온것으로 판단됩니다.

(해당 문자열들을 XREF로 추적하면 sub_402E7E(printf) 에서 사용되고 있습니다.)

MSDN : https://msdn.microsoft.com/en-us/library/6f8k7ad1.aspx

 

Tip. String Window에서 .data vs .rdata 섹션

 IDA를 이용한 초기 동적분석에서 항상 살펴보는 곳이 String Window입니다.

 그리고 String 값들은 대게 data/rdata 섹션에 존재했습니다. 어떤 차이일까요?

 

 data Section

 섹션헤더 Chracteristics 필드를 살펴보면 READ/WRITE 속성을 모두 가지고 있습니다.

 저장되는 변수는 초기화된 전역변수(global)와 정적변수(static) 등 입니다.

 

 ② rdata Section

 CONST변수, 하드코딩(printf 등) 문자열, 소스코드에 포함된 문자열 등이 저장됩니다.

 또한 Import 되는 DLL 목록이 저장되기로 하므로, API들과 DLL 이름등이 저장됩니다.

 

 ③ Test

 VS2015로 global, static, local 타입의 포인터/배열을 선언하여 테스트해보았습니다.

 테스트 환경은 학습을 위해 release 모드로 빌드후 XP에서 실행하였습니다.

   LOCATION : 선언된 변수의 위치,   VALUE : 문자열 위치,   STR : 문자열

 

 

 위 결과를 기반으로 정리하면 아래와 같습니다.

 ① ptr String : .rdata 섹션

 ② arr String : 전역변수  data섹션 (const 제외) , 지역변수  Thread Stack (static 제외)

 포인터 스트링은 모두 .rdata 섹션이며, 배열 스트링은 상황에 따라 복잡합니다.

 배열이라 하더라도 CONST는 상수이므로 자연스럽게 rdata 섹션에 저장되며,

 나머지는 data + Thread Stack 영역에 저장됩니다. 

 

 <참고>

 주소값들을 살펴보면 스택이 PE보다 낮은 주소에 있습니다.

 처음에는 오류인줄 알고 헷갈렸습니다. 왜냐하면 스택은 높은곳에서 거꾸로 자라며,

 계속 자라면 Heap하고 충돌...등등 어디서 주워들은 얘기로만 기억하고 있었고,

 구글링을 해보아도 같은 얘기를 하는게 맞나 싶을 정도로

 다양한? 메모리 구조 그림들이 나왔습니다.

 (아마 Unix계열과 Windows계열이 섞여서 돌아다녀서 그런가봅니다.) 

 

 Vmap으로 메모리를 힙/스택 모두 PE보다 상단(낮은주소)에 있었으며

 이것에 맞는 메모리 구조를 표현한 그림은 아래와 같았습니다.

  Thread Stack : 0x00030000 ~ 0x0012FFFF

 

 

 *그림출처 : http://www.securitysift.com/windows-exploit-development-part-1-basics

 

 

 

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

0x01 정적 분석

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

문제풀이를 위한 정적 분석을 진행합니다.

 

 

1. main 함수 - 인자가 1개인 경우

main 함수에 진입하면, __alloca_probe 함수를 통해 스택을 0x182C 만큼 할당한뒤

전달된 인자의 개수가 1인지 체크합니다. 만약 결과값이 1이라면 sub_401000 함수를 호출합니다.

__akkica_probe 함수의 경우 지난 문제에서 상세 분석을 마쳤으므로, 무난하게 패스합니다.

 

 

sub_401000 함수를 살펴보면,

RegOpenKeyExA 함수를 이용하여 빨간색으로 배경이 지정된 문자열의 레지스트리 키를 Open하고

"Configuration" 항목값을 가져옵니다.

 

가끔 IDA에서 String 값을 제대로 못읽어와서 위에처럼 빨간색으로 처리되는 경우가 종종 발생하는데

(불행하게도 아직 해결하는 방법을 몰라요)

함수에 들어가는 인자값들은 OllyDbg를 통해 좀더 직관적이고 빠르게 분석이 가능합니다.

레지스트리키는 HKLM하이브의 "SOFTWARE\Microsoft\WPS" 이며, 해당 키에서 "Configuration" 항목값을 가져옵니다.

※ sub_401000 정리 : 레지스트리 존재 여부 체크

 

레지스트리 open에 실패하거나, "Configuration" 항목값이 존재하지 않을 경우 sub_402410 서브루틴을 호출합니다.

sub_402410 함수의 경우, 아래 그림에서 보다싶이 main에서 매우 빈번하게 호출되는 함수입니다.

해당 함수의 내부를 살펴보면, GetModuleFileNameA, GetShortPathNameA 함수를 이용하여

Full/Short Path를 얻으며, 이후에 "/c del" 문자열을 Parameters 변수에 복사합니다. 

 

 

이후 이어지는 코드는 IDA에서는 비교적 복잡해 보이지만

이전 문제에서 한번 분석해본 내용으로서, do-while 문으로 문자열의 길이를 구하는 내용입니다.

현재 szLongPath 변수에 Short Path가 들어있으니, Short Path의 문자열 길이를 얻게 됩니다.

이러한 do wile문은 버전이 낮은 Hex-rays에서 문자열이 들어가는 함수가 나올경우 자주 등장하는

패턴입니다. 버전을 높이면 생략되더군요.

 

 

첫번째 탈출 조건은 변수 v0에 0xFFFFFFFF(-1)을 할당한 후 0이 될때까지 -1을 하여서 0이 되면 break 합니다.

두번째 탈출 조건은 문자열이 담긴 v1 변수에서 문자열을 끝을 나타내는 null 문자 발견입니다. 

처음에는 아래의 소스코드가 헷갈렸는데,

*v1++ == 0 조건이 성립할때 v2에 *v1++을 할당하는 내용으로 해석하면 될 것같습니다.

 ex) v2 = *v1++ == 0;   if( 변수 또는 계산식 == 0 ) v2 = 변수;

 

이후에 v4 = ~v0; 코드로 값을 반전하여 글자수를 얻습니다. 글자수는 8자리네요.

 

이어지는 코드는 비슷하게 Parameters 변수에 저장된 "/c del " 문자열의 길이를 동일 로직으로 얻습니다.

특이하게 v7 변수에 &v1[-v4] 주소값을 할당하는 부분이 있습니다.   v2 = *v1++ == 0;  

위 코드에서 *v1++ 로 인해 증가된 v1의 의 주소값을 다시 "/c del " 문자열을 가리키는 주소로 보정하기 위함입니다.

 

이후에 memcpy 함수를 이용하여 v7에 저장된 문자열을 (void *)(v6 -1)

주소값에 복사하여 아래의 문자열을 완성합니다.

경로값을 고려해서 del 문자열 뒤에 한칸 스페이스가 삽입되어 있는 부분을 체크합니다.

 

역시 동일한 방식으로 ">> NUL" 문자열의 글자수를 계산하고

memcpy 함수를 통해 최종문자열 "/c del c:\단축경로\Lab09.01.exe >> NUL" 문자열을 완성시킵니다.

해당 문자열의 의미는 스스로 파일을 삭제함과 동시에, 에러 출력을 버리는 의미를 가집니다.

완성된 문자열은 ShellExecuteA 함수를 통하여 CMD 명령어로 사용됩니다.

 

저는 몰랐지만, OllyDbg로 실행하고 있기 때문에 윈도우가 파일삭제를 허용하지 않는다고 하네요.

(사실 실수로 몇번 브레이크 포인트를 지나서 실행해버린 바람에 가슴철렁 했었는데...)

이후 exit 함수로 프로그램을 종료합니다.

※ sub_402410 정리 : 레지스트리 체크 + 디스크에서 악성코드 삭제

 

sub_401000 서브루틴에서 레지스트리가 존재할 경우, 프로그램이 종료되지 않고 sub_402360 서브루틴을 호출합니다.

해당 서브루틴의 내부 로직을 살펴보면, 원하는 조건이 충족될때까지 무한 루프를 수행합니다.

정확한 행위를 분석하려면 while문 내부의 sub_401280sub_402020 함수의 분석이 필요합니다.

 

sub_401280 서브루틴을 살펴보면,

sub_401000 서브루틴에서도 보았던 "\\XPS" 레지스트리를 Open하고 "Configuration" 값을 한번더 체크합니다.

계속 중복체크하는것을 보니, 분명 악성코드의 중요 행위중에 하나로 보여집니다.

만약 해당 레지스트리가 존재하지 않을 경우 함수의 인자로 받은 a1, a3, a5, a7 변수에

"Configuration" 항목에서 받아온 내용을 글자수만큼 주소값을 이동하며 복사합니다.

하지만 아직은 어떤 문자열이 해당 변수에 저장되는지는 알 수 없습니다.

※ sub_401280 정리 : 레지스트리에서 "Configuration" 항목값을 얻어와서 변수에 할당 

Tip. Hex-ray에 주석남기기

 이번 문제와 같이 함수에 함수가 꼬리를 무는 경우, 헷갈리기 시작하면서

 분석시간이 길어질 수 있습니다.

 이럴땐 단축키 "/" 를 눌러서 아래와 같이 주석을 남길 수 있습니다.

 사실 간단한 부분인데...이런 노하우가 늘어날수록 분석 시간이 조금이라도 단축되겠죠.

 

 

다시 while 문으로 돌아갑니다.

위에서 분석한 sub_401280 서브루틴으로 "Configuration" 항목값에 있는 값들을 변수에 담아 왔으니

이후에 실행되는 sub_402020 서브루틴에서는 해당 값들을 활용할 것으로 예상됩니다.

 

 

 

2. 명령어 제어 프로토콜(sub_402020) - Part1(명령어수신)

서브루틴 내부를 살펴보면, 가장 먼저 실행하는 sub_401E60 서브루틴을 비롯한 여러 문자열들이 보입니다.

"SLEEP", "UPLOAD", "DOWNLOAD", "CMD" 등의 문자열이 무엇을 의미하는지 알아볼 필요가 있습니다.

 Tip. Hex-ray 옵션 - Strings Generate names

 IDA 버전을 6.6으로 업그레이드 했습니다.

 Hex-ray 코드가 좀더 간결해 졌지만, 문자열이 아래와 같이

 변수명으로 표현되어 가독성이 떨어집니다.

 

 IDA 옵션에서 한참 삽질을 하다보니.. Hex-ray 옵션이 따로 있더군요.

 IDA 설치경로의 cfg 폴더에서 hexrays.cfg 파일로 옵션을 설정합니다.

 Analysis 옵션의 HEXOPTIONS 값에서 read-only만 literal로 출력하는

 HO_CONST_STRING 옵션 비트 조정합니다. ex) 0x01FF → 0x01BF

 

 이제 아래와 같이 Hex-ray 코드에서 문자열이 표현됩니다.

 

 

먼저  sub_401E60 서브루틴을 살펴봅니다.

저같은 경우 sub_401E60 서브루틴 내부에 있는 함수들이 단발성(Xref에서 하나의 상호 참조만 있는) 함수들이

많은데다가, 전체적인 기능 파악이 안되어 있는 상태에서 하나만 계속 파고드는 느낌이 들어서

바로 분석하기 부담을 느꼈습니다. 그래서 처음엔 pass 하고 가장 마지막에 분석하였습니다.

이건 아마 분석하시는 분들의 성향에 따라 달라질듯하네요.

 

일단 가장 먼저 실행되는 서브루틴은 sub_401420 서브루틴으로서, 내부를 살펴보면

바로 위에서 분석한 sub_401280 서브루틴의 축소판입니다. 함수 자체가 BOOL 타입이지만,

포인터를 활용하여 인자로 전달된 &name 변수에 레지스트리에 저장된 문자열을 가져옵니다.

아래쪽에서 레지스트리를 등록하는 부분이 분석되어 있지만, 미리 컨닝을 하자면 아래의 URL을 가져옵니다.

※ sub_401420 정리 : 등록된 레지스트리에서 URL 값을 가져옴, sub_401280의 축소판 

 

두번째로 실행되는 서브루틴은 sub_401470 입니다.

해당 서브루틴 역시 내부에서 sub_401280 서브루틴을 호출하고 있으며,

인자로 전달된 a1 변수에 아래와 같이 포트번호로 보이는 문자열 "50(=80)" 값을 저장합니다.

레지스트리에 등록되는 값이 문자열이기 때문에 atoi 함수를 이용하여 정수값으로 변환합니다.

※ sub_401470 정리 : 등록된 레지스트리에서 포트번호를 가져옴, susub_401D80b_401280의 축소판 

 

세번째로 실행되는 서브루틴은 sub_401D80 입니다.

눈에 띄는 부분은 time 함수와 왠지 복잡하게 보이는 여러개의 for문입니다.

먼저 time 함수값이 어떻게 사용되는지 살펴보면,

sub_4030E0 서브루틴에 전달되어 단순하게 dword_40C180 변수에 값을 할당합니다.

왜 이런 함수가 있을까 궁금해서 Xref로 검색해보니 rand 함수 내부에서 해당 변수가 사용되고 있습니다.

 

rand 함수 내부를 살펴보면 time(NULL) 값을 바탕으로 난수값을 return 하고 있습니다.

rand 함수는 srand 함수를 미리 호출하지 않으면 동일한 난수값을 발생시키므로,

time(0) + sub_4030E0 서브루틴의 조합은 srand(time(NULL)) 과 동일한 기능으로 판단됩니다.

 

for문의 경우 포인터의 주소값을 1씩(BYTE 단위) 증가시키며 sub_401D10 서브루틴의 값을 저장합니다.

중간중간 구분자로 보이는 47("/"), 46("."), 0(NULL) 값을 넣어주는군요.

 

어떤값을 채우느냐가 관건입니다. sub_401D10 서브루틴을 살펴봅시다.

do-while 문으로 조건이 충족안될때까지 실행을 반복합니다.

while( (v1 < 48 || v1 > 57) && (v1 < 97 || v1 > 122) && (v1 < 65 || v1 > 90) )

실제로 찍어보거나, 엑셀로 계산하거나, 암산을 하시거나... 확인 방법은 자유입니다.

결과는 영어대소문자 + 숫자 아스키코드 범위이며, 결과값은 아래와 같습니다.

※ sub_401D80 정리 : 특정 구분자가 포함된 문자열 반환 

 

네번째로 실행되는 서브루틴은 sub_401AF0 입니다.

초반부에서 sub_401640 서브루틴을 호출하고 있습니다. 먼저 분석하고 되돌아옵니다.

 

sub_401640 서브 루틴을 살펴보면,

Lab07-03 문제에서 나왔던 패턴이 보입니다. 윈속으로 통신을 수행하기 위해서

WSAStartup → gethostbyname → socket → connect 순서로 연결을 시도합니다.

※ sub_401640 정리 : 인자로 전달된 도메인(=name)에 winsock으로 connect 수행 

 

다시 sub_401AF0 서브루틴으로 돌아갑니다.

strcpy + stcat 함수를 이용하여 buf 변수에 "GET VuHD/2tCo.tjd HTTP/1.0\r\n\r\n" 문자열을 생성합니다.

이후 connect로 연결한 원격지에 send 함수를 이용하여 통신을 수행합니다. 

 

만약 정상적으로 서버와 연결이 되었다면,

서버로부터 변수 v6 에 512 길이만큼 데이터를 전송받습니다.

수신된 데이터는 qmemcpy 함수를 통해 a4 배열에 차곡차곡 복사합니다.

반복 루틴이 실행될때마다 strstr 함수를 이용하여 "\r\n\r\n" 문자열이 존재하면 break 합니다.

 

 Tip. qmemcpy 

 qmemcpy 함수는 msdn에 검색해도 나오지 않았습니다.

 하지만 Hex-rays Home에서 6.6 버전을 릴리즈하며 게시한 내용에

 간략한 설명이 있어서 올립니다.

 왜 앞에 "q" 문자가 붙었는지는 모르겠지만

 중요한건 memcpy와 사용방법이 동일하며, byte by byte 방식으로

 낮은 주소에서 높은 주소로 복사를 한다는 점이겠죠.

 

 

 

recv 함수는 정상적인 데이터 전송이 이루어졌다면, 수신한 바이트의 길이를 리턴합니다.

하지만 리턴값이 0 이하일 경우(데이터 못읽음, 세션종료, 오류 등) 또는

"누적된 데이터 길이 + 방금 받은 데이터 길이" 가 인자로 넣어준 변수 a5(=4096)의 크기를 초과할 경우

sub_401740 서브루틴을 호출하여 shutdown, closesocket, WSACleanup 등의 세션종료 함수를 호출합니다.

 Tip. Windows 함수에서의 ErrorCode

 위에서 등장한 recv, shudown 등의 windosck 함수들을 호출하고나서 결과값이 0 미만일 경우

 result 값을 1로 반환하여, 정상적인 진행이 되지 않았을 경우의 실행 코드가 존재합니다.

 0 미만의 값이 무엇을 의미하는지 MSDN에서 살펴보려고 했습니다만,

 아래와 같은 WSAGetLastError 함수와 함께 에러코드들의 링크가 존재하더군요.

 그런데 문제점은 모두가 하나같이 0 이상의 상수값이라서 헤멨습니다.

 (분명 0 미만일 경우의 코드들이 존재했는데, 왜 모두 0 이상이지? 라는 생각과 함께요)

 

 자세하게 설명을 읽어보지 못한 저의 불찰입니다만,

 GetLastError 또는 WSAGetLastError 함수들은 에러가 발생한 이후에

 별도로 호출하여 에러코드를 얻어내는 함수들입니다. 

 recv 함수는 정상적이라면 수신한 바이트 길이를 리턴하며, shutdown 함수는 정상적인 경우

 0(zero)을 반환합니다. 그래서 보통 0 미만의 값을 가지면 실패 또느 오류로 인지하는것이죠.

 

sub_401740 서브루틴에서도 원하는 결과를 얻지 못했다면, 마지막 else 문으로 진입합니다.

strstr 함수로 " `'`'` " 문자열의 위치를 검색하는 루틴입니다.

문자가 헷갈려서 간단하게 찍어보니 16진수값 0x27(" ' "), 0x60(" ` ") 이 나왔습니다.

교재에서는 백틱, 어포스트로피라고 되어 있는데...백틱이란 문자는 생소하네요.

만약 해당 문자열들이 둘다 검색된다면,

qmemcpy 함수를 이용하여 v5 변수가 가지고 있는 주소값의 내용을 a1 변수로 복사합니다.

그 다음줄은 마지막 라인에 0(null)값을 넣어주면서 문자열의 끝을 나타내는것으로 생각됩니다.

변수 v10 에는 sub_401AF0 서브루틴에서 GET 으로 얻어온 파일이 담겨있습니다.

서버에서 가져온 파일 또는 문서에서 특정 내용을 a1 변수에 반환하여

위에서 잠시 살펴보았던 "SLEEP", "UPLOAD" 등의 명령어일 경우 해당되는 함수들을 호출합니다.

※ sub_401E60 정리 : 등록된 레지스트리에서 항목값을 가져오거나, URL로부터 명령어 수신 

 

 

 

 

3. 명령어 제어 프로토콜(sub_402020) - Part2(명령어별 동작)

이제 명령어를 수신하였으니, 명령어별로 어떤 동작을 수행하는지 살펴봅니다.

v14 변수에 담긴 문자열과 명령어들과의 비교를 통해 일치할 경우, strotok 함수를 이용하여 문자열을 분리합니다.

strtok 함수의 특성상, 처음에만 주소값을 넣어주고 이후에는 Null 을 넣어줌으로서 지속적인 분리를 수행합니다.

이렇게 분리하는 이유는, 위쪽에서 strncmp 함수를 이용하여 문자열의 앞부분에서 특정 길이만큼만 체크한뒤

그 뒤에 "(공백)" " ' "  구분자로하여 이어지는 다른 명령을 파싱하기 위해서일겁니다.

그래서 각 명령어마다 서로 다른 sub_401790 / sub_401870 / sub_4019E0 함수 등을 호출하는것으로 보입니다.

 

① "SLEEP"

해당 명령의 경우 말그대로 Sleep 함수를 실행시킵니다.

함수 인자의 단위는 밀리초(=ms, milliseconds, 1/1000초) 이므로,

"1000 * v3" 계산에서 v3 변수가 몇초 동안 sleep 상태로 대기할 것인지를 결정합니다.

 

 "UPLOAD"

해당 명령의 경우 sub_4019E0 서브루틴을 실행합니다.

 

sub_4019E0 서브루틴의 내부 동작을 살펴보겠습니다.

CreateFileA 함수로 파일을 생성(또는 open) 하고 있으며,

실패할 경우 sub_401740 서브루틴으로 winsock 연결을 해제(shutdown, closeconnect) 합니다.

 

하지만 성공하였을 경우에는, 

방금 얻어낸 handle + recv 함수+ WriteFile 함수로 파일의 로우 데이터를 입력합니다.

여기서 의아했던 점은 분명 "UPLOAD" 인데 서버에서 데이터를 수신하고 있다는 점입니다.

뒤쪽에서 알게되었지만, "DOWNLOAD" 역시 반대로 서버에 데이터를 송신합니다.

이것으로 미루어보아, 명령어의 주체가 감염된 PC가 아닌 서버 or 악성코드 제작자임을 알 수 있습니다.

 

서버와의 연결이 성공적으로 종료되면, sub_4015B0 + sub4014E0 서브루틴을 호출하여

특정 파일 생성/복사 등을 수행합니다. 자세한 내용은 아래쪽의 "-in" 기능을 분석에 있습니다.

(내용이 길어지고 함수가 반복적으로 쓰이다보니 왔다갔다하네요..^^;)

 

 

"DOWNLOAD"

위에서 어느정도 파악을 했다싶이, 실제로는 감염된 PC에 파일이 다운로드 되어지는 루틴입니다.

 

호출되는 sub_401870 서브루틴을 살펴봅니다.

CreateFile 함수를 통해 파일을 오픈하는 과정은 들어가는 몇개의 인자값을 제외하고는 동일합니다.

다운로드의 경우 업로드와는 달리 "dwCreationDisposition" 인자값을 EXCREATE_ALWAYS OPEN_EXISTING

설정하였기 때문에 덮어쓰지 않고 기존에 존재하는 파일을 오픈합니다.

이후에 ReadFile 함수 + send 함수를 이용하여 서버 또는 악성코드 제작자에게

파일에서 읽어온 데이터(&Buffer)를 송신합니다.

 

 

 "CMD"

해당 함수는 말그대로 CMD 기능을 사용하는 명령어이며,

_popen 함수를 이용하여 cmd에서 명령어를 실행하고 있습니다.

"rb" 옵션의 경우 프로세스가 결과값을 볼 수 있음(=r) + 이진 데이터 반환(=b)을 의미합니다.

이후에 sub_401790 서브루틴을 호출합니다.

 

sub_401790 서브루틴의 경우 _popen 함수의 결과값을 인자로 받고 있으며,

fread 함수를 이용하여 결과값을 읽은 후에 send 함수로 서버에 송신하는 역할을 수행합니다.

 

 

4. main 함수 - 인자가 여러개인 경우

메인함수로 되돌아옵니다.  사실 저희는 아직 main의 6라인 밖에 분석을 못했습니다.

얼마 남지 않았음을 믿어 의심치 않으며...달려봅시다.

else문을 살펴보면, 프로그램 실행시 받은 마지막 인자를

v10 변수에 할당하고 sub_402510 서브루틴을 호출하는군요.

 

sub_402510을 살펴봅니다.

매개변수 int achar* 형으로 캐스팅한 뒤 문자열의 길이를 체크합니다.

만약 길이가 4가 아닐 경우에는 바로 result = 0 을 리턴하게 되며, 이럴경우

메인으로 돌아가서 본인 삭제 기능을 가진 서브루틴 sub_402410을 호출됩니다.

앞으로도 어떤 조건을 충족시키지 못하면 sub_402410을 호출하는 횟수가 빈번해집니다.

이는 프로그램 실행시 특정 조건을 반드시 맞춰야 하는 비밀번호와 같은 역할입니다.

문자열의 길이가 4라고 가정하고 다음으로 넘어갑니다.

 

int형 매개변수 a1을 _BYTE * 형으로 캐스팅한뒤 97(=a) 과 비교하여 일치하면 아래의 연산을 수행합니다.

 v2 = *(_BYTE *)(a1 +1) - *(_BYTE *)a1

이때 a1은 int형이므로 "[]" 연산이 불가능하며, a1이 가리키는곳의 문자열은 총 4자리임을 기억해야합니다.

*(_BYTE *)(a1+a1)이 의미하는 바는 a1에 저장되어 있는 숫자(주소값)에 +1을 더하여 값을 가져오므로 

a1이 가리키는 문자열 중에 두번째 문자를 의미합니다.

따라서 v2에는 (두번째 문자) - (첫번째 문자) 연산 결과가 할당됩니다.

v2 == 1이 되려면 자연스럽게 두번재 문자는 98(=b) 가 되며,

v3 = 99* v2 로 인해 세번째 문자는 99(=c) 입니다.

 

위와같은 로직으로 계산하게 되면, 이후에 실행되는 코드도 해석이 쉬워집니다.

변수 v3와 a1이 가리키는 문자열 중에 세번째 문자를 비교하는 코드입니다. 

 if( v3 == *(_BYTE *)(a1+2) ) 

 

만약 일치한다면, 세번째 문자와 네번째 문자가 일치하는지 확인하고, 일치할 경우 result에 저장합니다.

여기서 (char)(v3+1) 은 v3 주소값에서 +1을 하는것이 아니라,

말그대로 v3값(99)에서 +1을 해서 (char)형으로 캐스팅을 한겁니다. 즉, 네번째 문자는 100(=d) 입니다.

 result = (char)(v3+1) == *(_BYTE *)(a1 + 3) 

 

다시 main으로 돌아갑니다.

이후에 _mbscmp 함수를 이용하여 argv[1]에 있는 변수와 "-in" "-re" "-c" "-cc" 문자열을 비교합니다.

_mbscmp 은 strcmp 의 멀티바이트 문자 버전으로서, MBCS기반의 문자 비교를 수행합니다.

각 문자열 별로 비교하는 부분을 살펴봅니다.

 

① "-in"

일치 O : 인자의 개수에 따라 else if 또는 else 문 진입  sub_4025B0, sub_402600 서브루틴 실행

일치 X : IF문 내부로 진입 (_mbscmo : 문자열이 동일한 경우 0 반환)

 

"-in" "-re" "-cc" 의 공통점은 인자가 3개일때 sub_4025B0 서브루틴을 호출하는 부분입니다.

sub_4025B0를 살펴보면, 내부에서 getModuleFileNameA 함수를 호출하는것 외에는

특별한 기능을 수행하지 않습니다. _splitpath 함수는 디렉토리를 분리하는 함수로서

드라이브명, 디렉토리명, 확장자 등을 분리하여 각 변수에 담을 수 있는 함수입니다.

세번째 인자 a1 변수에 파일명이 분리되어 담길것입니다. 이후 0을 반환하고, main에서 sub_402600을 호출합니다.

sub_4025B0 정리 : 인자로 넣어준 변수의 파일명 반환

 

sub_402600를 살펴보면,

strcpy + strcat을 이용하여 "%SYSTEMROOT%\\system32\\서비스명(=파일명).exe" 문자열을 만듭니다.

사실상 여기서 서비스명은 sub_4025B0 에서 반환한 파일명입니다.

이후 OpenSCManagerA + OpenServiceA 함수를 이용해 서비스를 실행합니다.

 

만약 서비스가 제대로 Open 되었다면,

ChangeServiceConfigA 를 실행하고 서비스 핸들을 닫거나 or 그냥 닫아버립니다.

dwServiceType과 dwErrorControl 인자값이 0xFFFFFFFF 이어서 어떤 의미인지 헤메다가

MSDN의 "Changing a Service's Configuration" 예제를 발견하여 참고했습니다.

URL : https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms681987(v=vs.85).aspx 

 

서비스가 정상적으로 Open 되지 않았을 경우,

서비스명 "Lab09-01", 서비스 표시 이름 "Lab09-01 Manager Service" 으로 서비스를 생성합니다.

6번째 인자를 통해서 SCManager 가 Setup 될 때 자동으로 시작되는 서비스임을 알 수 있습니다.

들어가는 인자들은 Xref로 어셈블리 코드를 추적한 다음 Symbolic Constant로 변경하면 편리합니다.

 

이후에 ExpandEnvironmentStringA 함수를 호출하여 Src변수에 문자열로 저장해 두었던

환경변수 %SYSTEMROOT%을 확장한 문자열을 얻습니다. 이러한 절차는 설치되는 PC마다 환경변수가 다르기 때문에

오류를 최소화하기 위한 작업으로 보입니다. 다음에 또 해당 코드와 마주친다면 의도파악이나 분석 속도가 빨라지겠죠.

그아래에서는 파일을 다른 경로를 복사하기 위해 GetModuleFIleName 함수와 CopyFile 함수를 호출합니다.

이후에 sub_4015B0 함수 결과에 따라 sub_401070 함수의 호출 여부가 결정됩니다.

 

sub_4015B0 서브루틴을 살펴보면,

GetSystemDirectory + strcat 함수를 이용하여 아래와 같은 문자열을 만듭니다.

이후 sub_4014E0 함수의 결과가 0이 아니면 result로 반환합니다.

 

sub_4014E0 서브루틴을 살펴보면,

인자로 넘어온 2개의 경로를 이용하여 파일을 2개 생성합니다.

① a1 : C:\Windows\system32\Lab09-01.exe (GENERIC_WRITE)

② lpFileName : C:\Windows\system32\kernel32.dll (GENERIC_READ)

CreateFile 함수의 인자값들은 OllyDbg에서 파악하기 쉽습니다.

 

여기서 염두해둘 부분은 kernel32.dll 파일은 이미 존재할 것이므로, OPEN_EXISTING 옵션에 의하여

핸들값을 가져오고 난 이후에 system32 폴더에 생성된 Lab09-01.exe의 타임설정을 kernel32.dll과 동일하게 설정하는 점입니다.

만들어진 Lab09-01.exe의 등록정보를 살펴보면 제가 캡쳐를 위해 접근한 시간인 액세스 날짜를 제외하고

나머지 일자가 동일함을 알 수 있습니다.

sub_4014E0 정리 : 함수에 넘어온 인자를 이용하여 "Lab09-01.exe" 파일 생성, "kernel32.dll" 파일과 동일하게 타임 설정

sub_4015B0 정리 : "시스템 디렉토리 경로 + kernel32.dll" 문자열 생성, sub_4014E0 호출 

 

 

다시 sub_402600으로 돌아가서, sub_401070을 살펴보겠습니다.

함수에 "ups" "http://...." "80" "60" 인자들을 넣어주고 sub_401070 서브루틴을 호출합니다.

 

아마 이부분이 가장 궁금했던 "SOFTWARE\\Microsoft \\XPS" 레지스트리에

어떤 내용이 저장되길래 자꾸 체크하는지 알 수 있는 부분입니다.

함수 호출시 전달된 인자들을 주소값 연산을 통해 &Data 변수에 주르륵 할당합니다.

 

이후 RegCreateKeyExA 함수를 이용하여 "SOFTWARE\\Microsoft \\XPS" 서브키로 레지스트리를 생성하고

RegSetValueExA 함수를 이용하여 "Configuration" 항목값을 Data 변수에 있는 내용으로 채웁니다.

 

regedit을 이용하여 생성된 레지스트리르 체크해보면,

MicroSoft 문자열 뒤에 스페이스가 추가된 서브키가 추가로 생성되어

사용자가 악성코드로 인해 생성된 레지스트리인지 쉽게 인지할 수 없게 되어있습니다.

 

이것으로 길고 길었던 "in" 옵션에 대한 분석이 끝났습니다.

이후의 옵션들은 위에서 분석한 함수들의 내용이 중첩되는 부분이 많아서 좀더 수월할것으로 보입니다.

sub_401070 정리 : 레지스트리 생성 및 등록 

"in" 커맨드라인 옵션 정리 : 시스템 디렉토리에 악성코드 복사, 서비스 생성, 레지스트리 생성 

 

 

② "-re"

일치 O : 인자가 3개일 경우 sub_4025B0, sub_402900 호출, 4개인 경우 sub_202600 호출, 그외 sub_402410(삭제+종료) 호출

일치 X : IF문 내부로 진입 (_mbscmo : 문자열이 동일한 경우 0 반환)

 

인자가 3개 혹은 4개일 경우, "in" 옵션과 동일하게 sub_4025B0(파일명반환) 서브루틴을 호출합니다.

이후 sub_402900 서브루틴을 호출합니다. 해당 함수를 분석합니다.

 

가장 먼저 눈에 들어오는 부분이 OpenSCManagerA + OpenServiceA 입니다.

해당 함수들이 눈에 익어서 어디서 호출했는지 Xref로 확인해보면 sub_402600 서브루틴입니다.

sub_402600 서브루틴에서는 새로운 서비스를 생성하는데 초점이 맞춰져 있었습니다.

 

하지만 분석중인 sub_402900 서브루틴에서는 DeleteService + DeleteFileA 함수를 통해

악성코드가 생성한 서비스와 파일을 삭제를 수행합니다. 해당 명령어는 "remove"를 의미하는것 같네요.

 

sub_402600 서브루틴의 분석이 선행되어서 그런지 "re" 옵션에 대한 분석이 빨리 끝났습니다.

"re" 커맨드라인 옵션 정리 : 서비스 삭제, 시스템 디렉토리에 복사한 악성코드 삭제 

 

 

③ "-c"

일치 O : 인자가 7개인 경우 sub_401070 호출, 아닌 경우 sub_402410(삭제+종료) 호출

일치 X : IF문 내부로 진입 (_mbscmo : 문자열이 동일한 경우 0 반환

 

해당 명령어는 인자가 7개가 아니면 바로 삭제를 수행합니다.

위에서 이미 분석한 sub_401070(레지스트리 생성 및 등록) 서브루틴을 호출합니다.

해당 함수에 인자가 모두 argv인것으로 보아 레지스트리 값만 직접 등록하고 싶을때 사용하는 명령어입니다.

아마도 최초에는 "in" 명령어를 이용하여 설치할것이고, 향후 변동사항이 있을때 사용할것으로 예상됩니다.

"c" 명령어는 "configuration" 정도가 어울릴까요?

"c" 커맨드라인 옵션 정리 : 인자값을 이용한 레지스트리 직접 생성 및 등록 

 

④ "-cc"

일치 O : 인자가 3개가 아닌경우 sub_402410(삭제+종료) 호출, 그외 sub_401280, sub_402E7E 호출

일치 X : sub_402410(삭제+종료) 호출

 

"cc" 커맨드 옵션은 간결합니다.

sub_401280 서브루틴을 호출하여 레지스트리의 데이터를 얻어옵니다.

이후 sub_402E7E 서브루틴을 호출하는것을 볼 수 있는데, 

해당 함수 내부의 _stbuf _ftbuf 함수는 printf 내부 동작에 사용되는 함수이기도 합니다.

함수 호출시 전달된 인자 "k:%s h:%s p:%s per:%s\n" 의 형태가 화면에 해당값들을 출력하기 위한

용도로 보이기 때문에 직접 실행해봅니다.

"cc" 커맨드라인 옵션 정리 : 현재 레지스트리에 등록되어 있는 Config 값 출력 

 

 

 

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

0x02 문제풀이

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

1. 어떻게 하면 악성코드가 자신을 설치하게 할 수 있는가?

  옵션 "-in" 으로 패스워드를 입력해서 프로그램 자체를 초기설치할 수 있습니다.

  OllyDbg를 이용하여 패스워드 검증 부분을 뛰어넘을수도 있습니다.

 

2. 이 프로그램의 커맨드라인 옵션은 무엇인가? 패스워드 요건은 무엇인가?

   "-in" "-re" "-c" "-cc" 4가지 옵션이 있으며, 패스워드는 "abcd" 입니다.

   각 옵션의 기능과 패스워드를 구하는 방법은 위쪽의 분석을 참고하세요.

 

3. 이 악성코드가 특수 커맨드라인 패스워드를 요구하지 않게 영구 패치하려면 OllyDbg로 어떻게 해야 하는가?

   교재에서는 0x402510 주소에서 항상 참을 반환하게 함수의 첫번째 바이트를 변경하라고 되어 있지만

   코드를 패치할 수 있는 포인트는 더 다양할 것 같습니다.

 

4. 이 악성코드의 호스트 기반 지표(indicator)는 무엇인가?

   레지스트리키 생성, "파일명 Manager Service" 생성,

   원격 명령어 프로토콜을 이용한 백도어 기능, 파일 생성/복사 등이 있습니다.

 

5. 이 악성코드가 네트워크를 통해 수행할 수 있는 다른 행동은 무엇인가?

  악성코드는 네트워크를 통해 "SLEEP", "UPLOAD", "DOWNLOAD", "CMD", "NOTHING" 다섯 명령어 중

  하나를 실행하게 명령어를 받습니다.

 

6. 이 악성코드에 대한 유용한 네트워크 기반 시그니처가 있는가?

  기본적으로 악성코드는 http://...(중략)...analysis.com URL으로 통신하지만, 변경가능합니다.

  비컨은 xxxx/xxxx.xxx 형태로 HTTP/1.0 GET 리소스 요청을 합니다.

  여기서 x는 무작위로 구성한 문자와 숫자 ASCII입니다.

 

 

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

0x03 마무리

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

이번 포스팅은 IDA 소스코드만 보고 분석하기에도 정신없을 정도로 분량이 많았습니다. 

그러다보니 값을 검증하기 위해 올린 Ollydbg 화면 외에는 어셈블리어를 거의 살펴보지 못한 것 같습니다.

모르는게 많아서 포스팅 속도가 느리네요.

부족한글 봐주셔서 감사합니다.

 

 

posted By Message.

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

 

posted by Red_Message
2016. 11. 7. 03:29 :: 리버싱

Chapter 06 악성코드

Section 01 악성코드

- 제작자가 의도적으로 사용자에게 피해를 주고자 만든 모든 악의적 목적을 가진 프로그램 및 매크로, 스크립트 등 컴퓨터 상에서 작동하는 모든 실행 가능한 형태

 

1. 악성코드의 분류

가. 악성코드의 분류

이름(코드) 

설명 

바이러스 

- 사용자 컴퓨터(네트워크로 공유된 컴퓨터 포함) 내에서 사용자 몰래 프로그램이나 실행 가능한 부분을 변형해 자신 또는 자신의 변형을 복사하는 프로그램

- 가장 큰 특성은 복제와 감염

- 네트워크의 컴퓨터로 스스로 전파되지는 않음 

웜 

- 인터넷 또는 네트워크를 통해서 컴퓨터에서 전파되는 악성 프로그램

- 윈도우의 취약점 또는 응용 프로그램의 취약점을 이용하거나 이메일이나 공유 폴더를 통해 전파, 최든에는 공유 프로그램(P2P)을 이용하여 전파되기도 한다.

- 바이러스와 달리 스스로 전파되는 특성 

트로이 목마 

- 바이러스나 웜처럼 컴퓨터에 직접적인 피해를 주지는 않지만, 악의적인 공격자가 컴퓨터에 침투하여 사용자의 컴퓨터를 조종할 수 있는 프로그램

- 고의적으로 만들어졌다는 점에서 프로그래머의 실수인 버그와는 다름

- 자시 자신을 다른 파일에 복사하지 않는다는 점에서 바이러스와 구별 

인터넷 악성코드 

- 인가되지 않은 성인 사이트나 크랙 사이트 등에 접속할 때 감염

- 예전에는 인터넷 악성코드로 끝나는 경우가 많았으나 최근에는 웜의 형태로 전이되고 있다. 

스파이웨어 

- 자신이 설치된 시스템의 정보를 원격지의 특정한 서버에 주기적으로 보내는 프로그램

- 사용자가 주로 방문하는 사이트, 검새어 등 취향을 파악하기 위한 것도 있지만 패스워드 등과 같은 특정 정보를 원격지에 보내는 스파이웨어도 존재 


나. 악성 프로그램으로 인해 발생할 수 있는 증상

대분류 

소분류 

설명 

시스템 관련

시스템 설정 정보 변경 

변경 레지스트리 키 값을 변경하여 시스템의 정보를 변경

FAT 파괴 

시스템의 파일 시스템을 파괴 

CMOS 변경 

CMOS 내용을 변경하여 부팅 때 에러를 발생시킴 

CMOS 정보 파괴 

CMOS의 일부를 파괴 

기본 메모리 감소 

시스템의 기본 메모리를 줄인다. 

시스템 속도 저하 

시스템의 속도를 저하시킨다. 

프로그램 자동 실행 

레지스트리 값을 변경해 시스템을 부팅할 때 특정 프로그램을 자동으로 실행 

프로세스 종료 

특정 프로세스를 강제로 종료 

시스템 재부팅 

시스템을 재부팅 

네트워크 관련 

메일 발송 

특정 사용자에게 메일을 발송

정보 유출 

사용자의 정보를 네트워크를 통해서 공격자로 전송 

네트워크 속도 저하 

감염된 컴퓨터가 속한 네트워크가 느려짐 

메시지 전송 

메시지를 네트워크를 통해 다른 컴퓨터로 전송 

특정 포트 오픈 

특정 백도어 포트를 연다 

하드 디스크 관련 

하드 디스크 포맷 

하드 디스크를 포맷 

부트 섹터 파괴 

하드 디스크의 특정 ㅂ분을 파괴 

파일 관련 

파일 생성 

특정 파일(주로 백도어 파일)을 생성 

파일 삭제 

특정 파일이나 디렉터리를 삭제 

파일 감염 

바이러스가 특정 파일을 감염 

파일 손상 

바이러스가 특정 파일에 겹쳐 쓰기 형태로 감염되면 파일이 손상된다. 

특이 증상 

이상 화면 출력 

출력 화면에 특정 내용이 나타남 

특정음 

발생 컴퓨터에서 특정음이 발생 

메시지 상자 출력 

화면에 특정 메시지 상자가 나타남 

증상 없음 

특이한 증상 없음 



Section 02 바이러스

1. 1세대 : 원시형 바이러스

가. 부트 바이러스

- 플로피 디스크나 하디 디스크의 부트 섹터에 감염되는 바이러스로 MBR과 함께 PC 메모리에 저장

- 부팅할 때 자동으로 동작하여 부팅 후에 사용되는 모든 프로그램을 감염시킨다.

- 컴퓨터의 부팅 순서

■ 1단계 : POST

- POST(Power On Self Test)는 운영체제와 관련 있는 부분은 아님. 설치할때 하드웨어 자체가 시스템에 문제가 없는지 기본 사항을 스스로 체크하는 과정이다.

- BIOS(Basin Input/Outpur System)에 의해 실행

■ 2단게 : CMOS

- CMOS(Complementary Metal-Oxicle Semiconductor)에서는 기본 장치에 대한 설정과 부팅 순서를 설정할 수 있으며 BIOS는 CMOS에서 이런 기본 설정 사항을 읽어 시스템에 적용한다.

■ 3단계 : 운영체제 위치 정보 로드

- 윈도우 2003 이전 : 마스터 부트 레코드(MBr : Master Boot Record) 정보를 읽음. CMOS 정보를 읽어 부팅 매체를 확인할 뒤에는 부팅 매체의 MBR 정보를 읽는다.

- MBR은 운영체제가 어디에, 어떻게 위치해 있는지를 식별하여 컴퓨터의 주 기억장치에 적재될 수 있도록 하기 위한 정보

- 하드디스크나 디스켓의 첫 번째 섹터에 저장되어 있다.

- 메모리에 적재될 운영체제가 저장된 파티션의 부트 섹터 레코드를 읽을 수 있는 프로그램(부트 섹터 레코드는 운영체제의 나머지 부분을 메모리에 적재시키는 프로그램을 담고 있다.)


- 윈도우 2008 이후 : 윈도우 부트 서브 시스템(Window Boot Manager)이 실행됨.

- MBR에서 NTLDR이 실행되지 않고, 윈도우 부트 서브시스템이 실행된다. 윈도우 부트 서브 시스템은 bootmgr.exe가 실행되고 부트 설정 데이터(BCD, Boot Configuration Data)를 읽어 실행 가능한 운영체제의 목록을 보여주는데, 이것은 NTLDR이 boot.ini을 읽어 실행 가능한 운영체제의 목록을 보여주는 것과 같다.

- 부트 바이러스는 바로 이 3단계에서 동작.

- 과거 부트 바이러스에 감염된 플로피 디스크로 운영체제를 구동시키면 바이러스가 MBR과 함께 PC메모리에 저장되고 부팅 후에 사용되는 모든 프로그램에 자신을 감염시킨다.

- 부트 바이러스의 종류로는 브레인, 몽키, 미켈란젤로 바이러스가 있다.


나. 파일 바이러스

- 파일을 직접 감염시키는 바이러스.

- 바이러스에 감염된 실행 파일이 실행될 때 바이러스 코드를 실행한다.

- 프로그램을 덮어쓰는 경우, 프로그램 앞부분에 실행 코드를 붙이는 경우, 프로그램의 뒷부분에 코드를 붙이는 경우가 있다.

- 파일 바이러스에는 예루살렘 바이러스, 썬데이(Sunday), 스콜피온(Scorpion), 크로우(Crow), FCL, CIH 바이러스가 있다.


2. 2세대 : 암호형 바이러스

- 바이러스 코드를 쉽게 파악하고 제거할 수 없도록 암호화한 바이러스

- 암호형 바이러스의 종류로는 슬로우(Slow), 캐스케이드(Cascade), 원더러(Wanderer), 버글러(burglar) 바이러스가 있다.


3. 3세대 : 은폐형 바이러스

- 바이러스에 감염된 파일들이 일정한 잠복 기간을 가지게 한 바이러스

- 은폐형 바이러스에는 브레인(Brain), 조시(Joshi), 512, 4096 바이러스가 있다.


4. 4세대 : 다형성 바이러스

- 코드 조합을 다양하게 할 수 있는 조합(Mutation) 프로그램을 암호형 바이러스에 덧붙여 감염된다. 실행될 때마다 바이러스 코드 자체를 변경시켜 식별자를 통해 구분하기 어렵게 한다.


5. 5세대 : 매크로 바이러스

- 응용 프로그램 및 사무용 관련 프로그램이 개발되면서 스크립트 형태의 실행 환경을 이용하여 전파되는 바이러스이다. 주로 MS 오피스 프로그램의 매크로 기능을 이용하여 감염된다.



Section 03 웜

- 인터넷 또는 네트워크를 통해서 컴퓨터에서 컴퓨터로 전파되는 프로그램이다.

- 다른 컴퓨터의 취약점을 이용하여 스스로 전파되거나 메일로 전파된다.


1. MASS Mailer 형 웜

- 자기 자신을 포함하는 대량 메일 발송을 통해 확산되는 웜이다.

- 제목이 없거나 특정 제목으로 전송되는 메일을 읽었을 때 감염되며, 시스템 내부에서 메일 주소를 수집하여 메일을 계속 발송한다.

- 주요 증상

◆ 감염된 시스템이 많으면 SMTP(TCP 25)의 네트워크 트래픽이 증가

◆ 베이글은 웜 파일을 실행할 때 'Can't find a viewer associated with the file' 같은 가짜 오류 메시지를 출력

◆ 넷스카이는 윈도우 디렉터리 밑에 CSRSS.exe을 만든다.

- 베이글(Bagle), 넷스카이(Netsky), 두마루(Dumaru), 소빅(Sobig) 등이 있다.

- MASS Mailer 형 웜은 내부 교육을 통해 예방가능


2. 시스템 공격형 웜

- 운영체제 고유의 취약점을 통해 내부 정보를 파괴 혹은 컴퓨터를 사용할 수 없는 상태로 만들거나 외부 공격자가 시스템 내부에 접속할 수 있도록 백도어를 설치한다.

- 주요 증상

◆ 과다한 TCP 135, 445 트래픽이 증가

◆ windows, windows/system32, winnt, winnt/system32 폴더에 SVCHOST.EXE 등의 파일을 설치

◆ 공격 성공 후 UDP 5599 등의 특정 포트를 열어 외부 시스템과 통신한다.

- 아고봇(Agobot), 블래스터(Blaster worm), 웰치아(Welchia) 등이 있다.


3. 네트워크 공격형 웜

- 특정 네트워크나 시스템에 대해 서비스 거부(DoS) 공격을 수행한다.

- 주요 증상

◆ 네트워크가 마비되거나, 급속도로 느려진다.

◆ 네트워크 장비가 비정상적으로 동작한다.

- 져봇(Zerbot), 클레즈(Klea) 등이 있다.



Section 04 기타 악성코드

1. 백도어와 트로이 목마

- 정상적인 인증 과정을 거치지 않고 운영체제나 프로그램 등에 접근할 수 있도록 만든 일종의 관리 목적의 통로이다.


2. 인터넷 악성코드

- 인가되지 않은 성인 사이트나 크랙 사이트 등에 접속할 때 감염된다.

- 예전에는 인터넷 악성코드로 끝나는 경우가 많았으나 최근에는 웜의 형태로 전이되고 있다.

- 주요 증상

◆ 인터넷 익스플로러의 시작 페이지가 계속 다른 곳으로 변경

◆ 인터넷 속도가 느려지거나 끊긴다.

◆ 시스템의 비정상적인 작동으로 운영체제가 사용 불능의 상태가 된다.


3. 스파이웨이

- 자신이 설치된 시스템의 정보를 원격지의 특정한 서버에 주기적으로 보내는 프로그램이다.

- 사용자가 주로 방문하는 사이트, 검색어 등 취향을 파악하기 위한 것도 있지만 패스워드 등과 같은 특정 정보를 원격지에 보내는 스파이웨어도 존재



Section 05 악성코드 탐지 및 대응책

- 악성코드 탐지에 사용되는 툴은 다음과 같다.

■ 윈도우 7 악성코드 : Win-Trojan.Pearmor

■ Process Explorer

■ Total Commander(http://www.ghisler.com)

■ CPorts(http://www.nirsoft.net/utils/cports.html)


1. 네트워크 상태 점검하기

- 상당수의 백도어는 외부(해커, 악성코드 작성자)와의 통신을 위해 서비스 포트를 생성한다.

- 주요 백도어의 사용 포트

포트번호 

트로이 목마 

포트번호 

트로이 목마 

21

 TrojanFore

1080 

WinHole 

23 

Tiny Telnet Server[TTS]

1090 

Xtreme 

25 

NaebiHappy

1150 

Orion 

31 

Agent, ParadiseMasters

1234 

Ultors Trojan 

41 

DeepThroat Foreplay 

1243 

Backdoor G 

80 

WWW Tunnel 

1245 

VooDoo Doll 

119 

Happy 99 

1257 

Frenzy 2000 

133 

Farnaz 

1272 

The Matrix 

137 

ChodeMSint(UDP) 

1441 

Remote Storm 

514 

RPCBackdoor 

1524 

Trin00 

555 

Seven Eleven 

1999 

Sub Seven 

666 

ServeU 

2140 

Deep Throat 1.3 

667 

SniperNet 

2255 

Nirvana

777 

AIM Spy

2583 

WinCrash

808 

WinHole

2773 

Sub Seven Gold 2.1 

999 

Deep Throat 

3459 

Eclipse 2000 

1001 

Silencer 

5400 

Blade Runner 

1016 

Doly Trojan 

5880 

Y3K Rat 

1024 

NetSpy 

8787 

BackOrifice  

- 시스템에서는 netstat 와 같은 명령으로 열려 있는 포트를 확인할 수 있다.


2. 정상적인 프로세스와 비교하기

- 대부분의 백도어는 티가 나지 않기 때문에, 윈도우와 유닉스 시스템 등의 정상적인 프로세스를 외워두면 비정상적인 프로세스를 식별하는데 많은 도움이 된다.

- 현재 동작 중인 정상적인 프로세스를 아는 것도 매우 중요. 윈도우의 작업 관리자에서 확인 가능하다.

- 다음의 20여 개의 프로세스는 윈도우 시스템이 동작힉 위한 기본 프로세스로 알아두면 유용하다.

● Csrss.EXE(Client/Server Runtime SubSystem : Win 32)

 - 윈도우 콘솔을 관장하고, 스레드를 생성/삭제하며 32비트 가장 MS-DOS 모드를 지원한다.

● Explorer.exe

 - 작업표시줄, 바탕화면과 같은 사용자 셸을 지원한다.

● Lsass.exe(Local Security Authentication Server)

 - Winlogon 서비스에 필요한 인증 프로세스를 담당한다.

● Mstask.exe(Window Task Scheduler)

 - 시스템에 대한 백업이나 업데이트 등에 관련된 작업의 스케줄러이다.

● Smss.exe(Session Manager SubSystem)

 - 사용자 세션을 시작하는 기능을 담당한다.

 - Winlogon, Win32(Csrss.exe)을 구동시키고, 시스템 변수를 설정한다.

 - 또한 Smss는 Wonlogon이나 Csrss가 끝나기를 기다려 정상적인 Winlogon, Csrss 종료시 시스템을 종료시킨다.

● Spoolsv.exe(Printer Spooler Service)

 - 프린터와 팩스의 스풀링 기능을 담당한다.

● Svchost.exe(Service Host Process)

 - DLL(Dynamic Link Libraries)에 의해 실행되는 프로세스의 기본 프로세스이다.

 - 한 시스템에서 여러 개의 svchost 프로세스를 볼 수 있다.

- 이 중에 웜/바이러스가 주로 사용하는 서비스명은 Csrss와 Svchost이다.

● Services.exe(Service Control Manager)

 - 시스템 서비스를 시작/정지시키고, 그들간의 상호 작용하는 기능을 수행한다.

● System

 - 대부분의 커널 모드 스레드의 시작점이 되는 프로세스이다.

● System Idle Process

 - 각 CPU마다 하나씩 실행되는 스레드로서 CPU의 잔여 프로세스 처리량을 %로 나타낸 값이다.

● Taskmgr.exe(Task Manager)

 - 작업관리자 자신

● Winlogon.exe(Windows Logon Process)

 - 사용자 로그인/로그오프를 담당하는 프로세스이다.

 - 윈도우의 시작/종료 시에 활성화되며 Ctrl+Alt+Delete 키를 눌렀을 경우에도 활성화된다.

● Winmgmt.exe(Window Management Service)

 - 장치에 대한 관리 및 계정 관리, 네트워크 등의 동작에 관련한 스크립트를 위한 프로세스이다.

● msdtc.exe(Distriduted Transaction Coordinator)

 - 웹 서버 및 SQL 서버 구동 시에 다른 서버와의 연동을 위한 프로세스이다.

● ctfmon.exe(Alternative User Input Services)

 - 키보드, 음성, 손으로 적은 글 등 여러가지 텍스트 입력에 대한 처리를 할 수 있도록 지원하는 프로세스이다.

● dfssvc.exe(Distributed File System (DFS))

 - 분산 파일 시스템에 대한 지원을 위해 백그라운드로 실행되고 있는 프로세스이다.


3. 백도어의 실질적인 파일 확인하기

- 네트워크 상태와 프로세스 분석을 통해 확인한 백도의 실질적인 파일을 확인한다.

- 악성코드 파일을 확인할 때는 total commander 같은 툴을 사용한다.

- 윈도우 탐색기가 악성코드에 이미 공격을 받았을 때는 특정한 파일이 숨겨져서 보이지 않거나 삭제되지 않는 경우도 있다.

- 파일 확인 과정에서 반드시 확인하고 가야할 폴더인 C:\Windows\system32 가 있다. 이 폴더에는 주요 DLL과 운영체제의 기본적인 실행 파일이 존재하는데, 대부분의 악성코드는 이 폴더를 공략한다.

- 폴더 내에서 가장 최근에 생성된 파일 중에 이상한 것이 없는지 확인하는 방법을 우선 이용한다.


4. 시작 프로그램과 레지스트리 확인하기

- 시작 프로그램 목록은 'msconfig' 명령을 통해 쉽게 확인할 수 있다.


5. 백도어 제거하기

- 확인한 백도어를 삭제하는 절차는 다음과 같이 할 수 있다.

① 백도어 프로세스의 중지

② 백도어 파일의 삭제

③ 레지스트리 삭제

 - regedit 으로 레지스트리 경로를 확인할 수 있다.





출처 : 정보 보안 개론(한 권으로 배우는 보안 이론의 모든 것) / 양대일 저 / 한빛아카데미 출판

':: 리버싱' 카테고리의 다른 글

UPack PE 헤더 분석 + 디버깅  (1) 2016.12.31
Red_Seek :: 스택 프레임  (0) 2014.01.29
[Red_Seek] abex' crackme #1 (크랙미 #1)  (0) 2014.01.23
[Red_Seek] 스택  (0) 2014.01.22
[Red_Seek] IA-32 Register  (0) 2014.01.14
posted by Red_Seek
2016. 10. 16. 17:46 :: 문제풀이/Pracical Malware

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 7-3을 분석합니다.

이번 실습은 동적분석과 정적분석 이후에 교재의 문제를 풀이합니다.

 

분석환경은 WindowXP / Vmwre 12.1.0 build 입니다.

  # Windows7 에서 분석 수행 시 오류가 발생하여 멈추는 현상이 발생할 수 있습니다.

  # 기본적으로 실습교재의 샘플들은 XP에 최적화 되어 있습니다.

 

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

1. 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1. PE구조 & 패킹

exe 파일과 dll 파일 모두 별도의 패킹이 되어 있지 않습니다.

 

 

2. DLL

1) Lab07-03.EXE

기존에 실습했던 악성코드가 임포트하는 DLL과 다를바가 없습니다.

 

하지만 임포트 함수 중 CreateFileA, CreateFileMappingA, MapViewOfFile은 프로그램이

파일을 오픈해 메모리로 매핑함을 알려줍니다. 사실 해당 함수는 지난 실습에서 꾸준히 등장했지만

분석 포커스에 빗나가 있었기 때문에 주목(?) 받지 못했습니다.

또한 FindFirstFileA, FindNextFileA, CopyFileA 함수를 이용하여 디렉토리를 탐색하고

CopyFileA를 통해 악성코드 본인 또는 특정 파일을 복사할 것으로 짐작됩니다.

주목할만한 점은, Lab07-03.dll을 어떤 방식으로든 사용할 것이 분명하지만, 

지금까지 많이 보아 왔던 LoadLibrary, GetProcAddress 함수가 임포트 목록에 없는 사실은

런타임에 해당 DLL을 로드하지 않고 다른 방법을 사용하고 있음을 짐작케 합니다.

 

2) Lab07-03.DLL

Lab07-03.dll이 임포트하는 dll 파일은 WS2_32.dll 입니다.

교재의 첫 실습[1-1]을 통하여 공부했던 네트워크 관련 API들이 얼핏 기억납니다.

검색해보니 2014년도에 포스팅이네요.

 

특이한점은 dll의 주요 역할인 Export 목록이 비어있다는 점입니다.

익스포트가 없으면 다른 프로그램이 임포트할 수 없습니다.

이를 염두해두고 분석을 진행합니다.

 

3. String

1) Lab07-03.EXE

지난번 "kernel32.dll" 파일명을 "kerne132.dll" 으로 위장한 악성코드를 실습한 이후부터는

String 윈도우에서 관련 문자열을 발견하면 숫자가 섞이지 않았나 체크하게됩니다.

역시 영문자 L 을 숫자 1로 변경한 kerne132.dll 문자열이 보입니다.

 

2) Lab07-03.DLL

악성코드가 접속을 시도할것으로 추정되는 IP(127.26.152.13)가 눈에 띕니다.

.data 영역의 "exec, sleep, hello" 문자열을 통하여 네트워크 통신 관련된 기능을 염두해 둡니다.

 

4. Function Calls

이번 실습에서는 기존에 사용해본적 없는 [ Function Calls ] 윈도우를 사용해봅니다.

특히 현재 분석중인 DLL은 DllMain 중심으로 대부분의 기능을 구현하고 있기 때문에

호출하는 함수만 요약하여 볼 수 있는 기능이 악성코드 동적/정적 분석에 속도를 더해줍니다.

 

1) Lab07-03.EXE

CreateFileA, CreateFileMappingA, MapViewOfFile 등의 메모리맵 파일에 관련된 API들이 보입니다.

메모리맵 파일의 기능을 이용하면 EXE/DLL 파일의 읽기/쓰기를 할 수 있기 때문에

LoadLibrary, GetProcAddress 함수를 사용하지 않고도 DLL 파일을 사용할 수 있습니다.

메모리맵 파일을 사용하기 위해서는 3단계 실행흐름(CreateFIle > CreateFileMapping > MapViewOfFile)을 가지며,

아래의 Function Calls 에 나오는 함수명과 호출 순서가 일치합니다.

또한 중간에 서브루틴 sub_401040이 여러번 등장합니다. 아직 어떤 기능인지 알 수 없지만

빈번하게 호출되는 서브루틴임을 참고하고 넘어갑니다.

 

2) Lab07-03.DLL

지난 실습에서 마주쳤던 Mutex 관련 함수들(OpenMutex, CreateMutexA)이 보입니다.

해당 함수들을 이용하여 악성코드의 중복 실행을 방지하는것으로 추측됩니다.

또한 네트워크 연결을 위한 소켓 관련 함수들(socket, inet_addr, htons, connect, send, recv, closesocket)도 보입니다.

어떤 내용을 주고받는지는 모르겠지만, 대략적인 흐름이 보입니다.

 

 

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

2. 정적분석

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

문제를 풀이를 위한 정적분석을 진행합니다.

 

1. Lab07-03.DLL

1) 전체적인 코드구조 파악

DLL 파일의 DllMain을 Graph overview 윈도우로 살펴보면 매우 복잡해 보입니다.

똑같겠지 생각하고선 [Flow Chart/F12] 를 실행해보니 분기점이 정리된 그래프가 나오더군요.

그래프 오버뷰에서는 파악하지 힘들었지만, 조건문이 많다는걸 한눈에 알 수 있었습니다.

 

실제로 Hex-ray 기능을 이용해보면 다수의 If 문이 존재함을 알 수 있습니다.

아마 코드를 먼저 보았다면 머리속이 복잡했을테지만

함수목록으로 함수 목록을 체크한 것이 실행 흐름을 파악하는데 많은 도움이 되었습니다.

 

 

2) 스택할당

DllMain 초반 도입부에서 __alloca_probe 함수가 보입니다.

사실 이 함수는 책에서도 가볍게 언급하고 있을 뿐이며, 악성코드 분석에 있어서 중요한 부분은 아닙니다.

하지만 매번 보던 스택프레임이 아니어서 약간 찜찜하기도 했고

삽질 하다보니 배우는게 여럿 있어 살펴보았습니다. 악성코드의 핵심 내용만 보시려면 3)번 항목으로 직행하세요~

 

책에서는 "스택에 공간을 할당하는 함수이며, 프로그램이 큰 스택을 사용함을 알 수 있음" 이라고 설명합니다.

정확한 명세를 알아보고자 구글링을 했더니, 해당 함수에 대해 명확한 설명을 찾지 못했습니다.

 

이것저것 삽질을 하다가 Ollydbg를 통해 살펴보니

__alloca_probe 함수를 호출하는 명령 라인에 "Allocates 4600, bytes on stack" 코멘트가 달려 있었습니다.

코멘트의 4,600이라는 숫자는 __alloca_probe 함수 호출 바로 윗라인에서 EAX 레지스터에 넣어준 11F8 Hex값 입니다.

 

EAX 레지스터에 넣어준 값이 스택 할당에 영향을 주었다는 연관성은 알겠지만,

push를 통해서 인자를 전달한게 아니기 때문에 약간 의아했기에 함수 내부를 살펴보았습니다.

지금까지 함수 호출시에 넣어주는 인자들은 "push" 명령어를 사용한다는 고정관념을 깨고

함수의 정의 부분에서 EAX레지스터가 매개변수로 되어 있음을 발견하였습니다.

 Tip. __usercall vs __userpurge

 Hex-ray 기능을 사용하면 두가지 Calling Convention을 만날 수 있습니다.

 __usercall : 스택정리를 caller가 수행

 __userpurge : 스택정리를 callee가 수행

 

함수를 실행전과 후의 스택 주소값을 비교해보면, 

0018FC34 였던 스택 주소가 0x11F8(4,600) 만큼 할당되어

[ 18FC34 - 11F8 = 18EA3C ] 으로 변경되었음을 알 수 있었습니다.

 

어셈블리어로 좀더 자세히 __alloca_probe 함수를 살펴보겠습니다.

지난 실습에서 배운 반복문 for문의 구조가 보입니다.

For문의 조건식은 a1 >= 0x1000 이므로, 두번째줄의 cmp eax, 1000h 명령어로 최초 비교를 수행하고 난 후

JB(Jump if Below) 명령어로 반복문 내부로 진입할지 탈출할지를 결정합니다.

 

아래 보이는 loc_1000122C 은 반복문 내부입니다.

ECX 레지스터와 EAX 레지스터 모두 SUB 명령어를 이용하여 1000h 만큼 뺄셈을 수행합니다.

ECX에서 1000h를 빼는 이유는 For문 내부의 i -= 1000h 코드를 수행하기 위해서이며,

EAX에서 1000h를 빼는 이유는 For문을 한번 실행할때마다 수행되는 증감식 때문입니다.

증감식이 수행되면, 조건식에 의하여 반복문을 빠져나갈지 여부를 결정합니다.

 Tip. MOV vs LEA

 MOV와 LEA의 기능은 비슷합니다. 하지만 LEA에는 장점과 제약이 존재합니다.

 ① 장점 : LEA 명령어는 우변(SRC)의 값을 연산한뒤 좌변(DST)에 옮길 수 있다. ex) LEA EAX, [ESP+1000]

 ② 제약 : LEA의 좌변에는 레지스터만 올 수 있다.

 

위에서 분석한 반복문의 의미는 스택을 4096(1000h) 단위로 연산하기 위함입니다.

따라서 함수에 인자로 전달된 11F8 이라는 값은 4600이므로 반복문을 1회 수행합니다.

반복문을 탈출하고 4600 단위 보다 낮아서 할당되지 않은 잔여 값(504)은 SUB ECX, EAX 명령어로 조정됩니다.

이후 MOV ESP, ECX 명령어로 계산한 스택값을 ESP에 넣어줍니다.

결론 : 이 프로그램은 큰 스택을 사용합니다. (...)

 

__alloca_probe 함수가 끝나고 나면, ebx/ebp/esi/edi 레지스터가 순서대로 push됩니다.

특정함수가 호출되는것도 아니고, 그냥 백업용인가? 생각하여 함수가 리턴되는 끝부분으로 가보니

push한 역순으로 각 레지스터값을 pop 하며 복구하는 부분이 있었습니다. (백업이 맞나봅니다)

 

3) 중복 실행 방지

fdwReason 값이 1인지 CMP 명령어를 이용하여 비교하며, 1이 아닐 경우 loc_100011E8 으로 분기합니다.

이후 이전 문제에서 실습한 Mutex 함수를 이용하여 중복 실행을 방지합니다.

OpenMutex 함수 이전에 실행되는 memset 함수는 4)번 항목에서 분석합니다.

 Tip. DllMain에서 fdwReason의 의미

 ① DLL_PROCESS_ATTACH : 1

 - DLL이 프로세스(클라이언트 프로그램)의 주소 공간에 맵핑될 때 호출

 - 암시적호출 > 프로세스가 시작될 때

 - 명시적호출 > LoadLibrary가 리턴되기 이전에 이 값과 함께 DllMain 호출

 ② DLL_PROCESS_DETACH : 0

 - DLL이 프로세스 주소 공간에서 분리될 때 호출

 - 암시적호출 > 프로세스가 종료될 때

 - 명시적호출 > FreeLibrary 함수에 의해 이 값과 함께 DllMain 호출

 ③ DLL_THREAD_ATTACH : 2

 - DLL을 사용하는 클라이언트 프로세스에서 스레드를 생성할 때마다 이 값과 함께 DllMain 호출

 - DLL에서는 이 값을 받았을 때 스레드별 초기화를 수행해야 한다.

 ④ DLL_THREAD_DETACH : 3

 - DLL을 사용하는 클라이언트 프로세스에서 스레드가 종료될 때마다 이 값과 함께 DllMain 호출

 - DLL에서는 이 값을 받았을 때 스레드별 종료 처리를 한다.

 

 

4) 네트워크, 데이터 송수신

현재 분석중인 악성코드는 데이터 송/수신을 위해 윈속(Winsock) 을 사용합니다.

악성코드는 Server/Client 중에 Client에 해당하므로, bind, listen 등의 함수는 생략되며

WSAStartup > socket > connect > send/recv > WSACleanup 순서로 호출됩니다.

WSAStartup/WSACleanup은 윈속을 사용하기 위해 반드시 호출해야하는 초기화/해제 함수입니다.

 

Hex-ray로 변환된 코드를 보면 아래 스샷의 첫줄에 if( !WSAStartup() ) 코드가 보입니다.

어셈블리어로 살펴보면, 제법 자주나와 눈에 익은 TEST EAX, EAX -> JNZ 명령어 콤보입니다.

이후 socket 함수의 결과값은 CMP ESI, 0FFFFFFFFh 명령어로 -1이 아닌지 비교합니다.

데이터를 송수신하는 과정에서 주목해야할 부분은 inet_addr 함수에 들어가는 고정IP(127.26.152.13)

htons 함수로 변환되어 들어가는 0x50(80) 포트번호입니다. 웹트래픽이 발생하겠군요.

이후 connect 함수의 결과값이 -1인지 확인하기 위해

CMP EAX, 0FFFFFFFFh > JZ 명령어 콤보를 사용합니다. 이번기회에 아예 외워버리겠네요

 Tip. 2의보수

 1의 보수는 단순히 0과 1을 변경하여 음수를 표현합니다.

 하지만 0이 2개가 생기는 문제가 발생합니다. ex) 0000 0000 = 1111 1111 = 모두 0

 이러한 문제의 해결책이 2의 보수 입니다. 1의 보수의 결과값에 1을 더합니다.

 2의 보수를 이용하여 -1을 표현하려면 0000 00001 -> (1의보수) -> 1111 1110 -> (+1) -> 1111 1111

 따라서 위의 코드에서 if(v3 != -1) 을 어셈블리어로 표현할때 CMP ESI, 0FFFFFFFFh 명령어가 나왔습니다.

 

 

본격적으로 while문 내부에서 어떤 통신이 이루어지는지 살펴봅니다.

while문 내부의 조건에 2가지 함수가 위치하고 있습니다.

첫번째는 send 함수로서, 서버측으로 "hello" 문자열을 송신합니다.

두번째는 shutdown 함수로서 네트워크를 종료하는데 사용하는 함수입니다.

shutdown 함수는 close 함수와는 다르게, 옵션을 통해서 연결의 한쪽만 닫을 수 있습니다.

뒤쪽에 아직 recv, closesocket 함수 등이 남아있기 때문에 1(SD_SEND) 옵션을 통해 send 쪽만 닫습니다.

 

서버에 "Hello" 문자열을 보내고 sned 소켓을 닫아버렸습니다. 이후에는 서버의 응답을 기다립니다.

recv 함수의 리턴값은 수신한 바이트 크기이므로 [ 결과값 > 0 ]이면 다음으로 넘어가 문자열 비교를 수행합니다.

TEST EAX, EAX (플래그설정) + JLE(Jump If Less or Equal) (<=) 명령어 콤보로 [ 결과값 <= 0 ]인 경우 분기시킵니다.

이후 서버의 응답을 구분하기 위해 strncmp 함수를 통하여 5(unsigned int) 만큼 문자열 길이를 비교하여 "sleep" 인지 체크합니다.

strncmp 함수는 일치할 경우 0을 리턴하므로, TEST EAX, EAX (플래그설정) + JNZ(Jump If Not Zero) (!=0) 명령어 콤보로

[ 결과값 != 0 ]인 경우, 중략된 코드를 실행하기 위해 분기하고, [ 결과값 == 0 ]인 경우에는 1분간 Sleep 하게됩니다.

 

명령어가 "sleep"이 아닌경우, 분기하여 "exec" 문자열과 비교합니다.

일치하지 않을 경우에는 [ Str==113 ]인지 확인한 후 핸들을 닫지만,

일치한 경우에는 hex-ray 상으로 memset, CreateProcessA 함수를 호출합니다.

 Tip. memset이 없다?

위 어셈블리 코드에서 memset 함수는 rep stosd 명령어로 대체되었습니다.

① rep : 반복적인 작업 수행 시 사용하며, 한번 수행시마다 ECX값 -1씩 감소 ( ECX>0 일때까지 수행 )

② stosd : 4byte(DWORD) 단위로 EDI에 EAX값을 저장하며, 실행 될 때마다 EDI값 +4 증가

이를 바탕으로 위의 어셈블리 명령어를 해석하면

"ECX에 넣어준 11h(17번) 횟수 만큼 EDI에 넣어준 StartupInfo 구조체를 EAX에 넣어준 0 값으로 초기화" 입니다.

4byte 단위로 초기화 하므로 총 68byte를 초기화하는 셈이며, 실제 _STARTINFOA 구조체의 크기(sizeof)를 찍어보면 68입니다.

 

 

이러한 초기화 형태는 3)번 항목의 OpenMutex 함수 실행전에 있던 memset 함수에서도 동일하게 적용됩니다.

다만 그곳에서는 stosw 명령어로 2byte 초기화를 연속해서 수행합니다.

memset 이후에 수행되는 __int16(2byte) 형의 v13 변수 초기화를 수행하기 때문입니다.

char 형 변수인 v14 의 경우에는 [ v14 = 0 = null ] 로 처리되어 별도의 stosb 가 수행되지 않은것 같습니다.(추정)

 

어쨋든, 이러한 초기화 작업이 끝나고 나면

CreateProcess 함수를 통해 특정 프로세스를 실행시킵니다.

여기서 눈여겨 보아야할 것은, 실행시키는 프로세스가 무엇이냐 일텐데,

CreateProcess 함수의 두번째 인수에 들어가는 CommandLine 변수가 어떤 값으로 지정되는지는 아직 알 수 없습니다.

 

지금까지 분석한 DLL의 행위를 정리해보면 아래와 같습니다.

- 큰 스택을 사용한다. (이유는 아직 알 수 없다)

- 뮤텍스를 생성하여 DllMain 안에 있는 코드의 중복 실행을 방지한다.

- WinSock을 이용하여 특정 IP주소(127.26.152.13)와 통신한다.

- WInSock을 이용하여 받은 문자열이 "exec"일 경우, 특정 프로세스를 실행시킨다.

  다만, 아직 어떤 프로세스가 실행될지는 알 수 없다.

 

 

2. Lab07-03.EXE

 

1) 커맨드라인 인자 확인

실행파일의 main 함수를 먼저 살펴보면, 아래와 같이 인자의 개수와 값을 확인합니다.

argc 값이 2가 아니거나, strcmp 결과값이 "WARNING_...." 문자열과 일치 않으면 바로 종료됩니다.

 

문자열을 비교하는 부분은, 분홍색의 라이브러리 함수로 간단하게 표시될줄 알았지만

바이트 단위로 연산을 수행하는 어셈블리 명령어로 되어 있어서, 내부 동작을 살펴볼 수 있었습니다.

strcmp 함수 앞에 !(not) 연산자가 붙어 있다 보니, cmp 결과값이 0이 아니면 바로 점프하는 JNZ 명령어가 있습니다.

비교를 하고난 뒤에 TEST CL, CL + JZ 명령어를 통해 문자열의 끝을 체크합니다.

 Tip. 올리디버거 Dump Window

 argument의 값을 체크하려다가 Code Window에서 보니, 문자열 값이 명령어로 표시되더군요.

 

 확인하고 싶은 값을 Hex 또는 아스키값으로 확인하려면 아래와 같이 Dump Window를 활용합니다.

 

 

2) DLL 파일 생성, 읽기, 복사

현재 분석중인 악성코드는 메모리맵 파일을 이용하여 DLL 파일에 접근하고 있습니다.

파일을 올려 읽는 단계는 아래와 같이 이루어집니다.

① CreateFileA : 파일오픈

② CreateFileMappingA : 파일 내용을 메모리에 올림

③ MapViewOfFile : 메모리에 올려진 파일의 첫번째 주소값을 얻음

이와같은 실행흐름을 통해 Kernel132.dll 파일과 Lab07-03.dll 파일을 메모리에 올리고 있습니다.

이후 어떤 행동을 하는지 살펴봅니다.

 Tip. Symbolic Constant 또는 OllyDbg

 함수에 인자가 많을 경우 일일이 MSDN을 찾아가며 파악하면 시간이 너무 오래 걸리는 경우가 많습니다.

 또한 이전에 한번 파악해두었던 함수의 경우엔 Symbolic constant 기능을 이용하면 빠르게 파악이 가능합니다.

 또한 해당 기능을 통해 상수를 지정해 놓으면, Hex-ray 에서 변환된 코드에서도 동일하게 반영됩니다.

 

 

 IDA에서 일일이 Symbolic constant 를 검색을 통해 일일이 적용하기가 부담스러울 수 있습니다.

 MSDN을 뒤져가며 맞는지 확인해야 하는 경우가 있기 때문이죠.

 이럴땐 OllyDbg를 먼저 참조하는것이 도움이 될 수 있습니다.

 아래 스샷에 보시다시피 우리가 IDA에서 바꾸었던 상수들이 표기되어 있음을 알 수 있습니다.

 

 

이어지는 소스코드는 메모리로 복잡한 읽기와 쓰기가 진행됩니다.

프로그램의 모든 마지막 오퍼레이션까지 확인할 수는 없지만

악성코드의 핵심 기능에서 자주 쓰이는 sub_401040 서브루틴은 어떤 오프셋 계산을 수행하는지

뒤쪽에서 4)번 항목에서 분석합니다.

 

산술연산과 오프셋 계산 과정을 마치면,

메인 하단의 소스코드에서 CloseHandle 함수를 이용하여 파일 편집을 종료했음을 알 수 있습니다.

이후 kernel132.dll 에 Lab07-03.dll 파일을 복사합니다.

kerne132.dll은 kernel32.dll 대신 사용할 것이라고 추측할 수 있지만, 로딩될지 여부는 아직 알 수 없습니다.

주목할만한 사항은 sub_4011E0 함수에 전달되는 "C:\\*" 경로값 입니다.

해당 경로를 어떻게 사용하는지 살펴볼 필요성이 있습니다.

 

 

3) 디렉토리 탐색

sub_4011E0 함수 역시 main과 동일하게 각종 산술연산과 오프셋 계산으로 

어떤 동작을 하는지 파악하기가 어려웠습니다. 그래서 일단 Function Calls 윈도우를 통해

하나씩 차근차근 파악을 해보려 합니다. 주목할만한 단서는 2가지입니다.

① FindFirstFileA, FindNextFileA, FindClose + 경로문자열(C:\\*)

② sub_4011E0(본인/재귀함수)

 

①번을 이용하여 인자값으로 "C:\\*" 경로를 넣어주고

FindFirstFileA, FindNextFileA 함수를 이용하여 코딩하면 아래와 같이 파일을 탐색할 수 있습니다.

해당 함수의 결과값을 보았을때, 재귀함수가 사용되었던 이유는

C드라이브의 모든 디렉토리를 재귀함수를 이용하여 탐색하기 위함으로 생각됩니다.

 

함수의 도입부에서 IF문의 조건을 살펴보면

dwFileAttributes(파일속성) 값이 디렉토리(0x10, FILE_ATTRIBUTE_DIRECTORY16) 이며,

strcmp 함수를 통한 비교 결과가 0이 아닌 경우 진입합니다.

strcmp는 동일할 경우 0을 반환하므로, 비교 문자열이 "." 또는 ".." 이 아닌 경우이죠.

정리하면, 디렉토리일 경우에는 진입, 실행파일인 경우에는 else문입니다.

 

이렇게 진입을 나눠 놓은 이유는 재귀함수 구조를 이용하여 디렉토리를 탐색하기 때문일

가능성이 높습니다. 이를 확인하기 위해 코드를 살펴본 결과, 디렉토리인 경우에는

아래와 같이 본인을 호출하는 코드가 있었습니다.

 

탐색 결과가 실행파일인 경우의 else문을 살펴보면

함수의 마지막 부분에서 stricmp 함수를 통해 ".exe" 를 비교하는 부분이 눈에 띕니다.

EXE파일을 따로 추출하여 어떤 작업을 수행하는 부분은 직관적으로 알 수 있었지만,

IF문 안에 들어간 조건문이 생각보다 까다로웠습니다.

 

stricmp 함수의 첫번째 인자를 계산하는 부분은 LEA EBX, [ESP+ECX+154h+FIndFileData.dwReserved1] 명령입니다.

여기서 ECX는 strlen() 함수를 이용한 cFileName의 결과값입니다.

OR ECX, FFFFFFFF 명령어를 통해 최대값을 넣어준 후 repne scasb 명령어를 이용하여 문자열 길이를 얻어냅니다.

 Tip. IDA의 변수 표현법

 IDA에서는 변수를 표현하는 방법이 다양합니다.

 특히 FindFIleData 구조체는 [esp+154h+FindFileData] 으로 표현되고 있습니다.

 자칫 잘못 생각하면 esp에 계속 주소값이 + 된다고 착각할 수 있지만,

 FIndFileData가 선언된 부분을 살펴보면, 주소값이 ebp 기준으로 [-140h] 입니다.

 결국 [esp + 154h - 140h ] = [esp + 14h] 인 것이죠.

 분석가의 가독성을 높이기 위헤 + 로 표현되어 있지만,

 무심코 머리속으로 플러스(+)만 계산하면 낭패를 볼 수 있습니다.

 

 

 [ESP+154h+FindFileData] 명령어에서 154h가 스택포인터인지 알 수 있는 방법은

 아래처럼 [Options - General - Stack pointer] 옵션을 체크하면 됩니다.

 덤으로 [ Auto comment ] 옵션도 체크해 보았는데, Jump 구문이 해석되어 있어 유용했습니다.

 

 

 

 가독성을 높힌 코드가 아니라, 실제 ESP레지스터 기준의 어셈블리어 명령어를 보려면

 마우스 우클릭을 하여 아래와 같이 변경해주면 됩니다.

 

 

stricmp 명령어의 첫번째 인자에서 dwReserved1 주소값이 들어가는 이유는

아래 그림처럼 4바이트 앞의 주소값에서 시작하여 파일이름의 길이만큼 더하면 확장자만 추출할 수 있기 때문입니다.

(Hex-Ray 코드와 어셈블리코드가 dwReserved0과 dwReserved1을 혼용하고 있으므로, 어셈블리어 기준으로 진행)

 

결과적으로는 재귀함수를 통해 디렉토리를 탐색하며 EXE 파일이 보이면

sub_4010A0 서브루틴을 수행하는 것으로 보이며, 어떤 행위를 하는지 이어서 살펴봅니다.

 

 

4) EXE파일 변조, sub_4010A0()

EXE파일이 검색되면, sub_4010A0 함수에 아래와 같이 경로를 포함한 파일을 인자로 넣고 호출합니다.

분석을 용이하기 위해 notepad.exe 파일을 C:\ 경로에 미리 옮겨 놓았습니다.

 

MapViewOfFile 함수를 이용하여 메모리맵 파일의 Starting Address를 이용하여 PE Signature를 체크합니다.

주소값을 이용하기 때문에 중간중간 IsBadReadPtr 함수를 이용하여 포인터의 유효성을 검사합니다.

 Tip. "[ ]" 연산자를 이용한 주소값 계산 [1]

 ptr이 임의의 배열을 가리키는 포인터이고, n이 정수일때 ptr[n] = *(ptr+n) 이 성립합니다.

 즉, 위에서 *((_DWORD *)result +15) 코드에서, result 변수는 void 형이기 때문에

 사용과 동시에 (_DWORD *) 포인터형으로 캐스팅 되었고, "[]" 연산자를 사용할 수 있습니다.

 따라서 4byte(DWORD) 단위로 계산되어, ImageBase에서 60byte 떨어진 곳에 위치하는  

 _IMAGE_DOS_HEADER 구조체의 e_lfanew 멤버를 가리키게 됩니다.

 e_lfanew는 NT header의 오프셋을 가지므로, 

 (int)(char *)result + *((_DWORD *)result +15) 를 통하여 

 _IMAGE_NT_HEADERS 구조체의 Signature 멤버가 PE(45 50 00 00, 17,744) 인지 체크할 수 있습니다. 

 

"PE" 시그니처가 확인되면 sub_401040 함수에 인자 3개를 넣어줍니다.

① [arg1] : 7604 : *(_DWORD *)(v5 + 128) = *(_DWORD *)(3D0160) = Import Directory RVA

② [arg2] : v5 : 3D00E0 = ImageBase + E0 = NT헤더 시작주소 (PE Signature)

③ [arg3] : v2 : 3D0000 = ImageBase

 Tip. "[ ]" 연산자를 이용한 주소값 계산 [2]

 위의 "[]" 연산자와 동일하게 생각하여 v5가 4byte(int) 단위라는 생각에

 무심코 128*4 = 512 주소값을 더하여 계산하면 낭패를 보게됩니다.

 v5는 포인터형이 아닌 int 형이므로, "[]" 연산자의 조건이 성립하지 않습니다.

 따라서 (v5 + 128)의 의미는, v5에 저장된 정수(실제로는 주소)에 128을 더하는 연산입니다.

 OllyDbg로 확인해보면 위와 같이 EBP+80h(128) 으로 계산되는 부분을 확인할 수 있습니다.

 

해당 인자값들을 이용하여 어떤 값을 반환하는지 알아보기 위헤 sub_401040 서브루틴을 살펴봅니다.

함수 내부를 살펴보면, 또다시 매개변수 a1, a2를 이용하여 sub_401000 서브루틴을 실행합니다.

특히 리턴값이 result = a3 + a1 + *(_DWORD *)(result + 20) - *(_DWORD *)(result + 12) 계산식이므로,

sub_401000 의 결과값을 알지 못하면 해석이 불가능합니다.

 

sub_401000 서브루틴으로 한단계 더 깊숙히 들어가봅니다. 중간의 while 구문이 함수의 핵심 기능인것 같습니다.

while 문은 한번 반복할때마다 Import Directory RVA 값과 RVA(섹션시작주소) + VirtualSize(섹션크기) 값을 비교하여

Import Directory RVA 값이 더 크면 result 에 40을 더합니다.

이것이 의미하는 바는, Import Directory RVA 즉, IID가 존재하는 섹션의 헤더를 찾겠다는 의미입니다.

만약 IID가 포함된 섹션이 아니라면, IMAGE_SECTION_HEADER 구조체의 크기인 40씩 더하여

다음 섹션헤더로 넘어갑니다. 반복문에서 탈출하면 계산된 섹션헤더의 주소값을 리턴합니다.

sub_401000 함수의 기능을 정리해보면 아래와 같습니다.

> NT헤더 시작주소(Signature)를 첫번째 인자로 받아서, 두번째 인자의 RVA 주소가 위치하는 섹션 헤더를 찾아 반환

 Tip. sub_401000 서브루틴 주요 변수

 코드를 해석하기 위해 계산한 변수값들은 아래와 같습니다. (굵게 표시된 부분이 RVA)

 ① v3 = 3 (NumberOfSerctions)

    # *(_WORD *)(a2 + 6) = *(_WORD *)(3D00E0(a2) + 6) = *(_WORD *)(3D00E6) = 3

 ② result = 3D001D8 (Section Header Offset, Opiotnal Header 오프셋 + 헤더크기)

    # Opiotional Header Size : *(_WORD *)(a2 + 20) = *(_WORD *)(3D00E0+ 20) = *(_WORD *)(3D00F4) = E0

    # Optional Header Offset : a2 +24 = 3D00E0 +24 = 3D00F8 =NT헤더 오프셋 + NT헤더 크기

 ③ v5 = 1000 (Virtual Address, 메모리에서 섹션의 시작 주소, RVA)

    # *(_DWORD *)(result + 12) = *(_DWORD *)(3D001E4) = 1000

 

이제 sub_401000 서브루틴의 리턴값이 IID가 포함된 섹션헤더의 주소임을 알았습니다.

sub_401040 함수에서 분석하지 못했던 나머지 부분을 다시 살펴보면 아래와 같습니다.

result = a3 + a1 + *(_DWORD *)(result + 20) - *(_DWORD *)(result + 12)

이것을 sub_401000 함수의 리턴값으로 분석해보면 아래와 같습니다.

result = 3D0000(ImageBase) + 7604(RVA) - 1000(VA) + 400(Pointer to Raw Data) : 3D6A04 

해당 공식은 RVA 값을 이용하여 File Offset 을 구하는 공식입니다.

결과적으로 sub_401040 서브루틴의 주요 기능을 정리하면 아래와 같습니다.

> RVA, NT헤더 시작주소(PE Signature), RVA를 인자로 받고, 해당 RVA의 파일 오프셋을 게산하여 반환

 Tip. sub_401040 서브루틴 주요 변수

 result = 3D6A04 (File Offset)

    # result = 3D0000(ImageBase) + 7604(RVA) - 1000(VA) + 400(Pointer to Raw Data) : 3D6A04 

    #  *(_DWORD *)(result + 20) = *(_DWORD *)( 3D001D8+ 20)  = *(_DWORD *)(3D001EC) = 400 (Pointer to Raw Data)

    # *(_DWORD *)(result + 12) = *(_DWORD *)( 3D001D8+ 12) = *(_DWORD *)(3D001E4) = 1000 : (VA)

 

for( i = (int)((char *)v6 + 12); ; i +=20) 코드의 의미는 20byte 단위의 IID(IMAGE_IMPORT_DESCRIPTOR) 구조체에서

12byte 에 위치하고 있는 Name 멤버를 변수 i 에 할당한 후

stricmp 함수로 "kernel32.dll" 문자열을 비교하기 위해 수행하는 반복문입니다.

 

For문 내에서도 sub_401040가 또한번 호출되고 있음을 볼 수 있는데,

IID구조체의 첫번째 변수가 Import Name Table RVA 값을 가지기 때문에

이 역시 파일 오프셋 값으로 변환해야되기 때문입니다.

해당 악성코드가 왜 자꾸 RVA 값을 파일 오프셋으로 변환하는지 알아낼 필요가 있습니다.

 

문자열 비교 후에 do while 문으로 반복을 수행하는 것이 보입니다. 해당 부분은

REPNE SCASB 명령어로 문자열의 길이를 얻는 코드로서, 어셈블리 명령어로 보는것이 가독성이 빠릅니다.

길이를 계산한 이후에는 memcpy 명령어를 이용하여 IID의 Name 멤버의 "kernel32.dll" 문자열을

Lab07-03.dll의 복사 파일인 "kernel132.dll" 문자열로 패치합니다.

실행파일이 임포트하는 DLL을 조작하는 핵심코드이며, 이를 위해서 RVA -> FileOffset 변환이 필요했습니다. 

 Tip. 문자열길이 구하기 : REPNE + SCASB

 해당 명령어의 의미는 아래와 같습니다. 

 - rep : ecx > 0 인 동안 명령어 반복

 - repne : Zero Flag 가 1이고, ecx >0 인 동안 명령어 반복

 - scasb/scasw/scasd ptr : 단위별로 EAX와 ptr 값을 비교

 이것을 이용하여 memcpy 에서 사용되는 v12 변수의 문자열 길이를 구하는 부분을 살펴보면

 MOV EDI, EBX 명령어를 통해 EDI에 문자열을 입력하고 난 후

 OR ECX, FFFFFFFF 명령어를 통해 ECX 값을 최대치로 설정해둡니다.

 REPNE SCAS [EDI] 명령어를 통해 EDI에 저장된 문자(byte, word, dword 단위)와 EAX(=0) 값을

 비교하여 동일한 값이 아니면 [ECX = ECX -1] , [EDI = EDI +1] 연산을 반복합니다.

 EDI에 저장된 문자열의 끝의 Null(=0x00)을 만나거나, ECX의 값이 0이 되면 루틴이 종료되며, 

 마지막에 계속 -1씩 깍던 ECX의 값을 NOT 연산자로 반전시켜 문자열의 길이를 얻습니다.

 

 

패치 이후에는 [v5 + 208], [v5 + 208 + 4] 주소에 있는 값들을 0을 초기화 시킵니다.

해당 위치는 BOUND IMPORT_Directory_Table(BIT) 영역인데, 왜 0 값으로 설정할까요?

리버싱 서적인 나뭇잎 책에서는 패치를 통한 DLL Injection 부분에서

"BIT는 DLL 로딩 속도를 향상시킬 수 있는 기법이며, 그냥 놔둬도 문제 없는 경우가 많지만

인젝션을 테스트할 때 BIT 때문에 정상 동작하지 않아서 0으로 설정 한다" 는 내용이 있었습니다.

아마 우리가 분석중인 악성코드도 정상 실행을 보장하기 위해 해당 코드를 넣지 않았을까 생각됩니다.

모든 작업이 종료되면 UnmapViewOfFile, Close Handle 함수 등으로 할당받은 자원을 해제합니다.

 

지금까지 분석한 EXE의 행위를 정리해보면 아래와 같습니다.

- 메모리맵 파일을 이용한 DLL 복사

- 디렉토리를 탐색하여 EXE파일 검색

- EXE 파일의 IID에서 Name 멤버값이 "kernel32.dll" 인 경우 오프셋 계산

- 반환된 오프셋을 이용하여 정상 DLL의 이름을 악성코드 DLL로 패치

 

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

3. 문제풀이

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

 

1) 이 프로그램은 어떤 방식으로 컴퓨터가 재시작할 때마다 실행을 보장하는가?

이 프로그램은 C:\\Windows\System32에 DLL을 복사해서 영구적으로 감염시키고,

C드라이브의 모든 EXE 파일이 해당 DLL을 임포트하도록 변조합니다.

 

2) 이 프로그램을 탐지할 때 호스트 기반으로 좋은 시그니처는 무엇인가?

프로그램은 "kernel132.dll" 파일명으로 하드 코딩되어 있으므로,

호스트기반의 시그니처로 사용할 수 있습니다.

 

3) 이 프로그램의 목적은 무엇인가?

프로그램의 목저은 삭제하기 어려운 원격 호스트 접속을 가능하게 만드는 백도어를 생성하는데 있습니다.

백도어는 두 명령어를 가지는데, 하나는 명령어를 실행시키는 "exec" 이며, 나머지는 "sleep" 입니다.

 

4) 일단 악성코드가 설치된다면 어떻게 삭제할 수 있는가?

해당 프로그램은 C드라이브의 모든 exe 파일을 감염시키므로 매우 삭제하기 힘듭니다.

이런 경우는 백업해서 복구하는게 가장 좋으며, kernel32.dll의 이름을 kerne132.dll로 변경하면

임시적인 대처를 할 수 있습니다.

 

 

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

4. 마무리

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

이번 문제는 어디까지 분석을 진행하느냐에 따라 매우 분량이 커질 수 있다고 생각합니다.

실제로 오프셋을 계산하는 많은 부분들을 생략했음에도 불구하고,

지금까지 분석한 실습 악성코드중 가장 많은 포스팅 내용이 나왔습니다.

내용이 많아 오타 또는 잘못된 내용이 있을 수 있으므로,

댓글 남겨주시면 확인 후 수정하겠습니다.

 

By Red_Message.

 

posted by Red_Message

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 7-2을 분석합니다.

악성코드의 분량은 얼마 되지 않았지만.. 익숙하지 않은 내용들로 인해 시간이 많이 걸렸습니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

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

0. 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1) PE구조 & 패킹체크

별도의 패킹이 되어 있지 않습니다.

 

2) DLL 체크

ole32.dll, OLEAUT32.dll, MSVCTR.dll 총 3개의 DLL을 임포트합니다.

MSVCRT.dll에서 가장 많은 13개의 API를 임포트하고 있습니다.

 

- ole32.dll

3개 API 모두 생소합니다. 책에서는 COM과 관련된 API라고 되어있지만

COM 자체가 다뤄보지 않은 영역이다보니 개념이 잘 잡히지 않더군요.

 Tip. COM

 위키백과 : 컴포넌트 오브젝트 모델(Component Object Model, COM)은 마이크로소프트가 개발한

 소프트웨어 구성 요소들의 응용 프로그램 이진 인터페이스이다.

 COM을 이용해 개발된 프로그램들은 프로세스간 통신과 동적 오브젝트 생성이 가능하다.

 + 여러 컴포넌트 객체를 이용하여 프로그램을 개발하는 모델을 의미

 실습교재 : 이종의 소프트웨어 컴포넌트에서 상호 세부내용을 모르더라도 서로 다른 코드를

 호출할 수 있게 하는 인터페이스 표준이다. COM을 이용하는 악성코드를 분석할 때

 COM 함수 호출의 결과로 어떤 코드가 실행될지 알 필요가 있다.

 

- OLEAUT32.dll

Name에는 Ordinal 값만 적혀있으며, Hint가 N/A로 비어있네요.

DWalker로 살펴보았더니, N/A로 비어져 있는건 똑같지만

Ordinal 값에 해당하는 함수는 아래 스샷과 같이 보기 좋게 표기되어 있었습니다.

해당하는 API는 SysAllocString(0x0002), SysFreeString(0x0006), VariantInit(0x008) 이군요.

SysAllocString : COM에서 다루는 스트링인 BSTR생성 (VC++에서 유니코드 문자열은 BSTR타입으로 표시)

② SysFreeString : 할당한 BSTR 해제

③ VariantInit : COM에서 사용하는 VARIANTARG(or VARIANT) 구조체 초기화

 

- MSVCTR.dll

앞에 [ _ ] 문자가 1~2개씩 붙어있는 API 들이 많습니다.

검색해보니, 아래와 같이 나와있더군요.

"밑줄이 붙는 것은 MS 확장 키워드이며, 밑줄 한개의 경우는 하위 호환성을 위해 남겨져 있을 뿐 2개가 정식 표기"

"C++ 의 표준 키워드에 MS가 약간의 의미변경 or 기능추가한 경우, 맡줄을 추가하여 컴파일러가 판단하는 기준으로 사용"

별거 아닐 수 있지만...모르면 찜찜하니까요.

 

 

3) String 체크

지금까지의 악성코드 실습파일중 역대급으로 출력되는 스트링이 없네요.

.rdata 영역에 dll 이름만 덩그러니 놓여 있습니다.

 

뭔가 허전하기도 해서, 지난번 Tip을 복습할겸 .rdata 영역에 대해서 다시 보았더니...

>> .rdata : 프로그램 내의 전역에서 접근 가능한 읽기 전용 데이터를 담고 있음.

>> .idata : 존재 시 임포트 함수 정보를 저장하고 있으며, 존재하지 않는다면 .rdata 섹션 내의 임포트 함수 정보에 저장

그래서 .idata가 정말 없나? 하고 CFF로 구조를 살펴보니 정말 .idata 섹션이 없네요!

 

 

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

1. 이 프로그램은 어떤 방식으로 지속 메커니즘을 보장하는가?

2. 이 프로그램의 목적은 무엇인가?

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

일단 악성코드를 실행해 보면 특정 URL의 ad.html 페이지가 뜨게됩니다.

웹페이지의 이름으로 미루어보건데, 광고페이지로 추정됩니다.

 

Hex-ray 기능을 이용하여 해당 악성코드의 main을 살펴보면

별도의 지속성을 위한 메커니즘이 보이지 않으며, 굉장히 짧은 코드내용을 가지고 있습니다.

 

어셈블리코드를 보면서 찬찬히 분석을 해봅니다.

COM을 사용하는 각 스레드는 다른 COM 라이브러리 함수 호출 전에 적어도 한번은

OleInitialize나 CoInitializeEx 함수를 호출해야합니다.

현재 분석중인 악성코드 역시 OleInitialize 함수를 호출하고 있으며,

이후에 CoCreateInstance에 RCLSID, RIID, PPV 등의 인자값을 넣어주며 COM 기능에 접근합니다.

RCLSID : 클래스식별자 / 생성하고자 하는 컴포넌트의 CLSID

② RIID : 인터페이스식별자 / 얻고자 하는 컴포넌트의 인터페이스

③ PPV : 객체 인터페이스를 받을 수 있는 포인터

 

악성코드는 COM에 접근하며 넣어주는 클래스식별자(CLSID)는 0002DF01-0000-0000-C000-000000000046 입니다.

CLSID값이 의미하는 것이 무엇인지 알아내려면 레지스트리 HKLM∖SOFTWARE∖Classes∖CLSID 에서 찾아보아야 합니다.

LocalServer32Internet Explorer 경로가 존재하므로,

CoCreateInstance가 호출될때 로드할 실행 파일임을 유추할 수 있습니다.

 

인터페이스식별자(RIID)로 넣어주는 값은 0D30C1661-0CDAF-11D0-8A3E00C04F0C90E26E 입니다.

해당값은 검색해 보았더니 MSDN에서 친절하게 알려주더군요!

 

CoCreateInstance 함수가 성공적으로 동작하게 되면, PPV로 명명한 변수 내의 스택에 저장되며

COM의 기능을 사용하기 위해 인자값으로 사용될 변수(URL 등)들을

VariantInit 함수와 SysAllocString 함수를 이용하여 세팅합니다.

 

COM의 기능을 호출하는 부분은 0x401074 주소입니다.

하지만 지금까지 편리하게 분홍색으로 표시해주는 API들과는 달리

명령어 call dword ptr [edx+2Ch] 로 표기되어 있습니다. 단번에 어떤 기능을 호출하는지 파악하기 어렵습니다.

악성코드 프로그램이 COM 함수를 호출했을 때 무슨 일이 일어나는지 인지하기 위해

IDA Pro의 구조체 서브뷰(SubView)에서 [INSERT - Add standard structure] 기능을 이용합니다.

 Tip. Structure 기능 활용하기

 구조체 표기를 변경하기 위해 퀵뷰에서 Structure를 선택합니다.

  

 

 우리가 필요한 구조체는 CoCreateInstance 함수를 호출할때 인자값으로 넣어주었던

 인터페이스식별자, 즉 IwebBrowser2 이므로, 해당값으로 검색해줍니다.

 검색결과는 IwebBrowser2②IwebBrowser2Vtbl 2개가 나옵니다.

 결과적으로 ①번은 오프셋이 나오지 않았고, ②번을 추가했더니 정상적으로 명명이 됐습니다.

 아마 오프셋을 명명하는데 필요한 부분이 IwebBrowser2 구조체 자체가 아니라

 가상 포인터 테이블 목록(VTBL, Virual Function Table)이 필요해서가 아닐까 추측합니다.

 

 

 이후에는 2Ch에 마우스 우클릭을 하면, 아래와 같이 오프셋을 명명할 수 있습니다.

 

 

 

변경하고나니 아래처럼 스택에 푸시되는 인자값들의 설명도 자동으로 표기됩니다. 

이후에 SysFreeStringOleUninitialize 등을 호출하며 main이 종료됩니다.

 

 

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

3. 이 프로그램은 언제 실행을 종료하는가?

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

결과적으로 COM 기능을 이용하여 광고 웹페이지를 띄운 후 실행을 종료합니다.

 

posted by Red_Message

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 7-1을 분석합니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

 

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

0. 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1) PE구조 & 패킹체크

별도의 패킹이 되어 있지 않습니다.

 

2) DLL 체크

KERNEL32.dll, ADVAPI32.dll, WININET.dll 총 3개의 dll을 임포트합니다.

눈여겨 보지 않았었는데, 각 dll별로 임포트하고 있는 숫자를 CFF에서 표기해주는군요.

각 DLL에서 43개, 3개, 2개의 API를 임포트하고 있습니다.

 

- KERNEL32.dll

항상 가장 많은 API들을 임포트하고 있어서 그런지, 딱 보았을때 어떤 API를 이용하여

어떤 악성행위를 하는지 가장 감이 오지 않습니다.

 

- ADVAPI32.dll

서비스 관련 API들이 보입니다. 아마 백그라운드에서 동작할 수 있음을 염두해둡니다.

 

- WININET.dll

지난번의 문제들과 동일하게 Internet 연결 관련 API들이 보입니다.

 

 

3) String 체크

IDA의 String Window(Shift+F12)를 보면 아래와 같이 .data섹션(초기화된 전역변수, 읽기/쓰기 가능) 에서

MalService, 악성URL, IE와 버전 등의 스트링값이 존재합니다.

 

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

1. 이 프로그램은 어떤 방식으로 컴퓨터가 재시작할 때마다 실행(지속 메커니즘)을 보장하는가?

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

악성코드를 호출하면 main에서 가장 첫번째로 호출하는 함수가 바로 StartServiceCtrlDispatcher API입니다.

실전 악성코드 책에 있는 악성코드를 분석하면서, 서비스에 등록되는 악성코드 실습을 이미 해봤기에

그냥 비슷하거나 동일한 케이스라고만 막연히 생각하였는데, 가만보니 처음보는 API였습니다.

그래서 지난 실습을 검색하여서, 어떤 차이점이 있나 확인해보았습니다.

- 실습3-2 URL : http://redscreen.tistory.com/13

 

결론적으로 주요 골자는 아래와 같습니다.

"서비스는 WinLogon.exe가 호출하는 Services.exe(SCM:서비스제어관리자)에 의해 관리되지만

예외적으로 DLL 기반의 서비스는 svchost.exe에 의해 관리된다"

지난번엔 DLL기반의 서비스를 분석하였고, 오늘은 SCM이 관리하는 서비스에 관한 내용을 살펴봐야 합니다.

 

main 어셈블리 코드를 살펴보면 아래와 같습니다.

아직 악성코드가 어떤 방식으로 실행되어지고, 서비스가 등록되는지 파악은 안되지만

"MalService" 라는 서비스를 만들어 동작하고 있음을 확인되었습니다.

 

 

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

2. 이 프로그램은 왜 뮤텍스를 이용하고 있는가?

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

짧은 메인에서 StartServiceCtrlDispatcherA API를 호출하고 난 이후에 바로 sub_401040 서브루틴을 호출합니다.

해당 서브루틴은 아래와 같이 OpenMutexA API를 호출하고

함수가 종료된 이후에 값이 0인지 확인하는 TEST EAX, EAX 명령어를 이용해 프로세스 종료 혹은 진행을 결정합니다.

특이한점은, "HGL345" 라는 이름의 뮤텍스를 오픈해서 핸들을 정상적으로 가져왔을 경우에

왼쪽으로 분기하여 ExitProcess를 호출하며 프로세스를 종료시키는 점입니다. 

 

만약 해당 뮤텍스가 존재하지 않는다면, 오른쪽으로 분기하여 아래와 코드를 실행합니다.

기존에 있는 뮤텍스에 접근하는것이 아니라, CreateMutexA API를 이용하여

방금 시도했던 "HGL345" 뮤텍스를 새로 생성합니다.

이런 코드구조로 실행되는 이유는 특정 시간에 시스템에서 실행 파일 사본이 하나만 동작하게 보장하기 위해서입니다.

만약 이미 악성코드가 수행중이라면, 바로 위의 OpenMutexA 함수 호출을 성공하면서, 프로세스가 종료됩니다.

 

사실 뭔가 내용은 많아보이지만, Hex-ray 상으로 살펴보면, 그냥 스쳐지나가는 체크일뿐입니다.

이후에 맨아래의 CreateServiceA를 호출하기 위해

OpenSCMManagerA, GetCurrentProcess, GetModuleFileNameA API를 순차적으로 호출합니다.

OpenSCMManagerA : 프로그램이 서비스를 추가하거나 수정할 수 있는 서비스 제어 관리자 핸들 반환

② GetCurrentProcess : 현재 프로세스의 핸들 반환 (근데 왜 실행했는지는 모르겠네요)

③ GetModuleFileNameA : 첫째 인자가 0 인 경우, 현재 동작중인 실행 파일이나 로드된 DLL의 전체 경로명 반환

Tip. API 뒤에 붙은 A or W는 String을 다루는 API임을 알려줍니다. A는 ANSI를, W는 UNICODE를 의미합니다.

 

CreateServiceA API를 호출하기 위해서는 아래처럼 많은 인자값들이 필요합니다.

그중에 주요한 인자만 정리하고 넘어갑니다.

- hSCManager : 서비스 실행에 필요한 SCM / ①에서 얻은 SCM 핸들

- dwServiceType : SERVICE_WIN32_OWN_PROCESS(0x10)

        ※ SERVICE_KERNEL_DRIVER(0x01) / SERVICE_FILE_SYSTEM_DRIVER(0x02) / SERVICE_ADAPTER(0x04) / SERVICE_RECOGNIZER_DRIVER(0x08)

- dwStartType : 시스템 시작 시 자동 시작 / SERVICE_AUTO_START(0x02)

       SERVICE_BOOT_START(0x00) / SERVICE_SYSTEM_START(0x01) / SERVICE_DEMAND_START(0x03) / SERVICE_DISABLED(0x04)

- lpBinaryPathName : 실행파일의 바이너리 경로 / ③에서 얻은 경로

 

서비스함수를 실행한 이후에는 시간과 관련된

SystemTimeToFileTIme, CreateWaitableTimerA, SetWaitableTimer, WaitForSingleObject API들을 순차적으로 실행합니다.

여기서 중요 포인트는 WaitForSingleObject API입니다.

 

아래 Hex-ray 결과에서 알 수 있다싶이 WaitForSingleObject의 결과값이 0이되면

20번 반복수행되는 CreateThread가 동작합니다.

WaitForSingleObject의 인자값을 넣어주기 위해서 CreateWaitableTimerA, SetWaitableTimer 등을 실행합니다.

SystemTimeToFileTIme : 시스템 타임 --> 타임 포맷으로 변환 / ③의 인자값으로 사용하기 위해

② CreateWaitableTimerA : waitable 타이머 객체 생성( 생성시 Non-Signaled 상태 ) / ①의 결과값을 인자로 사용

③ SetWaitableTimer : waitable 타이머 객체에 알람 시간 설정 ( 알람 시간이 되면 Signaled 상태 ) / ②의 결과값을 인자로 사용

WaitForSingleObject : 넣어준 waitable 타이머 객체가 Signaled 상태가 되면, WAIT_OBJECT_0(0x00000000L) 반환

Tip. 자료형에 따른 접미사

 

SystemTime.wYear에 2100을 할당함으로서, 2100년 1월 1일이 되어

반환값이 WAIT_OBJECT_0(0x00000000L)이면 20번의 CreateThread를 수행하게됩니다.

CreateThread안에는 StartAddress 라는 함수가 들어있는데,

InternetOpenA -> InternetOpenUrl 순으로 "http://www.......book.com" URL에 IE8을 이용하여

반복적으로 접근을 시도합니다. 20개의 스레드가 동작하므로 상당한 트래픽이 유발될 수도 있습니다.

DDoS 공격을 위한 악성코드가 이런식으로 구성되는구나...싶었습니다.

 

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

3. 이 프로그램을 탐지할 때 호스트 기반으로 좋은 시그니처는 무엇인가?

4. 이 악성코드를 탐지할 때 네트워크 기반으로 좋은 시그니처는 무엇인가?

5. 이 프로그램의 목적은 무엇인가?

6. 이 프로그램은 언제 실행을 종료하는가?

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

3. 호스트기반의 좋은 시그니처는 "MalService"라는 서비스와 "HGL345"라는 이름의 뮤텍스입니다.

4. 네트워크 기반의 시그니처는 IE8.0과 URL입니다.

5. 프로그램의 목적은 특정 시간대에 수행되는 DDoS 공격입니다.

6. 종료되지 않으며, 2100년 1월 1일부터 20개의 스레드를 동작시켜 무한루프로 동작합니다.

posted by Red_Message

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 6-4을 분석합니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

 

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

1. 실습 6-3과 6-4의 main 함수에서 호출한 함수 간의 차이점은 무엇인가?

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

 

[Flow Chart]를 이용하여 main의 구조를 살펴보면,

지난 문제들과는 구조가 약간 상이함을 알 수 있습니다.

기존의 0x401000(인터넷 연결 확인 함수), 0x401040(HTML파싱), 0x4012B5(printf) 등은 동일합니다.

 

Tip. 주석 남기기

기존에 분석한 함수의 기능을 빨리 파악하기 위해 주석을 남기는게 좋습니다.

- 명령어 옆에 코멘트 : 단축키 [ : ]

- 라인추가 : 단축키 [ INS ], [ Shift+INS ] (코드 위 아래로)

 

 

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

2. 어떤 새로운 코드 구조가 main 함수에 추가됐는가?

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

 

그래프상에서 아래에서 위로 향하는 굵고 파란 화살표가 눈에 띕니다.

바로 반복문을 뜻하는 화살표입니다.

 

어떤 반복문이 추가되었는지 hex-ray 기능을 이용하여 살펴보면

아래와 같이 for문이 추가된것을 알 수 있습니다.

 

어셈블리코드에서 살펴보면,

main 초반에 인터넷 연결을 확인한 이후에 바로 반복문에 진입합니다.

만약 인터넷 연결이 되어있지 않다면,

XOR EAX, EAX 명령어로 Overflow Carry 플래그를 청소한 뒤 loc_4012B1 으로 분기합니다.

 

이후부터 [ebp+var_C]에 0을 할당하게 되는데, 여기서 var_C 변수는 반복 카운터에 사용됩니다.

초기에 0 으로 설정된 이후에, 바로 아래에 있는 loc_401251 에서 add 명령어를 통해 +1씩 증가합니다.

for문이 종료되는 시점은 loc_40125A 에서 cmp [ebp+var_C], 5a0h(1440) 명령어의 결과값이 (op1 - op2)

0 이상이면 jge(greater or equal, <=) 명령어에 의하여 loc_4012AF 주소로 분기하게 됩니다.

 

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

3. 이 실습의 HTML 함수 파싱이 기존 실습과 어떤 차이점이 있는가?

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

기존 HTML 파싱을 수행했던 0x401040 서브루틴의 경우 인자값이 없었습니다.

하지만 지금 실습중인 악성코드는 for 문의 var_C 변수를 인자값으로 받습니다.

해당 인자값이 어떻게 사용되는지 살펴보면 아래와 같이 sub_401040의 매개변수 a1으로 선언되어

sprintf 함수의 3번째 인자로 사용됩니다.

 

sprintf 함수로 인하여 Dest 변수에 입력된 "Internet Ex..../Pma%d" 문자열은 다시

InternetOpenA API의 첫번째 인자인 User-Agent 값에 들어가게 됩니다.

결국 0x401040에 넣어주는 인자값은, 서버에 접속할때마다 Uger-Agent 값을 +1 씩 변경할 것이고

서버측에서는 클라이언트의 Uger-Agent 값을 통해 얼마나 오래 감염된 PC인지 추정할 수 있는 근거가 될것입니다.

 

 

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

4. 프로그램이 얼마나 오래 동작하는가? (인터넷에 연결돼 있다고 가정)

5. 이 악성코드에서 네트워크 기반의 행위가 존재하는가?

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

얼마나 동작하는지 알아내기 위해, Sleep 함수가 있는곳으로 가보겠습니다.

Sleep 함수에 입력되는 인자값은 0EA60h(60,000) = 60초 입니다.

push되는 "0EA60" 값이 문자열스럽지만, 계산기로 16진수 계산하시면 정확합니다.

for문은 1440번 반복되므로, 60이 곱해지면 자연스럽게 1440분이 됩니다.

1440분 = 24시간 이므로, 하루동안 동작하는 프로그램입니다.

 

네트워크 기반의 행위는 매번 변경되는 User-Agent 값이 되겠습니다.

 

 

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

6. 이 악성코드의 목적은 무엇인가?

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

전 문제와 동일하게 인터넷 연결 여부, HTML 파싱등을 수행하나,

증가되는 User-Agent 값을 통하여 클라이언트를 관리할 수 있는 기능이 추가되었습니다.

 

posted by Red_Message

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 6-3을 분석합니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

 

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

0. 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1) PE구조 & 패킹체크

별도의 패킹이 되어 있지 않습니다.

 

2) DLL 체크

KERNEL32.dll, WININET.dll, ADVAPI32.dll 총 3개의 dll을 임포트합니다.

앞선문제와는 달리 ADVAPI32.dll이 추가되었으며, 레지스트리 관련 API가 보입니다.

 

3) String 체크

IDA의 String Window(Shift+F12)를 보면 아래와 같이 .data섹션(초기화된 전역변수, 읽기/쓰기 가능) 에서

앞선 문제와는 달리 자동실행 관련 레지스트리 키 값이 보이고,

디렉토리 경로들이 보입니다.

 

Tip. Quick View

몇일간 또 안봤다고 윈도우들의 단축키가 가물가물합니다.

애매할땐 [Ctrl+1] 을 이용한 Quick View 호출이 최고입니다..^^;

  

 

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

1. 실습 6-2의 main 함수와 비교하라. main이 호출한 새로운 함수는 무엇인가?

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

실습 [6-2]와 다르게 main이 호출한 새로운 함수는 sub_401130 입니다.

 

 

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

2. 새로운 함수는 어떤 인자를 갖는가?

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

call sub_401130 명령 이전에 push되는 인자를 확인하면 총 2개입니다.

첫번째 인자는 lpExistingFIleName argv 이고, 두번째는 var_8 이네요.

argv의 주소는 argv[0]과 동일하므로,  프로그램의 이름으로 볼 수 있습니다.

 

[ebp+var_8] 의 경우는 좀더 위쪽으로 올라가서 값을 할당받는 부분을 살펴보아야 합니다.

0x0040122D 주소에 있는 명령어를 보면, AL(1byte of EAX) 레지스터 값을 var_8 에 할당합니다.

EAX의 경우는 함수의 리턴값을 가지므로, 결론적으로 sub_401040 리턴값이 var_8 입니다. 

 

sub_401040의 리턴값은 파싱했던 HTML 문서 주석문 "<!--" 다음에 나오는 문자입니다.

자세한 내용은 전 포스팅을 참조해주세요.

 

 

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

3. 이 함수가 가지는 주요 코드 구조는 무엇인가?

4. 이 함수는 무슨 일을 하는가?

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

 

대략 어떤일을 하는지 [User Xrefs chart] 기능을 이용하면 파악하기 용이합니다. 

DeleteFileA, CopyFileA, RegOpenKeyExA 등등...파일/디렉토리/레지스트리 관련 API 들이 보입니다.

 

API를 통하여 파일생성, 삭제, 레지스트리와 관련된 행위를 할 것으로 생각됩니다.

 

 

일단 전체적인 그래프의 모양을 살펴보면, 지난 문제에서 마주쳤던 if-else 문과는 다르게

중간의 분기문에서 여러군데로 뻗어나가는 모양세입니다.

 

Hex-ray 기능을 이용하여 코드를 살펴보면, 역시 switch 구문인것을 알 수 있습니다.

다음부터 이와 유사한 그래프 모형을 보게된다면, switch 구문임을 직감할 수 있겠네요.

 

0x401130 주소에서 스택프레임이 생성된 이후에 arg_0var_8 을 이용하여 특정 연산을 수행합니다.

특히 0x401140 주소의 sub 명령어로 61h 값을 빼준 이후에 곧장 CMP 명령어로 4와 비교합니다.

비교가 끝나면 JA명령어(OP1 > OP2 --> jump) 를 이용하여 분기합니다.

61h는 아스키코드 a(97) 를 의미하며, 97을 뺀 이후에

CMP 명령어에 의하여 4를 또 뺏는데, 크다면 값이 f(102) 이상이라는 소리입니다. 

이것이 의미하는 바는 알파벳 a, b, c, d, e 의 범주가 아니면 switch 구문의 default로 분기하는 것이며,

a~e 의 범주 내에 있다면 0x00401153 주소의 jmp 명령어로 이용하여 분기합니다.

 

jmp 명령어에서 edx4를 곱하는 이유는 ex) ds:off_4011F2[edx*4]

아래처럼 참조하는 메모리 주소 집합이 4바이트 단위로 존재하기 때문입니다.

하지만, IDA에서 살펴보니 주소값이 0x4011F2 로 동일하게 보이더군요.

 

Ollydbg로 확인하면 실제 주소값이 4byte 단위로 리스트되어 있습니다.

Tip. 이렇게 점프테이블을 사용하는 패턴은, 컴파일러가 siwtch 구문을 생성할때 종종 사용한다고 합니다.

 

 

이제 본격적으로 각 Switch 구문에서 어떤일을 하는지 알아봅시다.

case 97 C:\\Temp 디렉토리 생성

case 98

- C:\\Temp 디렉토리에 lpExistingFileName 을 cc.exe 이름으로 파일 복사

- lpExistingFileName의 경우 sub_401130에 인자로 넣어준 값이므로, 프로그램의 이름입니다.(argv[0])

- 어떤 값을 넣어주었는지 다시 복기하려면 초반에 많이 사용했던 Xrefs 기능 [Ctrl+X] 단축키를 사용합니다.

case 99C:\\Temp\\cc.exe 파일 삭제

case 100

- HKEY_LOCAL_MACHINE의 Run 레지스트리 오픈

- RegSetValueEx API를 이용하여 C:\\Temp\\cc.exe 악성코드를 자동실행 레지스트리에 등록

case 101100초간 Sleep

default : 에러메시지 출력

 

 

 

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

5. 이 악성코드에서 호스트 기반의 행위(indicator)가 존재하는가?

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

호스트기반의 행위는

Software\Microsoft\Windows\CurrentVersion\Run 레지스트리 값 변경과

C:\Temp\cc.exe 경로의 파일생성(복사) 로 볼 수 있습니다.

 

 

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

6. 이 악성코드의 목적은 무엇인가?

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

 

인터넷 연결확인과 주석확인의 경우, 전 문제와 동일하며

다운로드된 HTML파일의 값에 따라

 

switch 구문을 이용하여 파일생성 및 레지스트리값 변경, Sleep 등이 주 목적입니다.

 

posted by Red_Message

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 6-2을 분석합니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

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

0. 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1) PE구조 & 패킹체크

별도의 패킹이 되어 있지 않습니다.

 

2) DLL 체크

샘플 [Lab06-01.exe]와 동일하게 KERNEL32.dll, WININET.dll 2개의 dll을 임포트합니다.

다만 WININET.dll의 임포트 함수 목록이 5개로 증가하였습니다.

InternetOpenA, InternetOpenUrlA 등 샘플 [Lab01-02.exe] 에서 분석하였던 API가 보입니다.

아마 문자열을 어떤 IE를 사용하는지, 어떤 URL로 접속하는지 나오지 않을까요?

 

3) String 체크

IDA의 String Window(Shift+F12)를 보면 아래와 같이 .data에서

IE버전과 URL을 발견할 수 있습니다.

 

 

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

1. main 함수가 호출하는 첫 번째 서브루틴은 무슨 오퍼레이션을 수행하는가?

2. 0x40117F에 위치한 서브루틴은 무엇인가?

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

호출되는 첫번째 서브루틴은 샘플 [Lab06-01.exe]와 동일합니다.

[ View-Graphs-FlowChart(F12) ] 를 이용하여 전체적인 맥락을 살펴보면

전 샘플과는 다르게 if문이 중첩되어 있는 사실을 알 수 있습니다.

어쨋든 첫번째 서브루틴은 동일 기능입니다.

 

0x40117F에 위치한 서브루틴은 샘플 [Lab06-01.exe]와 동일하게 printf 기능을 수행합니다.

 

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

3. main 함수가 호출하는 두 번째 서브루틴은 무엇인가?

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

main 함수가 호출하는 두번째 서브루틴(loc아님)은  아래와 같은 구조를 가지고 있습니다.

전체적인 파악을 하려면 시간이 좀 걸리겠습니다.

 

그래프는 복잡하지만 IDA의 Hex-ray 기능을 이용하면 좀더 직관적인 파악이 가능합니다.

소스코드를 통해 유추한 내용을 바탕으로 대략적인 흐름은 아래와 같습니다.

① InternetOpenA : WinNet 라이브러리 사용 초기화

② InternetOpenUrlA : FTP or HTTP URL이 명시하는 위치로 핸들 얻기

③ InternetReadFile : InternetOpenUrlA를 통해 얻은 핸들을 이용하여 데이터를 읽음

④ InternetCLoseHandle : 오픈한 핸들 닫기

 

해당 프로그램이 어떤 결과를 도출하는지 알아내기 위해 OllyDbg를 통해 살펴봅니다.

본래 InternetOpenUrlA API를 호출하여 연결에 성공하면 EAX 레지스터에

URL에 대한 올바른 핸들값이 리턴되지만, 성공하지 못하면 NULL 값이 리턴됩니다. 

 

이후 "Error 2.1: Fail to OpenUrl" 문자열을 출력하고 InternetCloseHandle API를 이용하여 핸들을 닫습니다.

 

하지만, 해당 프로그램이 연결에 성공했다면 어떻게 되었을까요?

InternetReadFile API를 이용하여 데이터를 읽었을겁니다.

이후에 if문을 통해 buffer의 값을 비교하기 시작합니다.

 

buffer의 값을 1씩 올리면서 비교하는 구문은 OllyDbg를 통해 보면 아래와 같습니다.

같지 않을 경우 무조건 0x0040111D로 분기하는 것이 else 구문을 표현하는 것이었군요.

 

물론 IDA의 Graph 모드를 통하면 좀더 직관적으로 알 수 있습니다.

하지만 이부분도 Hex-ray 기능이 없었다면 파악하기 힘들었을 것 같네요.

이러한 패턴을 잘 눈여겨 봐야겠습니다.

  

비교하는 문자열 3Ch와 21h 등을 IDA에서 우클릭을 통해 변환하면

위와 같이 "<!--" 형태의 HTML 주석 문자열임을 알 수 있습니다.

문자열 비교가 성공적(?) 으로 끝나면 5번째 문자열을 AL(1byte 0f EAX) 레지스터에 옮깁니다.

즉 리턴값인거죠.

 

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

4. 이 서브루틴에서 사용한 코드 구조는 어떤 유형인가?

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

3번 과정에서 AL에 리턴값을 성공적으로 가져왔다면 어떤 구조로 실행되는지 다시 Main으로 가봅니다.

AL레지스터의 값을 비교하는데 TEST EAX, EAX 명령어를 사용했습니다.

지난번에 Tip에 해당 명령어에 대해 기재했었지만 다시 까먹은 관계로 복습하면

"함수에서 돌아온 이후 수행하는 TEST EAX, EAX 명령어는 두 OP가 모두 0인지 판단" 합니다.

즉 AL에 0이 리턴됐다면 실패한 것이고, 성공했다면 AL에 5번째 문자 1Byte가 할당되어 있겠죠?

 

이후 printf 함수로 "Success: Parsed command is %c\n" 문자열을 출력하고

Sleep 함수를 실행합니다.

여기서 %c 문자는 레지스터 AL에 담긴 1byte 문자입니다.

 

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

5. 이 프로그램에서 네트워크 기반의 행위가 존재하는가?

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

① User-Agent 값으로 IE 7.5 를 사용

http://mal...(중략).../cc.htm 을 다운로드

 

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

6. 이 악성코드의 목적은 무엇인가?

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

인터넷 연결의 활성 여부를 확인한 이후

특정 User-Agent 를 이용하여 웹페이지 다운을 시도합니다.

해당 웹페이지는 주석으로 시작하며, 파싱에 성공하면 출력 후 종료합니다.

 

 

posted by Red_Message

안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 6-1을 분석합니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

 

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

0. 기초 동적 분석

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

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

먼저 Imprt 함수 목록을 살펴보면 아래와 같이 Kernel32와 WININET 2개가 존재합니다.

WININET은 HTTP 프로토콜과 상호작용할 수 있도록 해주며

임포트 하고 있는 InternetGetConnectedState는 로컬 시스템의 인터넷 연결 상태를 확인하는 API입니다.

 

StringWindow(Shitft+F12)에서도 해당 API가 사용하는 "Success : Internet Connection" 문자열을 발견할 수 있습니다.

 

이제 해당 악성코드(?)를 실행시켜보면 위에서 파악한 문구가 출력됨을 확인할 수 있습니다.

 

Tip. PE 파일 영역

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

위 String Window를 살펴보면 WININET.dll 에서 사용하는 String의 .data 섹션이고

나머지는 .rdata 섹션에 있는것을 발견할 수 있습니다.

궁금증에 생겨 각 섹션에 내용을 정리합니다.

 

 

.text : CPU가 실행하는 명령어를 담고 있다. 일반적으로 실행 가능한 코드를 포함하는 유일한 섹션.

.data : 프로그램의 초기화된 전역 데이터를 저장하는데, 프로그램의 어디에서든지 접근 가능함. 읽기/쓰기 가능

.rdata : 프로그램 내의 전역에서 접근 가능한 읽기 전용 데이터를 담고 있음.

.bss : 프로그램의 초기화 되지 않은 전역 변수들을 위한 섹션. 가상주소공간에 매핑될 경우 보통 .data 섹션에 병합됨.

.idata : 존재 시 임포트 함수 정보를 저장하고 있으며, 존재하지 않는다면 .rdata 섹션 내의 임포트 함수 정보에 저장

.edata : 존재 시 익스포트 함수 정보를 저장하고 있으며, 존재하지 않는다면 .rdata 섹션 내의 익스포트 함수 정보에 저장

.pdata : 64비트 실행 파일에만 존재하며, 예외 처리 정보를 저장

.rsrc : 실행 파일에 필요한 리소스를 저장

.reloc : 라이브러리 파일 재배치 정보

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

 

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

1. Main이 호출하는 서브루틴만으로 발견한 주요 코드 구조는 무엇인가?

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

Main의 코드는 아래와 같습니다.

가장 눈에 띄는 것은 [ call sub_401000 ] 명령어입니다.

아마 Sub 함수이니 eax에 리턴값이 저장되어 있을겁니다.

전 문제에서 Tip으로 기재한 부분이었는데, 이번 문제에서 또 보니 복습이 되네요.

따라서 eax값을 mov문으로 변수 var_4에 담은 이후에 cmp문을 이용하여

loc_401056 또는 loc_40105B 루틴으로 분기를 하는 내용입니다.

 

 

사실 코드로 보니 한눈에 안들어오는 경향이 있습니다.

아래와 같이 Graph 모드로 보면 좀더 이해하기 쉽습니다.

근데 이번 챕터가 아니었으면, 이러한 Graph 구조가 if문 구조라는 사실을 잘 몰랐을 것 같네요.

 

이를 Hex-ray 기능을 이용하여 보면 아래와 같이 간단한 구조임을 알 수 있습니다.

약간 의아했던 점은, sub_401000()의 결과 값을 담았던 var_4 변수가 없다는 점입니다.

 

궁금해서 상세 보기를 했더니, 스택상에서는 [ebp-4] 주소에 할당되어 있었습니다. (이미 변수 이름이 var_4 지만요)

아마 명시적인 선언은 하지 않았지만, if문 안에 sub루틴이 있다보니

true false를 판단하기 위해 스택에 공간이 할당된것 같습니다.

 

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

2. 0x40105F에 위치한 서브루틴은 무엇인가?

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

0x40105F에 위치한 서브루틴은 아래와 같습니다.

어떤 기능을 해야 하는지 알아보려면 call 위에 push 되는 인자값들을 알아야 합니다.

IDA가 표현해주는 arg 값을 통해 유추가 가능하지만 직관적으로 잘 파악이 안됩니다.

 

해당 부분을 올리디버거로 보면 이해하기가 수월합니다.

Call 하기 전에 넣어주는 인자값이 "Success..." 문자열입니다.

또한 하이라이트 처리된 Call 명령어를 수행하고 나면 CMD창에 해당 문자열이 출력됩니다.

 

이를 보았을때 0x40105F에 위치한 서브루틴의 기능은 Printf로 볼 수 있습니다.

하지만 IDA Pro 상용버전과 무료버전 모두 항상 printf를 인식해 명명하지 않는다고 하니

push되는 문자열의 끝부분에 라인피드 문자  "\n"  를 캐치하여 printf 함수임을 예측할 수 있어야 합니다.

해당 함수에 printf 라고 레이블을 달아주면 그 다음부터는 이해하기 쉬워지겠죠?

분명 5-1 문제에서 rename을 배웠는데, 이런 부분은 분석을 수월히 해내기 위해 습관화 되어야 할 것 같습니다.

 

 

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

3. 이 프로그램의 목적은 무엇인가?

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

인터넷 연결의 활성 여부를 확인한 후 확인 결과를 출력하는 프로그램입니다.

악성코드가 사용할법한 API이니 기억해둡니다!

 

posted by Red_Message

#1 : http://redscreen.tistory.com/66

#2 : http://redscreen.tistory.com/67

#3 : http://redscreen.tistory.com/68

#4 : http://redscreen.tistory.com/71

 

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

13. DllMain이 직접 호출하는 윈도우 API 함수는 몇 개인가? (두번째 깊이에서)

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

DllMain이 호출하는 API를 알아보기 위해 DllMain에서 [Xrefs from] 기능을 실행했더니 어마어마한 결과가 나왔습니다.

그래서 문제에서 두번째 깊이에서 하라고 했구나..싶었습니다.

 

깊이를 설정하기 위해 [View-Graphs-User xrefs chart] 를 선택합니다.

이후에 아래와 같이 설정해줍니다. 깊이는 DllMain 자신을 제외하고 "1"로 설정합니다.

 

이후에 아래와 같은 결과를 얻을 수 있습니다.

예전에 막무가내로 분석할때는 막대한 차트량을 보고 그냥 꺼버렸는데,

책을 통해 차근차근 보니 배를 냅두고 수영했다는 생각이 드네요.

 

 

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

14. 0x10001358에서 Sleep 호출이 존재한다. (sleep까지 수밀리초 값을 파라미터로 갖는 API)

      코드 후반부를 보면 이 코드가 수행되려면 프로그램이 얼마동안 sleep하는가?

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

해당주소로 가보면 아래와 같은 코드가 나옵니다.

sleep함수를 실행하기 위해 eax 인자값을 받고있습니다.

eax값이 무엇인지 알아내기 위해 eax값에 관여하는 명령어들을 살펴보겠습니다.

10001341 mov eax, off_10019020 : 주소값 10019020에서 eax에 "[This is CTI]30" 문자열을 할당합니다.

10001346 add eax, 0Dh : eax의 값에 0Dh(13)을 더합니다. 앞에 있는 "[This...]" 값을 지나 30을 가리키게 합니다.

1000134A call ds:atoi : 문자열 30을 숫자로 바꿉니다.

10001350 imul eax, eE8h : eax에 저장된 30에 3E8h(1,000)을 곱합니다.

즉, 정리하면 30,000 밀리초 = 30초 를 기다리게합니다.

 

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

15. 0x10001701에서 소켓을 호출한다. 세 가지 파라미터는 무엇인가?

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

해당주소로 가보면 아래와 같은 코드가 나옵니다.

socket에 들어가는 값은 6, 2, 1 값입니다.

MSDN에 들어가 해당 상수값이 의미하는바를 찾아보려 했는데

문제의 출제 의도는 이 상수값을 확인하라는 것이 아닙니다.

다음 문제에서 나머지를 다룹니다.

 

 

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

16. 소켓과 IDA Pro에서 명명한 심볼 상수 기능을 이용해 이 파라미터를 좀 더 유용하게 가능?

      변경 후 파라미터는 무엇인가?

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

각 숫자를 오른쪽 클릭해서 Use Symbolic Constant를 선택하면 IDA Pro에서

특정 값을 갖는 모든 상수 리스트를 나열한 대화상자가 나타납니다.

해당값들에서 문자열을 입력하면 자동으로 검색이 됩니다.

값을 찾아 지정하면 아래와 같이 변경된 인자들을 확인할 수 있습니다.

조금더 유용해진건 사실이지만, 함수에 따라 상수값을 자동으로 넣어주는 기능이 있었으면 좋겠네요.

있는데 제가 모를 확률이 큽니다.

 

 

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

17. 명령어 옵코드 0xED의 사용법을 찾아보자. 이 명령어는 VMware 탐지를 수행하는 VMXh

      매직 문자열로 사용한다. 이 악성코드는 이를 이용하고 있는가?

      VMware를 탐지하는 다른 증거가 있는가?

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

먼저 OPCODE중의 하나인 0xED의 사용법에 대해 찾아보았습니다.

검색에서 명확한 답을 찾기 힘들어서, 가지고 있는 어셈블리언어 책을 참고하였습니다.

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

IN : Input from Port

- 포트에서 한 바이트나 워드를 AL 또는 AX로 입력한다.

- 소스 피연산자는 포트 주소로서 8비트 상수나 DX내에 16비트 주소로 표현된다.

- IA-32에서는 더블워드가 포트를 통해 EAX에 입력될 수 있다.

- 명령어 형식 : IN accum, imm 또는 IN accum, DX

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

사실 읽고도 이해가 잘 안갔습니다.

갑자기 포트 얘기가 나오면서 첫번째 멘붕이었고

이것이 VMware 탐지를 수행하는 매직문자열 VMXh 문자열과 무슨 상관이 있는건지...

이해가 안되자 계속 IN명령어와 0xED에 대해 검색했지만, 명쾌한 답을 찾지 못하여 생각을 달리하였습니다.

VMware 탐지 우회에 대한 내용으로 검색하자 관련 내용이 나오더군요

내용에 대해 알고싶으신 분들을 위해 블로그 URL을 남깁니다. (URL : http://goo.gl/BvFjJF)

 

간단히 정리하자면, "VMXh" 문자열은 VM을 탐지하기 위한 다양한 방법들중 하나입니다.

원리는 가상머신 환경에서 Guest와 Host 간의 통신채널 확인입니다.

Intel x86 환경에서 I/O 작업을 수행하기 위해서는 IN OUT 명령어를 사용하며

"VMHx" 문자열은 가상머신 환경에서 IN 명령어에 의해 생성되는 Host와 Guest간의 고유한 통신 채널을 의미합니다.

 

이제 왜 IN명령어를 쓰고, "Vmxh" 문자열을 사용하는지 알았으니,

해당 명령어와 문자열이 있는지 검색해보겠습니다.

[Search-text] 를 선택하여 문자열 0xED를 검색합니다.

 

생각보다 결과값이 많이나옵니다. 문자열을 입력하면 바로 검색기능이 활성화 되므로

"in"을 입력하면 아래와 같이 우리가 원하는 라인을 찾을 수 있습니다.

 

더블클릭하면 아래와 같은 프로시져가 나오게 되며, 100061C7부터

아랫부분 In명령어를 이용한 Vmware 환경 탐지 부분이 나옵니다.

 

 

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

18. 0x1001D988로 점프해보자. 무엇을 찾을 수 있는가?

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

해당주소로 가보면 아래와 같은 무작위 문자열이 나옵니다.

 

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

19. IDA 파이썬 플러그인을 설치했다면 Lab05-01.py를 실행해보자.

      IDA 파이썬 스크립트는 이 책의 악성코드와 함께 제공한다.

      (커서가 0x1001D988에 위치해야 함) 스크립트 실행 후 무슨 일이 일어났는가?

21. 문자 편집기로 스크립트를 열어보자. 어떻게 동작하는가?

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

파이썬 코드는 문제 폴더에 Lab-01.py 이름으로 포함되어 있습니다.

커서의 현재 위치를 sea 변수에 저장한뒤, 0x00 ~ 0x50 까지 루프를 돌며 XOR 연산을 수행합니다.

 

0x1001D988에 커서를 두고 [File-Script File]을 선택하면 아래와 같이 문자열이 변경됩니다.

  

 

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

20. 동일한 위치에 커서를 두고 이 데이터를 ASCII 문자열로 어떻게 변환할 수 있는가?

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

문자열이 시작하는 주소에 커서를 두고 단축키 "A" 를 누르면 아래와 같이 아스키 문자열로 변환됩니다.

 

 

 

posted by Red_Message