1. 종류

2. 디컴파일

.... 2.1 AutoHotKey_B

.... 2.2 AutoHotKey_L





1. 종류

  오토핫키의 경우 여러 종류가 있다. 공식 홈페이지에서 다운로드 받을 수 있는 공식 버전들부터 살펴보면 버전 1.01.1이 나뉜 것을 볼 수 있다. 이 외에도 아직 알파 버전이지만 2.0도 존재하며 공식 버전은 아니지만 H 버전도 존재한다. 여기서는 B, L 버전만 다룰 것이며 H 버전 및 2.0 버전은 L 버전을 기반으로 하는 것으로 보아 디컴파일 방식은 동일할 것으로 추측하여 제외하기로 한다.


  버전 1.0은 AutoHotKey Basic 또는 AutoHotKey_B로 불린다. 그리고 버전 1.1은 AutoHotKey_L로 불린다. 굳이 같은 공식 버전인데도 이렇게 나뉘는데는 이유가 있다. 2003년 경 원래 개발자가 개발한 버전이 있었는데 조금씩 업데이트 되다 2009년을 마지막으로 안정화되어 이제 추가적인 기능 추가는 없는 상태이다. 이것이 현재 AutoHotKey_B로 불리는 버전이다. AutoHotKey_L 즉 버전 1.1은 다른 개발자가 AutoHotKey_B를 기반으로 기능을 추가해 가고 있는 버전으로서 이후 공식 버전이 되었고 현재도 지속적으로 업데이트 중이다. L 버전은 B 버전을 포함한다.


  공식 버전들 외에도 AutoHotKey_H 버전도 존재한다. [ https://hotkeyit.github.io/v2/ ] 이것은 또 다른 개발자가 AutoHotKey_L 버전을 기반으로 멀티 쓰레딩 등의 기능을 추가한 버전으로서 L 버전의 업데이트에 맞추어 업데이트된다. 또한 L 버전을 포함한다. 


  참고로 H 버전은 이러한 특징 외에도 다른 특징이 있다. C나 파이썬 같은 다른 언어를 이용해 개발하면서 H 버전에서 제공되는 AutoHotKey.dll을 임포트하여 함수를 사용할 수 있는데 이를 통해 타 언어 개발 환경에서도 오토핫키를 사용할 수 있다.





2. 디컴파일

2.1 AutoHotKey_B

  오토핫키는 오토잇(AutoIt)과 비슷하게 Ahk2Exe.exe를 이용해 ahk 소스 코드를 exe 바이너리로 변환할 수 있다. 그리고 변환 시 비밀번호를 입력하여 디컴파일을 방해할 수 있다. 그리고 PE 적인 특징을 보자면 자동으로 upx를 이용해 압축해 준다.


  변환된 바이너리는 exe2aut라는 도구를 이용해 디컴파일을 할 수 있다. 만약 비밀번호가 걸려 있다면 인자로 넣어야 한다. 하지만 구조 상 cmp 문으로 비밀번호를 비교하는 부분을 디버깅을 통해 어셈블리에서 찾을 수 있기 때문에 exe2aut를 수정하여 비밀번호를 추출하는 도구도 존재한다.


[ https://github.com/Kalamity/Exe2AhkPatched ]


  결론적으로 B 버전은 위의 Exe2AhkReturnPassword.exe 또는 Exe2AhkAnyPassword.exe를 통해 디컴파일을 쉽게 진행할 수 있다. 바이너리가 비밀번호에 걸려있지 않은 경우 인자로 바이너리를 넣으면 두 프로그램 모두 ahk 파일을 반환한다. 비밀번호에 걸려있는 경우에는 exe2aut 처럼 두 번째 인자로 비밀번호를 넣으면 된다. 여기까지는 exe2aut와 사용법이 같다.


  Exe2AhkReturnPassword.exe는 바이너리에 비밀번호가 걸려있는 경우 비밀번호를 반환해 준다. 이 비밀번호를 가지고 두 번째 인자로 넣어서 스크립트를 추출할 수 있다. Exe2AhkAnyPassword.exe는 더 간단한데 비밀번호에 걸려있다고 하더라도 비밀번호를 반환하는 것이 아니라 바로 스크립트 추출까지 진행해 준다. 그렇기 때문에 비밀번호의 유무에 상관없이 스크립트를 추출하고 싶다면 Exe2AhkAnyPassword.exe를, 비밀번호를 확인하고 싶다면 Exe2AhkReturnPassword.exe를 사용하면 된다.


  참고로 바이너리는 upx로 패킹된 상태 그대로여야 인식되며 그렇지 않으면 notarc라는 에러가 발생한다.



2.2 AutoHotKey_L

  현재 가장 많이 사용되는 버전으로 보인다. 이 버전은 과거 버전과 달리 PE 파일에, 정확히 말하자면 리소스 섹션의 RCDATA에 스크립트가 그대로 존재하기 때문에 쉽게 추출할 수 있다. 이것은 ANSI 및 Unicode 버전 모두 호환된다. 다음 링크의 파이썬 코드를 이용하자.


[ http://anch0vy.tistory.com/21 ]


  Ahk2exe 사용 시 추가 옵션으로 mpress 패커를 이용한 패킹이 지원된다. mpress.exe를 같은 폴더에 넣고 이 옵션을 체크하면 바이너리 생성 및 생성된 바이너리를 패킹까지 해준다.


  Mpress로 패킹된 경우 리소스 섹션의 RCDATA도 같이 패킹되기 때문에 스크립트를 추출할 수 없으며 mpress를 언패킹해야 가능하다. 먼저 mpress 용 언패커를 찾아보니 찾을 수 없었고 버전도 다양하기 때문에 자동화는 포기했다. 대신 간단하게 바이너리를 실행시킨 후 Attach하면 이미 언패킹이 끝나 있기 때문에 Dump하고 여기서 추출하면 된다.


  또는 간단한 패커이기 때문에 H/W Breakpoint를 이용하여 언패킹 후 덤프를 뜨면 된다. (PUSHAD 이후 스택의 주소들 중 하나에 H/W BP를 걸고 F9를 누르면 JMP문에서 BP가 걸리는 것을 확인할 수 있다. JMP문 바로 위가 POPAD이기 때문. 이후 JMP문 다음부터가 진짜 OEP이다. 여기서는 단지 소스 코드가 위치한 부분만 언패킹되면 되기 때문에 간단하게 덤프 후 스크립트를 추출하면 된다)


  참고로 Dump 한 바이너리의 rcdata를 살펴봐도 보이지 않는다. 그 이유는 일반적으로 패킹 시 리소스 섹션은 제외되며 (rcdata 말고) 언패킹 이후에는 rcdata가 리소스 섹션의 rcdata 부분이 아닌 데이터가 영역 부분에 언패킹되기 때문이다. 하지만 링크의 파이썬 스크립트는 시그니처를 기반으로 하기 때문에 위치와 상관없이 간단하게 스크립트를 추출해 준다.



Posted by SanseoLab

0. 개요

1. PE의 특징

2. Manual Extracting





0. 개요


VBScript

Scripting Engine [VBScript.dll] not registered

Scripting Engine [VBScript.dll] not started

Scripting Engine Parser not available

Could not host scripting site

...


  악성코드를 분석하다 보면 위와 같은 문자열들을 가진 바이너리를 볼 수 있다. 내용을 보고 추측해 봤을 때 AutoIt이나 AutoHotKey 등 처럼 VBScript를 컴파일하여 바이너리 형태로 변환한 것이라는 생각이 들어 더 찾아보게 되었다.


  이것들은 대부분의 경우 VbsEdit [ http://www.vbsedit.com ] 이라는 VBS 에디터 프로그램의 추가 기능을 이용한 방식으로 생성된 것으로 보인다. 이 기능은 내부적으로 script2exe라는 프로그램을 사용한다.


  생성 주체는 찾았고 이제 AutoIt 처럼 스크립트를 추출할 수 있는 방법을 알아내던 중 다음 링크를 찾을 수 있었다. 첫 번째 링크는 이렇게 만들어진 PE의 특징부터 디컴파일하는 과정에 대해 정리된 글이며, 두 번째 링크는 이 과정 즉 스크립트 추출을 자동화해 주는 IDA Pro 파이썬 스크립트이다.


[ https://www.politoinc.com/single-post/2017/11/03/Unpacking-script2exe-Malware ]

[ https://github.com/PolitoInc/script2exe_dumper ]


  하지만 직접 사용했을 때 특정 분기문에서 이 스크립트가 전제로 하는 부분이 실행되지 않아 항상 결과를 반환하지는 않는다는 것을 확인하였다. 또한 IDA Pro가 필요하며 디버깅을 전제로 하기 때문에 동적으로 실행될 위험이 존재한다는 단점도 있다. 물론 어쩔 수 없는 경우라면 이 스크립트를 수정하여 처리해야 하겠지만 뒤에서 살펴보듯이 RC4 암호화에 사용되는 키가 항상 같다는 특징을 이용해서 충분히 자동화 스크립트를 제작할 수 있다.


  여기에서는 차례대로 생성된 PE의 특징과 직접 스크립트를 추출하는 방식을 설명한다. 자동화 스크립트 개발 시 개인적으로 사용했던 부분들도 중간 중간에 들어갈 것이다.





1. PE의 특징

  Resource Hacker나 PEview 등의 도구를 통해 리소스 섹션 내부에 129번 (0x81) "Bitmap" 타입이 포함되어 있는 것을 확인할 수 있다. 이것은 VBS가 RC4 암호화 알고리즘으로 인코딩되어 저장된 것이다.


  이 특징은 자동화에 쉽게 사용될 수 있는데 해당 Bitmap을 PE 섹션에서 찾아서 추출하는 스크립트를 짤 수도 있을 것이고, 더 간단하게 7zip 같은 압축 프로그램을 이용해 PE 압축 해제 시 결과로 받는 129.bmp 파일을 이용할 수도 있다. 물론 매뉴얼하게 Resource Hacker를 이용해 해당 부분을 추출할 수도 있다. 참고로 리소스 섹션에 위치하던 Bitmap 파일로 존재하던 두 형태에 대한 구조를 알아야 크기 및 오프셋을 확인하고 추출할 수 있다.


  다음 특징으로 pdb 문자열이 있다. 최신 버전에서는 이것을 확인할 수 없지만 과거 버전에서는 변환 시 다음과 같은 문자열이 pdb 관련해 항상 PE에 저장되었던 것으로 추정된다.


"C:\\Projets\\vbsedit_source\\script2exe\\Release\\mywscript.pdb"





2. Manual Extracting

  RC4 암호화 알고리즘으로 인코딩된 데이터는 확인하였으니 이제 키 값만 구하면 해당 스크립트를 완벽하게 추출할 수 있다. 위의 참고 자료를 보면 IDA Pro 파이썬 스크립트를 사용하여 키 값을 구할 때까지 디버깅을 진행한 후 이 키를 이용해 복호화를 진행하는 방식을 사용한다.


  정확히는 IDA Pro를 이용해 "_rand" 함수를 구하고 이 함수를 참조하는 루틴을 찾았을 때 ( Cross Reference ) 특정 루프문을 볼 수 있다. 이것은 바이트 연산 후 저장하는 행위를 반복하여 결국 0x20 크기의 키를 생성하는 역할을 한다. 이 루프문이 끝나면 저장된 버퍼에서 0x20 크기의 키를 획득할 수 있다.


  문제는 현재 구할 수 있는 이 VbsEdit 프로그램은 모두 같은 RC4 키를 사용한다는 점이다. 이를 통해 디컴파일 과정을 디버깅 필요 없이 간단하게 진행할 수 있다. 먼저 PE에서 인코딩된 VBScript를 추출한 후 다음의 RC4 키를 이용하는 복호화 알고리즘을 사용하면 된다.


[ AgyUouKrxGu0q4lFjxxbWR4agvL7xFR0 ]


  참고로 RC4 알고리즘의 경우 파이썬으로도 많이 나와 있지만 문제는 스트링에 대한 것들이 대부분이고 파일을 대상으로 하는 코드는 찾기 힘든 편이다. 스트링을 대상으로 하는 코드를 사용하면 0x00 같은 값을 인식하지 못해 이 부분까지만 복호화가 진행된다. 그래서 개인적으로 참조했던 오픈 소스를 여기에 올리기로 한다.


[ https://github.com/g2jun/RC4-Python ]



Posted by SanseoLab



  Autoit은 스크립트 언어이지만 aut2exe.exe라는 변환기를 이용해 쉽게 exe 실행 파일로 변환할 수 있다. 대신 디컴파일로 간단한데 exe2aut.exe라는 디컴파일러를 통해 쉽게 autoit 스크립트 소스 코드를 얻을 수 있다. 만약 이 실행 파일에 또 다른 파일이 (악성코드의 경우 대부분 바이너리) 포함되어 있다면 그것도 드랍해 준다.


  이렇게 디컴파일이 가능하고 문법 자체도 그렇게 어렵지 않기 때문에 처음 본 경우라도 쉽게 분석할 수 있다. 하지만 분석하다 보면 트릭을 사용하거나 해서 이해하기 힘든 부분이 존재하는데 여기에서는 그러한 내용들을 다루려고 한다.





1. FileInstall() 함수

  FileInstall() 함수는 실행 파일로 변환 시 특정 파일을 포함할 수 있고 실행 시에는 드랍할 수 있는 기능을 가진 함수이다. 문제는 이것이 설명만 보아서는 이해하기 힘든 내용이라는 점이다. 물론 원래 이러한 기능을 생각한 것은 아니었지만 일종의 트릭이 되어서일 수도 있다.


FileInstall("PEview.exe", @TempDir & "\PEview.exe",1)

Run(@ComSpec & " /c " & @TempDir & "\PEview.exe", "", @SW_HIDE)


  위와 같은 내용의 .au3 소스 코드를 만든다. 이후 Aut2Exe를 이용해 .au3 소스 코드를 .exe 실행 파일로 변환한다. 이 때 소스 코드와 같은 디렉터리에 PEview.exe를 같이 두어야 한다. (FileInstall() 함수 첫 번째 인자의 경로가 현재 디렉터리이므로) 그러면 실행 파일로 변환될 때 PEview.exe가 실행 파일에 같이 통합된다.


  이제 이 실행 파일을 실행시키면 FileInstall() 함수의 두 번째 인자인 Tmp 디렉터리에 PEview.exe가 추출되어 위치하며 이후의 Run() 함수를 통해 그곳에서 실행된다. 즉 FileInstall() 함수의 첫 번째 인자는 바이너리에 통합할 실행 파일이며 두 번째 인자는 나중에 실행 시에 통합된 실행 파일이 추출될 위치이다.





2. 셸코드

  Autoit 스크립트 내에서 트릭을 이용해 직접 셸코드를 실행시킬 수도 있다. 다음은 그 예제인데 DllStructCreate()를 이용해 구조체를 생성하고 DllStructSetData()로 셸코드를 설정한 후 DllStructGetPtr() 해당 셸코드의 주소를 얻어온 후 DllCallAddress()로 실행시키는 방식이다. 이후 DllStructGetData()로 반환 값을 얻어와서 스크립트에서 그 결과를 사용할 수도 있다.


[ https://www.autoitscript.com/forum/topic/192706-extended-cpuid-x86-x64/ ]





3. 관리자 권한을 가진 바이너리

  프로그램이 관리자 권한을 가지고 실행되는 속성을 가진 경우가 있다. Windows Vista 이후부터는 아이콘에 추가적인 그림이 표시되기 때문에 쉽게 확인할 수 있을 것이다. 정확히는 리소스 섹션의 Manifest 부분을 통해 다음을 확인할 수 있다.


<security>

  <requestedPrivileges>

    <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>

  </requestedPrivileges>

</security>


  이러한 경우는 해당 바이너리를 디컴파일하기 위해 exe2aut에 드래그 앤 드랍으로 끌어놓으면 exe2aut가 "Couldn't invoke the file!"이라는 메시지를 내보내는 것을 볼 수 있다. exe2aut는 일반 사용자 권한으로 실행되었기 때문에 관리자 권한으로 실행되는 프로그램을 다룰 수 없기 때문이다. 만약 exe2aut를 관리자 권한으로 실행시킨다면 드래그 앤 드랍 방식이 통하지 않는다. cmd를 관리자 권한으로 실행시킨 후 파일을 끌어다 놓는 드래그 앤 드랍 방식이 통하지 않았던 것을 보면 윈도우에서는 기본적으로 이러는 것 같다.


  문제는 exe2aut는 드래그 앤 드랍 방식 말고 커맨드 라인 방식으로 인자를 받거나 하지 않기 때문에 일반 사용자 권한으로는 관리자 권한인 바이너리를 다룰 수 없고 관리자 권한으로는 드래그 앤 드랍이 되지 않아서 삽질을 많이 했다.


  결론만 이야기하자면 이 경우에는 간단하게 관리자 권한 메커니즘 즉 Integrity Level이 존재하지 않는 Windows XP에서 작업하면 된다.


  또는 아래의 x64 바이너리를 디컴파일하는 방법에서 설명된 방식을 사용할 수 있다. 간략하게 설명하자면 컴파일된 소스 코드를 추출하여 빈 바이너리에 덧붙인 후 이것을 exe2aut에 올려서 디컴파일하는 방식이다.





4. x64 바이너리 디컴파일

  Exe2aut는 x64 바이너리를 지원하지 않지만 트릭을 사용하면 x86과 마찬가지로 쉽게 디컴파일이 가능하다. 바로 위에서 간략하게 설명하였듯이 준비물은 AutoIt이 스크립트 파일을 exe 파일로 변환하는데 사용하는 dumb 바이너리와, 컴파일된 소스 코드가 필요하다. 빈 바이너리는 설치 폴더에서 찾을 수 있으며 컴파일된 소스 코드 부분은 아래의 방식처럼 쉽게 바이너리 검색으로 찾을 수 있다.


  이것은 다음의 링크에서 참조한 내용이며 여기서는 간략하게 방식만 설명한다.

http://www.hexacorn.com/blog/2015/01/08/decompiling-compiled-autoit-scripts-64-bit-take-two/ ]


  먼저 x64 버전의 AutoIt 바이너리에서 컴파일된 소스 코드를 추출한다. 컴파일된 소스 코드는 다음 값으로 시작한다.


=> Hex : A3 48 4B BE 98 6C 4A A9 99 4C 53 0A 86 D6 48 7D41 55 33 21 45 41 30 36 4D ...

=> Text : £HK¾˜lJ©™LS†ÖH}AU3!EA06M ...


  그리고 여기부터 시작해서 다음 값으로 끝난다.


=> Hex : ... 41 55 33 21 45 41 30 36

=> Text : ... AU3!EA06


  저 시작 부분부터 끝 부분까지를 저장해 놓자.


  이후 AutoIt을 설치하고 해당 설치 폴더에서 Exe2Aut 폴더에 들어간다. 그러면 [ AutoItSC.bin ]이라는 파일을 찾을 수 있다. 이 파일을 열어서 파일의 마지막 부분에 앞에서 저장했던 컴파일된 소스 코드를 덧붙이고 저장한다. 이렇게 간단히 모든 과정이 끝나고 이제 저장된 바이너리를 Exe2aut에 올리면 정상적으로 디컴파일된 것을 확인할 수 있다.



Posted by SanseoLab



0. 개요

  공부를 하다보면 CreateProcess() 함수를 후킹하거나 탐지를 피하기 위해 내부 함수를 사용하는 경우가 많은데 보면 볼수록 혼란스러워서 정리려고 한다. 그 대상이 되는 함수들은 다음과 같다.



CreateProcessA()  /  CreateProcessW()

CreateProcessInternalA()  /  CreateProcessInternalW()

NtCreateUserProcess()

NtCreateProcess()  /  NtCreateProcessEx()

RtlCreateUserProcess()  /  RtlCreateUserProcessEx()





1. 큰 그림  (XP와 Vista의 차이)

  아래의 함수 이름들처럼 이후 설명하면서 뒤의 A, W 또는 Ex를 붙이지 않는다면 간략하게 나타내기 위함이라고 생각하자. 다음 설명에서 상세하게 붙여가면서 정리할 것이다.


  크게 정리하자면 먼저 윈도우 XP에서는 아래와 같은 흐름을 가진다.

CreateProcess() -> CreateProcessInternal() -> NtCreateProcess()


  윈도우 비스타 이후부터는 아래와 같은 흐름을 가진다.

CreateProcess() -> CreateProcessInternal() -> NtCreateUserProcess()


  마지막 부분이 달라졌다는 것을 확인할 수 있을 것이다. 이렇게 큰 그림을 파악했으니 차례대로 유니코드 여부, 확장 여부 등에 대해서 살펴보겠다.





2. 세세한 차이

2.1 CreateProcessInternal()까지

  먼저 CreateProcess()는 두 종류가 있는데 결국 CreateProcessInternal() 함수를 호출한다. 유니코드인 CreateProcessW()를 호출하면 간단하게 CreateProcessInternalW()까지 호출되서 깔끔하지만, 아스키 형태인 CreateProcessA()를 호출하면 CreateProcessInternalA()를 거쳐서 CreateProcessInternalW()로 가게 된다.


CreateProcessA() -> CreateProcessInternalA() -> CreateProcessInternalW()

CreateProcessW() -> CreateProcessInternalW()



2.2 XP와 비스타의 차이점

  CreateProcess() -> CreateProcessInternal() 까지 살펴보았다. 이제 XP와 비스타 이후부터 달라지는 부분을 보겠다. 비스타 이후의 경우 NtCreateUserProcess() 부분은 깔끔해서 더 살펴볼 내용이 없다. 


CreateProcessInternalW() -> NtCreateUserProcess()


  하지만 XP의 경우 즉 NtCreateProcess()는 확장 부분이 존재한다. 물론 대부분의 API 함수들과 같이 마지막에 호출되는 부분이 확장 함수이다.


CreateProcessInternalW() -> NtCreateProcess() -> NtCreateProcessEx()



2.3 RtlCreateUserprocess()

  마지막으로 RtlCreateUserProcess() 부분이 남았다. 사실 이 부분은 CreateProcess()의 호출에 포함되는 부분은 아닌것 같고 단순히 NtCreateuserProcess()에 대한 Small Wrapper 함수라고 한다. 문제는 이것도 확장이 존재한다. 간단하므로 아래의 순서도를 보겠다.


RtlCreateUserProcess() -> RtlCreateUserProcessEx() -> NtCreateUserProcess()


  XP의 경우에도 존재하지만 마지막이 NtCreateProcess()인지는 확인해보지 않았다.





3. 기타

  Process Doppelganging을 공부하는데 NtCreateProcessEx() 함수를 사용하는 부분이 나왔다. 이 기술은 윈도우 비스타 이후부터 사용 가능한데 비스타에서 사용되지 않는 즉 XP 까지 사용되던 내부 함수를 사용하는 것이다.





4. CreateThread()

  쓰레드 관련한 내용도 신기해서 덧붙이기로 한다.


CreateThread() -> CreateRemoteThreadEx() -> NtCreateThreadEx()

CreateRemoteThread() -> CreateRemoteThreadEx() -> NtCreateThreadEx()

RtlCreateUserThread() -> NtCreateThreadEx()


  참고로 NtCreateThread()와 NtCreateThreadEx() 모두 마지막 단의 함수인데 NtCreateThread()는 호출되는 부분은 찾지 못했다. XP에서 사용되었던 함수였나 보다.


  또한 RtlCreateUserThread()도 NtCreateThreadEx()에 대한 Small Wrapper 함수라고 보면 될 듯 싶다.



Posted by SanseoLab



1. 개요

2. 분류

.... 2.1 Self Creation

.... 2.2 Debug Blocker / Self Debugging

.... 2.3 Process Hollowing / PE Image Switching / RunPE / Process Injection

.... 2.4 Process Doppelganging





1. 개요

  제목을 Process Hollowing이라고 썼지만 방식들 뿐만 아니라 이름도 다른 여러 기술들을 한번에 정리하려고 한다. 어떻게 보면 공통점이라고는 자식 프로세스를 실행하며 그 자식 프로세스에서 악의적인 행위가 이루어진다는 점만 있는것 같기도 하다.


  아래의 이름들은 "리버싱 핵심원리"나 여러 외국 자료들을 보고 정리한 것인데 사용하는 사람들마다 다른 이름을 붙여서 사용하거나 이름 자체에는 관심을 가지고 있지 않는 경우도 많기 때문에 이름 보다는 기술의 내용에 집중해야 할 것이다.


  이 글에서 가장 중점적인 내용은 Process Hollowing (RunPE, Process Injection 등) 기법이다. 코드 및 데이터를 인젝션하는 기법에는 첫 번째로 셸코드 및 프로시저 형태로 존재하는 루틴 자체를 인젝션하는 방식으로서 그냥 코드 인젝션이라고도 불리며, 두 번째로 DLL 형태로 만들어 이것을 인젝션하는 방식 (DllMain()에 악성 루틴이 존재하여 DLL 로드 시 실행)으로서 DLL 인젝션이라고 불리는 방식이 있다.


  Process Hollowing은 또 다른 이름인 Process Injection 처럼 프로세스에 프로세스 자체를 인젝션하는 (간단히 말해서) 방식이다. 다음에 나오는 프로세스 도플갱잉도 마찬가지로 Process Injection 방식으로 분류된다. 이러한 방식들 모두 인젝션 방식에 포함되므로 다른 인젝션 기법들과 함께 언급되는 경우가 많아서 굳이 이렇게 분류하기로 했다. 코드 인젝션과 DLL 인젝션은 다음 링크에서 후킹과 같이 정리되어 있으며 프로세스 인젝션은 이곳에서 2.3 및 2.4 항목을 통해 정리한다. http://sanseolab.tistory.com/28 ]





2. 분류

2.1 Self Creation

  가장 간단한 형태이다. 진행하면서 자기 자신을 (실행 파일) 자식 프로세스로 실행시킨다. 이 경우에는 부모로서 실행될 때와 자식으로서 실행될 때의 실행에 차이가 있어야 한다. 부모로 실행될 때는 자식 프로세스를 실행시키며 자식 프로세스로 실행될 때는 악의적인 행위를 해야 하기 때문이다. 이것은 Mutex 같은 기술들을 이용해서 분기를 나눌 수 있다.


  이러한 방식 외에 다른 방식도 존재한다. 실행시킬 때 CreateProcess()의 인자 중 dwCreationFlags를 CREATE_SUSPENDED (0x00000004)로 설정하여 대기 상태로 만든 후 GetThreadContext()로 Context를 읽어와 Entry Point를 수정한 후 SetThreadContext()로 Context를 설정하는 방식이다. 이를 통해 자식 프로세스로 실행될 때는 원래의 EP와는 다른 주소에서 실행될 수 있다. 이렇게 EP를 수정하는 방식 또한 여러 종류가 있으므로 뒤에서 따로 정리를 하겠다.





2.2 Debug Blocker / Self Debugging

  이 방식은 Self Creation 방식에서 더 발전된 기술이다. 코드 가상화 방식을 사용하지 않는 여러 프로텍터들에서 사용되는 방식이기도 하다. 개인적으로는 PEspin 프로텍터를 분석하면서 이 기술을 공부하는 것을 추천한다. PEspin은 이 방식 외에도 여러가지 안티 디버깅 / 안티 덤프 기술이 사용되지만 난이도가 높지 않아서 공부하기 괜찮은 도구이다. [ http://sanseolab.tistory.com/34 ]


  부모 프로세스는 디버거로서 동작하며 자식 프로세스는 디버기로서 동작하는 방식이다. CreateProcess()의 인자 중 dwCreationFlags를 DEBUG_PROCESS (0x00000001) | DEBUG_ONLY_THIS_PROCESS (0x00000002)로 설정하여 자식 프로세스를 디버그 모드로 실행시킨다. 디버거와 디버기의 관계이기 때문에 디버기에 대한 제어가 가능해져서 다양한 방식으로 안티 디버깅을 수행할 수 있다.


  먼저 이 방식은 기본적으로 디버기에서 실행 중에 예외를 발생시키고 이 경우 디버거로 그 제어가 오기 때문에 상황에 맞게 추가적인 조작을 하는 메커니즘이다.


  예외를 발생시키는 방식은 개인적으로 3가지를 파악했으며 더 많은 종류가 있을 수 있다. 하나는 유효하지 않은 명령어를 이용하는 방식이고 다른 하나는 Nanomites라고 불리는데 INT 3 (0xCC) 명령어를 이용한다. 또한 Guard Pages라는 방식도 있는데 특정 코드 영역을 VirtualProtect()를 이용해 실행 불가능하게 만들어서 예외를 발생시킨다. 


  참고로 디버거의 경우 WaitForDebugEvent()와 ContinueDebugEvent()를 이용한 루프를 돌며 디버그 이벤트를 기다린다. 이 경우 DEBUG_EVENT Structure  [ https://msdn.microsoft.com/en-us/library/windows/desktop/ms679308(v=vs.85).aspx ] 부분을 보면 알겠지만 여러 디버그 이벤트들이 존재하는데 예를들면 DLL 로드도 거기에 포함된다. 이벤트 중에서 EXCEPTION_DEBUG_EVENT (0x00000001)를 필터링 하고 이 중에서도 EXCEPTION_RECORD structure [ https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx ]의 ExceptionCode에 따라서 필터링을 수행해야 한다.


  참고로 INT 3 명령어를 이용한 Nanomites 방식으로 예외를 일으키는 경우에는 EXCEPTION_DEBUG_EVENT 중에서 ExceptionCode가 EXCEPTION_BREAKPOINT (0x80000003)인 경우에만 디버거가 인식하도록 필터링을 해야할 것이며 이때 원하는 행위를 수행할 것이다. 또한 Guard Pages의 경우에는 EXCEPTION_ACCESS_VIOLATION (0xC0000005)인 경우에 특정 행위를 수행할 것이다.


  지금까지 말했던 특정 행위는 다른 것들과는 달리 디버거와 디버기의 관계이기 때문에 여러 다양한 방식이 사용될 수 있다. 예를들어 간단하게 GetThreadContext()로 문맥을 얻고 수정한 후 (대부분 EIP) SetThreadContext()로 설정하는 방식이 있을 수 있다. 또 다른 방식도 존재하는데 아예 WriteProcessMemory()를 이용해 다른 부분에 저장해 놓았던 코드를 예외가 발생한 부분에 새로 써넣는 방식도 존재한다.





2.3 Process Hollowing / PE Image Switching / RunPE / Process Injection

  주로 악성코드에서 많이 사용되는 방식이라서 그런지 붙여진 이름들이 많다. 또한 세부적인 기술들도 매우 다양한 편이다. 기본적으로 정상 프로그램을 CreateProcess()로 실행시키는데 인자는 CREATE_SUSPENDED를 넣는다. 이후 메모리 내부에 매핑되어 있는 원본 코드 및 데이터 섹션들을 Unmap시킨 후 악성코드를 써넣고 실행시키는 방식이다. 각 단계별로 살펴볼 내용이 많다.


  이 방식을 사용하면 겉은 정상 프로그램이지만 실제로 하는 행위는 악성이기 때문에 여러 모로 인기가 많다고 한다. 그리고 실행 자체가 정상 프로그램이었기 때문에 보여지는 프로세스 이름이나 권한을 그대로 가지고 있으며 드로퍼나 다운로더를 이용해서 악성코드 바이너리를 파일로 생성한 후 실행하는 방식과 같이 파일 형태로 존재하지 않아서 탐지하기도 쉽지 않다. 물론 일반적으로 정상 프로그램에 써질 패킹 또는 암호화된 악성코드가 이 Process Hollowing을 수행하는 바이너리 안에는 존재하기 때문에 정적 분석으로 내부에 의심스러운 부분을 찾을 수 있겠지만 어떠한 악성코드는 심지어 네트워크를 통해 메모리로 악성코드(파일)를 받아서 인젝션하는 경우도 있다고 한다.


  악성코드가 대상으로 하는 정상 프로그램으로는 여러 가지가 있겠지만 개인적으로는 svchost.exe를 대상으로 하는 것을 많이 본 것 같다. 악성코드를 써넣는 방식부터 Context를 수정하는 방식까지 종류가 다양하기 때문에 차례대로 살펴보겠다.



2.3.1 시작

  먼저 CreateProcess()를 이용하 CREATE_SUSPENDED를 플래그로 사용한다. 이후 생성된 프로세스의 Image Base 주소를 알아야 한다. 그래야 이 주소를 기반으로 원본 바이너리를 메모리에서 Unmap할 수 있기 때문이다.


  이것은 두 가지가 있다. 하나는 NtQueryProcessInformation()을 이용한 방식이다. 이 함수를 호출함으로써 PEB를 구할 수 있다. PEB를 구하는 이유는 PEB에 해당 프로세스에 대한 정보가 담겨 있기 때문이며 이후 이 PEB + a 주소에 해당하는 주소를 읽어오면 대상 프로세스에 대한 특정 정보를 획득할 수 있다. PEB는 이 외에도 GetThreadContext()를 이용해 구할 수 있다. CREATE_SUSPENDED로 생성된 프로세스의 경우 Context의 EBX에는 PEB의 주소가 들어가 있다. 이렇게 PEB의 주소를 구했는데 PEB + 0x08 오프셋에는 해당 프로세스의 Image Base 주소가 들어가 있으며 이 주소에 대하여 ReadProcessMemory() 함수를 호출하여 대상 프로세스의 Image Base 주소를 구한다.


  이제 NtUnmapViewOfSection() 함수를 호출하는데 인자로는 대상 프로세스의 핸들과 대상 프로세스의 Image Base 주소를 넣는다. 이를 통해 메모리에 로드된 원본 실행 파일은 Unmap된다. 


  그리고 이 주소에 대하여 VirtualAllocEx() 함수를 이용해 다시 메모리를 할당한다. 권한과 관련된 세부 사항은 무시하겠다. 참고로 그냥 메모리를 할당하는 것은 아니고 악성코드 실행 파일의 Image Base 주소를 구한 후 그 주소에 맞게 Image Base 주소를 맞추어서 할당한다. 그리고 Image Base 주소가 서로 다르다면 PEB의 Image Base 값도 수정해주어야 한다. 이제 WirteProcessMemory()로 악성코드의 실행 파일을 메모리에 올라온 것 처럼 섹션마다 각각 해당되는 오프셋에 써준다.


  이렇게 VirtualAllocEx()  ->  WriteProcessMemory()를 사용하여 악성코드를 쓰는 방식 외에도 다른 방식이 존재한다. NtCreateSection() 함수는 섹션 객체를 생성해 주는 함수로서 인자로는 파일 핸들을 받는다. 즉 악성코드가 파일로 존재하는 경우에는 NtCreateSection()에 파일 핸들을 넣고 섹션 객체를 생성한 후 NtMapViewOfSection()을 통해 매핑할 수 있다.



2.3.2 Entry Point

  마지막으로 Entry Point를 수정한 후 ResumeThread()를 통해 쓰레드를 실행하면 정상적으로 실행된다. EP 부분은 아까 위에서도 언급했지만 자세히 다루어야 한다.


  먼저 배경지식부터 말해보자면 CREATE_SUSPENDED로 실행되어 대기하는 순간 EBX는 PEB의 주소가 들어가 있다고 했다. EAX에는 Entry Point의 주소가 들어가 있다. 우리는 아직 Context를 수정하지 않았기 때문에 EAX에는 정상 프로그램의 EP가 그대로 들어가 있게 되서 악성코드 바이너리의 EP로 수정해야 할 필요가 있다. 이것은 간단하게는 GetThreadContext()로 구한 Context에서 EAX 부분을 수정한 후 SetThreadContext()로 설정하면 된다. 이 외에도 다른 방식이 존재하는데 NtQueueApcThread()를 이용한 방식이다. 자세한 사항은 잘 모르겠지만 이 함수의 인자로 Entry Point를 지정하면 해당 주소에서 실행되게 할 수 있다.


  마지막으로 Context 수정 부분에서 EAX 대신 EIP를 EP로 변경할 수도 있다. 이것은 개인적으로 이해하기 힘든 부분이었다. CREATE_SUSPENDED로 실행시키는 경우 PE Loader가 도중에 멈출 것이고 이후 EAX를 EP로 수정해 놓으면 나머지 초기화(Import Table 복구 등)를 진행한 후 설정된 EP로 가서 실행될 것이라고 생각했다. 하지만 EIP를 바꾸어버리면 제어가 바로 EP로 가기 때문에 Import Table이 복구되지 않아 정상적으로 실행되지 않을거라고 생각했던 것과는 달리 정상적으로 실행되었다.


  완벽하게 파악하지는 못했지만 마지막에 ResumeThread()를 하는 순간 자동으로 Import Table 복구 등 초기화를 담당하는 부분이 실행된다는 것을 확인할 수 있었다. 이것은 Dump를 떠서야 정확하게 파악할 수 있었는데 왜냐하면 Debugger로 Attach하는 순간에도 Import Table이 복구되기 때문이다. 확실한 것은 EP 부근에 무한 루프를 걸어놓고 ResumeThread() 전 후로 덤프를 떠보면 이후에 Import Table이 복구된다는 것이다.


  결론적으로 말하자면 특정 쓰레드가 실행될 즈음에 나머지 초기화가 이루어 진다. 먼저 ResumeThread()의 경우 당연히 쓰레드를 실행하는 역할을 하기 때문이다. 그렇다면 디버거의 경우는 무엇인가라는 생각에 알아보다가 추가적인 사항을 알 수 있었다. [ http://hooked-on-mnemonics.blogspot.kr/2013/01/debugging-hollow-processes.html ]


  간략하게 정리하자면 프로세스를 CREATE_SUSPENDED로 실행할 경우 아직 PEB가 완벽하게 초기화되지 않아서 OllyDbg 1.10의 경우 Attach가 불가능하다. 하지만 Sleep() 함수를 호출하는 간단한 Dummy 쓰레드를 CreateRemoteThread()로 인젝션하면 (쓰레드를 인젝션하는 것이기 때문에 자동으로 실행까지 된다) 비록 Main 쓰레드는 계속 Suspend 상태로 있다고 하더라도 이 Dummy 쓰레드가 실행됨으로써 프로세스 자체의 초기화가 완료되서 (PEB가 완전히 초기화 된다) 이후 OllyDbg 1.10으로 Attach가 가능해진다는 내용이다.


  개인적인 생각이지만 아마 OllyDbg 2에서는 이러한 귀찮음을 방지하기 위해서 Attach하기 전에 쓰레드를 인젝션하기 때문에 CREATE_SUSPENDED 상태인 프로세스도 Attach할 수 있으며 대신 부작용으로 Import Table 및 PEB의 초기화가 완료된다는 것이 확실해 보인다.


  결론적으로 프로세스가 Suspended 상태더라도 쓰레드가 실행되면 적어도 PEB 및 Import Table이 복구되며 구체적으로 무엇이 이러한 역할을 해주는지는 알 수 없지만 이것은 유저 모드에서는 파악할 수 없고 커널 디버깅이 필요한 부분으로 보인다.



2.3.3 Double Process Hollowing

  기술적으로 추가된 부분은 없고 응용한 방식이다. Ursnif라는 악성코드에서 사용되어서 이렇게 이름붙여졌다. 먼저 svchost.exe를 대상으로 Process Hollowing 방식을 사용한다. 문제는 이것이 SYSTEM Integrity Level을 가지고 있어서 Privilege Escalation을 위해 이 부분을 거친다고 한다. 이후 explorer.exe를 대상으로 Process Hollowing 방식을 사용하며 실제 악성 루틴은 여기에서 실행된다고 한다. 


  직접 분석해보지 않아서 몇몇 사항들은 아직 완벽하게는 모르겠다. 먼저 svchost.exe가 평소에 SYSTEM 레벨로 실행된다는 것은 알지만 아무나 실행시켜도 SYSTEM 레벨로 실행됨으로 인해 왜 아직까지도 Process Hollowing 공격의 좋은 공격 대상이 되는지를 이해할 수 없다. 다른 하나는 explorer.exe를 대상으로 Process Hollowing을 하기 위해서는 SYSTEM 권한이 필요한 것인지이다. 마지막으로 explorer.exe에 인젝션하는 것이 아니라 이 방식을 사용한다는 것인데 그럼 원본 explorer는 종료되는 것인지 아니면 2개가 실행되는 것인지 또 종료되서 Process Hollowing으로 실행되는 거라면 그 거대한 explorer의 바이너리 전체를 메모리에서 Unmap시키는 것인지 등이다. 



2.3.4 정리

  악성코드에서 많이 사용되다 보니 보안 프로그램들의 주요한 관심을 받고 있으며 결론적으로 후킹의 대상이 되어 탐지되는 경우가 많아서 앞의 API 함수들을 사용하기 보다는 이러한 함수 내부에서 사용되는 ntdll 함수들을 사용하는 경향이 많다고 한다. 각각의 함수들에 상응하는 ntdll 함수들을 정리하겠다.


CreateProcess() / NtCreateUserProcess()

VirtualAllocEx() / NtAllocateVirtualMemory()

ReadProcessMemory() / NtReadVirtualMemory()

WriteProcessMemory() / NtWriteVirtualMemory()

GetThreadContext() / NtGetContextThread()

SetThreadContext() / NtSetContextThread()

ResumeThread() / NtResumeThread()



2.3.5 분류

- PEB 얻기

NtQueryProcessInformation()

/

GetThreadContext()


- 공간 할당 및 쓰기

VirtualAllocEx()  ->  WriteProcessMemory()

/

NtCreateSection()  -> NtMapViewOfSection()


- 시작 주소 설정 및 시작

Context 조작  ->  SetThreadContext()  ->  ResumeThread()

/

NtQueueAPCThread()





2.4 Process Doppelganging

  이 기술은 크게 두 가지로 나뉜다. 하나는 트랜잭션과 관련된 내용이며 다른 하나는 실행과 관련된 내용이다. 이것을 나누어서 정리하겠다.



2.4.1 커널 트랜잭션 관리자 (Kernel Transaction Manager)

  윈도우는 비스타 이후로 많은 기능들이 추가되었다. 대표적으로 UAC나 볼륨 섀도 복사본과 관련된 내용은 악성코드에도 많이 사용되기 때문에 익숙할 것이다. 추가된 내용에는 이 두 가지 말고도 많이 있는데 막상 깊게 공부할 계기는 없었다. 그러던 중 Process Doppelganging이라는 기술을 공부하면서 KTM과 관련된 내용이 사용되는 것을 보게 되었고 이것도 비스타 이후 추가된 대표적인 기능 중 하나라는 것을 알게 되었다.


  NTFS에 추가된 기능으로서 기본적으로 DataBase와 관련된 개념으로 보인다. 완벽한 이해는 불가능하고 예제를 보면서 이해한 내용을 설명해 보자면 애플리케이션 업데이트나 여러 파일들의 이름 변경 같은 작업 중에 에러가 발생할 수 있다. 이러한 경우 에러가 발생하기 전에 이미 업데이트나 수정이 된 파일들이 있을 것이며 다른 파일들은 아직 원본 상태로 남아 있을 것이다. 그렇기 때문에 어디까지 작업이 완료되었는지 파악하는 것이나 변경 내용들을 모두 기록하고 복구하는 작업들은 매우 힘들다고 한다. Transaction 기능은 업데이트 등의 작업 시에 실제 파일을 건들지 않고 트랜잭션 객체를 이용해 수정한 후 마지막에 Commit을 함으로써 한 번에 작업을 완료할 수 있게 하며 도중에 에러 발생 시 Rollback 시킬 수도 있다.



2.4.2 트랜잭션 객체를 이용해 쓰기

  이러한 방식을 이용한 것인데 먼저 CreateTransaction()을 통해 트랜잭션 객체를 생성한다. 이후 CreateFileTransacted()로 트랜잭션 동작에 사용될 파일을 오픈한다. 이 핸들을 가지고 WriteFile()을 이용해 쓰는 것이다. 참고로 일반적인 방식과 달리 정상적인 파일이 아니라 트랜잭션 파일을 대상으로 한다. 그리고 NtCreateSection()로 이 파일에 대한 핸들을 인자로 넣고 섹션 객체를 생성한다. 이 과정을 통해 만들어졌던 트랜잭션 파일은 RollbackTransaction()을 통해 밀어버린다.



2.4.3 프로세스 실행

  Process Hollowing에서 정리했던 NtCreateSection()은 NtMapViewOfSection()과 함께 사용되었다. 하지만 여기에서는 이 섹션 자체를 사용한다. 프로세스 실행 시 NtCreateProcessEx()를 호출하는데 여기에 인자가 바로 섹션 객체에 대한 핸들이기 때문이다. 사실 이 함수와 관련된 내용도 이야기할 것이 있는데 다음 링크를 참고하자. [ http://sanseolab.tistory.com/58 ]


  이렇게 직접적으로 실행하는 것이기 때문에 추가적으로 설정해야할 일이 많다. NtQueryInformationProcess(), NtReadVirtualMemory()를 통해 PEB 주소를 얻어와 Image Base 수정 같은 것은 기본이며 프로세스의 이름 등도 설정해주어야 한다. 이것은 RtlCreateProcessParametersEx() 함수를 이용하여 Parameter Block을 생성한 후 대상 프로세스에 메모리를 할당하고 써주기까지 해야 한다. 이렇게 써준 Parameter Block의 주소도 PEB에서 수정해 주어야 한다.


  마지막으로는 NtCreateThreadEx()를 이용해 쓰레드를 실행시킴으로써 끝난다.



2.4.4 장단점

  초기 자료이기는 하지만 PPT를 보면 장점이 인상 깊다. 여러 제품들에서 탐지되지 않았다는 것은 지금은 모르므로 제외하고 그 외에도 Fileless라는 점이 있을 것이다. 또 다른 점으로는 Unmapping 시에 보안 솔루션에서 탐지되기 쉬운 편인지 모르겠지만 언매핑을 하지 않아서 보통 탐지되지 않는다는 내용도 보인다. 그리고 DLL 인젝션에도 응용될 수 있다는 점도 있다.


  단점으로는 먼저 NTFS의 KTM이 비스타부터 적용되었기 때문에 비스타 이후의 운영체제에서만 동작하며 이유는 모르지만 윈도우 10에서는 Blue Screen이 뜬다고 한다. 그리고 앞에서의 내용과는 달리 NtCreateThreadEx()를 이용해 실행시키는데 이 함수가 CreateRemoteThread()처럼 스레드를 생성하는 함수이다. 즉 보안 제품의 드라이버에서 사용되는 PsSetCreateThreadNotifyRoutine()에 의해 탐지될 수 있다고 한다.



2.4.5 링크

- 가장 쉽게 잘 정리된 링크 : [ http://kali-km.tistory.com/entry/Process-Doppelganging-1 ]


- PPT 자료 : [ https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf ]


- 이것을 구현한 간략한 소스 코드들 : [ https://github.com/Spajed/processrefund

[ https://github.com/KernelMode/Process_Doppelganging/blob/master/main.cpp ]




'악성코드 분석' 카테고리의 다른 글

Autoit 스크립트  (0) 2018.04.01
CreateProcess / CreateThread 내부  (1) 2018.03.03
델파이 바이너리 분석 방법론  (1) 2018.02.25
IDA Pro 시그니처 사용 및 제작 [ Flirt ]  (1) 2018.02.25
Anti-AV와 Anti-VM (Sandbox)  (1) 2017.12.30
Posted by SanseoLab



0. 개요

.... 0.1 개념

1. 분석

.... 1.1 간단한 크랙미 분석

.... 1.2 악성코드 분석

2. 추가적인 문제와 해결 방안

.... 2.1 함수 호출 규약

.... 2.2 객체 지향적이다.

.... 2.3 다른 Unit을 임포트할 경우





0. 개요

  델파이로 만들어진 바이너리를 분석하기 위해서는 알아야 할 것들과 필요한 것들이 많다. 간단한 크랙미의 경우라면 인터넷의 자료들을 보면서 손쉽게 처리할 수 있겠지만 진지하게 만들어진 악성코드의 경우라면 사람이 할 짓이 못 된다는 것을 느낄 수도 있기 때문이다.


  개인적으로 델파이 개발 경험은 예제를 그대로 따라 써가면서 만들어 본 몇 개가 전부이기 때문에 언어적인 개념 및 더 깊은 내용을 다룰 수는 없다. 하지만 이미 관심이 생겼으므로 꾸준히 공부해 나가면서 이곳에도 배우고 정리한 내용을 계속 추가할 예정이다. 그러므로 아직까지는 분석에 필요한 부분들 위주로 정리하겠다.


  기본적으로 델파이는 Visual Basic과 비슷한 언어로 보인다. GUI 형태의 프로그램을 간단하게 제작할 수 있는 방식에서 그렇게 보인다는 말이다. 물론 오래된 언어이며 매니아들도 많아서인지 편리성 외에도 시스템적인 내용까지 다룰 수 있으며 성능 같은 면에서도 괜찮다고 한다.


  문제는 이러한 형태의 언어들이 그렇듯이 객체 지향적인 언어이며 이러한 개념을 매우 많이 사용할 것이라는 점이다. 또한 그래픽 처리와 관련된 수 많은 루틴들 덕분에 크기도 커지게 된다. 객체 지향적이라는 것은 콜백 함수도 많고 객체 (단순히 구조체 수준의 난이도가 아니다) 생성 및 해제도 많으며 함수 호출 시에 인자로 객체가 들어가게 될 것이며 가상 함수라는 개념이 많이 사용될 것이다.


  물론 꼭 GUI 형태가 아니라 콘솔 애플리케이션 형태로도 제작할 수 있는데 이 경우에는 조금 더 간단해지기는 한다. 하지만 델파이는 Visual Basic과는 달리 MS에서 제공해주는 DLL이 없다. 즉 VB는 MS에서 만들었기 때문에 vbvm60.dll 같은 VB 만의 DLL이 운영체제에서 제공되어 (최신 버전은 모르겠지만) 언어만의 기능을 가져다 쓸 수 있다. 하지만 델파이의 경우 저러한 런타임 라이브러리와 같이 배포하지 않는 이상 즉 독립적인 실행 파일로 제공하기 위해서는 델파이의 언어적인 기능을 직접 바이너리에 정적으로 링크해야 하므로 용량이 매우 크다. 예를들어 WriteLN()이라는 printf()와 비슷한 역할을 하는 함수도 간단하게 DLL에서 임포트하는 것이 아닌, 직접 구현해야 한다는 것이다. 물론 kernel32.dll 같은 기본 DLL들은 임포트할 것이기 때문에 아주 마지막에는 윈도우의 API 함수를 사용할 것이다.


  결론적으로 델파이 바이너리를 디버거로 켜는 순간 뭐부터 시작해야 할지를 모르는 상황이 온다. 그러므로 여기에서는 차례차례 쉬운 것부터 방식에 대해서 정리하기로 한다.



0.1 개념

  이것은 나중에 공부해가면서 추가해야 하지만 그래도 간단하게 배운 내용을 적어보기로 한다. 스텁의 처음 부분을 보면 @Sysinit@@InitExe을 호출하는데 인자 즉 EAX에는 배열이 들어간다. 델파이는 객체 지향 언어이기 때문에 사용할 라이브러리들을 초기화할 필요가 있다. 배열에는 각 라이브러리 init이 있으며 클래스의 Init 함수를 호출하여 Sub 함수들에 대한 포인터를 저장한다. 즉 사용할 것들에 대한 초기화가 진행되는 것이다.





1. 분석

1.1 간단한 크랙미 분석

  먼저 간단한 크랙미 문제라고 가정해 보겠다. 사실 이렇게 간단한 것은 필요한 루틴이 어딘지만 파악한 후에 그 부분만 분석하면 되므로 분석보다는 그 루틴을 찾는 것이 중요하다. 예를들어 시리얼을 입력받아 일치 여부를 확인하는 크랙미가 있다고 하자.


  여기서 중요한 것은 GUI를 초기화하고 메시지 루프를 돌면서 이벤트를 받는 등의 과정이 아니다. 우리는 버튼을 누르는 이벤트가 발생했을 때 수행되는 루틴을 찾아서 거기에서 시리얼 번호를 비교하는 부분이 있다면 그 부분만 분석하면 된다.


  이 경우에 유용한 것이 DeDe라는 도구이다. 크랙미를 열고 "Process" 버튼을 눌러서 분석한다. 이후 분석이 완료되면 "Procedures" 탭을 보자. 왼쪽에는 Unit1이 보인다. 간단해서인지 unit은 하나밖에 없는 것 같다. Unit이란 그냥 모듈이라고 생각하면 될 듯 싶다. 큰 프로그램이라면 언어에서 제공하는 것들도 많이 가져다가 쓰고 직접 제작한 여러 모듈들도 모두 임포트해서 프로젝트가 구성될 것이기 때문에 많은 Unit들을 볼 수 있을 것이다.


  어쨌든 여기에는 Unit1에 Class 이름은 TFom1이다. 오른쪽을 보면 Event와 RVA가 보인다. Event는 "SpeedButtion1Click", "SpeedButtion2Click"이다. 크랙미를 보면 버튼이 2개 있었다. 이것을 가지고 추측해 보면 각각 특정 버튼이 눌렸을 때의 이벤트를 의미하는 것으로 보이며 옆의 RVA에 나온 주소는 그 이벤트가 발생했을 때 실행될 루틴의 주소라고 할 수 있을 것이다. 이에 따라 우리는 이 루틴으로 가서 BP를 건 후에 해당 루틴만 분석하면 될 것이다.



1.2 악성코드 분석

  악성코드를 상세하게 분석해 보려고 하자. 일반적으로 Console 형태로 만들어졌거나 GUI 형태라고 하더라도 GUI 객체들을 초기화하는 루틴에서 악성 루틴이 실행되는 등 GUI와 관련된 내용은 별로 없을 수 있다. 어쨌든 중요한 것은 진행해 가면서 루틴을 파악해 가는 것이다.


  하지만 디버거로 열어보면 수 많은 함수들이 있고 직접 진행하면서 분석해 보려고 하더라도 이해도 못하면서 진행만 할 수 밖에 없을 것이다. 개인적으로는 윈도우의 API를 호출하는 부분들 위주로라도 분석하려고 했지만 이것도 쉬운 일이 아니었다. 또한 MOVE라는 memcpy()와 비슷해 보이는 명령어라고 하더라도 내부적으로는 본 적도 없는 명령어들이 사용되어 구현되어 있는 경우도 있었다.


  이것을 제대로 분석하는 것은 말이 안되기 때문에 도구의 힘을 빌릴 수 밖에 없다. 앞에서 말한 DeDe는 없는 것 보다는 낫지만 어느 정도 이상의 분석을 제공하지 않는다. 이것은 IDA Pro의 시그니처도 마찬가지이다. [ http://sanseolab.tistory.com/55 ] 사실 IDA Pro도 여러 개의 델파이 관련 시그니처를 제공해 주지만 개인적으로 반 이상 제대로 분석해 주는 것을 보지 못했다.


  이 경우에 쓸 수 있는 것이 IDR이라는 델파이 디컴파일러이다. 이것은 kbsyskb라는 이름이 붙은 DB 파일을 가지고 해당 버전에 맞는 델파이 바이너리를 분석해 준다. 인터넷에 IDR을 다운로드 받을 수 있는 곳이 많이 있지만 이것은 옛날 버전의 컴파일러로 제작된 바이너리만 통한다. 최신 버전의 경우에는 추가적인 DB 파일들이 필요하다.


  결론부터 말하자면 다음 링크에서 idr.exe 및 dll 파일들을 다운로드 받는다. https://github.com/crypto2011/IDR ] 이것을 받는 것이 중요한 이유는 최신 버전이라서 더 빠르며 최신 버전의 DB를 제대로 인식할 수 있기 때문이다. 그래서 옛날 버전의 idr에 뒤에서 말할 DB만 추가하면 안된다.


  이제 DB를 추가해야 한다. DB 파일은 kb2.bin, kb2010.bin 등의 kb 파일들과 syskb5.bin, syskb2007.bin 같은 syskb 파일들이다. 인터넷에서 자주 볼 수 있는 IDR은 최신 버전의 DB가 포함되어 있지 않을 확률이 높다. 다음 링크에서 2014 버전까지 다운로드 받자. [ https://www.dropbox.com/sh/9ran313nidqtagb/AADl_m_9GVYSiXUviZtDQWQHa?dl=0 ] 델파이의 버전 구분을 정확히는 모르지만 적어도 XE4까지는 커버되는 것으로 보인다.


  이제 IDR을 켜고 바이너리를 오픈하면 "Use native Knowledge Base?"라는 팝업이 뜨는데 최신 버전이므로 OK를 누르자. 그러면 분석을 시작하게 되고 Unit3, Unit4 등으로 보였던 부분들이 System, SysInit, System.SysUtils 등으로 이름이 붙는 것을 볼 수 있다. 그대로 Unit3 같이 있다면 이것은 제작자가 직접 제작한 부분이므로 직접 리버싱을 해서 기능을 파악해야 한다. 하지만 걱정할 것이 없는 것이 C로 제작한 프로그램이 API 함수를 이용해서 진행하듯이 델파이의 정적으로 링크된 함수들이 IDR을 통해 이름이 붙여져서 이 함수들을 파악해가면 분석하다 보면 그다지 큰 어려움을 느끼지 않을 것이다. IDR이 분석해주는 전과 후의 차이는 DLL 내부로 들어가 API 함수까지 분석하는지 여부의 차이와 같은 수준이다.


  여기서 멈추지 않고 "Tools - MAP Generator"를 클릭하여 .map 파일을 생성하자. 이후 OllyDbg 1에서는 MapConv라는 플러그인을, OllyDbg 2에서는 VicPlugin이라는 플러그인을, x64dbg에서는 SwissArmyKnife라는 플러그인을 사용해서 맵 파일을 임포트시킨다. 그러면 Label에 등록되어 함수에 이름이 붙는 것을 확인할 수 있다. 이것과 관련된 내용은 이 블로그에 많으니 블로그 내부 검색을 이용해서 알아보자.


  그럼 이제 이 함수들이 어떤 함수들인지만 파악한다면 내부에 들어가서 기능을 파악할 필요가 없고 인자만 파악한 후에 Step Over로 간단하게 넘길 수 있을 것이다. 사실 델파이에서 사용되는 내부 시스템 함수들은 MSDN과는 다르게 검색하기도 쉽지 않다. 시스템 API 관련 설명이 나누어져 있고 통합해서 검색하는 부분이 없기 때문이다. 그렇기 때문에 구글에서 다음과 같이 검색한다.


함수이름 site:embarcadero.com


  이것은 embarcadero.com 사이트 내에서 우리가 원하는 함수와 관련된 레퍼런스를 찾을 수 있게 해준다. 예를들어 "strcat site:embarcadero.com"를 검색하면 구글에서 처음 1, 2번째에 검색 결과가 나온다. SysUtils.StrCat이라고 나오는 것을 클릭하여 인자 및 여러 설명을 볼 수 있다.


  이런 방식으로 시스템 함수들을 마주칠 때마다 인자와 반환값을 확인하고 인터넷으로 그 기능을 익혀가면서 진행하다 보면 그나마 손쉽게 분석이 가능해 진다.


  참고로 Unit1이 System이라면 디버거에서도 (맵 파일이 생성될 때) "System.함수 이름" 같이 볼 수 있지만 어떤 것들은 시스템 함수들이 사용된 유닛인데도 불구하고 그대로 Unit3 같이 나오는 경우를 볼 수 있다. 이것은 해당 Unit이 여러 가지를 임포트해서인 것으로 보이며 함수 자체는 잘 파악되어 있기 때문에 큰 상관은 없다. 





2. 추가적인 문제와 해결 방안

  지금까지 진행함으로써 우리는 델파이 컴파일러가 정적으로 링크시켜 준 함수들을 파악할 수 있었고 찾아가면서 진행할 수 있다는 것을 알게되었다. 하지만 앞에서 다 설명하지 못한 부분도 있고 추가적인 부분도 존재하므로 아래에서 못다한 설명을 추가하도록 하겠다.



2.1 함수 호출 규약

  VC++로 컴파일된 32비트 바이너리에 익숙하다면 스택을 통해 파라미터를 넣는다는 것에 편해져 있을 것이다. 하지만 델파이를 분석하면서 느꼈겠지만 델파이는 함수 호출 규약이 좀 다르다. 델파이는 먼저 레지스터를 이용해서 인자를 전달한다. 순서는 EAX, EDX, ECX 순이며 그 이후로는 스택을 통해 집어넣는다. x64에서의 호출 방식도 레지스터를 이용하므로 이러한 방식에 익숙해질 필요가 있어 보인다. 간단해 보이는 차이지만 분석하다 보면 은근히 귀찮은 점을 느낄 수 있다. 스택을 사용한 경우에는 거의 그대로 그 위치에 원본 인자가 존재하지만 레지스터를 사용하는 경우에는 필연적으로 이 값을 스택 등으로 옮겨야 이후 해당 레지스터를 사용할 수 있기 때문에 추가적인 루틴이 더 들어가며 그것까지 파악해야 하기 때문이다. 하지만 그렇게까지 귀찮은 특징은 아니다.



2.2 객체 지향적이다

  이 말은 예를들어 함수를 호출할 때 인자를 집어넣는데 이게 객체가 될 수 있으며 우리는 이것의 주시해야 한다는 점이다. 이 객체라는 것이 여러 멤버로 이루어져 있고 한 번의 참조가 아니라 두 번 이상의 참조를 통해 접근되거나 하는 경우에는 난이도가 매우 높아질 수 밖에 없다.


  사실 여기에 대한 답은 없다. 하지만 개인적으로 OllyDbg와 x64dbg의 Label (블로그 내부 검색으로 찾아보자) 기능을 이용한다면 꽤 편리하게 분석할 수 있을거라는 생각이 든다. 메커니즘은 파악한 구조체를 디버거에 등록하고 거기에 각 멤버별로 Label을 달아줄 경우 추후 분석에는 매우 유용해질 것이기 때문이다. 이것을 자동화 해주는 플러그인을 구현한 후에 따로 다른 문서에서 예시와 함께 정리하기로 하겠다.



2.3 다른 Unit을 임포트할 경우

  개발자가 제작한 부분은 그렇다고 치지만 어떤 라이브러리를 임포트해서 정적으로 링크한 경우에는 분석의 난이도가 다시 높아질 수 밖에 없다. 특히 이것이 암호화 라이브러리라면 답이 없다. 물론 이것이 오픈소스일 경우 아주 고전적인 방식으로 상수 값이나 문자열을 찾아서 소스 코드를 보며 루틴을 파악한 후 어셈블리와 비교해가며 파악할 수도 있고 단지 공개 버전이라면 더 추상적으로 접근해야 할 것이다. 또는 직접 컴파일해 가면서 비교해야할 필요가 있을 수도 있다.


  아까 IDA Pro의 시그니처가 그다지 쓸만하지 않다고 했는데 (꽤 괜찮은 편이지만 IDR이 워낙 좋다보니) 여기서부터는 유용하게 사용될 수 있다. 여기서 원하는 것은 VC++에서 처럼 라이브러리를 생성한 후에 시그니처를 따던가 idb2pat이라는 플러그인을 이용해 파악한 함수들에 대해 각각 시그니처를 딸 수 있을 것이다. idb2pat을 이용하는 방식은 같으므로 그냥 넘어가기로 하고 여기서는 라이브러리를 이용한 방식을 설명하겠다.


  사실 델파이에 대해서 잘 모르지만 델파이가 exe 파일은 생성해도 .dll이나 .lib 파일을 생성할 것이라는 기대조차 하지 않았기 때문에 직접 라이브러리를 사용하는 스텁 프로그램을 만든 후에 idb2pat으로 시그니처를 따야 하나 생각했다. 하지만 인터넷을 보니 dcu2pat이라는 프로그램이 존재한다. 이것을 통해 우리는 델파이가 .lib 생성을 지원하지 않아도 특정 라이브러리를 .dcu (Delphi Compiled Unit)로 생성한 후에 .pat 패턴 파일을 생성할 수 있게 되었다. 이후 sigmake를 이용하는 방식은 동일하다. 이제 악성코드를 분석하다가 오픈 소스 암호 라이브러리를 사용하는 델파이 바이너리를 만나면 직접 .dcu로 컴파일한 후에 패턴 파일을 추출할 수 있을 것이다. dcu2pat은 다음 링크에서 다운로드 받을 수 있다. [ http://uploaded.net/file/kgw21z97 ]


  추가 사항으로서 이것을 읽는 사람들은 델파이 개발자가 아니기 때문에 .dcu를 만드는 방식도 찾아봐야 할 것이다. 본인도 개발하는 법을 모르지만 그나마 가장 쉬운 방법을 찾게되었다. 델파이의 버전마다 다르겠지만 여기서는 델파이 6 기준으로 설명하겠다. 먼저 다음을 선택한다.


File - New - Others


  이후 Package를 선택하면 패키지 관리자 창을 볼 수 있다. 여기서 add를 선택하고 Browse를 누른 후 원하는 .pas 소스 코드들을 모두 선택한다. 일반적으로 원하는 유닛만을 선택하고 싶더라도 Dependency에 의해 이 유닛이 Import하는 다른 유닛들도 그리고 이 유닛들이 사용하는 다른 유닛들까지 모두 선택해야 하므로 어느 정도 이상 큰 경우에는 이 방식을 이용하는 것이 가장 쉬운 방식이다.


  이렇게 관련된 모든 소스 코드들 즉 .pas 파일들을 add하였으면, 단순하게 Compile을 누르면 된다. 원래는 .bpl을 만드는 과정이지만 어찌되었든 .pas 즉 소스 코드들이 있던 디렉터리에 

각각의 소스 코드에 해당하는 .dcu 파일들이 모두 생성되어 있는 것을 확인할 수 있다. 이제 Batch 스크립트를 이용해서 각각의 .dcu 파일들을 .pat 파일로 변환시키면 된다.



Posted by SanseoLab

0. 개요

1. 사용

.... 1.1 폴더

2. 제작

.... 2.1 툴

.... 2.2 라이브러리

.... 2.3 패턴 파일

.... 2.4 시그니처 생성

.... 2.5 충돌 처리

3. 플러그인 및 파이썬 스크립트

4. 기타

.... 4.1 링크

.... 4.2 TODO

.... 4.3 예시

.... 4.4 델파이





0. 개요

  IDA Pro로 분석하다 보면 디버거에서는 보지 못했던 기능을 확인할 수 있는데 자동으로 파악한 함수들의 이름을 붙여주는 것이다. 사실 대부분의 프로그램들이 DLL을 사용하는 방식으로 제작되기 때문에 즉 /MD로 컴파일되어 제공되기 때문에 IDA Pro에서 확인해주는 함수들은 C 런타임 초기화와 관련된 내용들 뿐일 것이다. 간단하게 말하면 Main()을 호출하기 전의 Stub 코드들에서 사용하는 함수들이다. 물론 리버싱에 익숙하지 않은 경우라면 이러한 Stub 코드들에도 익숙하지 않기 때문에 어느 정도는 도움을 받을 수 있다.


  분석하다 보면 이러한 Stub 코드들 외에도 기본적인 함수들 예를들면 memset(), memcpy() 또는 rand() 등의 함수들처럼 컴파일러가 최적화 과정을 통해 코드에 통합해주는 함수들도 볼 수 있다. 지금까지는 C나 C++의 경우였고 델파이 같이 순수하게 정적으로 링크되어 제공되는 함수들의 경우에는 많은 도움을 받을 수 있다. 물론 델파이는 특이한 경우로서 IDR이라는 디컴파일러를 이용하면 더 많은 도움을 받을 수 있는데 이것은 다른 문서에서 정리한다. IDA Pro는 반이 못되게 겨우 분석해 주는데 방식의 차이로 인한 결과로 보인다.


  물론 이외에도 정말 많은 도움을 받을 수 있는 기능이다. 개인적으로는 악성코드가 OpenSSL 같은 암호화 라이브러리를 정적으로 링크하여 사용하는 경우 분석하기 정말 어려워지기 때문에 미리 한번 DB를 만들어 놓을까 하는 생각으로 공부하게 되었다. 이것은 뒤에서 예를 들어서 설명하겠다.


  Flirt의 원리에 대해서도 추가하자면 함수의 처음 32바이트에서 패턴을 추출하여 이것을 시그니처로 만드는 방식이다. 물론 패턴이 동일하다면 충돌이 나기도 한다. 큰 라이브러리라면 매우 많은 충돌이 발생하는 것을 확인할 수 있을 것이다.





1. 사용

  Flirt 기능의 경우에는 사용하는 방식도 알아야 한다. 간단한 경우에는 IDA Pro에서 로드할 때 자동으로 인식해서 적용시켜 주지만 직접 제작한 경우나 인식하지 못할 때는 수동으로 인식시켜 주어야 할 필요가 있다.


[ File - Load file - FLIRT signature file ... ]


  이 버튼을 누르면 IDA Pro에서 제공되는 여러 시그니처 파일들을 확인할 수 있다. 시그니처 파일은 확장자가 .sig이며 설치 디렉터리에서 sig 디렉터리를 찾으면 확인할 수 있다. 또한 "Signatures" 창을 켜면 현재 적용된 시그니처 윈도우가 나와 시그니처를 확인할 수 있다. 개인적으로 "Shift + F5"를 누른 후 나오는 시그니처 창에서 마우스 오른쪽 버튼을 클릭하여 고르는 방식이 가장 편한것 같다.


  IDA Pro가 바이너리를 제대로 인식하였다면 이미 적용된 시그니처들을 볼 수 있을 것이다. 먼저 각 시그니처에 대한 설명도 필요해 보인다. 여기서는 VC++로 제작된 바이너리 기준으로 설명할 것이다. 개인적으로는 IDA Pro에서 제공되는 FLIRT가 가장 질이 좋은 것 같다.


vc32rtf : 일반적인 32비트 애플리케이션의 경우 적용되며 앞에서 설명했던 Stub 관련 내용이나 최적화 시에 정적으로 링크되는 CRT 관련 함수들에 대한 시그니처

vc64rtf : 64비트 버전

vcseh : 이유는 모르겠지만 예외 처리와 관련해서 시그니처를 따로 제공한다.

vc64seh : 64비트 버전

vc64_14, vcucrt : VC++ 버전 14부터는 Universal CRT 등의 개념으로 인해 많은 변화가 생겼다. 그래서 추가적인 시그니처를 제공하는 것으로 보인다. 관련 내용은 다음 링크를 [ http://sanseolab.tistory.com/17 ] 확인하자.


  참고로 여기에서 적은 vc32rtf 같은 이름은 .sig 파일의 이름이며 시그니처 창에서는 간단한 설명도 볼 수 있다. 뒤에서 제작할 때 설명을 넣는 부분도 설명할 것이다.


  위의 내용은 VC++로 제작된 바이너리만 설명하였지만 델파이나 여러 다른 컴파일러도 제공하며 적어도 수 십개는 되기 때문에 상황에 맞게 적용하면 될 것이다.



1.1 폴더

  x86이나 x64 아키텍처의 경우에는 sig 폴더 즉 루트 폴더에 그대로 두면 Flirt를 로드할 때 인식이 되지만, 다른 아키텍처들의 경우에는 해당 아키텍처에 해당하는 폴더에 위치시켜야 인식이 가능하다. arm이나 mips 같은 경우에는 해당 폴더가 이미 존재하기 때문에 (관련 시그니처도 소수 존재한다) 그곳에 위치시키면 되지만 여기에 없는 아키텍처의 경우 다음과 같이 이름을 짓고 이 폴더에 위치시켜야 한다.


- arm 폴더 : ARM

- mips 폴더 : mips

- ppc 폴더 : powerpc

- sh3 폴더 : sh4

- sparc 폴더 : sparc

- mc68 폴더 : m68k





2. 제작

  이미 제공되는 것을 적용하는 것은 간단하지만 이 문서의 주된 내용은 Flirt를 직접 만드는 것이다. 앞에서도 말했듯이 필요한 경우가 생기기 때문에 미리 만들어 놓던지 그때 그때 환경에 맞게 제작하는 방식을 알아놓는다면 분석에 매우 도움이 될 것이다.


  기본적인 방식은 라이브러리에서 패턴을 추출하고 이것을 이용해 시그니처를 만드는 것이다. 여기서는 차레대로 툴, 라이브러리, 패턴 추출, 시그니처 생성으로 설명할 것이며 플러그인을 이용하여 필요한 함수만 추출하는 경우는 따로 정리하기로 하겠다.



2.1 툴

  제작에 필요한 도구는 헥스레이 사의 홈페이지에서 다운로드 받을 수 있는 Flair라는 도구이다. 여기에도 여러가지 툴이 있지만 사용해본 것은 패턴을 추출하는데 사용되는 pcf.exe와 패턴을 이용해 시그니처를 제작하는데 사용되는 sigmake.exe이다. 직접 사용해 보면서 설명하도록 하겠다.



2.2 라이브러리

  기본적으로 시그니처를 만들 라이브러리 파일이 필요하다. 라이브러리 파일이란 .lib 파일로서 .dll과 상응하는 개념이다. 지금 생각해보니 이것을 읽을 정도면 당연히 알고 있을 것이므로 설명이 필요 없을것 같다. 인터넷에는 원하는 라이브러리가 소스 코드나 dll로 제공되는 경우는 많이 있어도 .lib으로 제공하는 경우는 잘 없는 것 같다. 그래서 코드를 직접 .lib으로 제작해야 하는 경우가 많다.


  제작 시에는 .lib을 선택해야 할 것이고 옵션도 /MT, /MTd를 이용하여 정적으로 링크해야 한다. 참고로 악성코드가 릴리즈 모드로 개발할지 디버그 모드로 개발할지 모르므로 두 종류를 모두 준비해야 한다. 사실 시그니처라는 것이 좀 그렇다. OpenSSL을 예로 들자면 이것의 버전별로도 .lib 파일을 준비해야 하며 release, debug 모드로도 준비해야 하고 마지막으로 컴파일러의 버전별로도 결과가 다를 수 있으므로 이것도 따로 버전별로 준비해야 완벽한 시그니처가 생성될 수 있을 것이다. 이러한 것들을 각각 하나씩 시그니처로 생성해도 되며 통합해서 생성해도 되는데 무엇이 더 좋을지는 상황에 따라 판단해야 할 것으로 보인다.


  추가적으로 가끔씩 마주칠 수 있는 에러가 있다. pcf를 이용해 .lib 파일에서 패턴 파일을 생성하려고 할 때 어떤 .obj 파일이 "not a coff module"이라며 계속 에러가 뜨는 상황이 있을 수 있다. 이 경우에는 .lib을 컴파일 할 때 옵션 중에서 "/GL"을 해제해야 한다. 아마 최적화 때문에 생기는 에러로 보인다.



2.3 패턴 파일

  이제 각 버전별로 .lib 파일을 확보하였을 것이다. 이것을 이용해 .pat이라는 패턴 파일을 생성한다. 사용법은 간단하다. 위의 에러가 나오지 않는 이상 다음의 명령어를 사용하면 된다. 각각의 .lib 파일에 대해서 다음의 명령어를 이용해 .pat 파일을 생성하자. 결과는 myLib.pat 파일이 될 것이다.


> pcf.exe  myLib.lib


  우리는 coff 파일인 .lib의 패턴을 따기 위해 pcf를 사용하였지만 리눅스의 elf 파일이라면 pelf.exe(최신 버전의 경우 pelf.rtb 추가)를 사용하면 되고 확인해보지는 않았지만 plb.exe라는 파일도 존재한다. 그리고 IDA Pro가 리눅스 버전을 지원하듯이 Flair 툴도 리눅스 용 바이너리가 존재한다.


  .pat 파일은 아래와 같은 형태로 메모장으로도 볼 수 있다. 앞에서도 설명한 것처럼 처음 32바이트에서 추출한 패턴인 것을 확인할 수 있다.

 

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

558BEC8B450825FFFFFF7F5DC3...................................... 00 0000 000D :0000 _curlx_ultosi 

558BEC8B450825FFFFFF7F5DC3...................................... 00 0000 000D :0000 _curlx_uztosi 

558BEC8B45085DC3................................................ 00 0000 0008 :0000 _curlx_uztoul 

558BEC8B45085DC3................................................ 00 0000 0008 :0000 _curlx_uztoui 

558BEC8B450825FFFFFF7F5DC3...................................... 00 0000 000D :0000 _curlx_sltosi 

558BEC8B45085DC3................................................ 00 0000 0008 :0000 _curlx_sltoui 

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


  참고로 pelf.exe 사용 시 다음과 같은 에러가 발생할 수 있다.

> pelf.exe mips_libc.a

Fatal [C:\Users\sanseolab\Desktop\arm_libc.a] (clone.o): Unknown relocation type 10 (offset in section=0x44).

Use -r switch or edit pelf.rtb to process it.


  이 경우에는 다음과 같은 옵션을 추가해서 사용해야 한다. (위 에러 로그 중 type 10을 보자)

> pelf.exe -r10:0:0 arm_libc.a


  그리고 pelf.exe가 지원하는 아키텍처가 그렇게 많은 편은 아닌것 같다. 물론 x86, x64 외에 arm, mips 정도까지는 지원한다.



2.4 시그니처 생성

  이제 원하는 .pat 파일을 생성했니 이것을 이용하여 .sig 파일 즉 시그니처 파일을 만들어 보자.


> sigmake.exe -nmyLib myLib.pat myLib.sig


  옵션 -n 뒤에 붙은 문자열은 설명이다. 앞에서도 언급했듯이 시그니처 창을 켤 때 설명 부분에서 볼 수 있는 부분이다. 이름을 붙이지 않으면 "Unnamed sample library"라고 붙는데 파일 이름만으로 모든 설명을 충족시키기 어렵기 때문에 설명을 꼭 넣는 것을 추천한다.


  이것은 간단한 방식이고 조금 더 실제처럼 .sig 파일을 생성해 보겠다.


> sigmake.exe  -n"MSVC C Standard Library x86 Release and Debug v15" "libcmt_15_msvc_x86.pat"+"libcmtd_15_msvc_x86.pat"  cmt_v15_rd_x86.sig


  "MSVC C Standard Library x86 Release and Debug v15"는 설명 부분이며 libcmt_15_msvc_x86.pat 파일과 libcmtd_15_msvc_x86.pat 파일을 + 기호로 더하여 cmt_v15_rd_x86.sig라는 시그니처 파일을 생성하였다.


  이 경우에는 기본 아키텍처라서 별 다른 옵션은 주지 않았지만 아키텍처 별로 추가 옵션을 주어야 한다. 옵션은 -p 다음에 숫자를 붙여서 주면 되며 sigmake.exe -hp 명령을 통해 아키텍처에 맞는 번호를 확인할 수 있다.


  참고로 .pat 파일이야 텍스트 형태이기 때문에 직접 수정이 가능하지만 .sig 파일은 특정한 포맷이 있다. 어느 부분에 버전 정보가 위치하는지는 알 수 없지만, 최신 버전의 sigmake.exe로 생성한 .sig 파일은 과거 버전의 IDA Pro에서는 인식할 수 없다. 물론 과거 버전으로 만든 것을 최신 버전의 IDA Pro에서 인식하는 것은 가능하다.



2.5 충돌 처리

  거의 대부분의 경우 sigmake로 시그니처를 생성할 때 Collision이 발생할 것이다. 이것은 각각의 함수들에서 추출한 패턴이 동일할 경우에 발생한다. 이 경우 myLib.exc 라는 파일이 만들어진 것을 볼 수 있다. 이것을 메모장으로 열어보자.


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

;--------- (delete these lines to allow sigmake to read this file)

; add '+' at the start of a line to select a module

; add '-' if you are not sure about the selection

; do nothing if you want to exclude all modules


_myFunc_1                              02 BB34 33D283F82B7416508B0668........50E8........83C40CBA4300000033C08B

_myFunc_2                        02 BB34 33D283F82B7416508B0668........50E8........83C40CBA4300000033C08B


_OurFunc_1                                      00 0000 558BEC668B45085DC3..............................................

_OurFunc_2                                      00 0000 558BEC668B45085DC3..............................................

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


  myFunc_1과 myFunc_2라는 함수가 초기 루틴이 비슷해서인지 충돌이 발생하였고 ourFunc_1, 2 함수도 마찬가지이다. 이 경우에는 각각의 저 함수들 중에서 하나를 선택을 하거나 아니면 주석으로 보여주게 하거나 또는 모두 무시할 수도 있다.


  예를들어 myFunc_1로만 보여줘도 충분하다고 생각하는 경우에는 "_myFunc_1 ... "의 라인 첫 부분에 + 기호를 붙인다. 즉 "+_myFunc_1 ... "와 같은 형태가 될 것이다. 또는 그냥 이 부분을 무시해버릴 수도 있다. 그대로 둔다면 시그니처는 생성되지 않을 것이다. - 기호의 경우에는 헷갈리지만 Flirt를 적용시킬 때 이름에 적용시키지 않고 대신 주석으로 달아준다고 한다. 마지막으로 앞의 4줄짜리 주석을 지운다. 다음 예제는 첫 번쨰 충돌은 선택을 하고 두 번째는 무시하기로 결정하였다고 가정하였을 때이다.


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

+_myFunc_1                              02 BB34 33D283F82B7416508B0668........50E8........83C40CBA4300000033C08B

_myFunc_2                        02 BB34 33D283F82B7416508B0668........50E8........83C40CBA4300000033C08B


_OurFunc_1                                      00 0000 558BEC668B45085DC3..............................................

_OurFunc_2                                      00 0000 558BEC668B45085DC3..............................................

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


  이제 앞의 명령어를 그대로 입력하면 .sig 파일이 생성된 것을 확인할 수 있다. 실수로 처리하지 못한 부분이 있다면 앞의 .exc 파일의 뒷 부분에 4줄 짜리 주석과 그 부분만 추가되어 있는 것을 볼 수 있는데 이것도 마찬가지로 주석 제거와 선택 또는 무시를 하면 시그니처가 정상적으로 생성된다.


  참고로 OpenSSL의 시그니처를 딸 때 보면 수 십개인지 수 백개인지 모를 양의 충돌이 발생하는 것을 볼 수 있다. 개인적으로 이 라이브러리를 충분히 공부한 후에야 제대로 된 선택을 할 수 있을 것으로 보인다.


  그리고 충돌보다는 자주 보이지 않는 에러이지만 다음과 같은 에러메시지도 가끔 볼 수 있다.

"myLib.pat (71): bad pattern"


  이 경우는 myLib.pat 파일에서 71번째 라인, 즉 해당 라인에 존재하는 특정 함수의 시그니처가 잘못된 패턴이라는 것을 의미한다. 가끔씩 나오는 에러로 보이는데, 함수 하나 정도는 포기한다는 생각으로 해당 라인을 지우면 에러를 없애고 계속 진행할 수 있다.





3. 플러그인 및 파이썬 스크립트

  아까 시그니처 생성 방식을 설명할 때 "기본적으로" .lib 파일을 이용해서 만든다고 하였다. 하지만 정말 유용하게 사용할 수 있는 플러그인이 존재한다. 


  우리는 IDA Pro에서 파악한 함수에 이름을 붙일 수 있다. "idb2pat"이라는 플러그인은 이렇게 이름 붙인 함수를 또는 함수들을 선택해서 그것만 패턴을 파악하여 .pat 파일을 만들어 줄 수 있다. 전체 생성 기능도 있지만 "User Selected Function" 기능이 정말 유용해 보인다. 어떤 악성코드가 특정 라이브러리를 정적으로 링크했는데 파악한 함수들만 시그니처를 따고 싶은 경우에 쉽게 .pat 파일을 생성할 수 있다.


  게다가 .sig 파일이 아니라 .pat 파일이므로 이렇게 딴 패턴들을 모아서 나만의 시그니처를 만들 수도 있다. 이 방식은 간단한데다가 필요한 것만 선택할 수 있고 소스 코드를 몰라도 되므로 최고의 방식이라고 생각한다.


  하지만 단점이 있는데 32비트 버전만 지원한다는 점이다. 그래도 소스 코드가 오픈되어 있으므로 직접 수정할 수 있을 것이다.


  개인적으로는 위의 툴 보다는 FireEye의 FLARE 팀에서 위의 플러그인을 참고해서 개발한 IDA Pro 파이썬 스크립트가 더 좋아보인다. 이것은 64비트도 지원해 주며 ida2pat.py라는 이름으로 다음 링크에서 다운로드 받을 수 있다. 참고로 IDA Pro 7.0 업그레이드에 맞춰서 업데이트되었기 때문에 이 버전보다 낮은 버전의 IDA Pro를 사용 중이라면 스크립트도 이전 버전을 다운받아서 사용하자.

https://github.com/fireeye/flare-ida/blob/master/python/flare/idb2pat.py ]





4. 기타

4.1 링크

  직접 만드는 것은 불편하므로 이미 존재하는 것들의 링크를 올리겠다.


[ https://github.com/Maktm/FLIRTDB ]

  Boost, OpenSSL, VC++ 14 Runtime 등의 라이브러리에 대해서 시그니처를 제공해 준다. 각 버전별로 수 십개가 있지만 .pat 파일 및 .exc 파일도 제공해 주므로 직접 통합하여 사용할 수 있다.


[ https://github.com/push0ebp/sig-database ]

  위의 링크보다 많지는 않지만 OpenSSL 등 여러 시그니처들을 제공해 준다.



4.2 TODO

  사실 라이브러리를 정적으로 링크하면 용량이 굉장히 커지기 때문에 일반 프로그램들 뿐만 아니라 악성코드들도 이러한 방식을 그다지 많이 사용하지는 않는 것 같다. 그래도 혹시나 모를 일을 대비해서 제작해 볼 만한 것을 찾아보았다.


- 라이브러리 : Tor (요즘 악성코드는 토르를 이용해 C&C 서버와 연결하는 경우가 있다고 한다. 제작 가능 여부는 불분명), Curl, Wget, mbedTLS(ARM의 암호화 라이브러리. 어떤 랜섬웨어에서 사용되었다고 한다)


- 언어 : golang과 같이 기본적으로 정적으로 링크되는 언어.



4.3 예시

  랜섬웨어 중에서 TeslaCrypt가 있는데 특정 버전에서 OpenSSL을 정적으로 링크하였다고 한다. 운좋게 샘플을 구해 시그니처를 적용시켜 보니 약 40여개가 매치되는 것을 확인하였다. 아직 OpenSSL 라이브러리를 이용한 개발에 익숙하지 않아서 필요한 함수들 위주로 충분히 인식되었는지 여부는 판단하기 어렵지만 이정도면 충분히 쓸만한 기능이라고 생각한다.



4.4 델파이

  델파이와 관련해서는 다음 링크를 참고한다. [ http://sanseolab.tistory.com/56 ]




'악성코드 분석' 카테고리의 다른 글

Process Hollowing 및 응용  (0) 2018.03.02
델파이 바이너리 분석 방법론  (1) 2018.02.25
Anti-AV와 Anti-VM (Sandbox)  (1) 2017.12.30
COM, OLE, .NET Frame work 등의 개념 및 사용  (0) 2017.12.18
exeinfo PE 사용법  (1) 2017.12.17
Posted by SanseoLab



목차



0. 개요

1. AV의 에뮬레이터 탐지

.... 1.1 Human Interaction

.... 1.2 Timing

.... 1.3 외부의 환경을 이용

.... 1.4 Process Introspection

.... 1.5 만들어진 환경 (Environmental Artifacts)

.... 1.6 감당할 수 없는

.... 1.7 API 함수 결과

2. VM (샌드박스) 탐지

.... 2.1 Human Interaction

.... 2.2 Timing

.... 2.3 외부의 환경을 이용

.... 2.4 Process Introspection

.... 2.5 만들어진 환경

.... 2.6 CPU "Red Pills"

3. Pafish

.... 3.1 기본 정보

.... 3.2 Debuggers detection

.... 3.3 CPU information based detections

.... 3.4 Generic sandbox detection

.... 3.5 Hooks detection

.... 3.6 Sandboxie detection

.... 3.7 Wine detection

.... 3.8 VirutalBox detection

.... 3.9 VMware detection

.... 3.10 Qemu detection

.... 3.11 Bochs detection

.... 3.12 Cuckoo detection

4. 기타

5. 참고자료





0. 개요

  Anti-Emulation이나 Anti-VM 같은 기법들은 인터넷에 많은 자료가 있음에도 불구하고 제대로 정리된 것은 찾아보기 힘든 편이다. 종류가 많기도 하고 이렇게 많은 제품들이 업데이트 또한 자주 하기 때문에 정리가 힘든 것이 이유일 수도 있을것 같다. 


  여기서는 이러한 내용들을 전체적인 분류에서 시작해서 Pafish라는 Anti-VM, Anti-Sandbox 전용 프로그램 분석을 통해 직접적인 구현까지 정리하려고 한다. 물론 오래된 것도 있을 것이고 최근의 것도 있겠지만 여기서 모든 것에 대해 완벽하게 정리할 수는 없을 것 같고 말 그대로 정리로서의 의미만 갖게될 것 같다.





1. AV의 에뮬레이터 탐지

  AV에는 대표적으로 바이너리 에뮬레이터와 스크립트 에뮬레이터가 있다. 따로 표시하지 않은 것은 바이너리 에뮬레이터를 의미한다.


1.1 Human Interaction

  먼저 메시지 후킹을 통해 마우스 클릭 여부를 기다린 후 일반적인 환경에서 사용자가 마우스를 클릭하듯이 클릭을 확인하면 이 때 악의적인 기능이 활성화되는 방식이 있다. 또한 대화 상자를 보여주고 버튼이 클릭된 경우라던지, 문서 파일의 경우 스크롤을 어느 정도 이상 내리면 활성화되는 방식도 존재한다.


  생각해보지 못한 방식도 있었는데 웹 브라우저의 히스토리를 확인한다던가 최근에 열린 파일들이 있는지 그 외에도 로그인 시간 검사 등도 여기에 포함될 수 있다. 이러한 방식들은 VM에서도 똑같이 적용된다.



1.2 Timing

  기본적으로 sleep() 함수를 이용해 긴 시간을 기다리게 하는 방식인데 이것은 에뮬레이터나 샌드박스에서 대개 주어진 시간이 있기 때문이다. 또한 특정 날짜나 시간에 활성화되는 방식도 존재한다. 참고로 이 방식은 자주 사용되서인지 시간 관련 함수들에 대한 패치가 많은 것 같다. 에뮬레이터의 경우 반환 값으로, 가상머신의 경우 함수에 대한 후킹을 통해 제어해서 요즘은 간단한 방식은 잘 먹히지 않는다고 한다.



1.3 외부의 환경을 이용

  에뮬레이터는 그렇다고 치더라도 샌드박스 환경에서도 가상 머신들이 가상 네트워크에 연결되어 있는 경향이 있다. 다시 말해서 악성코드를 직접 실행하는 환경이더라도 인터넷의 연결은 허용하지 않는 경우가 대부분이다. 이 경우에는 에뮬레이터처럼 직접 결과를 반환시켜 준다.


  이렇게 에뮬레이터나 가상 환경에서는 인터넷 연결을 허용치 않는 대신 성공했다는 결과를 반환해준다는 것을 악용하여 존재하지 않는 url에 접속한 후 성공한 경우에 가상 환경이라는 것을 탐지하는 방식을 사용할 수 있다. 또는 특정한 사이트의 특정한 파일에 대한 해시값을 가지고 있다가 인터넷을 연결하여 그 파일을 다운로드한 후 그 값을 비교하는 방식도 있다. 보통 에뮬레이터나 샌드박스 환경에서는 이러한 경우에 직접 생성한 파일을 주는 것을 악용한 방식이라고 한다.


  참고로 먼저 앞에서 설명한 Timing 방식에서도 시간을 웹 같은 외부에서 얻는 방식을 사용할 수도 있다.



1.4 Process Introspection

  프로세스 내부의 여러 상태를 검사하는 방식이다. 예를들어 API 호출 이후 레지스터들의 상태라던지 프로세스 구조체들, 초기화되지 않은 메모리, 로드된 DLL 검사 등이 존재한다. DLL의 경우 로더를 확인하여 실제 로더로 실행되는지를 확인한다. 이 외에도 라이브러리에서 함수들 사이의 패딩의 값을 검사하기도 한다(int 3). 또한 로드된 DLL의 종류를 확인하는 방식도 있다.



1.5 만들어진 환경 (Environmental Artifacts)

  실행되는 환경을 통해 탐지하는 방식으로서 많은 부분들이 사용될 수 있다. 대표적으로 악성코드가 애플리케이션의 취약점을 사용하는 경우 특정한 버전에서만 통하는 경우가 많다. 그러므로 특정한 버전이 아닌 경우에는 실행시키지 않아서 악성코드로 인식하지 못할 수도 있다. 이것은 가상 머신에서도 마찬가지이다.


  또는 argv[0]을 통해 본 프로세스의 이름을 구해서 프로세스나 바이너리의 이름이 자체적으로 변경되어 실행되는 경우를 이용할 수도 있다. 또는 자식 프로세스로 자기 자신의 바이너리를 실행시켜서 자식 프로세스로 실행된 경우에만 악의적인 행위를 수행하게 할 수도 있는데 이것은 가상 환경에서 부모 프로세스만을 스캐닝하는 경우를 악용한 방식이다.


  하지만 이것은 특별한 경우이고 악성코드의 입장에서는 수 많은 부분을 통해 현재 실행되는 환경이 에뮬레이터인지를 파악할 수 있다. 에뮬레이터의 경우에는 여러 환경을 직접 구현해주어야 하는 방식이므로 악성코드와 관련이 없는 부분이지만 일반적인 컴퓨터 환경에서는 존재하는 파일이나 레지스트리 등을 검색하여 없을 경우에 그것을 의심해볼 수 있는 것이다.


  예는 일일이 나열하기도 힘들 정도인데 대표적으로 레지스트리 값이나 시스템 파일들 같이 일반적인 컴퓨터 환경에서는 존재하지만 에뮬레이터에서는 구현하지 않았을 것으로 보이는 내용을 조회하여 그 존재 여부에 따라 판단할 수도 있다. 이 두가지 외에도 프로세스 이름도 잇을 수 있으며 만약 AV가 프로세스 목록에 특정한 프로세스를 보여주게 만들었다고 하더라도 해당 바이너리 또한 조회하여 존재하지 않는 경우가 있을 수도 있다.


  또는 조금 다른 예로 MAC 주소가 특정한 값들의 집합으로 하드코딩되어 있고 그것을 아는 경우에는 MAC 주소를 조회하여 그 주소에서는 활성화되지 않게 함으로써 우회하는 방식도 존재한다.



1.6 감당할 수 없는

  이 방식은 sleep을 이용해 주어진 한계 시간을 초과하여 에뮬레이터나 샌드박스를 우회하는 방식과 비슷하다. 예를들어서 큰 크기의 메모리를 malloc()으로 할당거나 (더 나아가 memset()을 이용해 간단한 내용을 더 추가할 수도 있다) 루프문의 반복을 감당할 수 없을 정도로 설정하는 것이다. 물론 이것은 일반적인 컴퓨팅 환경이나 가상 머신에서는 CPU가 충분히 감당할 수 있어 실질적으로는 사용자가 인식하지는 못할 정도의 범위를 사용한다. 하지만 에뮬레이터의 경우에는 그렇지 않다.



1.7 API 함수 결과

  API 함수를 호출하는 부분이 있다면 OS를 통해 직접 실행하는 대신 에뮬레이터는 이 결과를 대신 반환해 준다. 하지만 제대로된 결과(반환 값 뿐만 아니라 에러 코드도 포함)가 아니라던지 일반적으로 실패해야 하는데 성공한다던지 하는 것을 기반으로 에뮬레이터의 존재를 확인할 수 있다. 예를들어 너무나 방대한 윈도우의 API를 이용한 방식으로서 거의 사용되지는 않지만 에뮬레이터에서는 구현하지 않았을 것으로 여겨지는 함수를 실행하여 성공한 경우에만 활성화시키는 방식이 있다.


  또 다른 예로서 악성코드가 일반 사용자 권한으로 실행된다는 가정 하에 만약 OpenProcess()를 통해 시스템 프로세스의 핸들을 얻는 경우에는 실패할 수 밖에 없다. 하지만 AV의 에뮬레이터가 이러한 권한을 고려하지 않고 성공한 값을 준다면 이것은 우회하는데 사용할 수 있다.





2. VM (샌드박스) 탐지

2.1 Human Interaction



2.2 Timing



2.3 외부의 환경을 이용



2.4 Process Introspection

  VM 환경에서도 일반적인 환경과 프로세스 내의 다른 점을 발견할 수 있다. 유저 모드 API 후킹을 사용하는 경우 후킹 엔진(dll)이 삽입되며 kernel32.dll이나 ntdll.dll 등의 라이브러리에서 후킹 엔진으로 분기하는 부분이 존재하는데 이 경우 원래 라이브러리 코드와 비교하여 차이점을 통해 구별할 수 있다. 또한 후킹 엔진이 인젝션되었으므로 로드된 DLL의 종류를 확인하는 방식도 있다.



2.5 만들어진 환경

  취약점 부분이라던지 프로세스 이름 관련 부분은 에뮬레이터와 마찬가지일 것이다. 하지만 다른 환경의 경우에는 에뮬레이터와는 정 반대인데 위에서 설명했듯이 에뮬레이터를 우회하기 위해서는 에뮬레이터가 모든 것을 구현하지 못했을 것이라는 가정 하에, 있어야 할 것이 없는 것을 통해 판단했었다. 하지만 가상 머신의 경우에는 일반적인 컴퓨터 환경과 비교하여 없는 부분을 찾는 것은 그다지 의미가 없을 것이다. 대신 반대로 가상 머신에서만 존재하는 것 또는 가상 머신에서만 가질 수 있는 환경을 비교하는 방식을 사용한다. 이 부분은 아래에 구체적인 대상을 가지고 설명하도록 하겠다.



2.6 CPU "Red Pills"

  어떤 명령어의 경우 실제 환경의 CPU에서 실행될 때 동작하는 것과 가상 환경에서의 행위가 다르다. 이 경우 이 명령어를 사용하여 결과를 통해 구별한다.





3. Pafish

  Paranoid Fish는 샌드박스나 가상 머신 같은 가상 환경을 검사해주는 툴이다. 내용도 간단하고 소스 코드도 잘 정리되어 있기 때문에 직접 확인할 수도 있겠지만 여기에서 정리하는 의미로 검사 방식들을 정리하고자 한다.


  사실 여기에 포함되지 않지만 인터넷에는 여러 자료들이 존재한다. 이러한 것들을 모두 테스트해 볼 여력이 되지 않기 때문에 pafish의 내용을 공식적인 내용 쯤으로 가정하고 인터넷의 내용들은 추가적인 설명으로 덧붙이기로 하겠다.



3.1 기본 정보

3.1.1 윈도우 버전


3.1.2  CPU Vendor

  "GenuineIntel" 같은 결과가 나온다.


3.1.3 CPU Hyperviser Vendor

  "VBoxVBoxVBox" 같은 결과가 나온다.


3.1.4 CPU Brand

  "Intel<R> Core<TM> i5-5200U CPU @ 2.20GHz" 같은 결과가 나온다.



3.2 Debuggers detection

3.2.1 Using IsDebuggerPresent()

  현재 프로세스가 디버깅 중인지를 판단하는 방식이므로 가상 환경 탐지와는 관련이 없어보인다.


3.2.2 Using OutputDebugString()

  이 방식은 윈도우 비스타 이전의 경우에만 사용된다. 위와 같이 디버거 탐지에 사용되는 방식으로서 Last Error Value를 설정한 후 OutputDebugString()을 호출하는데 만약 디버거가 붙어있다면 해당 값은 변화가 없을 것이며 디버거가 붙어있지 않아서 실패한다면 거기에 해당하는 에러 코드가 Last Error Value에 설정되어 이전 값과 차이가 발생할 것이다. 안티 디버깅으로서 전과 후의 값을 비교하여 변화가 없을 때 디버깅 중으로 판단한다.


  참고로 SetLastError()와 GetLastError()로 Last Error Value를 설정/확인하거나 아니면 직접 FS:[0x34]를 이용할 수도 있다.



3.3 CPU information based detections

3.3.1 Checking the difference between CPU timestamp counters (rdtsc)

  rdtsc 명령어를 이용한 전형적인 안티 VM 방식이다. 두 rdtsc 명령어를 연속으로 사용한 후 그 값의 차이를 구하는데 가상 머신에서 실행되는 경우에는 일반적인 실제 머신에서 실행되는 환경보다 그 값의 차이가 크다는 (즉 더 많은 시간을 요구한다는) 특징을 이용한다. 지정한 값보다 큰 경우에는 가상 머신 환경에서 실행 중인 것으로 탐지한다.


3.3.2 Checking the difference between CPU timestamp counters (rdtsc) forcing VM exit

  앞과 비슷하지만 rdtsc 명령어 사이에 cpuid 명령어가 추가되어 있다. (물론 명령어가 하나 더 추가되었으므로 앞의 방식보다는 비교할 값이 조금은 더 크다) cpuid는 VM과 Hypervisor 사이에 문맥을 전환시켜주는 명령어로써 이것을 실행하면 trap이 유발되며 더 많은 딜레이 즉 시간이 요구된다.


  참고로 cpuid는 인자를 받을 때 eax를 사용한다. 여기서는 eax에 0을 넣고 cpuid 명령어를 실행하여 CPU Vendor ID String을 받았다. 물론 이 값을 구하려는 목적 보다는 단지 cpuid 명령어를 실행시키려는 목적을 가지고 있었기 때문에 반환된 값들은 사용되지 않았다.


3.3.3 Checking hypervisor bit in cpuid feature bits

  eax에 1(CPUID_GETFEATURES)을 넣고 cpuid를 호출한다. 실행 후 ecx의 31번째 비트가 1로 설정되어 있는지 검사한다. Hypervisor와 Physical CPU들은 이 비트를 0으로 설정하지만 게스트 운영 체제는 1로 설정되어 있다. 이 값이 1인 경우 가상 머신 환경으로 탐지한다.


3.3.4 Checking cpuid hypervisor vendor for known VM vendors

  "3.1.3 CPU Hyperviser Vendor"의 결과가 알려진 VM 벤더들인지를 검사한다. 다음의 표로 정리한다. 예를들면 KVM을 사용하는 경우에는 문자열 "KVMKVMKVM"을 얻게되고 이를 통해 가상 환경인지 여부를 판단할 수 있는 것이다.


벤더 이름 : 출력 결과

KVM : KVMKVMKVM

Microsoft Hyper-V or Windows Virtual PC : Microsoft Hv

VMware : VMwareVMware

Xen : XenVMMXenVMM

Parallels : prl hyperv

VirtualBox : VBoxVBoxVBox



3.4 Generic sandbox detection

3.4.1 Using mouse activity

  GetCursorPos() API 함수를 호출하여 마우스 커서의 위치를 구한 후 Sleep()하고 다시 마우스 커서 위치를 구하여 차이가 있는지를 비교한다. 없다면 샌드박스 환경으로 탐지한다.


3.4.2 Checking username

  GetUserName() API 함수를 통해 사용자 이름 문자열을 얻은 후 다음의 문자열과 비교한다.


SANDBOX

VIRUS

MALWARE


3.4.3 Checking file path

  GetModuleFileName() API 함수로 이 파일이 위치하는 경로를 구한 후 다음의 경로와 비교한다.


\SAMPLE

\VIRUS

\SANDBOX


3.4.4 Checking common sample names in drives root

  C:\ 같은 drive root에 "sample.exe", "malware.exe"라는 일반적인 샘플 이름을 갖는 파일이 존재하는지 검사한다.


3.4.5 Checking if disk size <= 60GB via DeviceIoControl()

  DeviceIoControl() API 함수를 이용하여 디스크의 크기가 60GB 이하인지 검사한다. 일반적으로 요즘 60GB 보다 적은 용량을 갖는 컴퓨터들이 잘 없으며 샌드박스 환경에서는 어느 정도 이상의 용량을 할당하지 않는다는 특징을 이용하는 것으로 보인다.


3.4.6 Checking if disk size <= 60GB via GetDiskFreeSpaceExA()

  GetDiskFreeSpaceExA() API 함수를 이용하여 디스크의 크기를 검사한다.


3.4.7 Checking if Sleep() is patched using GetTickCount()

  sleep() 함수를 이용하여 샌드박스를 우회하는 방식이 존재한다. 악의적인 행위 없이 오랫동안 대기하게 하여 이를 분석하는 샌드박스가 지정된 시간 동안 악성 행위를 탐지하지 못하게 하는 방식이다. 이를 보완하기 위해 요즘 샌드박스는 sleep() 함수를 후킹하여 오랜 시간 동안 대기하지 못하게 하는데 이렇게 sleep() 함수가 패치되었는지 여부를 GetTickCount()를 이용하여 검사한다.


  두 GetTickCount() 사이에 sleep() 함수를 호출하는데 만약 두 GetTickCount()의 차이가 sleep()에 인자로 넣은 시간과 비교해서 주어진 정도보다 크다면 sleep() 함수가 패치되었다고 판단하는 것이다.


3.4.8 Checking if NumberOfProcessors is < 2 via raw access

  PEB의 "0x064 DWORD NumberOfProcessors;" 멤버를 이용하여 프로세서의 수를 구한다. 프로세서의 수가 2개 미만이면 샌드박스 환경으로 탐지한다. 대부분 컴퓨터가 듀얼 코어 환경이라서 그런것 같으며 샌드박스 환경 구성 시에 가상 머신에서 프로세서를 하나로 설정하는 것이 일반적인가 보다.


  32비트 환경에서 FS 레지스터를 이용하여 PEB의 NumberOfProcessors 인자를 구하는 루틴은 다음과 같다. FS 레지스터 관련 내용은 다음을 참고한다. [ http://sanseolab.tistory.com/47 ]


MOV EAX, DWORD PTR FS:[18]

MOV EAX, DWORD PTR DS:[EAX+30]

MOV EAX, DWORD PTR DS:[EAX+64]


3.4.9 Checking if NumberOfProcessors is < 2 via GetSystemInfo()

  위와 같지만 직접 FS 레지스터를 이용하는 것이 아니라 GetSystemInfo() API 함수를 통해 SYSTEM_INFO 구조체를 구하고 이후 dwNumberOfProcessors 멤버를 통해 프로세서의 수를 구한다.


3.4.10 Checking if pysical memory is < 1Gb

  GlobalMemoryStatusEx() API 함수를 이용해 MEMORYSTATUSEX 구조체를 구한 후 이 구조체의 ullTotalPhys 멤버를 통해 물리 메모리를 구한다. 이후 물리 메모리의 크기가 1Gb보다 적은지 비교한 후 적다면 샌드박스 환경으로 탐지한다.


3.4.11 Checking operating system uptime using GetTickCount()

  GetTickCount() API 함수는 시스템이 부팅된 이후부터 지금까지의 시간을 반환해준다. 그 값을 구한 후에 12분보다 적은지를 비교한다. 만약 부팅 이후 12분 이상이 흐르지 않았다면 샌드박스 환경으로 탐지한다.


3.4.12 Checking if operating system IsNativeVhdBoot()

  IsNativeVhdBoot() API 함수를 이용해 VHD(Virtual Hard Disk)를 탐지한다. 일반적인 가상 머신의 경우 VHD 메커니즘을 이용해 하드 디스크를 제공하기 때문인 것으로 보인다. 이 함수는 VHD를 통해 부팅된 경우에는 1을 반환하며 아니라면 0을 반환한다. 만약 1을 반환 즉 VHD를 통해 부팅된 경우에는 샌드박스 환경으로 탐지한다.


  참고로 그냥 호출하는 것은 아니고 직접 GetModuleHandleA()와 GetProcAddress()를 이용하여 IsNativeVhdBoot() 함수를 구한 후 호출한다. 아마도 이 함수가 후킹되었거나 에뮬레이터에서 이 함수 호출 시에 0을 반환하도록 만들어졌을 수도 있으므로 그것을 우회하기 위함으로 보인다.



3.5 Hooks detection

3.5.1 Checking function ShellExecuteExW method 1

  ShellExecuteExW() 함수가 후킹되었는지 여부를 판단하여 후킹되었을 시 샌드박스 환경으로 인식한다.


  MS 윈도우에서 제공하는 라이브러리에 존재하는 각 API 함수들은 처음에 "MOV EDI, EDI" 명령어로 시작된다. 이것은 MS가 재부팅 과정 없이 패치를 수행하기 위해 즉 핫패칭을 위해 인라인 후킹이 더욱 쉬워질 수 있게 제공하였다고 한다. 즉 "mov edi, edi", "push ebp", "mov ebp, esp" 명령어가 5바이트인데 인라인 후킹 시 이 5바이트를 jmp 명령어로 바꾸어 후킹을 쉽게 할 수 있도록 만들어준 것이다. 사실 목적까지 자세히 찾아보지는 않았으므로 확실하지는 않다.


  어쨌든 가장 처음 즉 "mov edi, edi"는 0x8b, 0xff이다. 올리디버거로 보면 이 부분의 옵코드가 8BFF인 것을 볼 수 있다. pafish는 이렇게 "mov edi, edi" 부분이 그대로 존재하는지 즉 ShellExecuteExW() API 함수의 첫 부분이 후킹되지 않고 그대로 8BFF로 존재하는지를 검사한다. 만약 후킹되어서 다른 값을 갖는다면 샌드박스 환경으로 탐지한다.


3.5.2 Checking function CreateProcessA method 1

  CreateProcessA() API 함수라는 대상만 다르고 메커니즘은 위와 같다. 



3.6 Sandboxie detection

3.6.1 Using GetModuleHandle(sbiedll.dll)

  GetModuleHandle() API 함수를 이용해 sbiedll.dll을 구한다. 만약 이 DLL이 메모리에 로드되어 있다면 구하는데 성공할 것이고 샌드박스 환경으로 탐지한다. 이것은 Sandboxie라는 샌드박스가 사용하는 라이브러리로서 일반적인 환경에서는 프로세스에 로드될 일이 없다. Sandboxie는 특정한 목적을 위해 프로세스에 이 DLL을 인젝션한다.


  이 DLL은 많은 기능을 제공하는것 같은데 사용해보지는 않았지만 대표적인 것이 유저 모드 함수에 대한 후킹 기능으로 보인다. AV나 HIPS 등에서 API 후킹을 위해 DLL을 인젝션하는 것은 여러 글에서 이미 적어 놓았다.


[ http://sanseolab.tistory.com/33 ] 4.2 참고

[ http://sanseolab.tistory.com/35 ]

[ http://sanseolab.tistory.com/39 ]


  Sandboxie의 sbiedll.dll 외에도 비슷한 많은 것들이 존재한다고 한다. pafish에는 없는 내용이며 모두 확인해보지는 않았지만 적어보도록 하겠다. VMware는 dbghelp.dll을, SunBelt SandBox는 api_log.dll, dir_watch.dll, pstorec.dll을, Virtual PC는 wmcheck.dll을 마지막으로 WPE Pro는 wpespy.dll이 인젝션된다고 한다.



3.7 Wine detection

3.7.1 Using GetProcAddress(wine_get_unix_file_name) from kernel32.dll

  WINE은 오픈소스 윈도우 API 호환 계층이다. 즉 윈도우 API에 대한 호환을 지원하기 위해 DLL들도 오픈소스로 개발할 수 밖에 없는데 당연히 가장 중요한 라이브러리 중 하나인 kernel32.dll도 거기에 포함될 것이다. 와인에서 실행 중일 경우 구현한 kernel32.dll이 로드될 것이고 GetProcAddress() API 함수를 호출하여 wine_get_unix_file_name()라는 함수를 구한다. 일반적인 윈도우 환경이라면 kernel32.dll에 이 함수는 존재하지 않을 것이기 때문에 해당 호출은 실패하게 되지만 와인에서 제공되는 kernel32.dll에서는 어떤 이유에서인지 이 함수가 구현되어 있어서 호출이 성공한다. 만약 호출이 성공한다면 샌드박스 환경으로 탐지한다.


3.7.2 Reg key (HKCU\\SOFTWARE\\Wine)

  와인이 설치된 경우 레지스트리에 "HKCU\\SOFTWARE\\Wine" 키가 등록되는 것으로 보인다. 해당 레지스트리 키가 존재하는 경우에는 샌드박스 환경으로 탐지한다.



3.8 VirutalBox detection

3.8.1 Scsi port->bus->target id->logical unit id-> 0 identifier

  레지스트리 키 "HKLM<\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical> Unit Id 0"에서 값(이름)이 "Identifier"이며 데이터가 "VBOX"인 것을 찾으면 VirtualBox로 탐지한다.


3.8.2 Reg key (HKLM\\HARDWARE\\Description\\System \"SystemBiosVersion\)

  레지스트리 키 "HKLM\\HARDWARE\\Description\\System"에서 값이 "SystemBiosVersion"이며 데이터가 "VBOX"인 것을 찾으면 VirtualBox로 탐지한다.


3.8.3 Reg key (HKLM\\SOFTWARE\\Oracle\\VirtualBox Guest Additions)

  레지스트리 키 "HKLM\\SOFTWARE\\Oracle\\VirtualBox Guest Additions"를 찾으면 VirtualBox로 탐지한다.


3.8.4 Reg key (HKLM\\HARDWARE\\Description\\System \"VideoBiosVersion\")

  레지스트리 키 "HKLM\\HARDWARE\\Description\\System"에서 값이 "VideoBiosVersion"이며 데이터가 "VIRTUALBOX"인 것을 찾으면 VirtualBox로 탐지한다.


3.8.5 Reg key (HKLM\\HARDWARE\\ACPI\\DSDT\\VBOX__)

  레지스트리 키 "HKLM\\HARDWARE\\ACPI\\DSDT\\VBOX__"를 찾으면 VirtualBox로 탐지한다.


3.8.6 Reg key (HKLM\\HARDWARE\\ACPI\\FADT\\VBOX__)

  레지스트리 키 "HKLM\\HARDWARE\\ACPI\\FADT\\VBOX__"를 찾으면 VirtualBox로 탐지한다.


3.8.7 Reg key (HKLM\\HARDWARE\\ACPI\\RSDT\\VBOX__)

  레지스트리 키 "HKLM\\HARDWARE\\ACPI\\RSDT\\VBOX__"를 찾으면 VirtualBox로 탐지한다.


3.8.8 Reg key (HKLM\\SYSTEM\\ControlSet001\\Services\\VBox*)

  다음의 레지스트리 키 중 하나 이상을 찾으면 VirtualBox로 탐지한다.

"HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxGuest"

"HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxMouse"

"HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxService"

"HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxSF"

"HKLM\\SYSTEM\\ControlSet001\\Services\\VBoxVideo"


3.8.9 Reg key (HKLM\\HARDWARE\\DESCRIPTION\\System \"SystemBiosDate\")

  레지스트리 키 "HKLM\\HARDWARE\\DESCRIPTION\\System"에서 값이 "SystemBiosDate"이며 데이터가 "06/23/99"인 것을 찾으면 VirtualBox로 탐지한다.


3.8.10 Driver files in C:\\WINDOWS\\system32\\drivers\\VBox*

  다음의 파일 중 하나 이상을 찾으면 VirtualBox로 탐지한다.

"C:\\WINDOWS\\system32\\drivers\\VBoxMouse.sys"

"C:\\WINDOWS\\system32\\drivers\\VBoxGuest.sys"

"C:\\WINDOWS\\system32\\drivers\\VBoxSF.sys"

"C:\\WINDOWS\\system32\\drivers\\VBoxVideo.sys"


3.8.11 Additional system files

  다음의 파일 중 하나 이상을 찾으면 VirtualBox로 탐지한다.

"C:\\WINDOWS\\system32\\vboxdisp.dll"

"C:\\WINDOWS\\system32\\vboxhook.dll"

"C:\\WINDOWS\\system32\\vboxmrxnp.dll"

"C:\\WINDOWS\\system32\\vboxogl.dll"

"C:\\WINDOWS\\system32\\vboxoglarrayspu.dll"

"C:\\WINDOWS\\system32\\vboxoglcrutil.dll"

"C:\\WINDOWS\\system32\\vboxoglerrorspu.dll"

"C:\\WINDOWS\\system32\\vboxoglfeedbackspu.dll"

"C:\\WINDOWS\\system32\\vboxoglpackspu.dll"

"C:\\WINDOWS\\system32\\vboxoglpassthroughspu.dll"

"C:\\WINDOWS\\system32\\vboxservice.exe"

"C:\\WINDOWS\\system32\\vboxtray.exe"

"C:\\WINDOWS\\system32\\VBoxControl.exe"

"C:<\\program files\\oracle\\virtualbox guest additions\\>"


3.8.12 Looking for a MAC address starting with 08:00:27

  08:00:27로 시작하는 MAC 주소를 찾으면 VirtualBox로 탐지한다.


3.8.13 Looking for pseudo devices

  다음의 pseudo 디바이스들 중 하나 이상을 찾으면 VirtualBox로 탐지한다.

"\\\\.\\VBoxMiniRdrDN"

"\\\\.\\pipe\\VBoxMiniRdDN"

"\\\\.\\VBoxTrayIPC"

"\\\\.\\pipe\\VBoxTrayIPC"


3.8.14 Looking for VBoxTray windows

  FindWindow() API 함수로 다음의 두 VBoxTray 윈도우 중 하나 이상을 찾으면 VirtualBox로 탐지한다.

"VBoxTrayToolWndClass"

"VBoxTrayToolWnd"


3.8.15 Looking for VBox network share

  WNetGetProviderName() API 함수에 WNNC_NET_RDR2SAMPLE를 인자로 넣고 호출하여 provider가 "VirtualBox Shared Folders"이면 VirtualBox로 탐지한다. 공유 네트워크를 검사한다고 하는데 자세한 내용은 모르겠다.


3.8.16 Looking for VBox processes (vboxservice.exe, vboxtray.exe)

  다음의 프로세스를 검사하여 찾을 시 VirtualBox로 탐지한다.

"vboxservice.exe"

"vboxtray.exe"


3.8.17 Looking for VBox devices using WMI

  WMI도 SQL과 비슷한 WQL(WMI Query Language)가 지원된다. "SELECT DeviceId FROM Win32_PnPEntity" 쿼리문으로 조회한 "DeviceId""PCI\\VEN_80EE&DEV_CAFE"라면 VirtualBox로 탐지한다.

  



3.9 VMware detection

3.9.1 Scsi port 0,1,2 ->bus->target id->logical unit id-> 0 identifier

  각각 다음의 레지스트리 키, 값 그리고 데이터가 존재하는 경우 VMware로 탐지한다.

"HKLM<\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical> Unit Id 0"      "Identifier"      "VMWARE"

"HKLM<\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 1\\Scsi Bus 0\\Target Id 0\\Logical> Unit Id 0"      "Identifier"      "VMWARE"

"HKLM<\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 2\\Scsi Bus 0\\Target Id 0\\Logical> Unit Id 0"      "Identifier"      "VMWARE"


3.9.2 Reg key (HKLM\\SOFTWARE\\VMware, Inc.\\VMware Tools)

  레지스트리 키 "HKLM\\SOFTWARE\\VMware, Inc.\\VMware Tools"를 찾으면 VMware로 탐지한다.


3.9.3 Looking for C:\\WINDOWS\\system32\\drivers\\vmmouse.sys

  파일 "C:\\WINDOWS\\system32\\drivers\\vmmouse.sys"를 찾으면 VMware로 탐지한다.


3.9.4 Looking for C:\\WINDOWS\\system32\\drivers\\vmhgfs.sys

  파일 "C:\\WINDOWS\\system32\\drivers\\vmhgfs.sys"를 찾으면 VMware로 탐지한다.


3.9.5 Looking for a MAC address starting with 00:05:69, 00:0C:29, 00:1C:14 or 00:50:56

  MAC 주소가 다음의 주소들 중 하나로 시작하면 VMware로 탐지한다.

"00:05:69"

"00:0C:29"

"00:1C:14"

"00:50:56"


3.9.6 Looking for network adapter name

  네트워크 어댑터 이름이 "VMware"이면 VMware로 탐지한다.


3.9.7 Looking for pseudo devices

  다음의 pseudo 디바이스들 중 하나 이상을 찾으면 VMware로 탐지한다.

"\\\\.\\HGFS"

"\\\\.\\vmci"


3.9.8 Looking for VMware serial number

  "SELECT SerialNumber FROM Win32_Bios" 쿼리문으로 조회한 후 "SerialNumber""VMware"라면 VMware로 탐지한다.



3.10 Qemu detection

3.10.1 Scsi port->bus->target id->logical unit id-> 0 identifier

  레지스트리 키 "HKLM<\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical> Unit Id 0"에서 값이 "Identifier"이며 데이터가 "QEMU"인 것을 찾으면 QEMU로 탐지한다.


3.10.2 Reg key (HKLM\\HARDWARE\\Description\\System \"SystemBiosVersion\")

  레지스트리 키 "HKLM\\HARDWARE\\Description\\System"에서 값이 "SystemBiosVersion"이며 데이터가 "QEMU"인 것을 찾으면 QEMU로 탐지한다.


3.10.3 cpuid CPU brand string 'QEMU Virtual CPU'

  CPU Brand가 "QEMU Virtual CPU"라면 QEMU로 탐지한다.



3.11 Bochs detection

3.11.1 Reg key (HKLM\\HARDWARE\\Description\\System \"SystemBiosVersion\")

  레지스트리 키 "HKLM\\HARDWARE\\Description\\System"에서 값이 "SystemBiosVersion"이며 데이터가 "BOCHS"인 것을 찾으면 Bochs로 탐지한다.


3.11.2 cpuid AMD wrong value for processor name

  CPU Brand가 "AMD Athlon(tm) processor"라면 Bochs로 탐지한다.


3.11.3 cpuid Intel wrong value for processor name

  CPU Brand가 "              Intel(R) Pentium(R) 4 CPU        "라면 Bochs로 탐지한다.



3.12 Cuckoo detection

3.12.1 Looking in the TLS for the hooks information structure

  Cockoo 샌드박스에서 구현한 후킹 관련된 내용을 탐지하는 기법이다. 관련 내용을 완벽히 이해하지 못해서 자세한 글은 쓰지 못하지만 나중에 공부한 후에 추가하도록 하겠다.





4. 기타

  IDT(Interrupt Descriptor Table), LDT(Local Descriptor Table), GDT(Global Descriptor Table)을 이용한 방식은 대표적이지만 최신 버전의 VM에서는 통하지 않는 경향이 있다고 하여 정리를 하지 않았다. 이것과 관련된 어셈블리 명렁어는 각각 sidt, sldt, sgt 이다. 


  VMware와 관련해서는 x86의 STR 어셈블리 명령어도 사용되는데 이것은 Task Register로부터 Segment Selector를 저장하는 명령어로서 VMware에서는 첫 번째와 두 번째 비트가 각각 0x00과 0x40이라고 한다. 


  마지막으로 어셈블리 in 명령어도 있는데 VMware의 게스트 OS에서는 이 명령어를 사용할 시에 예외가 발생되지 않고 ebx에 "VMXh"라는 고유한 문자열이 저장되는 것을 이용한 방식이라고 한다. 참고로 이 명령어는 I/O 포트 (호스트 OS와 게스트 OS 간 데이터를 주고 받을 수 있는 채널)를 지원하기 위한 명령어라고 한다.

http://kali-km.tistory.com/search/anti%20virtual%20machine ]


  Parallels는 macOS의 가상화 프로그램이다. 이것은 "prl_cc.exe", "prl_tools.exe"라는 패러렐즈 전용 프로세스 이름을 검사하여 탐지할 수 있다. VirtualPC의 경우도 마찬가지로서 "VMSrvc.exe", "VMUSrvc.exe"를 검사한다. 마지막으로 Xen은 "xenservice.exe"를 검사한다.


  추가적으로 해야할 사항으로서 VMware에서 다음과 같은 기능을 제공하여 Anti-VM 기법에 대응하는 것 같은데 아직 제대로 찾아보고 직접 테스트해보지 않아서 이후에 정리하려고 한다.


isolation.tools.getPtrLocation.disable = "TRUE"

isolation.tools.setPtrLocation.disable = "TRUE"

isolation.tools.setVersion.disable = "TRUE"

isolation.tools.getVersion.disable = "TRUE"

monitor_control.disable_directexec = "TRUE"

monitor_control.disable_chksimd = "TRUE"

monitor_control.disable_ntreloc = "TRUE"

monitor_control.disable_selfmod = "TRUE"

monitor_control.disable_reloc = "TRUE"

monitor_control.disable_btinout = "TRUE"

monitor_control.disable_btmemspace = "TRUE"

monitor_control.disable_btpriv = "TRUE"

monitor_control.disable_btseg = "TRUE"


  마지막으로 지금까지 살펴본 내용이 아닌 여러 다른 방식들도 생겨나고 있다.

http://blog.trendmicro.com/trendlabs-security-intelligence/new-emotet-hijacks-windows-api-evades-sandbox-analysis/ ]

https://www.zscaler.com/blogs/research/malicious-documents-leveraging-new-anti-vm-anti-sandbox-techniques ]





5. 참고자료

i. FireEye

영어 ]  HOT KNIVES THROUGH BUTTER: Evading File-based Sandboxes -  Abhishek Singh and Zheng Bu [ https://www.fireeye.com/content/dam/fireeye-www/current-threats/pdfs/pf/file/fireeye-hot-knives-through-butter.pdf ]

한글 ]  파일 기반의 샌드박스를 쉽게 회피하는 악성코드 [ https://www.fireeye.kr/content/dam/fireeye-www/regional/ko_KR/current-threats/pdfs/fireeye-hot-knives-through-butter.pdf ]


ii. [ https://www.usenix.org/system/files/conference/woot16/woot16-paper-blackthorne_update.pdf ]


iii. [ https://wikileaks.org/ciav7p1/cms/files/BypassAVDynamics.pdf ]


iv. Detecting & Evading Automated Malware Analysis - Alexei Bulazel


v. Pafish [ https://github.com/a0rtega/pafish ]


Posted by SanseoLab


1. 정리

2. OLE

3. COM

4. WMI





1. 정리

  MSDN에 따르면 COM(Component Object Model)은 상호작용할 수 있는 바이너리 소프트웨어 컴포넌트를 생성하기 위한 플랫폼에 독립적이고 분산적이며 객체 지향적인 시스템이다. 개인적으로 COM 뿐만 아니라 .NET이나 WSH 그리고 WMI까지 모두 추상적인 개념들이라서 완벽히 이해는 못하더라도 간략한 메커니즘을 이해하려고 이렇게 정리한다. 물론 더 공부해 가면서 잘못 이해한 부분은 고치기도 하고 추가적인 내용을 덧붙이기도 할 것이다. 원래 파워셸이나 VBS, Powershell 등의 스크립트 언어를 이해하려다가 여기까지 왔지만 무턱대고 공부하기는 너무 힘들것 같아서 최대한 이해한 후 정리하려고 한다. 아마도 이해의 기준은 나일 것이므로 정리된 내용이나 이해한 부분도 내가 알고자 하는 것들 위주일 것이다.



  수 많은 설명들을 보면 대부분 어떤 의미에서든 COM을 어떻게 사용하는지에 대한 내용과 더욱 추상적인 개념들만 나와있다. 어쨌든 위의 개념들을 정리하기 위해서 이해한 내용은 COM은 메커니즘이라는 것이다. COM 객체가 생성된 경우 우리는 객체 내의 데이터를 참조하거나 조작할 수 있는데 이것은 COM 객체가 객체 지향형 인터페이스를 expose하기 때문이다. 즉 어디에선가 COM을 이용해서 인터페이스를 expose하면 COM을 사용할 수 있는 다른 곳에서도 마찬가지로 이 인터페이스를 이용해 데이터를 참조하거나 조작할 수 있을 것이다.



  기본적으로 윈도우 운영체제 자체도 많은 유용한 COM 객체들을 expose하며 IE나 MS Office도 자동화를 위해 많은 COM 인터페이스를 expose한다. 참고로 MS Office 등 어떤 애플리케이션들은 OLE Automation 객체를 제공하여 다른 애플리케이션이 이것과 커뮤니케이션할 수 있게 한다. exe 파일임에도 불구하고 export table이 있어서 OLE Automation을 위한 함수들을 내보내는 것이다. JS에서는 ActiveXObject() 객체를 사용하면 (참고로 IE만 지원한다) OLE Automation 객체의 인스턴스를 생성할 수 있다.



  이제 본론으로 넘어가서 VBS를 이용한 악성코드를 보면 대부분 WSH(Windows Script Host) 객체를 이용하는 것을 볼 수 있다. 예를들면 CreateObject 함수를 이용해 WScript.shell이라는 객체를 생성하고 특정 메소드를 사용하는 것이 대부분이다. WSH는 스크립트 기능을 제공해 주는데 이 기능은 COM 인터페이스들을 expose하는 객체 모델을 구현함으로써 가능해진다. VBS는 COM을 이용해서 이 인터페이스에 접근함으로써 WSH의 기능을 사용할 수 있다. 이렇듯 COM은 특정 기능을 사용할 수 있는 매개가 되는 방식으로서 사용된다.



  WSH 뿐만 아니라 WMI(Windows Management Instrumentation)도 마찬가지이다. 이것은 분산된 컴퓨터 시스템의 관리를 통합해 주는데 결론적으로는 통합된 인터페이스를 제공해주는 것이 목적이다. 이것도 관련 데이터 그리고 이것을 조작할 수 있는 메소드를 제공해 줄 것이다. WMI도 COM을 이용하며 이에 따라 C나 C++ 그리고 VBS 등에서도 WMI를 이용할 수 있다. 즉 COM은 WMI와 관리되는 윈도우 객체들 사이에서 매개 역할을 한다. 참고로 WMI 관련 내용은 Powershell과 함께 자주 나오는 편인데 이것은 .NET을 설명한 후에 같이 언급하기로 한다.



  .NET Framework는 윈도우 프로그램 개발 및 실행 환경으로서 간단히 말해서 여러 목적을 가지고 여러 기능이 구현되어 있는 플랫폼이다. 대표적으로 애플리케이션의 개발과 실행 시에 언어에 종속적이지 않은 플랫폼을 제공하기 위한 공통 언어 기반(CLI : Common Language Infrastructure)라는 개념이 있으며 이것은 표준이라고 할 수 있고 이 표준을 구현한 것이 공통 언어 런타임(CLR : Common Language Runtime)이다. 실질적으로 VM이라고 여겨지며 C# 등의 언어가 컴파일된 결과인 바이트코드를 실행시켜주는 역할을 한다. 이걸 설명이라고 할 수는 없겠지만 다음을 위해 적어보았다.



  파워셸은 .NET Framework를 기반으로 만들어진 객체 지향형 스크립트이다. cmdlet이라는 기본적인 명령어들은 내부적으로 .NET Framework Class Library를 이용해서 구현되었다. 중요한 점은 위에서도 언급하였듯이 WMI는 COM을 이용하여 인터페이스를 제공한다는 것이고 파워셸은 .NET Framework를 기반으로 만들어졌다는 점이다. 관련 내용을 찾아보니 .NET에서도 COM Inter-Op 메커니즘을 이용해 COM에 접근할 수 있다고 한다. COM을 직접 다루지는 않으므로 개발이 비교적 빠르지만 단점으로는 COM을 직접 사용하는 방식보다는 구조적으로 느릴 수 밖에 없다고 한다. 



  조금 더 설명하자면 RCW(Runtime Callable Wrapper), CCW(COM Callable Wrapper)라는 기능을 통해 COM과 .NET은 상대 기술로 제작된 오브젝트들을 사용할 수 있다고 한다. COM 클라이언트가 .NET 객체를 호출할 때 CLR은 객체와 이것을 위한 CCW를 생성한다. .NET 오브젝트를 직접 참조할 수 없기 때문에 COM 클라이언트는 CCW를 해당 객체를 위한 프록시로서 사용한다. 


  RCW는 COM 객체를 .NET 클래스에 expose하는데 사용됨으로써 .NET 코드가 COM 객체를 호출할 수 있게 한다. interop 어셈블리들은 원본 COM 타입에 대한 정보를 기술하는 .NET 메타데이터들을 포함하는데 CLR은 런타임 시에 이 정보를 이용해 RCW를 생성하고 .NET 응용 프로그램과 해당 COM 객체를 이어준다.



  지금까지의 설명은 COM에 대한 기본적인 설명부터 시작하여 COM을 매개로 하는 VBS 등의 스크립트와 WSH, WMI와의 관계 그리고 .NET Framework에 대한 기본적인 설명 및 파워셸 관련 내용이었다.





2. OLE

  추가적으로 COM의 역사와 관련하여 그리고 또 다른 중요한 내용인 OLE 그리고 ActiveX와 관련된 내용을 보겠다. 사실 이 부분은 앞의 부분보다 더더욱 헷갈리기도 하고 확실한 내용인지도 잘 모르겠다. 아무래도 이 문서는 자주 수정을 해야할 것이다.


  OLE(Object Linking and Embedding)는 문서와 다른 객체들 간에 삽입과 링킹을 지원해주는 기술이다. 예를들어 한 종류의 문서에서 다른 종류의 문서로 복붙이 가능해진다. OLE 1은 DDE(Dynamic Data Exchange)라는 기술을 기반으로 만들어졌다. DDE는 원본 데이터가 변경되면 애플리케이션에 통지하는 방식의 윈도우 메시지를 사용하여 구현되었다. 이후 DDE의 한계로 COM과 OLE 2를 설계하였고 COM을 기반으로 OLE 2를 만들게 된다. 어쨌든 앞에서 설명한 COM을 기반으로 만들어졌기 때문에 ole32.dll을 통해 OLE API 또는 COM API라고 불리는 여러 함수들을 expose하였고 다른 애플리케이션들이 이러한 기능에 접근할 수 있게 된다. COM API를 사용할 수 있게 되자 MS는 OLE Custom Control이라고 불리는 것을 개발하였는데 이것을 통해 OLE 객체들을 다룰 수 있게 되었다. 이후에는 이것을 확장하여 웹 브라우저에서도 사용할 수 있게 하였으며 이름을 ActiveX Control로 바꾸게 된다. [ ref : https://stackoverflow.com/questions/820614/com-ole-activex-idispatch-confusion ]


  ActiveX Control은 IE 브라우저의 Add-on으로서 COM과 OLE라는 기술을 이용해 플랫폼으로서의 역할을 하는 것으로 보인다. 예를들어 Adobe Flash Player가 ActiveX Control 기반 프로그램이다.





3. COM

  다음으로 COM과 관련한 간략한 내용과 악성코드에서 사용되는 방식에 대해서 다룬다.


  클라이언트는 CoCreateInstance()라는 COM API 함수를 이용해서 컴포넌트로부터 인스턴스 즉 객체를 생성한다. 이 함수는 매개변수로 컴포넌트 식별자와 Interface 식별자를 받는데 컴포넌트 식별자와 일치하는 컴포넌트를 찾아 객체를 생성하고 Interface 식별자와 일치하는 Interface를 찾고 이에 대한 포인터를 반환한다. 클라이언트는 이 Interface의 포인터를 이용해 원하는 COM 컴포넌트의 서비스를 받는다.


  이후 애플리케이션이 COM 서비스를 사용하기 위해서는 COM 라이브러리를 프로세스의 주소 공간에 로드시키고 이를 초기화해주어야 하는데 이때 CoInitialize()가 사용된다. 참고로 각 스레드별로 이것을 호출해서 COM 라이브러리를 초기화해주어야 한다.


  컴포넌트 식별자와 인터페이스 식별자는 GUID(Globally Unique Identifers)라는 같은 구조체를 사용하지만 각각 CLSID, IID로 구분하여 사용한다. CoCreateInstance() 호출 시 인자로 받은 CLSID와 일치하는 컴포넌트를 찾을 때는 레지스트리에서 찾는데 정확한 위치는 HKCR\CLSID\{CLSID들}이다. 추가 사항으로 IUnknown 인터페이스가 있는데 이것은 다른 모든 인터페이스들의 부모로서 모든 컴포넌트가 제공해야 하는 인터페이스이다.


  악성코드에서 사용되는 방식은 다음 링크를 참조한다. [ http://sanseolab.tistory.com/30 ]





4. WMI

  WMI와 관련해서는 다음 링크 [ https://msdn.microsoft.com/en-us/library/aa394553(v=vs.85).aspx ]에 매우 자세하게 나와있기 때문에 여기를 참조하면 될것으로 보인다.




'악성코드 분석' 카테고리의 다른 글

IDA Pro 시그니처 사용 및 제작 [ Flirt ]  (1) 2018.02.25
Anti-AV와 Anti-VM (Sandbox)  (1) 2017.12.30
exeinfo PE 사용법  (1) 2017.12.17
TEB 및 PEB를 활용하는 루틴  (1) 2017.12.04
EFLAGS 상태 레지스터  (0) 2017.11.22
Posted by SanseoLab

http://exeinfo.atwebpages.com/ ]


0. 개요

1. 화면

2. 버튼

3. 기타

4. Console Mode





0. 개요

  PE 분석기로는 PEiD를 쓰고 있었지만 업데이트도 없고 64비트 바이너리도 지원하지 않아서 새로운 PE 분석기를 찾는 도중 Exeinfo PE를 알게되었다. 원래 단순히 패킹 정보만을 원했었지만 수많은 기능들이 있는 것을 보고 사용하던 pestudio, PEView를 모두 삭제하고 이것만 쓰고 있을 정도로 괜찮은 프로그램인것 같다. 물론 gui가 조금 조잡한 면이 있지만 그래도 기능적으로는 어지간한 것들을 모두 지원하고 있는 것으로 보인다. 업데이트도 꾸준히 이루어지고 있으며 최신 버전은 2017년 6월 5일 버전 0.0.4.7이다. 참고로 단점은 .pdata 섹션 관련된 분석은 따로 지원하지 않는것 같다.

  원래는 스크린샷을 찍어서 하나하나 표시한 후에 올리고 싶었지만 기능이 너무 많아서 그러다 글도 못 쓰게될것 같아 간단하게 글로만 쓰려고 한다. 크게 화면과 버튼으로 나누었는데 화면이야 내용이 있으므로 구별하기 쉽겠지만 버튼은 아이콘 또는 화살표만 있는 버튼들이 대부분이므로 어느 버튼인지에 대한 설명도 추가하기로 하겠다. 마지막으로 버전은 0.0.4.7을 기준으로 한다.





1. 화면

1.1 Entry Point

  간단하게 EP 주소를 보여준다.



1.2 EP Section

  EP과 위치한 섹션 이름을 보여준다. EP Section 부분에 마우스 커서를 대고 클릭하면 입력창이 뜬다. 만약 EP가 0000125D인 경우 10을 입력하면 0040126D를 시작으로 하는 [ Disassembler ] 윈도우를 보여준다. 해당 윈도우는 뒤에서 다루겠다.



1.3 File Offset

  메모리 기준이 아니라 파일 오프셋 기준 EP의 주소.



1.4 First Bytes

  EP에 위치한 첫 명령어들의 값. EP에 위치한 명령어가 sub rsp, 28인 경우에는 48.83.EC.28 등으로 보여준다. First Bytes 부분에 마우스 커서를 대고 클릭하면 [ Disassembler ] 윈도우를 보여준다.

  [ Disassembler ] 윈도우에서는 각 섹션별로 디스어셈블리를 볼 수 있다. 오른쪽 버튼들을 보면 32비트 또는 64비트로 설정할 수 있으며 (기본적으로 해당 바이너리에 맞게 설정되므로 만질 필요는 없다) 디스어셈블러로 NASM, MASM, GoAsm을 선택할 수 있다. 참고로 모든 영역에 대한 디스어셈블리를 보여주지는 않고 EP 기준으로 보여준다. 대신 위의 섹션을 클릭하면 해당 섹션 시작 주소부터 보여주기도 하고, 특정 주소를 더블 클릭하면 해당 주소 기준으로 보여준다. 아니면 "1.2 EP Section"을 클릭하고 EP 기준으로 떨어진 위치를 입력하여 해당 주소 부분을 볼 수도 있다. 여러모로 디스어셈블러로는 그다지 의미는 없다.



1.5 Linker Info

  링커 버전. 패킹된 경우에도 보여주며 왼쪽과 같이 "6.0"이나 "14.1" 같이 나온다. 오른쪽과 비교하여 해당 VS에서 만들어졌다는 것을 확인할 수 있다.


MSVC++ 5.0   (Visual Studio 97)

MSVC++ 6.0   (Visual Studio 6.0)

MSVC++ 7.0   (Visual Studio .NET 2002)

MSVC++ 7.1   (Visual Studio .NET 2003)

MSVC++ 8.0   (Visual Studio 2005)

MSVC++ 9.0   (Visual Studio 2008)

MSVC++ 10.0  (Visual Studio 2010)

MSVC++ 11.0  (Visual Studio 2012)

MSVC++ 12.0  (Visual Studio 2013)

MSVC++ 14.0  (Visual Studio 2015)

MSVC++ 14.1  (Visual Studio 2017)



1.6 Subsystem

  콘솔 응용 프로그램의 경우 "Win Console", GUI 형태는 "Windows GUI"로 보여준다.



1.7 File Size

  파일의 크기. File Size 부분에 커서를 대보면 File Alignment와 Section Alignment를 볼 수 있다.



1.8 Overlay

View overlay if exist, Right button - save ovl and Reopen with exeinfo.

  Overlay 관련된 내용은 "3. * Overlay" 부분에 정리하였다. 마우스 왼쪽 버튼을 클릭하면 [ Viewer ] 윈도우를 통해 해당 부분의 데이터를 볼 수 있다. 참고로 Overlay 필드의 오른쪽은 16진수 값으로 크기를 의미하며 [ Viewer ] 윈도우에서는 10진수로 몇 바이트인지 크기를 볼 수도 있다. 마우스 오른쪽 버튼을 클릭한 경우 해당 Overlay data 부분만을 exeinfo PE로 여는 것을 보았을 때 악의적인 PE 바이너리를 뒷 부분으로 추가한 경우에 사용될 수 잇는 기능으로 보인다.



1.9 Image is 32bit executable  /  64 bit executable

  해당 애플리케이션이 32비트 인지 64비트인지에 따라 위의 문자열을 보여준다.



1.10 RES/OVL : xx / xx %

show in percent exe Resources and Overlay data.

  첫 번째는 [ Header info ] 창에서 데이터 디렉터리의 리소스 부분에 나오는 것과 같다. 즉 EXE에서 리소스 영역에 차지하는 퍼센트 즉 리소스 섹션의 비율을 의미한다. 두 번째 부분은 EXE에서 Overlay data가 차지하는 퍼센트를 의미한다.



1.11 날짜

  헤더에서 생성 날짜 필드를 참고하여 생성 년도를 보여준다. 대보면 아래에 정확한 날짜를 보여준다.



1.12 시그니처 영역

  시그니처를 통해 구분한 후 컴파일러 정보 또는 패킹되어 있을 시 패커 정보를 보여준다. 가장 많이 보는 부분이며 일반적으로 사용자들에게 이 프로그램의 목적이기도 하다.



1.13 Lamer Info - Help Hint - Unpack info

  아래에 언패커 정보를 보여준다. 링크 뿐만 아니라 오른쪽 "Go !" 버튼을 통해 관련 링크로 바로 이동할 수도 있다.



1.14 서명 정보

  오른쪽 부분에 메달 형태의 이미지이다. 서명되지 않을 경우에는 특별한 색깔을 띄지 않으며 마우스 커서를 대면 "Digital Certificate X509 - Not Found on this file"라고 보여준다. 사인이 존재하는 경우에는 빨간 색으로 변하고 마우스 커서를 대면 "This is Real X509 Certificate - not a fake, but I don't check - sha 1RSA" 같은 문자열을 보여준다.

  클릭하면 다음 정보도 보여준다.

- Digi Cert size from Header

- Cert Size from Certificate





2. 버튼

2.1 File

- Rename file

  파일 이름 변경

- Copy file as ... .bak

  확장자가 bak인 파일 즉 백업 파일을 생성해 준다.

- Copy - part of a Big file and Save to disk

  바이트 값을 입력해서 파일의 시작부터 그 크기 만큼의 부분을 저장한다.

- Execute EXE

  바이너리의 경우 실행시킨다.

- Execute DLL,ZIP,IMG,RAR ...

  기타 파일들을 실행한다. 예를들어 .txt 파일의 경우 메모장으로 띄워주는 방식이다.

- Delete file (Alt+Del) global key

  파일 삭제

- Windows File Properties

  마우스 오른쪽 버튼 클릭 후 속성 항목을 누르면 나오는 파일 속성 창을 띄워준다

- Kill Windows exe process (via TerminateProcess)

  프로세스 이름을 입력받고 해당 프로세스를 종료시킨다.

- Run script - Exeinfo PE format *.Eis

  Exeinfo PE 용 스크립트와 관련된 내용으로 보이며 인터넷에서 스크립트 엔진이 추가되었다는 내용까지는 찾았지만 그 이상의 정보는 볼 수 없었다.

- .Ext - check file extension via net : www.fileinfo.com

  해당 사이트에서 해당 파일의 확장자를 검색한다.

- Send hash to : virustotal - SHA256 check

  www.virustotal.com에서 해당 파일의 SHA256 값을 검색한다.

- F2 key - Run Multifile scanner - scan Directory

  Multifile scan 방식으로서 F2 키와 같다. 특정 디렉터리를 선택한 후 Start하면 그 디렉터리에 존재하는 실행 파일들을 한 번에 검사해 준다.

- R.M.Buton - Others detectors / unpackers MENU

  exeinfo PE 윈도우를 마우스 오른쪽으로 클릭한 것과 같다. 다음의 항목들이 뜬다. 개인적으로 설치한 것은 Trid.exe밖에 없어서 언급할 내용은 없다. Trid 설치는 해당 파일을 사이트에서 다운로드받은 후 이 항목을 클릭하고 이후 설치된 폴더를 선택하면 이후부터는 이 항목을 클릭함으로써 자동으로 Trid를 실행할 수 있다. Trid는 "3. * trid"에서 추가적으로 다루기로 한다.

-- CFF Explorer VIII - 2012 (.NET viewer)

-- Protection ID v.0.6.8.5 - 2016

-- FFI - File Format Identifier v.1.4 - 2008

-- Quick Unpack v2.3 - 2010

-- InnoExtractor v5.2.x - 2016

-- Universal Extractor v1.6.1 - 1.x - 2015 (mod.vers.)

-- Trid.exe - Non exe scan - binary detector

-- Media Info - audio video - detector

-- Don't close ExeinfoPE

- Global log file menu

- Create and Save Screenshot to JPG/PNG file

- Convert Hex Value to Decimal and Dec 2 Hex

  클릭하면 아래에 각각 10진수와 16진수를 변환해주는 계산기가 나온다.

- Detect installed .NET Framework versions

  컴퓨터에 설치된 .NET Framework 버전들을 보여준다.

- External signatures DB view (if file userdb.txt loaded)

  일반적으로 userdb.txt 파일이 exeinfo PE가 위치하는 디렉터리에 존재하며 로드된다. 그래서 이것을 클릭하면 그 내용들을 볼 수 있다. 텍스트 파일이므로 메모장으로 봐도 된다.



2.2 <  [ if > 0 then return back Oryginal EPoint and Re-diagnose ExE ]

  "1.1 Entry Point" 오른쪽에 위치한 아이콘이다. 파악하지 못했다.



2.3 <  [ view file at offset : 0000 ]

  "1.7 File Size" 오른쪽에 위치한 아이콘이다. 디스어셈블러 창에서 전체 데이터와 아스키 값들을 보여준다.



2.4 N  [ LMB - view .NET structure, RMB - .NET Detectors Menu ]

  "2.3 <" 오른쪽에 위치한 아이콘이다. .net 바이너리의 경우에만 적용된다. 마우스 왼쪽을 클릭하면 [ .NET Directory ] 윈도우가 떠서 .net 관련 정보들을 보여준다. PE Header - COM Descriptor Area, Meta Data Header, Meta Data Streams 등의 정보가 있다.

  마우스 오른쪽 클릭 시 다음 항목들을 볼 수 있으며 설치되어 있다면 실행할 수 있을 것이다. 개인적으로는 설치된 것이 없어서 확인은 해보지 못했다.

- DNiD v1.0 Coded by Rue - 2011.07.08

- RedGate.ObfuscationChecker - Trial Free only *

- Improve .NET - Deobfuscator v1.0.9 - 2013.12.28

- de4dot NET unpacker - deobfuscator v3.1 - 2015



2.5 HEX data search

  여기서부터는 오른쪽에서 두 번째 줄에 위치한 버튼들이다. 돋보기 모양의 아이콘이며 클릭 시 입력 창을 통해 검색할 Hex 데이터를 입력받으며 이후 결과는 [ Viewer ] 윈도우를 통해 매칭되는 HEX 데이터를 보여준다.



2.6 Section Viewer

  [ Section Viewer ] 윈도우가 나와서 각 섹션 정보를 보여준다. 주소와 크기, 플래그, 첫 바이트 값들 뿐만 아니라 왼쪽 버튼들을 보면 간단하게 전체 테이블을 클립보드로 복사하는 기능도 있다. 물론 각 라인을 마우스 왼쪽 버튼으로 클릭하면 그 라인에 한하여 자동으로 클립보드로 복사된다. 스크린샷 기능도 있는데 클릭하면 현재 바이너리가 있는 폴더에 스크린샷이 생성된다.

  오른쪽의 Cave 버튼은 각 섹션의 빈 공간에 대한 정보를 보여주며 S-Stat 버튼은 각 섹션에서 0x00 값을 갖는 데이터의 비율을 보여준다. Zero 바이트들의 비율을 가지고 각 섹션들이 패킹되었는지에 대한 추정도 보여준다.



2.7 Version info

  해당 바이너리에 버전 정보가 있다면 보여준다.



2.8 PE Header & Image Data entries

  PE라고 써진 아이콘이다. [ Header info ] 윈도우가 나와서 헤더 정보들을 보여준다. 여러 정보들부터 Data Directory 관련 정보들, 플래그들도 보여준다. 추가적으로 Import Table이나 Export Table이 존재한다면 오른쪽 버튼을 클릭하여 임포트하는 DLL 및 함수들 또는 익스포트하는 함수들에 대한 정보도 자세히 볼 수 있다. 클립보드 복사와 스크린샷 기능도 있다.



2.9 Scan / t

  userdb.txt를 이용하여 스캔한다. 결과는 왼쪽 창에서 볼 수 있다. 마우스 오른쪽 클릭 시 Trid.exe를 실행시킨다.



2.10 Go! - www sites

  파란색 구형의 형태를 띈 아이콘이다. 왼쪽에서 언패킹 관련 정보를 보여준다고 했는데 이 버튼을 클릭해 보면 언패킹과 관련된 링크들이 나오며 클릭하여 해당 웹사이트로 갈 수 있다.



2.11 click to reload this file

  순환하는 파란색 화살표 두 개가 보이는 아이콘이다. 바이너리를 재로드한다. Scan / t 버튼을 클릭하거나 했을때 원래 정보를 보기 위해 다시 복구할 때 같은 상황에서 사용할 수 있다.



2.12 Open file

  여기서부터는 가장 오른쪽에 위치한 버튼들이다. 아이콘은 폴더 모양을 하고 있다. 바이너리를 불러온다.



2.13 Stat - zero byte test

  아이콘은 막대 그래프 모양을 하고 있다. 클릭 시 [ Stat ] 윈도우를 통해 Byte Analyser에 따른 그래프를 보여준다. zero byte test라는 것을 잘 모르지만 결과적으로 Themida로 패킹된 바이너리에 대하여 Diagnose 항목을 보면 "Strong Packed (Test with Overlay)"라고 나오며 Yoda's protector의 경우 "Crypted", 패킹하지 않은 경우 "Very not packed"인 것을 보면 관련 검사인 것으로 보인다. ZERO value test의 퍼센트도 같이 보면 Themida 같이 더 강한 패커의 경우에 퍼센트 값이 훨씬 낮은 것을 볼 수 있다. Zero value test에 대해서는 "3. 기타 * Zero value test" 항목을 참고하자.

  이 외에도 CRC32, MD5, SHA1, SHA256에 해당하는 값도 보여준다.



2.14 Plug - Run plugin (compatible with Peid)

  딱히 쓸만한 것이 보이지 않는다.



2.15 About

  집 모양의 아이콘이다. 버전 및 시그니처 등과 관련된 기본 정보



2.16 Config - options, select skin, ...

- Fast Scan

  마우스 커서를 대보면 "unchecked - enable floating Entry Point (Deep area - 30 bytes)"라는 설명이 뜬다. 정확한 의미는 모르겠지만 그다지 느린 일이 없으므로 굳이 설정할 필요는 없을 것 같다.

- Ignore EXE errors

  PE 헤더나 I/O 에러 무시.

- Always on Top

- Shell integration

  설정 시 파일들을 마우스 오른쪽으로 클릭하면 Exeinfo PE로 바로 검사할 수 있게 항목이 생성된다.

- Close Exeinfo when running ext. detectors

  다른 Detector나 Tool 사용 시 종료.

- Big GUI-interface

- Select skin

- Log file

  ExeinfoPe 등의 폴더에 로그를 기록한다. 로그 파일 이름은 Exeinfo.log이다.

- Language



2.17 OFF - or Press ESC key

  빨간색 X 모양의 아이콘이다. 종료.



2.18 Rip - Ripper Menu

  기본적으로 파일 내부의 존재하는 리소스를 추출하는 기능으로 보인다. 마지막 항목 "I'm hungry for Ripping (All in One)"은 한 번에 추출할 수 있는 모든 것을 추출해 주므로 이것만 클릭하면 될 것 같다. 예를들어 아이콘이 있는 바이너리라면 아이콘이 있을 것이며 그 외에도 내부에 존재하는 또 다른 PE 파일 같은 것들도 추출해주는 기능이 있으므로 유용할 것으로 보인다.

- Ripper (search EXE PE inside EXE + base64 enncoded)

- Scan only (for EXE PE)

- MSI / MSI xor FF generic (msi/doc/xls/pps/adp/wps) - OLE2

- GFX pictures - RIP Submenu

-- PNG

-- JPG

-- BMP

-- GIF

-- --> All in one

- BIN archives - RIP Submenu

-- Ripper (search ZIP archives)

-- Ripper (search Rar archives)

-- Ripper (search 7zip archives) + xor FF

-- Ripper (search MSCF CAB archives)

-- Ripper (search Bzip2 archives)

-- Ripper (search Gzip archives)

-- Ripper Lzma Archive (5D 00 00)

-- *** Ripper Zlib Archives (78 xx xx)

-- ---> All in one

- Ripper (search www/http:/ftp - address inside)

- Ripper (search SWF flash animations - max v39.0! - zlib/lzma)

- Ripper (search hidden NSTD icon data)

- Ripper (search for .xml ver.1.0 script files)

- UNIVERSAL Ripper - At Entered String "???" to EOF

- I'm hungry for Ripping (All in One)



2.19 >> - Tools Menu

- External exe Run -> (from exeinfopeRUN.cfg) max 30 items 

  여러 바로가기들을 설정할 수 있다.

- Overlay Menu

-- Save Overlay as *.ovl (if exist)

  Overlay 데이터 부분만 떼어내서 저장해 준다.

-- Trunk overlay (generate new file)

  Overlay 데이터 부분만 떼어낸 실행 파일을 만들어 준다.

-- Add empty overlay data

  입력으로 크기를 받으며 0x00으로 채워진 빈 overlay data를 추가한다.

-- Add overlay from file

  overlay data가 파일로 존재하는 경우 실행 파일 뒷 부분에 이 데이터를 추가한다.

-- Overlay uncrypter XOR (if exe in ovl)

  overlay에 XOR로 암호화된 PE가 있는 경우에 복호화하는 것으로 보인다. 하지만 사용해보지 못해서 확실히는 알 수 없다.

- Entry Point - OEP Corrector menu

  아래의 부분들은 제대로 파악하지 못했다.

-- EP corrector (Delphi/C++6) multi files generator

  델파이로 만든 바이너리를 가지고 이것을 실행하자 EP를 변경한 바이너리를 20개 단위로 만들어 주었다. 제대로 된 EP를 찾는 것 같은데 그 기준은 잘 모르겠다.

-- EP corrector for Delphi v.5.0 - Runtime method

-- EP corrector for Dev C++ / MINGW32 / GCC 32 Compiler

- XoR permutator - generate *.xer file (xor, or, shl, shr, and) 

  아직 숙지하지 못했다.

- Section stripper - save sections to files (*.sec 1, 2, 3 ...) 

  각 섹션들을 파일로 저장한다.

- Registry Call scanner "SOFTWARE, HKLM, HKCU" + CLSID / RSD / TAGG 

  레지스트리 관련 문자열들을 찾아준다.

- DFM - Delphi Form Scanner (view as Text) 

  Delphi Form이 존재하는지 스캐닝한다. [ Delphi Forms File Scanner ] 윈도우에 찾은 결과를 파일 오프셋 기준으로 보여준다. Delphi 관련 내용은 알지 못하여 이 이상은 파악하지 않았다.

- (DATA) - Zlib unpacker v.1.2.8 - www.base2ti.com 

  Zlib으로 패킹된 경우 언패킹해주는 기능으로 보인다. 패킹된 바이너리를 가지고 있지 않아서 확인해보지는 않았다.

- Search Compiler Runtime Engine - Use only for not packed files

  여러 컴파일러 런타임 엔진들 중에서 실행 파일이 사용하는 것이 해당하는지 비교해서 보여준다.

- eXtra Tools

-- AT4RE v1.0 protector - unprotector Tool

  설치를 해야 하는 건지 모르겠지만 별다른 메시지가 없어서 확인하지 않았다.

-- Signatures counter for (userdb.txt)

  userdb.txt 파일에 존재하는 시그니처 개수 세기

-- Add Visual basic fake sign for UPX packer

  UPX 패커의 경우 빈 공간에 push와 call 문을 추가한 후 EP를 여기로 설정한다. call 문은 원래 EP를 가리킨다. 가짜 VB 용의 시그니처를 만드는 방식으로 보이는데 원래 VB 바이너리를 분석해본 경험이 없어서 확신할 수는 없다. 왜냐하면 Exeinfo PE 자체도 새로 만들어진 fake VB 파일을 분석하고 "Unknown Packer-Protector, 3 sections like UPX S-Structure UPX!" 라는 결과를 보여주기 때문이다.

-- set EP to 1000 like PEcompact, Asprotect and scan again

  Entry Point를 1000으로 설정하고 다시 스캔한다. 공교롭게 두 프로텍터 모두 분석해보지 않아서 무슨 의미인지 이해하지 못했으며 분석해본 후에 내용을 추가하려고 한다.

-- anti Flashback scrambler - inc EP for UPX - and scan again

-- Save modified Buffer to file

  위의 두가지를 같이 보겠다. 첫 번째는 뒤에서 설명하겠지만 바이너리를 수정해 준다. 하지만 저장까지는 하지 않는데 두 번째를 통해 수정한 것을 저장할 수 있다. 두 번째는 그냥 저장만 하는 역할을 하므로 첫 번째 위주로 설명을 하겠다.

  먼저 옵션에서 Fast Scan을 활성화해야 한다. 그리고 하는 행위는 두 가지인데 하나는 링커 정보 즉 링커 버전을 6.0으로 변경하는 것이다. 그리고 처음 바이트 60 어셈블리어로는 PUSHAD를 67로 변경한다. 올리디버거로 확인해보면 PUSHAD 부분은 실행되지 않는다는 것을 알 수 있다. 하지만 알고리즘을 이용한 것인지 실행 자체는 원래와 같이 잘 이루어진다. 디코딩 알고리즘을 직접 분석해봐야 자세한 내용을 알 수 있을것 같다.





3. 기타

* Overlay (Appended Data)

  PE 파일의 경우 바이너리 뒤에 추가적인 데이터를 덧붙일 수 있다고 한다. 메모리에 올라온 다던지 실행 파일에 특별한 영향을 주는 것은 아닌것 같으며 추가적인 정보나 그다지 의미없는 정보가 들어갈 수 있다고 한다. exeinfo PE는 이러한 데이터를 인식할 수 있으며 여기와 관련된 기능을 제공해주는 것으로 보인다. 

  참고로 여러 실행 파일들을 비교해본 결과 Overlay 데이터들은 일반적으로 디지털 서명과 관련된 것 같다. 마우스 오른쪽 버튼 클릭 후 속성을 보았을 때 디지털 서명 부분을 볼 수 있으며 PEview로는 마지막에 CERTIFICATE table이라는 부분이 보인다. 이것과 관련된 exeinfo PE의 여러 기능들을 보면 악성코드의 경우 이 영역에 악성 바이너리를 추가한다던지 인코딩해서 저장하는 방식이 사용되기도 하는 것 같다. 참고로 디지털 서명 정보가 이러한 방식으로 추가되는 경우가 대부분인지 항상인지는 모르겠지만 어지간한 경우에 Overlay 데이터가 존재하는 경우 "1.14 서명 정보"가 같이 존재하는 경향이 있는 것 같다.

[ ref : https://www.autoitscript.com/forum/topic/153277-pe-file-overlay-extraction/ ]



* Trid

  파일의 종류를 추측해 준다. 예를들어 exe 파일을 입력 받으면 다음과 같은 결과를 볼 수 있다. [ ref : http://mark0.net/soft-trid-e.html ]

--

42.2% (.EXE) Win32 Executable MS Visual C++ (generic) (31206/45/13)

37.3% (.EXE) Win64 Executable (generic) (27625/18/4)

8.8% (.DLL) Win32 Dynamic Link Library (generic) (6578/25/2)

6.0% (.EXE) Win32 Executable (generic) (4508/7/1)

2.7% (.EXE) Generic Win/DOS Executable (2002/3)

--

  사실 DLL을 분석해 봐도 15%만 dll로 보고 나머지는 exe로 여기는 것을 보면 그다지 성능이 좋아 보이지는 않는것 같기도 하지만 jpg, wav 등 대부분의 파일 형태들은 거의 확실히 구별해 주므로 악성코드 분석 시에 가끔씩은 쓸모있어 보이기도 한다.



* Zero value text

  엔트로피 관련된 것인지 말 그대로 exe에서 zero value가 얼마나 차지하는지를 나타내는 것인지는 모르겠지만 이것을 기준으로 패킹되었는지 여부를 추측하는 검사로 보인다. 엔트로피 관련된 테스트에 대해서 공부한 적이 없어서 아직까지는 쓸 내용이 없다.





4. Console Mode

  자동화를 위해서 콘솔 모드 즉 CLI 환경을 지원하느냐는 가장 중요한 부분으로 보인다. 다행히 exeinfo PE는 콘솔 모드를 지원한다. CMD 창에서 다음 명령을 입력하면 간단한 설명을 볼 수 있다.


> exeinfope.exe /?


  옵션은 /s 보다 /sx가 더 많은 것을 지원하는 것으로 보이므로 이것을 사용하여 aaa.exe를 인식하기로 하겠다.


C:\Users\monto\Documents\Reversing\ExeinfoPe_v0047_III\exeinfope.exe C:\Users\monto\Desktop\upx.exe* /sx /log:"C:\Users\monto\Desktop\log.txt"


  upx.exe 다음에는 *를 꼭 붙여주어야 한다. 이후 다음과 같은 로그 파일을 얻을 수 있다.


upx.exe             - UPX 0.89 - 3.xx -> Markus & Laszlo ver. [ 3.91 ] <- from file. ( sign like UPX packer ) 




'악성코드 분석' 카테고리의 다른 글

Anti-AV와 Anti-VM (Sandbox)  (1) 2017.12.30
COM, OLE, .NET Frame work 등의 개념 및 사용  (0) 2017.12.18
TEB 및 PEB를 활용하는 루틴  (1) 2017.12.04
EFLAGS 상태 레지스터  (0) 2017.11.22
USB 악성코드 분석  (0) 2017.11.09
Posted by SanseoLab
이전버튼 1 2 3 4 5 6 이전버튼

블로그 이미지
Malware Analyst
SanseoLab

태그목록

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.4
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

최근에 올라온 글

최근에 달린 댓글

글 보관함