
BlackLotus-Z2A-Challenge, Nothing to see here 4 now , please move along
BlackLotus-Z2A-Challenge, 지금은 여기 볼 것 없음, 그냥 지나가 주세요
먼저 가장 중요한 것부터

순전히 미관상의 목적을 위해서만, 저는 @darthmaulware(Ryan “DM” Smith) 의 솔루션에서 yara 탐지 규칙을 부끄럽게도 빌려(훔쳐)올 것입니다 :)) 그냥 전문적으로 보이게 하기 위해서요. 가서 그의 트위터를 확인해 보세요, 그는 진짜 멋진 사람입니다! 추신. Ryan, 제가 그걸 훔친 것 때문에 화내지 말아 주세요 :))) 그리고 디스코드에서 지원해 줘서 고마워요 :)
그의 작업도 확인해 보세요 :)
https://gitlab.com/malre-rcs/zero2automated/-/blob/main/solutions/bi_weekly_challenge/BlackLotus_20230321/BlackLotus_HTTP_Downloader.ipynb``` rule Blacklotus_HTTP_Downloader { meta: description = "Rule to detect Blacklotus HTTP Downloader" author = "Darth Maulware" sha256 = "d68f668b4240f9518e4f80499d93d8c5a1eddece0771658c33ae916cc54f5a66"
strings:
$opcode1 = {48 89 4C 24 08 48 89 54 24 10 4C}
$opcode2 = {89 44 24 18 4C 89 4C 24 20 48 83}
$opcode3 = {EC 28 B9 31 62 D7 2E 90 90 E8 ??}
$opcode4 = {?? ?? ?? 48 83 C4 28 48 8B 4C 24}
$opcode5 = {08 48 8B 54 24 10 4C 8B 44 24 18}
$opcode6 = {4C 8B 4C 24 20 4C 8B D1 90 90}
condition:
(uint16(0) == 0x5a4d and filesize < 500KB and all of them)
}
또한 미적인 이유로 이것도 훔치겠습니다. 멋져 보이니까요 :) 다시 한번 미안해요 ryan, 화내지 말아 주세요 :)```
C:\\Users\\REM\\Desktop>capa.exe -f pe -r capa-rules-5.0.0 d68f668b4240f9518e4f80499d93d8c5a1eddece0771658c33ae916cc54f5a66.exe
matching: 100%|████████| 124/124 [00:02<00:00, 46.96 functions/s, skipped 1 library functions (0%)]
+------------------------+------------------------------------------------------------------------------------+
| ATT&CK Tactic | ATT&CK Technique |
|------------------------+------------------------------------------------------------------------------------|
| DEFENSE EVASION | Obfuscated Files or Information T1027 |
| DISCOVERY | Process Discovery T1057 |
| EXECUTION | Shared Modules T1129 |
+------------------------+------------------------------------------------------------------------------------+
+-----------------------------+-------------------------------------------------------------------------------+
| MBC Objective | MBC Behavior |
|-----------------------------+-------------------------------------------------------------------------------|
| ANTI-BEHAVIORAL ANALYSIS | Debugger Detection::Process Environment Block BeingDebugged [B0001.035] |
| | Debugger Detection::Process Environment Block NtGlobalFlag [B0001.036] |
| CRYPTOGRAPHY | Encrypt Data::RC4 [C0027.009] |
| | Generate Pseudo-random Sequence::RC4 PRGA [C0021.004] |
| DATA | Encode Data::XOR [C0026.002] |
| DEFENSE EVASION | Obfuscated Files or Information::Encoding-Standard Algorithm [E1027.m02] |
+-----------------------------+-------------------------------------------------------------------------------+
+------------------------------------------------------+------------------------------------------------------+
| CAPABILITY | NAMESPACE |
|------------------------------------------------------+------------------------------------------------------|
| execute syscall instruction (35 matches) | anti-analysis |
| check for PEB BeingDebugged flag (2 matches) | anti-analysis/anti-debugging/debugger-detection |
| check for PEB NtGlobalFlag flag | anti-analysis/anti-debugging/debugger-detection |
| encode data using XOR (2 matches) | data-manipulation/encoding/xor |
| encrypt data using RC4 PRGA (2 matches) | data-manipulation/encryption/rc4 |
| get process heap flags | host-interaction/process |
| get ntdll base address (3 matches) | linking/runtime-linking |
| parse PE header (2 matches) | load-code/pe |
| resolve function by parsing PE exports (2 matches) | load-code/pe |
+------------------------------------------------------+------------------------------------------------------+
솔직히 말해서 내가 너라면 capa를 100% 믿지 않을 거야(적어도 이 특정 케이스에서는). 왜냐하면 여기서는 샘플이 rc4를 사용한다고 나오지만 실제로 샘플은 aes를 사용하거든. 어쨌든, 언급했듯이 이건 순전히 미관상(장식용)일 뿐이야 :)
시작하기 전에, 이 리포트에서 너는 이런 걸 많이 마주치게 될 거야

ida가 이걸 제대로 디스어셈블하지 못하니 무시해줘. 어셈블리에서 이건 이렇게 보여

그리고 이게 디버거를 크래시시키지? 왜?
왜냐하면 주소 0에 쓰기를 시도하기 때문이야. 해커 세계에서는 null 포인터 주소에 쓰는 것으로 알려져 있고, CE(코드 실행)를 얻기 위해 널리 악용되던 기법이었지. 그리고 이건 당연히 완화(mitigation)되어 있어.
그래서 현재 완화 조치 덕분에 0에 쓰기를 시도하면 그 프로세스, 즉 우리 경우엔 멀웨어가 크래시되고, 결과적으로 디버거도 크래시되는 거야.
이제 이걸 ida로 열어보면

나는 모든 함수가 수행하는 로직을 알 수 있도록 이름을 바꿔놨어. 첫 번째 함수인 do_syscall()부터 시작해보자.

우리는 즉시 syscall 사용을 발견할 수 있어. 동적 분석을 할 때 분석가의 삶을 힘들게 만드는 잘 알려진 방법이지.
Windows에서 syscall에 익숙하지 않은 사람들을 위해 oalabs가 만든 멋진 영상이 있어(https://www.youtube.com/watch?v=Uba3SQH2jNE). 꼭 봐줘. 나도 봤고 이 함수에서 무슨 일이 일어나는지 이해하는 데 많은 도움이 됐거든.
ida가 분석한 "의사 코드(pseudo-code)"를 살펴보면 이렇게 생겼어

solve_hash를 정적으로 분석하면 이렇게 보여


의사 코드 관점에서는 이렇게 보여

자동화 코드 조각을 보고 싶거나 syscall이 해싱 룩업 알고리즘으로 어떻게 해석되는지 알고 싶다면 solve_hash.py를 참조해서 내 "에뮬레이션"을 확인해봐. 어쨌든 이건 SysWhispers2라는 프로젝트의 일부야. 적어도 멀웨어 제작자가 영감을 얻은 게 그 프로젝트라고 추정해. 어쨌든 더 나은 이해를 위해 위의 oalabs 영상을 참조해줘.
어쨌든 anti_debug 함수는 쉽게 우회할 수 있고 잘 알려져 있어(https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-ntglobalflag). 우회하는 방법은 scyllahide를 설치하고 NtGlobalFlag를 체크하는 거야(x86dbg를 사용한다면 기본적으로 켜져 있어야 해). 어쨌든 이게 체크해야 할 항목이야

그리고 이게 anti-debug "의사 함수"의 모습이야

내가 보기엔 꽤 간단해
다음은 check_inmemory_ldr 함수야. 이렇게 생겼어


이제 함수 분석을 위해 x86dbg에게 친절하게 요청해보면, syscall 명령까지 실행하면 x86dbg가 실행하려는 syscall이 무엇인지 친절하게 알려줘. 우리 경우에는

현재 컨텍스트를 기준으로 Ntsetinformationthread가 일종의 안티 분석 트릭으로 사용될 거라고 추정할 수 있어. 실제로 구글에서 빠르게 검색해보면 이걸 찾을 수 있어. https://ntquery.wordpress.com/tag/ntqueryinformationthread/
다행히도 nop+ret 패치만 하면 쉽게 우회 가능해 :)
=============================================================================
실행 순서를 따라가다 보면 다음 함수는 이거야

다시 뭘 하는지 살펴보면

간단히 TEB의 플래그를 확인해서 현재 프로세스(우리 경우엔 exe)가 디버깅 중인지 확인해. 이것도 알려진 방법이라 역시 쉽게 우회 가능해(https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-peb-beingdebugged-flag). scyllahide를 사용했던 것과 같은 방식으로, 이번에는 이걸 체크해야 해

기본적으로 켜져 있어야 해
=============================================================================
이제 custom_hash2_and_aplib_possible 함수야. 이렇게 생겼어

그래프 관점에서는

이 함수의 "에뮬레이션"을 보려면 decompress_aplib.py를 참조해줘.
get_ntdll_and_unhook2를 살펴보면 이렇게 생겼어


그래프 뷰에서는 이렇게 보여

나쁘지 않네 :))
조금 거슬러 올라가면 aplib_decompress가 있어. 지금은 이렇게 생겼어

정적 코드 분석에서는 이렇게 보여




음, 좀 크긴 한데 걱정할 거 없어, 여러분. 충분히 할 수 있어 :)
aplib_decompress.py 에뮬레이션 스크립트를 사용하면 이런 결과를 얻게 될 거야

다른 블로그/분석에서는 보지 못했던 멋진 점이 하나 있어. ntdll_and_unhook2의 ida 의사 코드를 다시 살펴보면

흥미로운 memcpy를 볼 수 있어
동적 분석을 하던 중 한 지점에서 다른 dll이 로드되는 걸 발견했어. 그것은 ntdll의 다른 버전이었지

디버거에서 보면 이렇게 생겼어

무엇이 훅/패치됐는지 감지하지 못했어. 하지만 아는 사람이 있다면 풀 리퀘스트를 보내서 이 문서를 편집해줘.
또한 무엇이 훅되는지 찾기 위해 두 ntdll 사이에 bindiff를 시도했지만 아쉽게도 아무것도 찾지 못했어. 하지만 그건 이미 감염된 시스템 dll에 대해 diff를 수행하고 있었기 때문일 수 있어. 깨끗한 실행이 아니었으니 아마 그게 이유일 거야 ¯_(ツ)_/¯

=============================================================================
분석 과정을 더 진행하면, 아직 설명되지 않은 함수들로 some_hasing과 ntquertyinformationprocess_anti_debug가 있어. 이 둘은 아직 설명되지 않았지. check_if_being_debug_through_teb와 anti_debug는 다행히 이미 설명했어. 왜냐하면 그것들은 위 함수/함수들에서 사용되었거든. 그러니 그것들에 대한 지식을 복습하고 싶다면 위 섹션들을 읽어줘. 먼저 ntquertyinformationprocess_anti_debug부터 시작하고, 마지막에 some_hasing으로 마무리할게.
그것을 살펴보면 같은 함수가 3번 호출되는 것을 볼 수 있어.

어셈블리 관점에서는


편의를 위해 나는 이미 이름을 붙여놨는데, 그것은 ntquertyinformationprocess_ProcessDebugPort야. 호출된 함수들이 ntquertyinformationprocess_ProcessDebugPort라는 걸 어떻게 알았냐고? 그것들을 살펴보면 이미 본 함수 호출/우리에게 알려진 알고리즘이 드러나거든

ida에 달린 주석은 어떻냐고? 음, 구글에서 ntqueryinformationprocess를 검색하면 안티 디버깅에 대한 좋은 자료를 찾을 수 있어(https://anti-debug.checkpoint.com/techniques/debug-flags.html). 계속 따라가 보면, 이 함수에 매개변수로 전달되는 특정 값들에 따라 안티 디버깅 방법으로 사용될 수 있다고 설명하는 걸 볼 수 있어.
예를 들어 첫 번째 호출에서 디버거에 다음과 같은 스택 인자가 보여

이제 msdn 페이지에서 ntqueryinformationprocess를 확인해보면

같은 과정이 다음 두 syscall에서도 반복돼

여기서 0x1e는 ProcessDebugObjectHandle 안티 디버그 방법에 해당해(https://www.apriorit.com/dev-blog/367-anti-reverse-engineering-protection-techniques-to-use-before-releasing-software)
그리고 마지막으로 ProcessDebugFlags

그래서 어떻게 우회할까?! 진정해, 친구. ScyllaHide가 우리를 지켜주거든

보시다시피

그래서 우리는 안전해! 아냐, 완전히는 아니야. ScyllaHide가 처음 2개 syscall에서는 우리를 지켜주지만, 마지막 syscall은 수동으로 해야 해! 그리고 나는 대체 뭘 해야 하지???
음, 간단한 해결책이 있어! 이 함수, 즉 ntquertyinformationprocess_anti_debug 전체에서 반환하고 eax를 0으로 설정하면 돼. 그래서 정상적인 상황에서는 이렇게 보여

그리고 우리의 "도움"을 받으면 이렇게 보이고, 실행을 안전하게 진행할 수 있어 :)

=============================================================================
이제 some_hasing이야. 이미 절차는 알지?

그리고 이제 의사 코드

여기서 이상한 점을 발견하게 돼. Ida의 의사 코드는 여기서 실패해... 왜냐하면 call_syscall 이후 그래프를 따라가면 디스어셈블해야 할 명령어가 더 있거든. 그래서 이제 여기서 뭘 하냐고? 음, 여기서는 디버거에 의존해서 이 코드를 동적으로 분석할 거야...
그래서 그게 수행하는 syscall을 보면

이제 ntquerydefaultlocale을 찾아보면, 구글은 이게 2개의 인자를 받는 문서화되지 않은 api라고 알려줘(http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FLocale%2FNtQueryDefaultLocale.html). 좋아, 그럼 뭘 하는 거지? 현재 Locale Identifier를 반환해. 좋아, 그럼 Locale Identifier가 대체 뭐지? msdn에 따르면(https://learn.microsoft.com/en-us/windows/win32/intl/locale-identifiers) 언어 식별자와 정렬 순서 식별자로 구성된 32비트 값이야. tl;dr로 말하면 그 PC에서 어떤 언어를 사용하는지 알려주는 거지 :)
그런 다음 api가 실행에 실패하지 않았는지 확인하고, 실패하지 않았다면 ntquerydefaultlocale이 반환한 값을 가져와서 0x419를 뺀 다음 0x26(아마 상수)과 비교해. 아래 그림에서 볼 수 있듯이.

0x26보다 작거나 같지 않으면 0x818과 비교하고, 그렇지 않으면 0x819와 같은 비교를 수행해. 아래에서 명확히 볼 수 있지

그래서 여기서 대체 무슨 일이 일어나는 거지? 그리고 왜 이런 특정 상수들일까? 음, 바로 본론으로 갈게. 여러 상수들을 검색하던 중 이 글(https://www.cnblogs.com/DirWang/p/17281690.html#autoid-8-0-0)을 발견했어. 나보다 blacklotus를 더 잘 분석한 연구자가 쓴 글이야. 그리고 누군가가 한때 말했지: "멀웨어 분석에서 속임수를 쓸 수는 없고, 그저 작업을 더 쉽게 만들 수 있을 뿐이다". 그 연구자가 말한 것은 기본적으로 이 함수가 컴퓨터에서 사용되는 언어를 식별하는 특정 상수들을 확인한다는 거야. 그의 글에서 이 링크를 제공하는데(https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-LCID/[MS-LCID].pdf), 이건 모든 언어 식별자가 있는 마이크로소프트의 표준 문서 같은 거야.
이제 우리의 hax00r l33t 논리를 사용하면 0x26이 아마 오프셋으로 사용된다는 것을 추론할 수 있어. 즉, 0x419 다음의 0x26개 언어 식별자들, 즉

그 문서를 살펴보면 0x818이 다음에 해당한다는 것도 볼 수 있어

그리고 0x819는 다음에 해당하지

그리고 이전 "조사"/온라인 리포트에서 이 멀웨어가 특정 지역의 특정 PC에서는 실행되지 않았다는 것을 알고 있어. 그래서 이 함수가 감염된 머신이 어느 지역인지 확인한다고 결론 내릴 수 있어.
=============================================================================
지금까지 미친 햄버거였지, 친구. 다음은 뭘까? some_more_syscall 함수를 서빙할게. 좋아! 그래서 뭘 보여줄 수 있냐고, 어! 여기 있어!

우리는 더 원해! 물론이지, 친구!



으흠

그래서 kerneldebugger가 존재하는지 확인해(https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi/system_information_class.htm)(0x23). 체키 체키!
그래서 어쩌라고! 우리에겐 pcr(무더기와 조합 파티)이 있지

=============================================================================
이제 iterate_over_modules() 함수를 살펴보면 이렇게 생겼어

"의사 코드" 관점에서는 이렇게 보여

그 자체로 보면 어셈블리와 ida의 의사 코드가 일치해 보여. 그럼 이 함수의 로직은 뭘 할까? 음, 꽤 간단해. 메모리 내 모듈(dll)들을 반복하면서 해시 목록과 대조해 :)
v4[0] = 0x1E7EACEF; v4[1] = 0x4468A620; v4[2] = 0x68536B95; v4[3] = 0x73EBBB53; v4[4] = 0xDA165168; v4[5] = 0xB24D33A7; v4[6] = 0xB1E2CEC6; v4[7] = 0x5136992; v4[8] = 0x98C500D9; v4[9] = 0x3E0169B6;
그리고 메모리 내 dll 중 같은 해시를 가진 것이 없으면 0을 반환하고, 있으면 1을 반환해서 디버거를 크래시시켜. 그런 결론으로 볼 때 이것도 또 다른 안티 분석 방법이라고 할 수 있어. 맞아. 하지만 각 해시 값들은 어떨까? 음, 나는 또 치트를 쓸 거야(누군가 한때 멀웨어 분석에서는 치트를 쓸 수 없고, 그저 인생을 더 쉽게 만들 수 있을 뿐이라고 했지 :) ). 위에서 언급한 아시아 연구자가 값들이 파생된 목록을 친절하게 제공해줬어.
sbiedll.dll dbghelp.dll api_log.dll dir_watch.dll pstorec.dll vmcheck.dll wpespy.dll cmdvrt64.dll avghookx.dll snxhk.dll
이제 언급된 dll과 일치하는 값이 있는지 동적으로 확인해보면

그리고 예상대로 일치해 :)
하지만 알고리즘이 이 값들을 대조한다는 결론을 어떻게 내리게 됐을까? 음, 이 멀웨어의 일부를 에뮬레이션할 때 나는 iterate_over_module_name_and_hash(solve_hash_syscalls.py 파일에 있음)를 다시 구현해야 했어. 그리고 이 함수에는

여기서 x(전달된 인자)는 이 경우 v2인데, 값들의 배열(포인터)이야. 위에서 이미 언급한 값들과 일치하지 않는 한 계속 증가해(배열에서 가리키는 위치가). 와, 말로 하니 길다 :P
좋아! 다음
=============================================================================
iterate_over_modules2는 대충 같은 이야기야

그리고 의사 코드

대충 같은 이야기인데 해시만 다를 뿐이야 :)
v5[0] = 0x7D73878E; v5[1] = 0xEF36424B; v5[2] = 0xAF64BC2B; v5[3] = 0x1DBBC879; v5[4] = 0xAE6D1D56; v5[5] = 0x7B3242F2; v5[6] = 0x14D922B9; v5[7] = 0x4C92DF53;
다음에 해당하지
sample.exe bot.exe sandbox.exe malware.exe test.exe klavme.exe myapp.exe testapp.exe
=============================================================================
iterate_over_modules3도 같은 이야기야

그리고 의사 코드

같은 이야기, 다른 해시 :)v4[0] = 0x42D12D59; v4[1] = 0xEC5D7AA; v4[2] = 0x861E460F; v4[3] = 0x84BCC8DB; v4[4] = 0x6474D72B; v4[5] = 0xB8B9C504; v4[6] = 0x69A0620E; v4[7] = 0x6017EE43; v4[8] = 0xE93BE2E0; v4[9] = 0x149EFC55; v4[10] = 0xE3FA84A4; v4[11] = 0x7CFDD7AF; v4[12] = 0x5B098C67; v4[13] = 0x2F1FB18E; v4[14] = 0xFE8F2B18;
이는 다음에 해당합니다.
prl_cc.exe prl_tools.exe qemu-ga.exe vmtoolsd.exe vmwaretray.exe vmwareuser.exe VGAuthService.exe vmacthlp.exe vboxservice.exe vboxtray.exe VMSrvc.exe VMUSrvc.exe xenservice.exe
=============================================================================
Anti_debug_measure_1은 나중에 anti_debug_measure2_RtlAddVectoredExceptionHandler_int3으로 이름을 바꿨어. 왜 그런지는 곧 알게 돼. 그래서 이렇게 생겼어.

그리고 의사 코드(pseudo-code) 관점에서는 이렇게 보여.

그래서 도대체 이게 뭐냐고? 음, 기본적으로 __debugbreak(int 3)에 대한 예외 처리기(exception handler)를 만드는 거야. int3가 발생할 때마다 코드가 실행되도록 말이지. 더 찾아보다가 이런 걸 발견했어.
(https://blog.lexfo.fr/dridex-malware.html)
꽤 괜찮은 자료인데, 이게 단순히 안티 디버그(anti-debug) 메커니즘이라는 걸 알려줘. 우리 경우엔 그냥 이 함수를 실행하면서, int3를 실행할 때마다 트리거되는 sub_13F2820D0을 무시하고 eax를 0으로 패치하면 돼. 그래서 메모리에서 실행하면 이렇게 보여.
패치 전

패치 후

그런 다음 함수를 빠져나가면서 rax/eax도 0으로 수정해서 디버거를 크래시시키는 jne를 건너뛰어.

=============================================================================
Anti_debug_measure_2는 나중에 anti_debug_measure2_RtlAddVectoredExceptionHandler_int2로 변경했어.


역시 새로운 건 없어. 같은 방식인데 이번엔 다른 int/syscall인 int2를 후킹하는 거지. 우회 방법도 동일하게 반환 값을 동적으로 패치하는 트릭을 쓰면 돼.
구글에서 anti analysis 2dh를 검색하면 많은 자료가 나오니까, 그걸 참고해서 더 배워봐. 나는 이것에 많은 시간을 쓰지 않았어.
이건 step over를 시도하면 여기 보이는 것처럼 결국 ret에 도달하게 돼.

이걸 우회하려면 그냥 return까지 실행하면 돼.
=============================================================================
이제 anti_debug measure heap이 나와. 나중에 iterate_over_current_process_and_check_again_hases로 이름을 바꿨어.

이 함수의 멋진 기능은 iterate_over_current_process_and_hash_check 내부에서 ntquerysysteminformation을 사용하여 실행 중인 모든 프로세스를 나열한다는 거야. iterate_over_current_process_and_hash_check는 이렇게 생겼어.


좋아. 그런데 내가 어떻게 iterate_over_current_process_and_hash_check가 이름 그대로 동작한다는 결론을 내렸을까? 음, 첫 번째로 ntquerysysteminformation 호출이 있어. 구글에서 검색해 보면 실행 중인 프로세스 목록을 가져오는 데 사용된다는 걸 알 수 있어. 그리고 나서 다음 코드 스니펫을 기반으로 추측했어. LODWORD(v4) = RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, 8u, v10); v3 = v4; v5 = ntquerysysteminformation(); v6 = v3; .... memcpy(v9, v6[8], *(v6 + 28)); v9[*(v6 + 28) >> 1] = 0; if ( some_hash_0x1003F(v9) == a1 ) 그 코드는 프로세스 목록을 복사하고 각각을 반복하면서 이전 배열의 각 해시를 현재 실행 중인 프로세스와 비교하는 거야. 다시 한번 아시아 연구자의 블로그에 감사함을 표해. 왜냐하면 그가 v4 배열 값의 실제 이름을 어떻게 알아냈는지 도무지 모르겠거든. 이걸 어떻게 에뮬레이션했는지 보고 싶다면 iterate_over_modules.py를 참조해.
추신: 이 함수를 동적으로 디버깅하면 다시 int 2d에 부딪히게 돼. 해결책은 위에서 언급한 것과 동일한데, 기본적으로 return 지점까지 0xf번 실행하면 돼. 그건 기본적으로 힙(heap)을 탐색하는 거야. 그 후에 iterate_over_current_process_and_check_again_hases의 return에 확실히 도달했는지 확인하고 싶다면 이 함수의 끝에 브레이크포인트를 설정하면 안전해.
============================================================================= anti_debug_measure_heap



그래, 운이 좋게도 이건 그렇게 크지 않아. 게다가 우리는 이미 demangle_strings를 구현했어. 이제 더 큰 배열을 위해 이걸 다시 구현했으니 anti_debug_measure.py를 확인해 봐.
이것은 기본적으로 다음을 확인합니다.``` \Registry\Machine\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters \Registry\Machine\SYSTEM\ControlSet001\Services\vioscsi \Registry\Machine\SYSTEM\ControlSet001\Services\VirtIO-FS Service \Registry\Machine\SYSTEM\ControlSet001\Services\VirtioSerial \Registry\Machine\SYSTEM\ControlSet001\Services\BALLOON \Registry\Machine\SYSTEM\ControlSet001\Services\BalloonService \Registry\Machine\SYSTEM\ControlSet001\Services\netkvm \Registry\Machine\SOFTWARE\VMware, Inc.\VMware Tools \Registry\Machine\HARDWARE\ACPI\DSDT\VBOX__ \Registry\Machine\HARDWARE\ACPI\FADT\VBOX__ \Registry\Machine\HARDWARE\ACPI\RSDT\VBOX__ \Registry\Machine\SOFTWARE\Oracle\VirtualBox Guest Additions \Registry\Machine\SYSTEM\ControlSet001\Services\VBoxGuest \Registry\Machine\SYSTEM\ControlSet001\Services\VBoxMouse \Registry\Machine\SYSTEM\ControlSet001\Services\VBoxService \Registry\Machine\SYSTEM\ControlSet001\Services\VBoxSF \Registry\Machine\SYSTEM\ControlSet001\Services\VBoxVideo
Now get_oem_key







먼저 문자열을 디맹글링하여```
\Registry\Machine\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0
\Registry\Machine\SYSTEM\ControlSet001\Control\SystemInformation
\Registry\Machine\HARDWARE\Description\System
Identifier
SystemManufacturer
SystemBiosVersion
VMWARE
QEMU
VBOX
그런 다음 다음 항목들을 쿼리합니다
\Registry\Machine\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0
\Registry\Machine\SYSTEM\ControlSet001\Control\SystemInformation
\Registry\Machine\HARDWARE\Description\System
그리고 값들을 qemu, vbox, VMWARE와 비교합니다. IDA 스니펫과 x86 창에서 볼 수 있듯이 :)


첫 번째 스니펫에서 볼 수 있듯이, 분석 당시 제 VM에 저장되어 있던 값들을 vmware와 비교합니다 :)
별거 아니에요. 그냥 이 함수를 ret까지 실행하고, 반환될 때 eax를 0으로 바꾸면 됩니다 :) 이 안티 분석 기법을 우회하려면요 :)
=============================================================================
이제 get_oem_from_firmware





이제 흥미로운 점이 하나 보입니다. 이 함수는 sub_13F267288()로 시작하는데, 여기에 0x52534D42가 매개변수로 전달됩니다. 이 값을 구글에서 검색하면 여러 포럼에서 많은 흥미로운 질문들을 찾을 수 있습니다. 예를 들어 (https://ru.stackoverflow.com/questions/778618/c-%D0%9A%D0%B0%D0%BA-%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D1%8C-%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8E-%D0%BE%D0%B1-%D1%83%D1%81%D1%82%D1%80%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%B0%D1%85-%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D0%B0-%D0%BD%D0%B5-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D1%8F-wmi) (https://msdn-whiteknight.github.io/answers/html/tools/html/ru.stackoverflow.com/posts/780170.html) (https://www.maldun.com/analysis/YXNkZmRzZmFkc2Y2NDEwNjlkc2Zhc2RmYXNkZg==/) 또는 이 링크(https://github.com/digitalocean/go-smbios/blob/master/smbios/stream_windows.go) 등이요. 하지만 가장 흥미로운 것은, 이 내용을 읽고 RSMB를 검색하다 보면 이 링크(https://evasions.checkpoint.com/techniques/firmware-tables.html)를 발견하게 된다는 점입니다. 이 링크는 이 기법이 안티 분석 방법이라고 설명해 줍니다 :) 좋아요, 그럼 이건 도대체 뭐 하는 걸까요?
먼저 SMBIOS 펌웨어 테이블을 덤프한 다음, 첫 번째 문자열이 qemu가 되도록 문자열을 디맹글링하고, 디맹글링된 문자열과 펌웨어 테이블을 매개변수로 sub_13F2655E0를 호출합니다. 정적 분석에서 볼 수 있듯이


정확히 말하면, if ( v4 != v5) 부분을 보고 이게 strcmp 구현 비슷한 것이라는 결론을 내렸습니다. 펌웨어 테이블에 있는 값을 디맹글링된 문자열과 비교하는 역할을 합니다. 디버거에서 볼 수 있듯이
r8은 펌웨어 테이블을 가리키는 포인터이고, 더 흥미로운 것은 ... 뭐라고 해야 하나 ... 펌웨어 테이블에서 발견된 이름이 virtualbox이고 rcx는 qemu라는 점입니다. 따라서 이건 strcmp 비슷한 구현이라고 안전하게 말할 수 있습니다. 그리고 보시다시피 이 검사에 대해서는 우리는 안전합니다 :)

이제 이 과정은 VirtualBox, vbox, VBOX, VMware 문자열에 대해 반복됩니다 :) 일반적으로 이 함수 검사를 우회하는 방법은 ret까지 실행하고 eax를 패치한 다음 분석을 계속하는 것입니다 :)
============================================================================
좋아요, 이제 check_oem_key ()






그럼 이건 뭐 하는 걸까요? 아까 설명한 것과 같은 트릭이지만, 이번에는 ACPI 테이블을 사용합니다. 무슨 뜻이냐고요? 이 14개 슬라이드(https://dc4420.org/slides/2015-11-24/acpi_vm_detect.pdf)를 읽어보시는 게 좋습니다. 더 잘 설명해 줍니다. 하지만 똑같은 싸구려 트릭입니다 :)) 이번에는 BOCHS, BXPC, VMWARE를 대상으로 합니다.


============================================================================
먼저 anti_debug_processor_Timing() 함수부터 시작해서 check_for_flags()로 마무리할게요. 자,

이렇게 생겼습니다. 그럼 뭐 하는 걸까요? 프로세서의 타임스탬프 현재 값을 읽은 다음, 프로세서 이름(이 경우 GenuineIntel)을 가져옵니다. 그런 다음 프로세서 이름에서 검색된 타임스탬프를 뺀 다음, 그 결과가 19999보다 큰지 간단히 확인합니다.
rdtsc 이후

cpuid 이후

보시다시피 문자열은 3개의 레지스터로 나뉩니다.

그리고 우리의 경우 이 검사에 '실패'하고 eax를 1로 설정하는 것을 볼 수 있습니다.

이 안티 검사에 대한 해결책은 간단히 함수에서 반환하고 eax를 0으로 패치하는 것입니다 :)
============================================================================
이제 entry_to_peb()







의사 코드 관점에서



자, if 검사까지는 우리가 이미 본 유형의 알고리즘이라 새로운 것이 없습니다. 그래서 뭐가 다를까요? 이미 참조된 아시아 리서치의 write-up을 가보면, Windows 버전이 win7/server 2008 r2보다 큰지 확인한다고 되어 있습니다. 하지만 그는 이 아이디어를 어떻게 떠올렸을까요? http://waleedassar.blogspot.com/2012/08/major-minorsubsystemversion.html 를 읽어보면 이렇게 말합니다

또한 이 글도 읽어보세요 :)
그리고 여기서 말하는 내용을 적용하면, 자동으로 KUSER_SHARED_DATA 구조체로 캐스팅됩니다. 하지만 분석 중에 제 IDA가 버그가 있어서 그냥 그렇다는 겁니다 :)
대략 비슷한 알고리즘을 반복해서 지루하게 만들지 않기 위해, 최종적으로 추출되고 디맹글링된 문자열들을 여기 정리했습니다 :) 이건 디버거로 수행해야 했기 때문에 꽤 고된 작업이었습니다 :) 이걸 에뮬레이션하기엔 너무 게을렀거든요.``` user32.dll advapi32.dll Rpcrt4.dll bcrypt.dll ole32.dll Cabinet.dll
CreateWindowExW
ShutdownBlockReasonCreate
ShutdownBlockReasonDestroy
DestroyWindow
CloseHandle
CreateProcessW
InitializeProcThreadAttributeList
UpdateProcThreadAttribute
LoadAppInitDlls
Sleep
GetExitCodeProcess
MoveFileExW
OpenSCManagerW
OpenServiceW
QueryServiceStatus
StartServiceW
CloseServiceHandle
GetUserNameW
ConvertSidToStringSidW
LookupAccountNameW
CreateWellKnownSid
LookupPrivilegeValueW
ConvertStringSecurityDescriptorToSecurityDescriptorW
RpcStringBindingComposeW
RpcBindingFromStringBindingW
RpcStringFreeW
RpcBindingSetOption
RpcBindingSetAuthInfoExW
RpcBindingFree
NdrClientCall2
NdrClientCall3
BCryptOpenAlgorithmProvider
BCryptSetProperty
BCryptGenerateSymmetricKey
BCryptDecrypt
BCryptDestroyKey
BCryptCloseAlgorithmProvider
BCryptGetProperty
BCryptGenRandom
CoCreateInstance
CoInitializeEx
CoUninitialize
CoInitializeSecurity
CoSetProxyBlanket
if >win7/server 2008 r2
{
CreateDecompressor
CloseDecompressor
Decompress
}
이것으로 blacklotus 분석 챌린지의 전반부가 끝납니다.
============================================================================
이제 후반부입니다.
어셈블리 관점

의사 코드 관점

some_hash() 함수부터 분석을 시작하겠습니다.
============================================================================
그래프 관점

어셈블리 관점



의사 코드 관점
