Malware-analysis-and-Reverse-engineering/APT29_C2- Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md at main · Dump-GUY/Malware-analysis-and-Reverse-engineering By Dump-GUY Archived: 2026-04-05 20:19:54 UTC Static Code Analysis – “AcroSup64.dll” Upon library loading “AcroSup64.dll”, the first function (functions “DllEntryPoint” and “dllmain_dispatch” are not important in this case) which is performing the intended malicious behavior and gets automatically executed is “DllMain”. Right in start of function “DllMain”, we can see that first anti-analysis check is performed. Code is checking if main process module filename is “NV.exe” same as the delivered original filename of program responsible for loading “AcroSup64.dll”. Be aware that through whole this analysis - all code is already annotated and retyped in IDA IDB and functions are renamed according to their capabilities. We can also see the first thread execution hijacking which is processed via calling directly “NtCreateThreadEx” syscall. New thread is created in suspended state with flags set also to hide from debugger. Decoy start routine “RtlNewSecurityObjectWithMultipleInheritance” of newly created thread is replaced with setting the thread context of this thread – specifically via setting RCX register (NOT RIP as this new suspended thread is not initiated yet) pointing to code where the execution will be directed. This serves well as AV evasion and anti-debug technique. RCX is the first argument to function “RtlUserThreadStart” (thread start location) and this argument sets new thread entry routine different than the decoy one. https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 1 of 14 The “NtCreateThreadEx” syscall is dynamically resolved and gets executed directly via “syscall” assembly instruction where desired syscall number is set in RAX register, as we can see in the picture below: Resolving of syscalls is done via function “resolve_and_hash_all_syscalls” only once, on first execution. “resolve_and_hash_all_syscalls” function is hashing syscall names and populates it to table named as “hashed_syscalls_table”. This table later serves as lookup table to find specific syscall number for routine. Function “resolve_and_hash_all_syscalls”: https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 2 of 14 Whenever we see this kind of advanced technique (dynamic resolving of syscall via syscall name hashing and creating “hashed_syscalls_table” which serves as lookup table + direct syscall call via stub code similar like in ntdll.dll) we should do a little OSINT if this technique is based on some already published one. In this case, our assumption was correct and this technique is based on “SysWhispers2” published on Github [GITHUB - SysWhispers2]. C2-Client Dropbox Loader was reusing most of the original code from “SysWhispers2” also the syscall name hashing algorithm so with this information we can take some structures and implement it in IDA to get better understanding of this code like in pictures below: Using “hashed_syscalls_table” as lookup table for desired syscall hash to retrieve its syscall number: Hashing syscall names and populating + reordering the “hashed_syscalls_table”: https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 3 of 14 The main point of this kind of retrieving syscall numbers for routines is based on the fact that syscall numbers are in ascending order strictly connected to order of syscall´s virtual addresses “Zw*” inside of ntdll.dll – meaning that lowest virtual address of syscall = lowest number of syscall (highest virtual address of syscall = highest number of syscall). We can confirm this fact/idea with simple [python script] + ntdll.dll in IDA: Now when we have knowledge how this technique works, we can focus on syscall name hashing algorithm which after annotating and retyping looks similar like below: https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 4 of 14 This hashing routine we can easily reproduce in [IDA Python script] and create ENUM for all Syscall hashes: So whenever we see hashed syscall name, we can apply newly created ENUM and after we find out the correct invoked routine, we can retype whole function. We can get back to the first thread execution hijacking - “Pre_C2client_MAIN_redirect_exec” is the function where thread execution hijacking directs to. This function can be seen in the picture below. Function is trying to find “NV.exe” module name in memory and if found, another thread execution hijacking occurs. This time it hijacks already existing thread (no new thread created) and because of that, code can just set RIP register of thread context. Newly set RIP register is pointing to function named “C2_Client_MAIN” where all the main malicious C2 activity is implemented. https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 5 of 14 Start of function “C2_Client_MAIN” can be seen in the picture below. First what this function is doing, is calling function “Map_dll_restore_text_section”. After this, C2_client tries to authenticate itself to Dropbox service and if authentication is successful (there is unintentional exception – see below “http_dropbox_authenticate” function analysis), it sets persistence and continue with Dropbox communication otherwise it waits 5.5 minutes and try to authenticate itself again. All is performed in endless loop. https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 6 of 14 “Map_dll_restore_text_section” function serves well as AV evasion, anti-debug and anti-hooking technique as this function is searching for all already loaded modules (WININET.dll is the last one if found), finding them on disc, manually maps their “.text” (code) section into memory and replace with it the one “.text” section in corresponding library already loaded in memory. With this, malware destroys all installed inline hooks of AV and set breakpoints of debugger if any. So the AV solution will be blind from the user-space (ring 3) perspective. We can see function “Map_dll_restore_text_section” in the picture below: Back to the main function “C2_Client_MAIN”, “http_dropbox_authenticate” function is responsible for decoding strings related to authenticate the C2_Client on Dropbox service. It uses hardcoded token for authentication and if the token is still valid (not expired/revoked) it will receive another temporary token for further communication with Dropbox. One probably unintentional bug in code (function “http_dropbox_authenticate”) there is possibility that code will try to set persistence and continue in further communication (with wrong string content interpreting as bearer access token) even if authentication is not successful. This is caused by obtaining authentication response fulfilling certain format condition as explained in the picture below: https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 7 of 14 Decoded strings of function “http_dropbox_authenticate” can be seen in the picture below and contains information like HTTP User-Agent, HTTP Host name (api.dropbox.com), URL path, Basic authorization HTTP header and mainly the Token itself. We can also see that before the code reach the part of setting persistence (after authentication) it obtains current logged-in username (in NameSamCompatible format) calculates MD5 hash of it and converts it to hexstring. This hexadecimal string of Username MD5 is very important because it is later used to decrypt downloaded payload from Dropbox before execution. https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 8 of 14 According to usage of Username MD5 hexstring which is used for downloaded payload decryption, we can assume how C2 Dropbox server (serving payload to Dropbox) operates “per-victim” and is using infected currently logged-in Username MD5 hexstring for “per-victim” payload encryption. The expected functionality of infrastructure according to C2 Dropbox client code is in the picture below: Function “set_persistance” is spawning new process to open “blank.pdf” file. After that it starts to copy files “NV.exe”, “AcroSup64.dll” and “vcruntime140.dll” into the “%USERPROFILE%\AppData\Roaming\AdobeAcroSup” directory and sets persistence via ordinary auto-start location for current user “run” registry “HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run” with value name “Adobe AcroSup” and value data pointing to “%USERPROFILE%\AppData\Roaming\ AdobeAcroSup\NV.exe”. https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 9 of 14 Function “C2_Client_MAIN” continue execution and after “set_persistance” function it gets currently logged-in Username and Computername and creates string from it in format “Computername::Username”. In next step, this string is xored with hardcoded value “ME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUU” (which looks very similar to original LAME MP3 encoder header) as in the picture below. In next step, it calculates MD5 hash from string “Computername::Username”, converts it to hexstring format and uses it to create filename for uploading to Dropbox “Rock_ComputerNameUsernameMD5HashHexstring.mp3” – registering Client to C2 Dropbox Server (ex. “Rock_70a1e27ba30dd415155e68409d512a2d.mp3”). https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 10 of 14 Next function “pre_process_body_add_mp3header_xorkey” is preparing body content of “Rock_ComputerNameUsernameMD5HashHexstring.mp3” to upload. The body content contains xored “Computername::Username”, xor key “ME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUU” and to avoid detection - fictive MP3 header is added to the start of body content. The example structure of this body content is in the picture below: This body is uploaded by function “http_dropbox_upload”: Decoded strings from function “http_dropbox_upload” (ex. HTTP User-Agent, HTTP Host “content.dropboxapi.com”, URL Path “/2/files/upload”) can be seen in the picture below: The next step is preparing filename to download from Dropbox. This file is uploaded to Dropbox by C2 Dropbox Server. Filename to download is in format “Rock_ComputerNameUsernameMD5HashHexstring.mp3.backup” (ex. “Rock_70a1e27ba30dd415155e68409d512a2d.mp3.backup”) so the same as filename which was uploaded by Client but with “.backup” added. https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 11 of 14 Function responsible for downloading payload with filename “Rock_ComputerNameUsernameMD5HashHexstring.mp3.backup” is “http_dropbox_download”. Decoded strings of function “http_dropbox_download” (ex. HTTP User-Agent, HTTP Host “content.dropboxapi.com”, URL Path “/2/files/download”) can be seen in the picture below: Function responsible for processing downloaded payload is “process_exec_downloaded_payload”. Before stepping into this function we can see some first structure checks of obtained payload which will later help to recreate example structure of delivered payload. Function “process_exec_downloaded_payload” is responsible for processing downloaded payload, decrypting it with xor key “Username MD5 hash hexstring” (ex. "70c29c906cfa19759fa4776ea7c0973e") and creating new thread to execute it. First what we can see is xor decryption of downloaded payload which avoids processing first 21 bytes and last 36 bytes. Xoring starts with 22. byte of downloaded content and ends (content_length -57 +21). According to this, we https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 12 of 14 can assume example of the downloaded payload format (“X” – unknown, “PAYLOAD_CODE_ENCRYPTED” – encrypted code with unknown length which will be later executed) as in the picture below: In next step, this function is allocating enough executable memory for decrypted code. After this, syscalls “NtWriteVirtualMemory” and “NtCreateThreadEx” are resolved in similar manner as syscalls before via “resolve_syscall” function using already created table named “hashed_syscalls_table”. This table is used as lookup table to find specific syscall number for routine. Syscall “NtWriteVirtualMemory” is used to to write decrypted code to newly allocated executable memory. Syscall “NtCreateThreadEx” is used to create new thread in suspended state with flags set also to hide from debugger. Decoy start routine “RtlNewSecurityObjectWithMultipleInheritance” of newly created thread is replaced with setting the thread context of this thread – specifically via setting RCX register (NOT RIP as this new suspended thread is not initiated yet) pointing to decrypted code, already written to executable memory. Again this combination of directly called syscalls and thread execution hijacking serves as AV evasion and anti-debug technique. RCX is the first argument to function “RtlUserThreadStart” (thread start location) and this argument sets new thread entry routine (downloaded, decrypted code) different than the decoy. After the downloaded and decrypted payload is executed in new thread, execution in current thread comes back to the main function “C2_Client_MAIN”. Another uploading to Dropbox is processed. This uploading is overwriting the same filename which was downloaded “Rock_ComputerNameUsernameMD5HashHexstring.mp3.backup” (ex. “Rock_70a1e27ba30dd415155e68409d512a2d.mp3.backup”). https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 13 of 14 This serves probably to confirm execution of downloaded code. The content for Dropbox uploading which will be overwriting filename - “Rock_ComputerNameUsernameMD5HashHexstring.mp3.backup” is created again with fictive MP3 header, padding and “ME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUU” string which was previously used as xor key. Structure of this content can be seen in the picture bellow: All the main functionality of function “C2_Client_MAIN” - (without function “Map_dll_restore_text_section” – performed only once) is executed in endless loop when after each loop the execution sleeps for 5.5 minutes. Source: https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-D ropboxLoader_analysis.md https://github.com/Dump-GUY/Malware-analysis-and-Reverse-engineering/blob/main/APT29_C2-Client_Dropbox_Loader/APT29-DropboxLoader_analysis.md Page 14 of 14