1. 분석 환경
1.1. 분석 환경
l Windows 10 l Windows XP (VMware) |
1.2. 분석 도구
정적 분석 | l IDA Pro free (5.0) l IDA free (8.3) |
1.3. 분석 샘플
Practical Malware Analysis Labs - Lab05-01.dll, Lab05-01.py |
1.4. 질문
- DllMain의 주소는 무엇인가요?
- Imports window를 사용하여 gethostbyname에 액세스하세요. 해당 import는 어디에 위치하나요?
- gethostbyname을 호출하는 함수는 몇 개인가요?
- 0x10001757에서 위치한 gethostbyname 호출에 주목하면 어떤 DNS 요청이 이루어질 것으로 보이나요?
- 0x10001656 위치의 서브루틴에 대해 IDA Pro가 인식한 로컬 변수(지역변수)는 몇 개인가요?
- 0x10001656 위치의 서브루틴에 대해 IDA Pro가 인식한 매개변수(파라미터)는 몇 개인가요?
- Strings 창을 사용하여 어셈블리에서 문자열 \cmd.exe /c를 찾아보세요. 이것은 어디에 위치하나요?
- \cmd.exe /c에 대한 코드 영역에서 무슨 일이 일어나고 있나요?
- 같은 코드 영역의 0x100101C8에서, dword_1008E5C4는 어떤 경로를 선택할지 결정하는 전역 변수로 보입니다. 악성 코드가 dword_1008E5C4를 어떻게 설정하나요? (힌트: dword_1008E5C4의 cross-references를 사용하세요.)
- 0x1000FF58 메모리 주소에서 시작된 서브루틴 내의 몇 백 줄은 memcmp 함수를 사용하여 문자열을 비교하는 일련의 작업입니다. 만약 "robotwork"와의 문자열 비교가 성공적이라면(memcmp가 0을 반환할 경우) 어떤 일이 발생하는지에 대해 설명하세요.
- PSLIST export는 어떤 행위를 하나요?
- sub_10004E79에 xrefs graph from기능을 사용해보세요. 이 함수에서 호출될 수 있는 API 함수는 어떤 것이 있을까요? API 함수만을 기준으로 이 함수를 어떻게 이름 지을 수 있을까요?
- DllMain이 직접적으로 호출하는 Windows API 함수는 몇 개인가요? 깊이 2에서는 몇 개인가요?
- 0x10001358에서 Sleep 함수가 호출되고 있습니다 (이 함수는 밀리 초 단위로 지정된 시간 동안 프로그램을 일시 정지합니다). 코드를 리버싱 하면, 이 코드가 실행될 경우 프로그램은 얼마 동안 잠들게 될까요?
- 0x10001701에 socket이 호출되고 있습니다. 세 개의 매개변수는 무엇인가요?
- 소켓의 MSDN 페이지와 IDA Pro의 named symbolic constants 기능을 사용하여 매개변수를 의미 있는 값으로 바꿀 수 있을까요? 변경 후의 매개변수는 무엇인가요?
- in 명령어 (opcode 0xED)의 사용법을 검색하세요. 이 명령어는 매직 문자열 VMXh와 함께 VMware 탐지를 수행합니다. 이 악성코드에서 해당 명령어를 사용하나요? in 명령어를 실행하는 함수의 상호 참조를 통해 VMware 탐지를 하는지에 대한 추가 증거를 찾을 수 있나요?
- 0x1001D988로 커서를 이동하세요. 어떤 내용을 발견하나요?
- IDA Python 플러그인이 설치되어 있다면(이 플러그인은 IDA Pro의 상업용 버전에 포함되어 있습니다), malware과 함께 제공된 Lab05-01.py, IDA Pro Python 스크립트를 실행하세요. (커서가 0x1001D988에 위치해 있는지 확인하세요.) 스크립트를 실행한 후에는 어떤 일이 일어나나요?
- 동일한 위치에 커서가 있는 상태에서 이 데이터를 어떻게 하나의 ASCII 문자열로 변환할 수 있나요?
- 스크립트를 텍스트 편집기로 열어보세요. 이 스크립트는 어떻게 작동하나요?
2. 분석 과정
2.1. DllMain의 주소는 무엇인가요?
답 : 0x1000D02E
DLLMain의 주소는 functions window에서 확인 가능하다.
또는 IDA로 Lab05-01.exe을 로드 했을 때 첫 시작이 메인 함수이므로 eax 값을 확인하면 된다.
2.2. Imports window를 사용하여 gethostbyname에 액세스하세요. 해당 import는 어디에 위치하나요?
답 : 0x100163CC
굉장히 많은 import들이 있는데 ctrl+f를 사용하거나 그냥 이름 gethostbyname을 입력하면 찾을 수 있다.
2.3. gethostbyname을 호출하는 함수는 몇 개인가요?
답 : 5개의 함수가 총 9번 호출한다.
ctrl+X를 입력하면 List cross reference to 윈도우를 불러올 수 있다.
같은 내용이 중복되는 걸 볼 수 있는데, 각각 p(호출), r(읽기) 할 때 한번씩 세기 때문이다.
호출하는 함수는 sub_10001074 / sub_10001365 / sub_10001656 / sub_10002CCE 5개로, 총 9번 gethostbyname을 호출한다.
2.4. 0x10001757에서 위치한 gethostbyname 호출에 주목하면 어떤 DNS 요청이 이루어질 것으로 보이나요?
답: pics.praticalmalwareanalysis.com
Jump – jump to address 또는 G를 누르면 원하는 주소로 이동할 수 있다.
이동하게 되면 바로 ‘[This is RDO]pics.praticalmalwareanalysis.com’ 라는 문자열이 보인다.
여기서 중요한점은 add eax, 0Dh 부분으로, eax에 13(16진수0D)을 더해 14번째 값부터 읽어 오기 위해 사용되었다. 그래서 [This is RDO]을 제외한 pics.praticalmalwareanalysis.com가 실제로 DNS요청을 할 주소이다.
▼ H 단축키를 누르면 16진수를 10진수로 변환할 수 있다.
2.5. 0x10001656 위치의 서브루틴에 대해 IDA Pro가 인식한 로컬 변수(지역변수)는 몇 개인가요?
답 : 23개
2.6. 0x10001656 위치의 서브루틴에 대해 IDA Pro가 인식한 매개변수(파라미터)는 몇 개인가요?
답 : 1개
0x10001656 로 이동하면 아래 차트를 볼 수 있는데, Var~ arg_0까지 변수 선언 부이다.
주의할 점은 IDA Pro free 5.0 버전과 IDA Free 8.3버전의 결과가 다르다는 것인데,
5.0은 지역변수 20개, 매개변수 1개 / 8.3은 지역변수 23개, 매개변수 1개로 나온다.
버전에 따라 탐지하는 데에 차이가 있다.
⊙ 추가적인 이야기
이 목록에서 어떻게 지역변수와 매개변수를 구분할까? 변수의 이름과 변수의 위치를 확인한다. val = valriable 지역변수를 가리키며, arg = argument로 매개변수를 가리킨다. 위치도 차이가 있다. 지역변수들은 상대적으로 메모리에서 많이 떨어져있는데 반해 파라미터는 가까이에있다. 파라미터들은 빠른 액세스를 위해 레지스터에 저장되어있기 때문이다.
그렇다고 모든 지역변수와 매개변수가 구분이 명확히 되는것은아니다.최적화된 코드에서는 지역변수도 매개변수와 함께 레지스터에 저장될 수있으며, 컴파일러에 따라 매개변수가 레지스터가 아니라 스택에 저장될 수도 있다.
2.7. Strings 창을 사용하여 어셈블리에서 문자열 \cmd.exe /c를 찾아보세요. 이것은 어디에 위치하나요?
답: xdoors_d 섹션의 0x10095B34에 위치한다.
View – open subviews – strings 또는 shfit + F12 로 strings 창을 열고 ctrl + F 로 찾는다.
2.8. \cmd.exe /c에 대한 코드 영역에서 무슨 일이 일어나고 있나요?
답 : 원격 쉘 세션 생성, 그리고 문자열을 입력 받아 지정된 명령을 실행
코드영역을 살펴 보니, HI master.. Welcome Back 등 뭔가 단순한 실행 프로그램은 아닌것 같아보인다.
가장 아래에 Encrypt Magic Number For This Remote shell session이라는 문구가 있는데 보아 하니 원격 쉘 세션을 생성할 것으로 추측된다.
이제 이 Cmd를 호출하는 곳을 찾아 정확히 어떤 일이 일어나는지 살펴 보자. 상호 참조리스트를 확인하면 한 곳에서 호출하는 것을 알 수 있다.
호출된 곳으로 이동한다.
내려 보면 많은 양의 분기문이 보이는데, 모두 공통적으로 특정한 문자열들, buf1, memcmp함수의 호출이 있었다.
입력한 값을 Buf1에 저장하고, 특정한 문자열(language)과 Buf1을 memcmp 함수의 매개변수로 넘긴다.
memcmp 함수는 C라이브러리 함수인데, 두 매개변수 값을 비교하고, 두 값이 같을 경우 0을 반환한다.
즉 이 코드영역에서는 입력 값과 특정 문자열이 동일한지 확인하여, 동일하다면 각각의 입력한 명령어에 맞는 코드를 실행하도록 한다. 다르다면 다음 분기로 이동해 문자열을 입력한 값과 계속 비교한다.
* IDA에서 빨간줄은 False, 초록줄은 True를 뜻한다. Memcmp는 같으면 0을 반환하므로 0 =False로 이동하는 것이다.
보통 C에서 스위치문을 사용한 프로그램이 이런 그래프의 형태를 보였다. 앞의 hi master.. 부분은 프로그램을 시작했을때 인트로 부분이 될 것이고, 이 분기문들은 사용자가 선택할 메뉴가 될것으로 추측한다.
2.9. 같은 코드 영역의 0x100101C8에서, dword_1008E5C4는 어떤 경로를 선택할지 결정하는 전역 변수로 보입니다. 악성 코드가 dword_1008E5C4를 어떻게 설정하나요? (힌트: dword_1008E5C4의 cross-references를 사용하세요.)
답: GetVersionExA를 사용해 운영체제 버전에 따라 값을 설정한다.
코드영역에서 dword_1008E5C4는 cmd.exe냐 command.exe냐의 분기점을 선택하는 역할을 수행하는것으로 보인다.
그렇다면 이 값은 어떻게 세팅될까?
dword_1008E5C4의 상호 참조 리스트를 보면 w(write)하는 부분이 하나 보인다. 더블 클릭하여 따라간다.
아래그래프의 call sub_10003695 / mov dword_1008E5C4, eax 부분을 확인한다.
sub_10003695이 반환한 값 EAX을 dword_1008E5C4에 넣는 것을 볼 수 있다.
그러므로 전역변수 dword_1008E5C4의 값을 결정하는 것은 sub_10003695함수의 반환 값이다.
Sub_10003695 함수를 확인해보자.
주목할 부분은 getversionExA와 cmp, setz 이다.
GetVersionExA함수는 윈도우의 버전을 읽어오는 기능을 수행한다. 이것으로 현재 악성코드가 실행중인 운영체제가 윈도우일 경우 그 버전을 알아내기 위해 사용될 것으로 추측한다.
Cmp [ebp+VersionInformaton.dwPlatformId], 2 에서는. dwPlatFormId 값과 2를 비교한다.
아래의 표를 참고하면, win32_NT 버전일 경우 dwPlatFormId의 값은 2가 된다. 그러므로 현재 PMA의 분석환경인 windowsXP에서는 2가 되므로 2==2. cmp값은 0이되며, ZF는 1로 세팅된다.
Setz al setz는 ZF 값이 세팅 되어 있다면 메모리에 1을 저장한다. 그래서 여기서는 al에 1이 저장된다.
결과적으로 전역변수 dword_1008E5C4에는 1이 저장되는 것이다.
다시 0x100101C8로 돌아와서, 이제 전역변수는 ebx와 값을 비교하여 같으면 loc_100101D7로 점프한다.
결과적으로 현재 악성코드가 실행되는 운영체제가 win32_NT 라면 cmd.exe, 그 외 나머지들은 모두 command.exe가 실행 되도록 하는 목적을 갖고있다.
2.10. 0x1000FF58 메모리 주소에서 시작된 서브루틴 내의 몇 백 줄은 memcmp 함수를 사용하여 문자열을 비교하는 일련의 작업입니다. 만약 "robotwork”와 의 문자열 비교가 성공적이라면(memcmp가 0을 반환할 경우) 어떤 일이 발생하는지에 대해 설명하세요.
답: SOFTWARE\Microsoft\Windows\CurrentVersion의 worktime, worktimes 값을 가져와 문자열로 만든 다음 전송한다.
단축키 G를 사용해 0x1000FF58로 이동하여 robotwork를 찾는다.
Memcmp가 0을 반환한다면, test eax, eax의 결과값이 0이 되므로 빨간 줄로 이동한다
* (test는 두 인자의 and연산을 한다.)
따라가면 Sub_100052A2가 호출된다. 더블클릭해서 안으로 들어가 본다.
함수를 더블클릭해서 안으로 들어가면, 익숙한 문자열이 보인다.
RegOpenKeyExA로 SOFTWARE\Microsoft\Windows\CurrentVersion 키를 연다.
다음으로, \currentversion의 WorkTimes 값을 RegQueryValueA로 가져온다.
sprintf와 atoi를 각각 esi와 edi로 mov한다.
atoi를 사용하여 가져온 worktime 값을 정수로 변환한다.
* (atoi는 문자열을 정수로 변환하는 함수이다.)
그리고 sprintf를 사용하여 "[Robot_WorkTime:] %d"을 버퍼에 저장하는데, 이때 서식의 “%d”에 정수로 변환한 worktime 값을 넣어준다. 즉 "[Robot_WorkTime:] (가져온worktime값)" 이 저장 된다.
위 과정들을 한번 더 반복하는데, 이번엔 WorkTime’s‘값을 가져와 같은 내용을 반복 수행한다.
그리고 값을 가져와 저장하는 행위가 끝나면, sub_100038EE를 호출한다.
Sub_100038EE에서는 send 함수로 esi의 데이터를 전송하는 것을 볼 수 있는데 바로 이 esi가 방금 sprintf로 만들었던 문자열의 버퍼 주소 값을 갖고 있다.
즉 “[Robot_WorkTime:] worktime”과 “[Robot_WorkTimes:] worktimes” 문자열을 전송하는 것이다.
정리하자면, robotwork는 SOFTWARE\Microsoft\Windows\CurrentVersion의 worktime, worktimes 값을 가져와 문자열로 만든 다음 전송하는 행위를 한다.
-하- 에 계속 됩니다.