Deep Analysis of Mars Stealer

28 minute read

Introduction

Mars Stealer is an improved copy of Oski Stealer. I saw alot of tweets recently about it so i decided to write an analysis of the newer version V8. Enjoy reading!

  • Diffrences from the previous version:
    1. Anti analysis technique
    2. Diffrent encryption algoithm
    3. Introudcing new anti debug technique
    4. New configuration format
    5. External dlls are in one zip file

Overview

Anti-Analysis

Opening mars stealer in ida we can see an anti-analysis trick called Opaque Predicates it’s a commonly used technique in program obfuscation, intended to add complexity to the control flow.

This obfuscation simply takes an absolute jump (JMP) and transforms it into two conditional jumps (JZ/JNZ). Depending on the value of the Zero flag (ZF), the execution will follow the first or second branch.

However, disassemblers are tricked into thinking that there is a fall-through branch if the second jump is not taken (which is impossible as one of them must be taken) and tries to disassemble the unreachable instructions (often invalid) resulting in garbage code.

the deobfuscation is simple, we just need to patch the first conditional jump to an absolute jump and nop out the second jump, we can use IDAPython to achieve this:

import idc

ea = 0
while True:
    ea =  min(ida_search.find_binary(ea,idc.BADADDR, "74 ? 75 ?",16 ,idc.SEARCH_NEXT | idc.SEARCH_DOWN),  # JZ / JNZ
    ida_search.find_binary(ea,idc.BADADDR, "75 ? 74 ?",16, idc.SEARCH_NEXT | idc.SEARCH_DOWN))  # JNZ / JZ
    if ea == idc.BADADDR:
        break
    idc.patch_byte(ea, 0xEB)
    idc.patch_byte(ea+2, 0x90)
    idc.patch_byte(ea+3, 0x90)
    idc.patch_byte(ea+4, 0x90)

After Running the Script

now we can see a clear view , after reversing and renaming

0 0

First Mars get a handle to kernel32.dll by parsing InLoadOrderModuleList then it passes the handle to a fucntion that loops over the exported functions of the DLL to get the address of the LocalAlloc() and VirtualProtect() functions.

String Encryption

After that it decrypts some strings used for some checks , the decryption is a simple xor function

We can although see that the xor function is refrenced in another function which i renamed as Decrypt_String_2 if the malware passes the checks which we will see soon it decrypt those string which contanis strings needed for the malware to steal sensitive data .

We use idapython script to get those strings and rename the variables to make reversing easier

import string

def sanitize_string(name):
        return "".join([c for c in name if c in string.ascii_letters])[:20].capitalize()

def X0r(key, data, length):
    res = ""
    for i in range(length):
        res += chr(key[i] ^ data[i])
    return res


start_Addrs = [0x00401770,0x00401990  ]
end_Addrs = [0x00401967,0x0405444 ]

string_list = []
dectypred_data = b''
addrs = []

for i in range(len(start_Addrs)):
    ea = start_Addrs[i]
    end = end_Addrs[i]

    while ea <= end:
        if idc.get_operand_type(ea, 0) == idc.o_imm:
            addrs.append((idc.get_operand_value(ea, 0)))

        if len(addrs) == 3:
            length = addrs[0]
            data = idc.get_bytes(addrs[1], length)
            key = idc.get_bytes(addrs[2], length)
            dectypred_data = X0r(key, data, length)
            string_list.append(dectypred_data)
            addrs = []

        if idc.print_insn_mnem(ea) == "call":
            idc.set_cmt(ea, dectypred_data, 1)

        if idc.print_insn_mnem(ea) == "mov" and (idc.get_operand_type(ea, 0) == idc.o_mem) and (
                idc.get_operand_type(ea, 1) == idc.o_reg):
            global_var = idc.get_operand_value(ea, 0)
            idc.set_name(global_var, "Str" + sanitize_string(dectypred_data), SN_NOWARN)

        ea = idc.next_head(ea, end)


Here is a list of the decrypted strings :

Expand to see more
  LoadLibraryA
  GetProcAddress
  ExitProcess
  advapi32.dll
  crypt32.dll
  GetTickCount
  Sleep
  GetUserDefaultLangID
  CreateMutexA
  GetLastError
  HeapAlloc
  GetProcessHeap
  GetComputerNameA
  VirtualProtect
  GetCurrentProcess
  VirtualAllocExNuma
  GetUserNameA
  CryptStringToBinaryA
  HAL9TH
  JohnDoe
  ­6ʧÈ/2022 20:00:00
  http://
  194.87.218.39
  92550737836278980100
  /RyC66VfSGP.php
  Default
  %hu/%hu/%hu %hu:%hu:%hu
  open
  sqlite3.dll
  C:\ProgramData\sqlite3.dll
  freebl3.dll
  C:\ProgramData\freebl3.dll
  mozglue.dll
  C:\ProgramData\mozglue.dll
  msvcp140.dll
  C:\ProgramData\msvcp140.dll
  nss3.dll
  C:\ProgramData\nss3.dll
  softokn3.dll
  C:\ProgramData\softokn3.dll
  vcruntime140.dll
  C:\ProgramData\vcruntime140.dll
  .zip
  Tag:
  IP: IP?
  Country: Country?
  Working Path:
  Local Time:
  TimeZone:
  Display Language:
  Keyboard Languages:
  Is Laptop:
  Processor:
  Installed RAM:
  OS:
  (
  Bit)
  Videocard:
  Display Resolution:
  PC name:
  User name:
  Domain name:
  MachineID:
  GUID:
  Installed Software:
  system.txt
  Grabber\%s.zip
  %APPDATA%
  %LOCALAPPDATA%
  %USERPROFILE%
  %DESKTOP%
  Wallets\
  Ethereum
  \Ethereum\
  keystore
  Electrum
  \Electrum\wallets\
  *.*
  ElectrumLTC
  \Electrum-LTC\wallets\
  Exodus
  \Exodus\
  exodus.conf.json
  window-state.json
  \Exodus\exodus.wallet\
  passphrase.json
  seed.seco
  info.seco
  ElectronCash
  \ElectronCash\wallets\
  default_wallet
  MultiDoge
  \MultiDoge\
  multidoge.wallet
  JAXX
  \jaxx\Local Storage\
  file__0.localstorage
  Atomic
  \atomic\Local Storage\leveldb\
  000003.log
  CURRENT
  LOCK
  LOG
  MANIFEST-000001
  0000*
  Binance
  \Binance\
  app-store.json
  Coinomi
  \Coinomi\Coinomi\wallets\
  *.wallet
  *.config
  *wallet*.dat
  GetSystemTime
  lstrcatA
  SystemTimeToFileTime
  ntdll.dll
  sscanf
  memset
  memcpy
  wininet.dll
  user32.dll
  gdi32.dll
  netapi32.dll
  psapi.dll
  bcrypt.dll
  vaultcli.dll
  shlwapi.dll
  shell32.dll
  gdiplus.dll
  ole32.dll
  dbghelp.dll
  CreateFileA
  WriteFile
  CloseHandle
  GetFileSize
  lstrlenA
  LocalAlloc
  GlobalFree
  ReadFile
  OpenProcess
  SetFilePointer
  SetEndOfFile
  GetCurrentProcessId
  GetLocalTime
  GetTimeZoneInformation
  GetUserDefaultLocaleName
  LocalFree
  GetSystemPowerStatus
  GetSystemInfo
  GlobalMemoryStatusEx
  IsWow64Process
  GetTempPathA
  GetLocaleInfoA
  GetFileSizeEx
  GetFileAttributesA
  FindFirstFileA
  FindNextFileA
  FindClose
  GetCurrentDirectoryA
  CopyFileA
  DeleteFileA
  lstrcmpW
  GlobalAlloc
  FreeLibrary
  SetCurrentDirectoryA
  CreateFileMappingA
  MapViewOfFile
  UnmapViewOfFile
  FileTimeToSystemTime
  GetFileInformationByHandle
  GlobalLock
  GlobalSize
  WideCharToMultiByte
  GetWindowsDirectoryA
  GetVolumeInformationA
  GetVersionExA
  GetModuleFileNameA
  CreateFileW
  CreateFileMappingW
  MultiByteToWideChar
  CreateThread
  GetEnvironmentVariableA
  SetEnvironmentVariableA
  lstrcpyA
  lstrcpynA
  InternetOpenA
  InternetConnectA
  HttpOpenRequestA
  HttpSendRequestA
  HttpQueryInfoA
  InternetCloseHandle
  InternetReadFile
  InternetSetOptionA
  InternetOpenUrlA
  InternetCrackUrlA
  wsprintfA
  CharToOemW
  GetKeyboardLayoutList
  EnumDisplayDevicesA
  ReleaseDC
  GetDC
  GetSystemMetrics
  GetDesktopWindow
  GetWindowRect
  GetWindowDC
  CloseWindow
  RegOpenKeyExA
  RegQueryValueExA
  RegCloseKey
  GetCurrentHwProfileA
  RegEnumKeyExA
  RegGetValueA
  CreateDCA
  GetDeviceCaps
  CreateCompatibleDC
  CreateCompatibleBitmap
  SelectObject
  BitBlt
  DeleteObject
  StretchBlt
  GetObjectW
  GetDIBits
  SaveDC
  CreateDIBSection
  DeleteDC
  RestoreDC
  DsRoleGetPrimaryDomainInformation
  GetModuleFileNameExA
  CryptUnprotectData
  BCryptCloseAlgorithmProvider
  BCryptDestroyKey
  BCryptOpenAlgorithmProvider
  BCryptSetProperty
  BCryptGenerateSymmetricKey
  BCryptDecrypt
  VaultOpenVault
  VaultCloseVault
  VaultEnumerateItems
  VaultGetItemWin8
  VaultGetItemWin7
  VaultFree
  StrCmpCA
  StrStrA
  PathMatchSpecA
  SHGetFolderPathA
  ShellExecuteExA
  GdipGetImageEncodersSize
  GdipGetImageEncoders
  GdipCreateBitmapFromHBITMAP
  GdiplusStartup
  GdiplusShutdown
  GdipSaveImageToStream
  GdipDisposeImage
  GdipFree
  CreateStreamOnHGlobal
  GetHGlobalFromStream
  SymMatchString
  HEAD
  HTTP/1.1
  GET
  POST
  file
  Content-Type: multipart/form-data; boundary=----
  Content-Disposition: form-data; name="
  Content-Disposition: form-data; name="file"; filename="
  Content-Type: application/octet-stream
  Content-Transfer-Encoding: binary
  SOFT:
  PROF: ?
  PROF:
  HOST:
  USER:
  PASS:
  sqlite3_open
  sqlite3_prepare_v2
  sqlite3_step
  sqlite3_column_text
  sqlite3_finalize
  sqlite3_close
  sqlite3_column_bytes
  sqlite3_column_blob
  encrypted_key
  "}
  PATH
  PATH=
  NSS_Init
  NSS_Shutdown
  PK11_GetInternalKeySlot
  PK11_FreeSlot
  PK11_Authenticate
  PK11SDR_Decrypt
  SELECT origin_url, username_value, password_value FROM logins
  Cookies\%s_%s.txt
  SELECT HOST_KEY , is_httponly , path , is_secure , (expires_utc/1000000)-11644480800 , name , encrypted_value from cookies
  TRUE
  FALSE
  Autofill\%s_%s.txt
  SELECT name, value FROM autofill
  CC\%s_%s.txt
  SELECT name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards
  Card number:
  Name on card:
  Expiration date:
  History\%s_%s.txt
  SELECT url FROM urls
  Downloads\%s_%s.txt
  SELECT target_path, tab_url from downloads
  Login Data
  Cookies
  Web Data
  History
  SELECT host, isHttpOnly, path, isSecure, expiry, name, value FROM moz_cookies
  logins.json
  formSubmitURL
  usernameField
  encryptedUsername
  encryptedPassword
  guid
  SELECT fieldname, value FROM moz_formhistory
  SELECT url FROM moz_places
  cookies.sqlite
  formhistory.sqlite
  places.sqlite
  \Local State
  ..\profiles.ini
  C:\ProgramData\
  Chrome
  \Google\Chrome\User Data
  ChromeBeta
  \Google\Chrome Beta\User Data
  ChromeCanary
  \Google\Chrome SxS\User Data
  Chromium
  \Chromium\User Data
  Edge_Chromium
  \Microsoft\Edge\User Data
  Kometa
  \Kometa\User Data
  Amigo
  \Amigo\User Data
  Torch
  \Torch\User Data
  Orbitum
  \Orbitum\User Data
  Comodo
  \Comodo\Dragon\User Data
  Nichrome
  \Nichrome\User Data
  Maxthon5
  \Maxthon5\Users
  Sputnik
  \Sputnik\User Data
  EPB
  \Epic Privacy Browser\User Data
  Vivaldi
  \Vivaldi\User Data
  CocCoc
  \CocCoc\Browser\User Data
  Uran
  \uCozMedia\Uran\User Data
  QIP
  \QIP Surf\User Data
  Cent
  \CentBrowser\User Data
  Elements
  \Elements Browser\User Data
  TorBro
  \TorBro\Profile
  CryptoTab
  \CryptoTab Browser\User Data
  Brave
  \BraveSoftware\Brave-Browser\User Data
  Opera
  \Opera Software\Opera Stable\
  OperaGX
  \Opera Software\Opera GX Stable\
  OperaNeon
  \Opera Software\Opera Neon\User Data
  Firefox
  \Mozilla\Firefox\Profiles\
  SlimBrowser
  \FlashPeak\SlimBrowser\Profiles\
  PaleMoon
  \Moonchild Productions\Pale Moon\Profiles\
  Waterfox
  \Waterfox\Profiles\
  Cyberfox
  \8pecxstudios\Cyberfox\Profiles\
  BlackHawk
  \NETGATE Technologies\BlackHawk\Profiles\
  IceCat
  \Mozilla\icecat\Profiles\
  KMeleon
  \K-Meleon\
  Thunderbird
  \Thunderbird\Profiles\
  passwords.txt
  ibnejdfjmmkpcnlpebklmnkoeoihofec
  TronLink
  nkbihfbeogaeaoehlefnkodbefgpgknn
  MetaMask
  fhbohimaelbohpjbbldcngcnapndodjp
  Binance Chain Wallet
  ffnbelfdoeiohenkjibnmadjiehjhajb
  Yoroi
  jbdaocneiiinmjbjlgalhcelgbejmnid
  Nifty Wallet
  afbcbjpbpfadlkmhmclhkeeodmamcflc
  Math Wallet
  hnfanknocfeofbddgcijnmhnfnkdnaad
  Coinbase Wallet
  hpglfhgfnhbgpjdenjgmdgoeiappafln
  Guarda
  blnieiiffboillknjnepogjhkgnoapac
  EQUAL Wallet
  cjelfplplebdjjenllpjcblmjkfcffne
  Jaxx Liberty
  fihkakfobkmkjojpchpfgcmhfjnmnfpi
  BitApp Wallet
  kncchdigobghenbbaddojjnnaogfppfj
  iWallet
  amkmjjmmflddogmhpjloimipbofnfjih
  Wombat
  nlbmnnijcnlegkjjpcfjclmcfggfefdm
  MEW CX
  nanjmdknhkinifnkgdcggcfnhdaammmj
  GuildWallet
  nkddgncdjgjfcddamfgcmfnlhccnimig
  Saturn Wallet
  fnjhmkhhmkbjkkabndcnnogagogbneec
  Ronin Wallet
  cphhlgmgameodnhkjdmkpanlelnlohao
  NeoLine
  nhnkbkgjikgcigadomkphalanndcapjk
  Clover Wallet
  kpfopkelmapcoipemfendmdcghnegimn
  Liquality Wallet
  aiifbnbfobpmeekipheeijimdpnlpgpp
  Terra Station
  dmkamcknogkgcdfhhbddcghachkejeap
  Keplr
  fhmfendgdocmcbmfikdcogofphimnkno
  Sollet
  cnmamaachppnkjgnildpdmkaakejnhae
  Auro Wallet
  jojhfeoedkpkglbfimdfabpdfjaoolaf
  Polymesh Wallet
  flpiciilemghbmfalicajoolhkkenfel
  ICONex
  nknhiehlklippafakaeklbeglecifhad
  Nabox Wallet
  hcflpincpppdclinealmandijcmnkbgn
  KHC
  ookjlbkiijinhpmnjffcofjonbfbgaoc
  Temple
  mnfifefkajgofkcjkemidiaecocnkjeh
  TezBox
  dkdedlpgdmmkkfjabffeganieamfklkm
  Cyano Wallet
  nlgbhdfgdhgbiamfdfmbikcdghidoadd
  Byone
  infeboajgfhgbjpjbeppbkgnabfdkdaf
  OneKey
  cihmoadaighcejopammfbmddcmdekcje
  LeafWallet
  lodccjjbdhfakaekdiahmedfbieldgik
  DAppPlay
  ijmpgkjfkbfhoebgogflfebnmejmfbml
  BitClip
  lkcjlnjfpbikmcmbachjpdbijejflpcm
  Steem Keychain
  onofpnbbkehpmmoabgpcpmigafmmnjhl
  Nash Extension
  bcopgchhojmggmffilplmbdicgaihlkp
  Hycon Lite Client
  klnaejjgbibmhlephnhpmaofohgkpgkd
  ZilPay
  aeachknmefphepccionboohckonoeemg
  Coin98 Wallet
  bfnaelmomeimhlpmgjnjophhpkkoljpa
  Phantom
  hifafgmccdpekplomjjkcfgodnhcellj
  Crypto.com
  dngmlblcodfobpdpecaadgfbcggfjfnm
  Maiar DeFi Wallet
  ppdadbejkmjnefldpcdjhnkpbjkikoip
  Oasis
  hpbgcgmiemanfelegbndmhieiigkackl
  MonstaWallet
  fcckkdbjnoikooededlapcalpionmalo
  MOBOX
  jccapkebeeiajkkdemacblkjhhhboiek
  Crust Wallet
  mgffkfbidihjpoaomajlbgchddlicgpn
  Pali Wallet
  nphplpgoakhhjchkkhmiggakijnkhfnd
  TON Wallet
  ldinpeekobnhjjdofggfgjlcehhmanlj
  Hiro Wallet
  pocmplpaccanhmnllbbkpgfliimjljgo
  Slope Wallet
  bhhhlbepdkbapadjdnnojkbgioiodbic
  Solflare Wallet
  pgiaagfkgcbnmiiolekcfmljdagdhlcm
  Stargazer Wallet
  cgeeodpfagjceefieflmdfphplkenlfk
  EVER Wallet
  gjkdbeaiifkpoencioahhcilildpjhgh
  partisia-wallet
  bgjogpoidejdemgoochpnkmdjpocgkha
  Ecto Wallet
  ifckdpamphokdglkkdomedpdegcjhjdp
  ONTO Wallet
  agechnindjilpccclelhlbjphbgnobpf
  Fractal Wallet
  algblmhagnobbnmakepomicmfljlbehg
  ADS Wallet
  imijjbmbnebfnbmonjeileijahaipglj
  Moonlet Wallet
  kpjdchaapjheajadlaakiiigcbhoppda
  ZEBEDEE
  dlcobpjiigpikoobohmabehhmhfoodbb
  Argent X StarkNet Wallet
  bofddndhbegljegmpmnlbhcejofmjgbn
  X-Wallet
  mapbhaebnddapnmifbbkgeedkeplgjmf
  Biport Wallet
  kfdniefadaanbjodldohaedphafoffoh
  Typhon Wallet
  jaooiolkmfcmloonphpiiogkfckgciom
  Twetch Wallet
  aijcbedoijmgnlmjeegjaglmepbmpkpi
  Leap Wallet
  fhfffofbcgbjjojdnpcfompojdjjhdim
  Lamden Wallet
  agkfnefiabmfpanochlcakggnkdfmmjd
  Earth Wallet
  lpfcbjknijpeeillifnkikgncikgfhdo
  Nami
  fecfflganphcinpahcklgahckeohalog
  Coin Wallet
  ilhaljfiglknggcoegeknjghdgampffk
  Beam Web Wallet
  dklmlehijiaepdijfnbbhncfpcoeeljf
  FShares Wallet
  fkhebcilafocjhnlcngogekljmllgdhd
  WAGMIswap.io Wallet
  laphpbhjhhgigmjoflgcchgodbbclahk
  BLUE - Worlds Safest and Simplest Wallet
  mkjjflkhdddfjhonakofipfojoepfndk
  Unification Web Wallet
  jnldfbidonfeldmalbflbmlebbipcnle
  Infinity Wallet
  ellkdbaphhldpeajbepobaecooaoafpg
  Fetch.ai Network Wallet
  iokeahhehimjnekafflcihljlcjccdbe
  Alby Wallet
  omajpeaffjgmlpmhbfdjepdejoemifpe
  xBull Wallet
  pgojdfajgcjjpjnbpfaelnpnjocakldb
  Sugarchain Wallet
  pnndplcbkakcplkjnolgbkdgjikjednm
  Tronium
  fnnegphlobjdpkhecapkijjdkgcjhkib
  Harmony
  fhilaheimglignddkjgofkcbgekhenbh
  Oxygen
  cmbagcoinhmacpcgmbiniijboejgiahi
  JustLiquidity Wallet
  kmmolakhbgdlpkjkcjkebenjheonagdm
  AlgoSigner
  fnabdmcgpkkjjegokfcnfbpneacddpfh
  Goldmint Lite Wallet
  bgpipimickeadkjlklgciifhnalhdjhe
  GeroWallet
  hoighigmnhgkkdaenafgnefkcmipfjon
  EO.Finance
  nlgnepoeokdfodgjkjiblkadkjbdfmgd
  Multi Wallet
  nhihjlnjgibefgjhobhcphmnckoogdea
  Waves Enterprise Wallet
  ehibhohmlpipbaogcknmpmiibbllplph
  Bluehelix Wallet
  magbanejlegnbcppjljfhnmfmghialkl
  Nebulas Wallet
  fgkaeeikaoeiiggggbgdcjchmdfmamla
  Vtimes
  pnlfjmlcjdjgkddecgincndfgegkecke
  Crocobit Wallet
  bhghoamapcdpbohphigoooaddinpkbai
  Authenticator
  gaedmjdfmmahhbjefcbgaolhhanlaolb
  Authy
  oeljdldpnmdbchonielidgobddffflal
  EOS Authenticator
  ilgcnhelpchnceeipipijaljkblbcobl
  GAuth Authenticator
  imloifkgjagghnncjkhggdhalmcnfklk
  Trezor Password Manager
  %s\%s\Local Extension Settings\%s
  %s\CURRENT
  %s\%s\Sync Extension Settings\%s
  %s\%s\IndexedDB\chrome-extension_%s_0.indexeddb.leveldb
  Plugins\
  HARDWARE\DESCRIPTION\System\CentralProcessor\0
  ProcessorNameString
  SOFTWARE\Microsoft\Windows NT\CurrentVersion
  ProductName
  x64
  x86
  DISPLAY
  SOFTWARE\Microsoft\Cryptography
  MachineGuid
  SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
  DisplayName
  DisplayVersion
  screenshot.jpg
  ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
  /c timeout /t 5 & del /f /q "%s" & exit
  C:\Windows\System32\cmd.exe


Dynamic linking

The adress of GetProcAddress() and LoadLibraryA() is retrieved by the same method in Dynamic_Linking_1 looping over the exported functions of the kernel32.DLL , then it uses LoadLibraryA() to Load the specified module into the address space and get a handle that get passed to GetProcAddress() to retrieve the address of an exported function from the specified dynamic-link library.
Dynamic_Linking_2 is loading the APIs only needed to do some checks if it passes it will load others needed for stealing functionality.

dword_42774 is GetProcAddress() it is called in other function which is Dynamic_Linking_3 that will load other APIs needed for stealing functionality.

We use idapython to rename the global variables with the api name to make reversing easier

import idc

start_Addrs = [0x00415F86,0x00415FC0 ,0x004161A0 ]
end_Addrs = [0x00415FB7,0x00416176,0x00417034]

string_list = []

for i in range(len(start_Addrs)):
    ea = start_Addrs[i]
    end = end_Addrs[i]

    while ea <= end:

        if (idc.print_insn_mnem(ea) == "push"  )and (idc.get_operand_type(ea, 0) == idc.o_imm):
            name = idc.get_strlit_contents(idc.get_operand_value(ea, 0)).decode()

        if (idc.print_insn_mnem(ea) == "mov"  and (idc.get_operand_type(ea, 0) == idc.o_reg)and (idc.get_operand_type(ea, 1) == idc.o_mem)) :
            temp_name = idc.get_name(idc.get_operand_value(ea, 1))
            if "Str_" == temp_name[0:4]:
                name = temp_name[4::]

        if (idc.print_insn_mnem(ea) == "mov") and (idc.get_operand_type(ea, 0) == idc.o_mem) and (idc.get_operand_type(ea, 1) == idc.o_reg):
            global_var = idc.get_operand_value(ea, 0)
            idc.set_name(global_var, name, SN_NOWARN)

        ea = idc.next_head(ea, end)

Anti-Sandbox

Since a lot of sandboxes hook and bypass Sleep() preventing malware being idle over their execution time. The malware first calls GetTickCount() function that retrieves the number of milliseconds that have elapsed since the system was started, up to 49.7 days, that is our first timestamp. Then calls the Sleep() to suspend itself for 16 seconds. calling GetTickCount() again gets our second timestamp . The malware checks if at least 12 seconds diffrence between the 2 timestampes . If the function returns flase it means that the Sleep() hasn’t been skipped the malware assumes that it is running in a sandbox and exits immediately.

Anti-CIS

This is one of the easy tricks to check if the malware is not infected users from specific countries.

Mars checks the user language to determine if it’s part of the Commonwealth of Independent States (CIS) countrie it gets the user language ID by using GetUserDefaultLangID and it compares the user language ID to:

Language ID Country
0x43F Kazakhstan
0x443 Uzbekistan
0x82C Azerbaijan
0x43Fu Kazakhstan
0x419u Russia
0x423u Belarus

If the user language ID matches one of the IDs above, it will exit.

Anti-Emulation

If the malware is executed with the computer name HAL9TH and the username with JohnDoe it will exit . This check is done because it is the name given to the Windows Defender Emulator, this technique is used by malware to prevent itself from running in an emulated environment.

Mutex

The malware creates a mutex object using CreateMutexA() to avoid having more than one instance running. Then calls GetLastError() which gets the last error, and if the error code is equal to 183 (ERROR_ALREADY_EXIST) it means that mutex already exists and an instance of the malware is already running therefore malware exits.

Anti-Debug

The malware create thread that checks BeingDebugged flag which is Special flag in system tables, which dwell in process memory and which an operation system sets, can be used to indicate that the process is being debugged. The states of these flags can be verified either by using specific API functions or examining the system tables in memory. If the malware is being debugged it exits . The thread is going to keep running until the malware finishes excution or the thread end the malware excution if its being debugged .

Expiration check

The Expiration date variable contains the date 26/04/2022 20:00:00.

Mars uses GetSystemTime() to get current system date and time as SYSTEMTIME structe, then calls sscanf() to parse the Expiration date to a SYSTEMTIME structe . SystemTimeToFileTime() take SYSTEMTIME structe as argument then convert it to file time and Expiration date although is converted to file time.

If the current time exceedes the Expiration time, the malware calls ExitProcess() to exit immediately.

Main Functionality

0 0

Mars generate random string that will be the name of the zip file contains stolen data.

The communications between c2 and the malware is described as:

  1. sends a GET request to the C2 URL on the /RyC66VfSGP.php endpoint to grab its configuration .
  2. fetches all DLLs on the /request endpoint, the libraries are zipped
  3. Stolen data are posted to the C2 on the same URL used in step 1.

Dlls retrieved:

DLL Name Description Save path
sqlite3.dll Enables SQLite related operations none (mars doesnt write it on disk, parsed from memory)
freebl3.dll Library for the NSS (Gecko-based browsers) C:\ProgramData\freebl3.dll
mozglue.dll Mozilla Browser Library C:\ProgramData\mozglue.dll
msvcp140.dll Visual C++ Runtime 2015 C:\ProgramData\msvcp140.dll
nss3.dll Network System Services Library (Gecko-based browsers) C:\ProgramData\nss3.dll
softokn3.dll Mozilla Browser Library C:\ProgramData\softokn3.dll
vcruntime140.dll Visual C++ Runtime 2015 C:\ProgramData\vcruntime140.dll

Another diffrence from the last version is that sqlite3 isnt written on disk, it just get parsed and passed to another function to get handle to it and start loading needed function , the other dll are written .

Since the C2 was down i got the pcap from Hatching sandbox.

Understanding Configuration Format

configuration is base64 encoded

MXwxfDF8MXwxfDVxRGxQdVZLb1J8RGlzY29yZHwwfCVBUFBEQVRBJVxkaXNjb3JkXExvY2FsIFN0b3JhZ2VcfCp8MXwwfDB8VGVsZWdyYW1 8MHwlQVBQREFUQSVcVGVsZWdyYW0gRGVza3RvcFx0ZGF0YVx8KkQ4NzdGNzgzRDVEM0VGOEMqLCptYXAqLCpjb25maWdzKnwxfDB8MHw=

1|1|1|1|1|5qDlPuVKoR|Discord|0|%APPDATA%\discord\Local Storage\ |*|1|0|0|Telegram|0|%APPDATA%\Telegram Desktop\tdata\ |*D877F783D5D3EF8C*,*map*,*configs*|1|0|0|

import base64
config = base64.b64decode("MXwxfDF8MXwxfDVxRGxQdVZLb1J8RGlzY29yZHwwfCVBUFBEQVRBJVxkaXNjb3JkXExvY2FsIFN0b3JhZ2VcfCp8MXwwfDB8VGVsZWdyYW18MHwlQVBQREFUQSVcVGVsZWdyYW0gRGVza3RvcFx0ZGF0YVx8KkQ4NzdGNzgzRDVEM0VGOEMqLCptYXAqLCpjb25maWdzKnwxfDB8MHw=").decode()
config = config.split("|")
print("First Part : \n" ,config[0:6])
print("Second Part :" )
for i in range(6,len(config),7):
    print(config[i:i+7])
First Part : 
 ['1', '1', '1', '1', '1', '5qDlPuVKoR']
Second Part :
['Discord', '0', '%APPDATA%\\discord\\Local Storage\\', '*', '1', '0', '0']
['Telegram', '0', '%APPDATA%\\Telegram Desktop\tdata\\', '*D877F783D5D3EF8C*,*map*,*configs*', '1', '0', '0']
  • First part
Config Meaning
1 Downloads_history_Flag
1 Browser_History_Flag
1 Autofill_Flag
1 ScreenShoot_Flag
1 Self_Deletion_Flag
5qDlPuVKoR Explorer Credentials FileName
  • Second part
Config Meaning
Discord name for the zip file – will contain all the stolen files that related to the current task.so the name for the zip will be name.zip.
0 maybe max size (no indecation of use)
%APPDATA%\discord\Local Storage\ An environment variable name and folder name – a starting point for the recursive Grabber.
* A regex list – contains multiply parameters that are separated by “,” each one of them is a regex that represents a file type.
1 is_Recursive
0 Write to zip enabled if 0
0 Exclusion List

Grabber

lets dig into Config_Grabber function to see how you it works

after receiving the config we can see the it has a lot of | so it split the config with | delimiter and loop through the splited config. the first part enables/disable some of the stealer functionality then it starts in part 2 which start grapping files wanted.

as example

[‘Discord’, ‘0’, ‘%APPDATA%\discord\Local Storage\’, ‘*’, ‘1’, ‘0’, ‘0’]

it start recurseively grabbing all files in discord\\Local Storage\\ under %APPDATA% and put them in discord.zip

0 0
0 0

If there is more than one regex as in

[‘Telegram’, ‘0’, ‘%APPDATA%\Telegram Desktop\tdata\’, ‘D877F783D5D3EF8C,map,configs’, ‘1’, ‘0’, ‘0’]

it loops through them and call Recursive_Grabber with each regex .

Browsers

Mars steals credentials from browsers by static paths. It has four different methods to steal data from different types of browses, like Gecko-based browsers, Opera, Internet Explorer and Chromium-based browsers.

Data Extraction

All the extraction functions have the same scheme:

  1. The malware saves the addresses of the functions from sqlite3.dll
    • sqlite3_open
    • sqlite3_prepare_v2
    • sqlite3_step
    • sqlite3_column_bytes
    • sqlite3_column_blob
    • sqlite3_column_text
    • sqlite3_column_finalize
    • sqlite3_column_close
  2. It generates a random string (length of 8 characters) and copies the DB file to a temp folder named like the random string – all the extractions methods will be on the copied DB. In order to extract the data from the DB, the malware has to create the SQL query and query the DB using sqlite3.dll functions.
  3. The malware opens the DB by using sqlite3_open and passes the DB path.
  4. It calls to sqlite3_prepare_v2, the function gets a handle to DB and the SQL query and returns a statement handle.
  5. By using sqlite3_column_bytes/sqlite3_column_blob/sqlite3_column_text, the malware can get the results from the queries
  6. The Credentials in Chromium-based browsers DB are encrypted by DPAPI and, therefore, the malware uses the function CryptUnprotectData to decrypt the Credentials.

Mars steals information from the Windows Vault, which is the default storage vault for the credential manager information. This is done through the use of Vaultcli.dll, which encapsulates the necessary functions to access the Vault. The malware loops through its items using:

  • VaultEnumerateVaults
  • VaultOpenVault
  • VaultEnumerateItems
  • VaultGetItem
  • VaultFree

Targeted DB Files

File Name Affected Software
History Chromium-based browsers
Login Data Chromium-based browsers
Cookies Chromium-based browsers
Web Data Chromium-based browsers
formhistory.sqlite Gecko-based browsers
cookies.sqlite Gecko-based browsers
signongs.sqlite Gecko-based browsers
places.sqlite Gecko-based browsers

Queries Used

Query Target Browser Enabled
SELECT target_path, tab_url from downloads chromium , opera by default this feature is disabled, enabled if Downloads_history_Flag is set to 1
SELECT name, value FROM autofill chromium , opera by default this feature is disabled, enabled if Autofill_Flag is set to 1
SELECT url FROM urls chromium , opera by default this feature is disabled,enabled if Browser_History_Flag is set to 1
SELECT action_url, username_value, password_value FROM logins chromium , opera enabled by default
SELECT HOST_KEY, is_httponly, path, is_secure, (expires_utc/1000000)-11644480800, name, encrypted_value from cookies chromium , opera enabled by default
SELECT name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards chromium , opera enabled by default
SELECT host, isHttpOnly, path, isSecure, expiry, name, value FROM moz_cookies gecko enabled by default
SELECT url FROM moz_places gecko by default this feature is disabled,enabled if Browser_History_Flag is set to 1
SELECT fieldname, value FROM moz_formhistory gecko enabled by default

Cryptocurrency Wallets via browser extensions

Mars appears to also target additional Chrome-based browser extensions related to two-factor authentication (2FA) .

Mars steal files from 3 folders :

  1. \Local Extension Settings\Extension ID from Google Store
  2. \Sync Extension Settings\ Extension ID from Google Store
  3. \IndexedDB\Domain Name.indexeddb.leveldb

as example if the victim uses Google Chrome with a crypto browser wallet extension, the extension files will be stored in:

C:\Users\Username\AppData\Local\Google\Chrome\User Data\Default\Local Extension Settings\Extension ID from Google Store C:\Users\Username\AppData\Local\Google\Chrome\User Data\Default\Sync Extension Settings\ Extension ID from Google Store C:\Users\Username\AppData\Local\Google\Chrome\User Data\Default\IndexedDB\Domain Name.indexeddb.leveldb

Type Extension name Extension id
Crypto TronLink ibnejdfjmmkpcnlpebklmnkoeoihofec
Crypto MetaMask nkbihfbeogaeaoehlefnkodbefgpgknn
Crypto Binance Chain Wallet fhbohimaelbohpjbbldcngcnapndodjp
Crypto Yoroi ffnbelfdoeiohenkjibnmadjiehjhajb
Crypto Nifty Wallet jbdaocneiiinmjbjlgalhcelgbejmnid
Crypto Math Wallet afbcbjpbpfadlkmhmclhkeeodmamcflc
Crypto Coinbase Wallet hnfanknocfeofbddgcijnmhnfnkdnaad
Crypto Guarda hpglfhgfnhbgpjdenjgmdgoeiappafln
Crypto EQUAL Wallet blnieiiffboillknjnepogjhkgnoapac
Crypto Jaxx Liberty cjelfplplebdjjenllpjcblmjkfcffne
Crypto BitApp Wallet fihkakfobkmkjojpchpfgcmhfjnmnfpi
Crypto iWallet kncchdigobghenbbaddojjnnaogfppfj
Crypto Wombat amkmjjmmflddogmhpjloimipbofnfjih
Crypto MEW CX nlbmnnijcnlegkjjpcfjclmcfggfefdm
Crypto GuildWallet nanjmdknhkinifnkgdcggcfnhdaammmj
Crypto Saturn Wallet nkddgncdjgjfcddamfgcmfnlhccnimig
Crypto Ronin Wallet fnjhmkhhmkbjkkabndcnnogagogbneec
Crypto NeoLine cphhlgmgameodnhkjdmkpanlelnlohao
Crypto Clover Wallet nhnkbkgjikgcigadomkphalanndcapjk
Crypto Liquality Wallet kpfopkelmapcoipemfendmdcghnegimn
Crypto Terra Station aiifbnbfobpmeekipheeijimdpnlpgpp
Crypto Keplr dmkamcknogkgcdfhhbddcghachkejeap
Crypto Sollet fhmfendgdocmcbmfikdcogofphimnkno
Crypto Auro Wallet cnmamaachppnkjgnildpdmkaakejnhae
Crypto Polymesh Wallet jojhfeoedkpkglbfimdfabpdfjaoolaf
Crypto ICONex flpiciilemghbmfalicajoolhkkenfel
Crypto Nabox Wallet nknhiehlklippafakaeklbeglecifhad
Crypto KHC hcflpincpppdclinealmandijcmnkbgn
Crypto Temple ookjlbkiijinhpmnjffcofjonbfbgaoc
Crypto TezBox mnfifefkajgofkcjkemidiaecocnkjeh
Crypto Cyano Wallet dkdedlpgdmmkkfjabffeganieamfklkm
Crypto Byone nlgbhdfgdhgbiamfdfmbikcdghidoadd
Crypto OneKey infeboajgfhgbjpjbeppbkgnabfdkdaf
Crypto LeafWallet cihmoadaighcejopammfbmddcmdekcje
Crypto DAppPlay lodccjjbdhfakaekdiahmedfbieldgik
Crypto BitClip ijmpgkjfkbfhoebgogflfebnmejmfbml
Crypto Steem Keychain lkcjlnjfpbikmcmbachjpdbijejflpcm
Crypto Nash Extension onofpnbbkehpmmoabgpcpmigafmmnjhl
Crypto Hycon Lite Client bcopgchhojmggmffilplmbdicgaihlkp
Crypto ZilPay klnaejjgbibmhlephnhpmaofohgkpgkd
Crypto Coin98 Wallet aeachknmefphepccionboohckonoeemg
2FA Authenticator bhghoamapcdpbohphigoooaddinpkbai
2FA Authy gaedmjdfmmahhbjefcbgaolhhanlaolb
2FA EOS Authenticator oeljdldpnmdbchonielidgobddffflal
2FA GAuth Authenticator ilgcnhelpchnceeipipijaljkblbcobl
2FA Trezor Password Manager imloifkgjagghnncjkhggdhalmcnfklk

Crypto Wallets

Mars does not just stop at targeting crypto currencies via browser extensions. Many people prefer not to use third-party applications and services to store their digital currency. Mars will go through various folders looking for specific files related to cryptocurrency.

The first paramter detmerines the path if 0 then it’s under %appdata% if 1 it’s under %localappdata% then it search for other wallets with regex *wallet*.dat under %appdata%

Mars have dedicated functionality to target the following crypto wallets:

Wallet name Wallet folder Regex
Ethereum %appdata%\Ethereum\ keystore
Electrum %appdata%\Electrum\wallets\ .
Electrum LTC %appdata%\Electrum-LTC\wallets\ .
Exodus %appdata%\Exodus\ exodus.conf.json, window-state.json, \Exodus\exodus.wallet\, passphrase.json, seed.seco, info.seco
Electron Cash %appdata%\ElectronCash\wallets\ default_wallet
MultiDoge %appdata%\MultiDoge\ multidoge.wallet
Jaxx %appdata%\jaxx\Local Storage\ file__0.localstorage
Atomic %appdata%\atomic\Local Storage\leveldb\ 000003.log, CURRENT, LOCK, LOG, MANIFEST.000001, 0000*
Binance %appdata%\Binance\ app-store.json
Coinomi %localappdata%\Coinomi\Coinomi\wallets\ *.wallet, *.config
Other wallets %appdata% *wallet*.dat

System info

The malware grabs system info and store it in system.txt file

  1. IP and country
  2. Working path to EXE file
  3. Local time and time zone
  4. Language system
  5. Language keyboard layout
  6. Notebook or desktop
  7. Processor model
  8. Computer name
  9. User name
  10. Domain computer name
  11. Machine ID
  12. GUID
  13. Installed software and their versions

Mars althouge takes screenshot and then add all stolen files to a zip file which it will exfiltrate back to the c2 and get loader config.

Loader

Malware gets loader config as a response after exfiltrating data. This config looks like download_URL|An environment variable name and folder name |startup_parameter| .

After pasring the config Mars calls download_file() function with the url and a path which the file will be saved in . Then calls ShellExecuteExA() to execute executable with give paramters retrieved from the config.

Self Deletion

Malware gets the path to itself by using GetModuleFileName() and calls ShellExecuteExA() which executes the following command

"C:/Windows/System32/cmd.exe" /c timeout /t 5 & del /f / path_To_file & exit

After 5 seconds the executable will be deleted.

Generalized idapython Script using patterns

import idautils , idc, idaapi, ida_search, ida_bytes, ida_auto
import string



seg_mapping = {idaapi.getseg(x).name: (idaapi.getseg(x).start_ea, idaapi.getseg(x).end_ea) for x in
                   idautils.Segments()}
start = seg_mapping[0x1][0]
end = seg_mapping[0x1][1]
    
def sanitize_string(name):
    return "".join([c for c in name if c in string.ascii_letters])[:20].capitalize()

def Xor(key, data, length):
    res = ""
    for i in range(length):
        res += chr(key[i] ^ data[i])
    return res

def getData (addr):
    key_addr = idc.prev_head(addr)
    data_addr = idc.prev_head(key_addr)
    key_length_addr = idc.prev_head(data_addr)
    length = idc.get_operand_value(key_length_addr, 0)
    key = idc.get_bytes(idc.get_operand_value(key_addr,0),length)
    data =  idc.get_bytes(idc.get_operand_value(data_addr,0),length)
    return  key , data ,length


def rename_APIs(ea,end):
    
    func_addr = ea
    for i in range(20):
        if (idc.print_insn_mnem(ea) == "push"  )and (idc.get_operand_type(ea, 0) == idc.o_imm):
            name = idc.get_strlit_contents(idc.get_operand_value(ea, 0)).decode()
            break
        
        if (idc.print_insn_mnem(ea) == "mov"  and (idc.get_operand_type(ea, 0) == idc.o_reg)and (idc.get_operand_type(ea, 1) == idc.o_mem)) :
            temp_name = idc.get_name(idc.get_operand_value(ea, 1))
            if "Str_" == temp_name[0:4]:
                name = temp_name[4::]
                break        
        ea =  idc.prev_head(ea)
        
    ea = func_addr
    
    for i in range(20):
        if (idc.print_insn_mnem(ea) == "mov") and (idc.get_operand_type(ea, 0) == idc.o_mem) and (idc.get_operand_type(ea, 1) == idc.o_reg):
                global_var = idc.get_operand_value(ea, 0)
                idc.set_name(global_var, name, SN_NOWARN)
                return name         
        ea = idc.next_head(ea, end)


def API_resolve(start,end):
    Loadlibrarya_addr = 0x0
    GetProcAddress_pattern = "8B 55 ?? 52 8B 45 ?? 8B 4D ??  8B 55 ?? 03 14 ?? 52 E8 ?? ?? ?? ?? 83 C4 ??  85 C0 75 ??"
    GetProcAddress_addr = ida_search.find_binary(start, end, GetProcAddress_pattern, 16, idc.SEARCH_DOWN)
    GetProcAddress_addr = idaapi.get_func(GetProcAddress_addr).start_ea
    print('[*] Traget fucntion found at {}'.format(hex(GetProcAddress_addr)))

    for ref in idautils.XrefsTo(GetProcAddress_addr):
        addr = ref.frm
        x = rename_APIs(addr, end)
        if "Loadlibrarya" in x:
                Loadlibrarya_addr = idc.get_operand_value(idc.next_head(idc.next_head(addr, end), end), 0)


    new_GetProcAddress_addr = idc.get_operand_value(idc.next_head(idc.next_head(addr, end), end), 0)
    
    for ref in idautils.XrefsTo(new_GetProcAddress_addr):
        addr = ref.frm
        rename_APIs(addr, end)

    for ref in idautils.XrefsTo(Loadlibrarya_addr):
        addr = ref.frm
        rename_APIs(addr, end)

        
def Strings_resolve(start,end):
    xor_pattern = "8b 4d ?? 03 4d ?? 0f be 19 8b 55 ?? 52 e8 ?? ?? ?? ?? 83 c4 ?? 8b c8 8b 45 ?? 33 d2 f7 f1 8b 45 ?? 0f be 0c 10 33 d9 8b 55 ?? 03 55 ?? 88 1a eb be"
    xor_fun_addr = ida_search.find_binary(start, end, xor_pattern, 16, idc.SEARCH_DOWN)
    xor_fun_addr = idaapi.get_func(xor_fun_addr).start_ea
    print('[*] Traget fucntion found at {}'.format(hex(xor_fun_addr)))

    for ref in idautils.XrefsTo(xor_fun_addr):
        addr = ref.frm
        key, data, length = getData(addr)
        decrypt_string = Xor(key, data, length)
        idc.set_cmt(addr, decrypt_string, 1)
        ea = idc.next_head(idc.next_head(addr, end),end)
        global_var = idc.get_operand_value(ea, 0)
        idc.set_name(global_var, "Str_" + sanitize_string(decrypt_string), SN_NOWARN)
        
def Anit_Reverse():
    ea = 0
    while True:
        ea = min(ida_search.find_binary(ea, idc.BADADDR, "74 ? 75 ?", 16, idc.SEARCH_NEXT | idc.SEARCH_DOWN),
                 # JZ / JNZ
                 ida_search.find_binary(ea, idc.BADADDR, "75 ? 74 ?", 16,
                                        idc.SEARCH_NEXT | idc.SEARCH_DOWN))  # JNZ / JZ
        if ea == idc.BADADDR:
            break
        idc.patch_byte(ea, 0xEB)
        idc.patch_byte(ea + 2, 0x90)
        idc.patch_byte(ea + 3, 0x90)
        idc.patch_byte(ea + 4, 0x90)



def main():
    Anit_Reverse()
    Strings_resolve(start,end)
    API_resolve(start,end)

main()

for more Idapython scripts check my repo .

IOCs

  • Hashes:
    1. md5 : 880924E5583978C615DD03FF89648093
    2. sha1 : EF759F6ECA63D6B05A7B6E395DF3571C9703278B
    3. sha256 : 4bcff4386ce8fadce358ef0dbe90f8d5aa7b4c7aec93fca2e605ca2cbc52218b
    4. imphash : 4E06C011D59529BFF8E1F1C88254B928
    5. ssdeep : 3072:U/E8k9fjpIg+zNch12KbAwSaSMtmSu4/bVBt4b8EG:U/E8k9bwz6/tJc/4xM8EG
  • Mutex : 92550737836278980100
  • Files:
    1. C:\ProgramData\freebl3.dll
    2. C:\ProgramData\mozglue.dll
    3. C:\ProgramData\msvcp140.dll
    4. C:\ProgramData\nss3.dll
    5. C:\ProgramData\softokn3.dll
    6. C:\ProgramData\vcruntime140.dll
  • C2 Server : 194.87.218.39
  • C2 Domains:
    1. http://194[.]87[.]218[.]39/request
    2. http://194[.]87[.]218[.]39/RyC66VfSGP[.]php

YARA

rule Mars_Stealer: Mars Stealer
{
    meta:
        Author = "X__Junior"
        Description = "Mars Stealer v8 Detection"

    strings:
        $xor ={8b 4d ?? 03 4d ?? 0f be 19 8b 55 ?? 52 e8 ?? ?? ?? ?? 83 c4 ?? 8b c8 8b 45 ?? 33 d2 f7 f1 8b 45 ?? 0f be 0c 10 33 d9 8b 55 ?? 03 55 ?? 88 1a eb be}
        $debug = {64 A1 30 00 00 00 80 78 02 00}
	$thread_func = {B8 01 00 00 00   85 ??  74 ?? E8 ?? ?? ?? ?? 85 ?? 74 ?? 6A 00 FF ?? ?? ?? ?? ?? 6A ?? FF ?? ?? ?? ?? ?? EB ??}

        $api1 = "LocalAlloc" ascii
        $api2 = "VirtualProtect" ascii
        $api3 = "SetFileTime" ascii
        $api4 = "LocalFileTimeToFileTime" ascii
        $api5 = "HeapFree" ascii
        $api6 = "VirtualFree" ascii
        $api7 = "VirtualAlloc" ascii

        $s1 = "DPAPI" ascii
        $s2 = "memset" ascii
        $s3 = "msvcrt.dll" ascii
        $s4 = "_mbsnbcpy" ascii
        $s5 = "_mbsstr" ascii

    condition:
        uint16(0) == 0x5A4D and 2 of($api*) and 3 of($s*)  and $debug  and $xor  and $thread_func

}

Conclusion

The last sample of mars i saw came packed with custom packer , easy to unpack with x32dbg by just setting a breakpoint on VirtualAlloc() , nothing else was changed except for the C2 .

References