Introduction
Before getting into every detail, I would like to mention that I have a section called References, where I will leave some useful links that may help you understand some topics I will be ‘talking’ about. Even though I tried to explain as much as I could, I am not a professional in this area, so I may have made some mistakes. If you find any mistakes, please let me know so I can fix them and learn from them.
I’m not going to start with the basic defintion of malware, i will try to talk about some general things but i would like to mention that in this blog i didn’t talk about ida pro basics or asm basics so it would be nice if you have some knowledge about these topics.
Sample Data:
- Name: Unknown
- MD5 Hash: MD5:D69EBD183B2E0072C396E55503D5EDE7
- SHA256 Hash: 3FEF5C7FA519F5384DE6F61C954EAD6DFD4DA727005BFEC954DC801BD120A938
- File Type: PE32+ executable
- Language: Rust
- Compiler: Visual Studio
- Imports:
- kernel32.dll : CrateEventW, CreateFileW, CreateMutexA, AcquireSRWLockExclusive, IsDebuggerPresent, GetSystemInfo…
- bcrypt.dll
- advapi32.dll
- user32.dll

This blog post marks my first foray into malware analysis, where I examine a real-world sample. The sample in question is identified as SHA256[you can find it somwehre in sample data] and is detected as a ransomware variant that encrypts files on the infected system.
Now, let’s understand the purpose of MD5 and why it is used in malware analysis—if it’s even used 👀👀. After all, I’m not a malware analyst.
MD5:D69EBD183B2E0072C396E55503D5EDE7
SHA256: 3FEF5C7FA519F5384DE6F61C954EAD6DFD4DA727005BFEC954DC801BD120A938
In Malware Analysis, MD5 and SHA-256 are cryptographic hash functions employed to generate unique identifiers known as hash values for files. These hash values serve as digital fingerprints, providing analysts with crucial information that enables them to perform critical tasks such as:
-> File Identification and Classification
-> Data Integrity Verification
-> Data Searching
While MD5 has historically been popular , it is known that the MD5 algorithm has long been considered insecure for cryptographic purposes due to significant vulnerabilities. In contrast, we have SHA-256, which is of the SHA-2 family. The algorithm offers enhances security by generating a 256-bit hash value, making it more resistant to attacks.

Now let’s move to the analysis part. There are 2 types of analysis which are crucial in reverse engineering Static Analysis and Dynamic Analysis.. Let’s take a basic overview on these 2 types of Analysis , what’s the difference and what do we need them?
Static Analysis:
Static analysis is a method of “debugging” and acquiring information without running the actual software. It focuses on analyzing the structure, syntax, and code. This approach provides valuable insights that help us build a foundational understanding of how the software works.
-
Understanding Malware Behavior:
By dissecting the code, we can comprehend its intended operation, execution flow, and embedded functions. This step is crucial in malware analysis. -
Identifying Indicators of Compromise (IOCs):
At this stage, we can extract “artifacts” such as IP addresses, domain names, and specific code patterns. -
Detecting Obfuscation Techniques:
In the modern era, malware authors often employ obfuscation to conceal malicious code. Static analysis (and reverse engineering skills) helps identify these techniques, providing (or sometimes not) the possibility of deobfuscating the code and understanding its malicious intent.When do we use?
I think it’s pretty clear when to use static analysis, first and foremost, before running the software, of course! 🙂
Why?
Pre-execution examination can reveal potential risks without actually running the software. Besides that, many malware samples include techniques to detect sandboxes. If they detect one, they might delete themselves or enter sleep mode, making analysis harder—so it’s better to avoid execution when possible. In the end, it’s all about gathering information about the software, which is crucial for our analysis.
Dynamic Analysis:
Now that we’ve gathered our information, we can move on to Dynamic Analysis. This involves executing the software (preferably within a secure, controlled environment — yeah, a sandbox, you’re damn right). This approach allows us to gain a much better understanding of the malware’s actions during execution
- Behavioral Observation: By running the malware, we can monitor its interaction with our OS, file systems, and other processes, providing crucial information about its potential impact.
- Detection of Evasion Techniques: As I mentioned before, the deobfuscation methods are related to the code, and now we’re referring to the process. There exists malware designed to detect analysis environments and alter its behavior. Dynamic analysis helps us identify these techniques, and we’re not only referring to altering their actions — there are also anti-debugging techniques implemented, which can sometimes be a barrier to understanding them.
When to use it?
So I think it’s actually pretty clear that this is supposed to be post-static analysis; it’s more about understanding additional behaviors that may not be evident from code examination alone. However, we should be aware that malware that changes its code structure or is compressed to evade detection can often be more effectively analyzed through dynamic execution. Also, there’s something called “behavioral signatures,” which can help enhance detection capabilities.
Before getting into analyzing the malware, i just want to let you know that i will provide a link to every tool i’ve used.
Now let’s gather some information about our malware maybe we can create a basic idea of how it works and what it does.
One tool that i love is DIE knows as Detect-It-Easy. It is a multi-tool that can give us a a huge amount of information about the software. But the point isn’t to talk how nice is the tool but how useful it’s therefore you can play with it.
Let’s analyze the file and see what can we find about it.

So we found that is a PE64 which means it was compiled for x86_64 architecture or known as 64-bit. It was used Microsoft Linker but meh, the language used was rust. So by knowing that Microsoft Linker was used we know already(aka tool field) that it was used Visual Studio to compile sooo if the executable wasn’t stripped of debugging symbol it could contain some useful metadata, such as function, maybe variables names and file paths. Not to mention that it can give insights into the binary’s structure, such as how the sections are aligned or how the data and code are organized.
Nextttt one in the line please. Okay if we check the advanced box we will get more functions that DIE can do for us like FIle Info, Memory Map, Hash, Entropy, Signatures and so many mores.
But Pe-Bear is also pretty good to analyze the software related to PE structure. Okay we can use DIE or PE-Bear it doesn’t really mater for this part but as for me PE-Bear gives me everything on my face and i can see everything that i need in one blink.
We already know that is an PE but DOS Header and DOS stub also tells us that so yeah :) So first of all i would take a look at the Sections category because based on the nr of sections you can say if the malware was packed or not (sometimes).

Now if we take a loot everything seems legit we got our Code, Data and Relocation section.
->.pdata section it contains some information about exception handling for functions.

Let’s take a look at the Section Headers as it offers much more information and we’re interesed in the Raw Size and the Virtual Size.
Now before getting into analyzing the basic idea of them let’s understand what’s Raw Size and Virtual Size.
-> Virtual Size :
This is the size of the section as it is loaded into memory when the program is executed. It includes any extra space reserved for the section such as padding and sometimes it may be larger.
-> Raw Size:
This is the size of the section as it appears on the disk file. It’s the actual size of the section, including any compressed or packed data.
So based o this we can say that when the Raw Size < Virtual Size, it may indicate that the section is packed. Why? Because in packed executables, the raw data is compressed to reduce file size of to obfuscate the content.
-> Upon loading into memory the data is unpacked to retore the original code and data => Virtual Size appears much larger.

Here is an approximate representation of how a packed software runs.
[Still consider the unpacked still packed because Unpacking Stub is a small piece of code that is responsible for decompressing/ unpacking the original executable code into memory]
As we can see, the .text, .rdata, and .reloc sections are almost the same. For now, we can assume that there is no packer.
Import Section
->The Import Section it refers to the part of the PE file format that lists external functions or symbols the executable requires from other modules in order to function correctly**.
[There will be a blog about DLL hijacking :) ]**
This section provides details about which external libraries and functions are being imported and used by the binary.

Let’s take a look we got each library that our software uses and if we click on it we can see further information about the functions that are used from that library. The scope isn’t me to talk about what’s a Original Thunk or First Thunk or Thunk itself.
Okay joking.
Let’us have a brief look each one of them has a hex-value which is approximately equivalent to a mem address but The OriginalFirstThunk named OTF points to an array of pointer-sized IMAGE_THUNK_DATA structures that specify the functions being imported. This table allows the Windows loader to identify and resolve the addresses of imported functions when the executable is loaded into memory.
First Thunk: Is a pointer to the Import Address Table(IAT), which is also an array of IMAGE_THUNK_DATA structure. Initially, the IAT is a copy of the ILT from the OFT. When the Executable is loaded, the loader resolves the addresses of imported functions and updated the IAT with the actual function pointers. This allows the program to directly call imported functions at their resolved addresses.
Thunk: It refers to a small piece that acts as an intermediary between the calling function and the actual function being called. Thunks are used to facilitate various operations, such as dynamic function resolution, argument marshalling or implementing callbacks. In the import mechanism, thunks are used to redirect functions calls to the appropriate addresses in the IAT.
There are few functions that i want to talk about:
- CreateMutexA
- CreateFileW
- MoveFileExW
- CreateEventW
- IsDebuggerPresent
I just took a few functions that could help us to understand how does the software works, okay it is malware same thing.
CreateMutexA:
Microsoft: Creates or opens a named or unnamed mutex object.
Yay Microsoft thanks for the information, actually there is pretty good information at the Remarks section im going to attach the link to their webpage my point isn’t to re-write their stuff.
What is a mutex(mutual exclusion) is used to prevent multiple threads or processes from accessing a shared resource resource simultaneously which can lead to race conditions or data corruption.
What is a mutex object though? Microsoft-Mutex-Object
CreateFile and MoveFile are two functions that seem quite strange for now. Why does the software need to create a file and move it? Besides that, there is CreateEvent function , which doesn’t just imply a function to create and manipulate the path of a file, but also creates or opens events, making the software seem quite suspicious at the moment.
Yeah, creating and moving files isn’t really a sign of malicious activity, but we’ve got Mutexes, anti-debug techniques, and besides that, there are so many malware variants that have used
CreateEventW
. Therefore, we have something like proof that the software is starting to look more suspicious, not to mention theGetSystemInfo
function that is called.

CreateEventW:
An event object is a sync. primitive that allows threats to communicate with each other. Usually used for coordinating activities between threads or processes.
As for the last function it checks if there is any debuggers attached to the program. I talked about it in my post : Introduction-Anti-Debugging
[I’m not going to analyze each library using theory, as I mentioned in the first part of the blog. Instead, we can actually read some static code, which may give a better interpretation of everything.]
As for now, i’m going to open IDA Pro and analyze some of the code functions.

This is will be the main block of code that you’re going to see, as we can see there is the __fastcall convention and it’s called main.
As for the explanation of how calling conventions works here is a source: Calling-Conventions
We have a few subroutines here so let’s check which is the main one, the last one: sub_1400A3F80 seems more of a block of code that works with the exception handles but we don’t really need it so let’s search for another subroutine. And boom we have only 2 of them so if it’s not the last one it’s the first one in this case, there are other few methods to search for the main block of the code but we’re already here so let’s search what sub_14000C240 hides from us.

Isn’t that beautiful nor the worst.
But just as some additional information: The disassembly may appear messy, with poorly structured functions, excessive jumps, or an overly complex control flow. This can happen due to the way Rust compiles code and interacts with the Windows toolchain.
; __unwind { // __CxxFrameHandler3
push rbp
push r15
push r14
push r13
push r12
push rsi
push rdi
push rbx
sub rsp, 968h
lea rbp, [rsp+80h]
movaps [rbp+920h+var_50], xmm6
mov [rbp+920h+var_58], 0FFFFFFFFFFFFFFFEh
lea rcx, [rbp+920h+var_978] ; Dst
call sub_14000D540
mov [rbp+920h+var_5B], 1
So this seems to be the prologue as we can see that we’re saving the non-volatile registers , we can see the sub
instruction that indicates that it reserve stack space for local variables, and we can observe lea rbp, [rsp+80h]
that indicated the adjustment of the frame pointer
As i’ve been looking throughout the code i saw some stack and exception handling as for now that doesn’t really tell us anything about the malware and what is his intention buuut, i found something interesting and i will share it here.
cmp dword ptr [rbp+920h+var_978], 2
jnz loc_14000CF03
If the Zero Flag (ZF) is set to 0, the jump will be made to loc_14000CF03
. We need the ZF to be 1 in order to jump to the code below.
mov rax, qword ptr [rbp+920h+var_978+8]
test rax, rax
jz loc_14000CFA3
Jz means that the ZF is supposed to be 0 in order to make the jump. Somehow if the ZF is 0 it brings us to the loc_14… yeah you got it. :)
loc_14000CFA3:
lea rdx, aTheFollowingRe_1 ; "The following required argument was not"...
mov r8d, 35h ; '5'
mov cl, 9
call sub_140036F30
mov rsi, rax
cmp [rbp+920h+var_80], 0
jz short loc_14000CFDD
Even though we cannot really call it an IOC, it tells us something like: We can run the malware from cmd and use some arguments.

As we can see in the image, there are multiple blocks that tell us that there is an argument that wasn’t provided. For now, I’m not sure why there are three blocks indicating that the argument was not provided.
aTheFollowingRe_1 db 'The following required argument was not provided: srv'

Thanks to IDA and not only there is DIE, there is Pe-Bear and so many tools that show us the strings inside a software.
Ughhh yeah breaking the rules but isn’t that bad , isn’t it?
C:\Users\ldmd>C:\Users\ldmd\Desktop\3fef5c7fa519f5384de6f61c954ead6dfd4da727005bfec954dc801bd120a938.exe -srv
error: the following required arguments were not provided:
--key <K>
Usage: 3fef5c7fa519f5384de6f61c954ead6dfd4da727005bfec954dc801bd120a938.exe --key <K> --srv <SRV>
So, of course, I messed up the command to check the purpose of these args. Also, ‘k’ stands for key, and ‘srv’ may come from the server, which I realized after using the —help argument.
Usage: 3fef5c7fa519f5384de6f61c954ead6dfd4da727005bfec954dc801bd120a938.exe [OPTIONS] --key <K>
Options:
-k, --key <K> Password
-p, --path <P> Path [default: C:\]
-s, --srv <SRV> Service stop [default: 1]
-h, --help Print help
Now, this information makes me think that the creator may have wanted to write a debug part to check if the malware works, as the -k set might be used to decrypt the files. Still, this is just an assumption. Let’s take a note, the malware accepts parameters via command-line arguments to control it’s behavior.
Just a note, it seems that the assembly code uses lea
instruction to push the arguments, in the most cases push
or mov
is used to load the arguments of a function.
mov qword ptr [rbp+920h+var_978], rdi
lea rax, sub_140009EC0
mov qword ptr [rbp+920h+var_978+8], rax
lea rax, off_1400E7108 ; "Password - "
mov qword ptr [rbp+920h+Dst], rax
mov qword ptr [rbp+920h+Dst+8], 2
mov [rbp+920h+var_3C0], 0
mov qword ptr [rbp+920h+var_3D0], rbx
mov qword ptr [rbp+920h+var_3D0+8], 1
mov [rbp+920h+var_59], 1
loc_14000C868:
; try {
lea rcx, [rbp+920h+Dst]
call sub_1400A8A00
RBP is the base pointer registers used for local variables i mean static, but that’s not my main point, as you can observe rbp is used to reference local variables.
lea rcx, [rbp+920h+Dst]
Rcx is the registers that is pushed as a function parameter. If we double click on the subroutine we get into the subroutine’s code.
__int64 __fastcall sub_1400A8A00(_QWORD)
As we can see our subroutines has 1 parameter. Besides that, as a reminder, IDA isn’t perfect, and sometimes it may not load parameters or detect the calling convention
Let’s analyze another part of code:
lea rax, aStdout ; "stdout"
mov [rbp+20h+var_30], rax
mov [rbp+20h+var_28], 6
call sub_1400A87F0
The mov
instruction is used to set up function parameters by storing them in memory locations relative to the base pointer (rbp
). In this context, var_30
holds the address of the string we want to print ( “stdout”), and var_28
stores its length.
[Assumptions]
As for now i will come back at this part if i will find anything interesting but let’s analyze those functions like CreateFile, MoveFile and so on to check what do they use them.
GetSystemInfo function
This function retrieve the information about the current system
void GetSystemInfo(
[out] LPSYSTEM_INFO lpSystemInfo
);
lpSystemInfo
is a pointer to a SYSTEM_INFO structure that receives the information.
Let’s analyze the SYSTEM_INFO structure:
typedef struct _SYSTEM_INFO {
union {
DWORD dwOemId;
struct {
WORD wProcessorArchitecture;
WORD wReserved;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO, *LPSYSTEM_INFO;
Member Name | Description |
---|---|
**dwOemId** | Obsolete member – No longer in use. |
**wProcessorArchitecture** | Specifies the processor architecture of the installed OS (e.g., x86, x64). |
**dwPageSize** | Defines the smallest chunk of memory the system can manage or allocate, typically in bytes. |
**lpMinimumApplicationAddress** | Denotes the highest memory address that an application can use. |
**dwActiveProcessorMask** | Indicates which processors are available, using a bitmask where each bit corresponds to a processor. |
**dwNumberOfProcessors** | The total number of logical processors that are active and available on the system. |
**dwProcessorType** | Obsolete member – No longer in use. |
**dwAllocationGranularity** | Defines the smallest block of memory that can be allocated by the system. |
**wProcessorLevel** | Represents the generation of the processor, often indicating the family or type of CPU. |
**wProcessorRevision** | Provides details on the specific revision of the processor, useful for identifying minor changes. |
sub_1400A4DB0 proc near
.....
movaps xmmword ptr [rsp+58h+SystemInfo.dwNumberOfProcessors], xmm0
movaps xmmword ptr [rsp+58h+SystemInfo.lpMaximumApplicationAddress], xmm0
movaps xmmword ptr [rsp+58h+SystemInfo], xmm0
lea rcx, [rsp+58h+SystemInfo] ; lpSystemInfo
call cs:GetSystemInfo
mov eax, [rsp+58h+SystemInfo.dwNumberOfProcessors]
...
The MOVAPS instruction (Move Aligned Packed Single-Precision Floating-Point Values) moves a 128-bit chunk of data containing four single-precision floating-point values (each 32-bit) from one source to another.That is, it’s used to:
- Load an XMM register from a 128-bit memory location.
- Store the contents of an XMM register to memory.
- Move data between XMM registers.
The most critical thing about MOVAPS is that it requires the memory address to be aligned to a 16-byte boundary that is, the
movaps xmmword ptr [rsp+58h+SystemInfo.dwNumberOfProcessors], xmm0
movaps xmmword ptr [rsp+58h+SystemInfo.lpMaximumApplicationAddress], xmm0
movaps xmmword ptr [rsp+58h+SystemInfo], xmm0
These 3 lines of code clear some memory before calling GetSystemInfo. The memory being cleared is where the system information will be stored.
lea rcx, [rsp+58h+SystemInfo] ; lpSystemInfo
This prepares the function argument for GetSystemInfo.
Think more of: We need to tell GetSystemInfo where to put the system details. So we grab, the address of a blank form and have it over.
call cs:GetSystemInfo
This calls the GetSystemInfo function.
cs
refers to the Code Segment. So cs:function
means that the call is made to a function located within the code segment.
The function will fill the SystemInfo
structure with details about the system.
mov eax, [rsp+58h+SystemInfo.dwNumberOfProcessors]
This extract the number of processors from the structure and puts it into the eax register.
In GDB Basic i talked about rax and eax. GDB-Basic Therefore test rax, rax is almost as the same as test eax, eax but as we know we’re in a x64 app not in a x32.
test rax, rax
jz short loc_1400A4DE6
Here we have a condition jump and you can translate like jump if ZF == 1
, in other words if RAX == 0 then the ZF is set.
If ZF is set our code is beign redirected to loc_1400A4DE6:
lea rax, off_1400FF408 ; “The number of hardware threads is not known for the target platfom” mov [rsi+8], rax mov eax, 1
So if the number of hardware threads is not known then mov 1 inside eax which cand be a signal of an error.
Otherise:
```cpp
mov [rsi+8], rax
xor eax, eax
jmp short loc_1400A4DF6
So, it may be used to detect if the sandbox and we stores the value of rax which it seems to holds the number of proc. in rsi+8 or we could say it stores the value of rax at memory location [rsi+8]
xox just sets eax 0 much faster than mom eax, 0.
Be pressing the magic key F5 we can read the disassembled code in something much more readable to humans.
memset(&SystemInfo, 0, sizeof(SystemInfo));
GetSystemInfo(&SystemInfo);
if ( SystemInfo.dwNumberOfProcessors )
{
a1[1] = SystemInfo.dwNumberOfProcessors;
v2 = 0LL;
}
else
{
a1[1] = (__int64)&off_1400FF408; // "The number of hardware threads is not known for the target platform"
v2 = 1LL;
}
*a1 = v2;
return a1;
As we can see we were right and eax (32 bit) or rax is the values that is returned. As for now it seems to be a sandbox evasion but let’s check where si this function used.

sub__bla_bla it’s kinda hard to read and keep them in our memory, let me change the name by pressing n.

lea rcx, [rbp+20h+var_40]
call sandbox_evasion_maybe
cmp [rbp+20h+var_40], 0
jz loc_14002A975
[rbp+20h+var_40] - it seems to be the variables that is set based on the return value. Therefore if RAX would be 0 then [rbp+20h+var_40] = 0. The next instruction says something like:
if ([rbp + 0x20 + var_40] == 0) {
goto exit_block_code;
}
As of now, it seems that the malware tries to reduce its activity when it is run in a sandbox environment. Therefore, it makes sense why ANY.RUN reports that there isn’t anything malicious.
[Headache]
Let’s check the next function “CreateEventW”
CreateEventW
is a Windows API function used to create or open an event object. Events are synchronization primitives that allow threads to signal each other when a certain condition is met.
HANDLE CreateEventW(
[in, optional] LPSECURITY_ATTRIBUTES lpEventAttributes,
[in] BOOL bManualReset,
[in] BOOL bInitialState,
[in, optional] LPCWSTR lpName
);
Member Name | Description |
---|---|
**pEventAttributes** | A pointer to a SECURITY_ATTRIBUTES structure, specifying security settings for the event. |
**bManualReset** | Defines whether the event is manual-reset or auto-reset: |
- True → manual-reset: The event stays signaled until explicitly reset using ResetEvent . | |
- False → auto-reset: The event automatically resets to non-signaled after releasing a waiting thread. | |
**bInitialState** | Determines the initial state of the event: |
- TRUE → The event starts in the signaled state, allowing threads to proceed immediately. | |
- FALSE → The event starts in the non-signaled state, and threads must wait for it to be signaled. | |
**lpName** | Name of the event. If NULL, the event is unnamed. |
xor ecx, ecx ; lpEventAttributes
mov edx, 1 ; bManualReset
mov r8d, 1 ; bInitialState
xor r9d, r9d ; lpName
call cs:CreateEventW
----
xor ecx, ecx => lpEventAttributes = NULL
mov edx, 1 => bManualReset = manual-reset-event
xor r9d, r9d => lpname=> Null (The event cannot be shared across processes; only threads within the same process can use it.)
If the function succeeds, the ret value is a handle to the event object, otherwise the return value is NULL;
By analyzing the created event is somehow related to the heap allocation which is more related to the process itself as for now i can’t really say a lot. [Beta]
MoveFileExW
BOOL MoveFileW(
[in] LPCWSTR lpExistingFileName,
[in] LPCWSTR lpNewFileName
);
lpExistingFileName
-> This is a pointer to a wide-character string (UTF-16 encoded) that specifies the path of the file or directory to be moved or renamed.
lpNewFileName
-> This is a pointer to a wide-character string (UTF-16 encoded) that specifies the new path or name for the file or directory.
mov r15, [rbp+30h+lpExistingFileName]
mov rcx, r15 ; lpExistingFileName
mov rdx, rdi ; lpNewFileName
mov r8d, 1 ; dwFlags
call cs:MoveFileExW ; ;0x1 -> MOVEFILE_REPLACE_EXISTING
test eax, eax
This function attempts to move or rename the file/directory, with the flag MOVEFILE_REPLACE_EXISTING
specifying that the destination should be overwritten if it exists.
Still based on the code there isn’t such ifnormation to tell what;s moved or anything like this.
Another interesting thing is that it generates a file called HelloReadme.txt

The content of the file is:

For now i will move on to dynamic analysis, there is still information that can be extracted. The software checks if the pages are valid, uses DeviceIoControl
to send a control code directly to a specified device driver, prompting it to perform the corresponding operation, and much more.
Dynamic Analysis
The sad reality is that this ransomware doesn’t actually run by itself; it needs external help to execute. Do you remember when I mentioned that the malware accepts parameters via the command line? ->Bingo. So what happens if I run the ransomware? Nothing. It will either close or perform some actions, but nothing significant.
Now this doesn’t mean we’ll stop here and say: oh then here is the sign to stop.
First of all, I’m interested in what the program does when I run it, even without providing arguments. For this, there is a tool called procman
that can help us.
By pressing Ctrl + L
, we can set a filter based on the process name. If the process name is found active, it will register every action the process performs.

”There are quite a few things it does even though it hasn’t started yet. Even though there are many SUCCESS messages, they are almost all related to creating, closing, opening, loading images, and closing threads.”
In order to debug the code i had to make a shortcut “Ransomware.exe” -k 3 Now when you run the exe the memory addresses from IDA PRO and the exe are not going to be the same maybe not even mine or yours. So what can you do is to go to Memory Map and search for you exe copy the address go to ida pro->Edit->Segments->Rebase and paste 0xcopied_value there and press the button to rebase.
->From here it will be kinda hard for me to give any screenshots but i will try my best.
```"ERROR: The process \"sql*\" not found.\r\n"
"ERROR: The process \"oracle*\" not found.\r\n"
"ERROR: The process \"ocssd*\" not found.\r\n"
"ERROR: The process \"dbsnmp*\" not found.\r\n"
"ERROR: The process \"synctime*\" not found.\r\n"
"ERROR: The process \"agntsvc*\" not found.\r\n"
"ERROR: The process \"isqlplussvc*\" not found.\r\n"
[I found the print function - yeah it's useless]
if you wish to check it:
loc_7FF6D4CEA278:
; try {
lea rsi, [rbp+12180h+var_21D0]
mov rcx, rsi
call maybee_print
“Here’s a tip for when you’re debugging the program.

Keep an eye on the RFlags to observe any changes. Pay attention to which registers change their values and note what those values are. This can help you understand the program’s behavior and what it’s trying to do.
Right now, I’m analyzing the “sub_7FF6D4D09020” function, and it seems this is the main “ransomware thing,” if we can call it that. :))
The fastcall convention in x64 uses 4 registers as arguments: RCX, RDX, R8, and R9.
One of the functions run by our dear non-malicious software is…
vssadmin.exe delete shadows /all /quiet /C
The command vssadmin.exe delete shadows /all /quiet /C
is used to delete all shadow copies (also known as Volume Shadow Copies) on the specified volume (in this case, the C: drive) without displaying any messages or prompts. This command is often used by ransomware to remove backup copies, making it harder to recover files.
Besides that there is another command that is executed by the software:
```lea rcx, [rax+1]
mov [rbp+440h+var_1E8], rcx
shl rax, 4
movups xmm6, [rbp+rax+440h+var_3D8]
mov r8d, 3
mov rcx, r12
lea rdx, aCmd ; "cmd"
call sub_7FF6D4D96F80
mov r8d, 0C8h ; MaxCount
mov rcx, r14 ; Dst
mov rdx, r12 ; Src
call memcpy
lea rax, aC_0 ; "/C"
mov [rbp+440h+Src], rax
mov [rbp+440h+var_1D0], 2
lea rax, aCmdExe ; "cmd.exe"
mov [rbp+440h+var_1C8], rax
mov [rbp+440h+var_1C0], 7
lea rax, aC_1 ; "/c"
mov [rbp+440h+var_1B8], rax
mov [rbp+440h+var_1B0], 2
lea rax, aTaskkill ; "taskkill"
mov [rbp+440h+var_1A8], rax
mov [rbp+440h+var_1A0], 8
lea rax, asc_7FF6D4DC8C22 ; "/f"
mov [rbp+440h+var_198], rax
mov [rbp+440h+var_190], 2
lea rax, aIm ; "/im"
mov qword ptr [rbp+440h+var_188], rax
mov qword ptr [rbp+440h+var_188+8], 3
movups [rbp+440h+var_178], xmm6
mov [rbp+440h+var_160], 7
xor eax, eax
nop dword ptr [rax+rax+00000000h]
Reading assembly isn’t exactly pleasing, is it?

mov r8d, 3
mov rcx, r12
lea rdx, aCmd ; "cmd"
call sub_7FF6D4D96F80
Based on the call subroutine:
- r8d is actually MaxCount.
- rcx appears to be a pointer to some structure.
- rdx is cmd.
There happens some memory manipulation so i will mark it as [Beta].
Besides that there is another command that is executed by the software:
lea rdx, aCmd ; "cmd"
lea rsi, [rbp+440h+a1]
mov r8d, 3 ; MaxCount
mov rcx, rsi ; a1
call sub_7FF6D4D96F80
lea rax, aC_0 ; "/C"
mov [rbp+440h+Src], rax
mov [rbp+440h+var_1D0], 2
lea rax, aCmdExe ; "cmd.exe"
mov [rbp+440h+var_1C8], rax
mov [rbp+440h+var_1C0], 7
lea rax, aC_1 ; "/c"
mov [rbp+440h+var_1B8], rax
mov [rbp+440h+var_1B0], 2
lea rax, aPowershell ; "powershell"
mov [rbp+440h+var_1A8], rax
mov [rbp+440h+var_1A0], 0Ah
lea rax, aCommand ; "-command "
mov [rbp+440h+var_198], rax
mov [rbp+440h+var_190], 9
lea rax, aGetVmStopVmFor ; "\"Get-VM | Stop-VM -Force\""
mov qword ptr [rbp+440h+var_188], rax
mov qword ptr [rbp+440h+var_188+8], 19h
mov qword ptr [rbp+440h+var_178+8], 6
Again, we can find the same call sub_7FF6D4D96F80. It may be used to write the command strings into a specific memory location, obfuscate its activities, or prepare data structures that are going to be used afterward.
Based on those 2 block of code we can observe the commands that are executed:
cmd.exe /C "powershell -command \"Get-VM | Stop-VM -Force\""
This command is running cmd.exe with the /C option to execute the specified command and then terminate. The command being executed is powershell -command “Get-VM | Stop-VM -Force”, which uses PowerShell to stop all virtual machines (VMs) forcibly.
and
cmd.exe /C "taskkill /f /im <process_name>"
This command runs cmd.exe with the /C option to execute the specified command and then terminate. The command being executed is taskkill /f /im <process_name>, which uses the taskkill command to forcefully (/f) terminate the specified process (/im <process_name>).
Something interesting though is that there is something that is kinda executed a little bit different:
cmd.exe/cpowershell-command \"Get-VM | Stop-VM -Force\"taskkill/f/imnetstopRAYON_NUM_THREADSRAYON_RS_NUM_CPUSfailed to write whole buffer
This is stored in rbp+x which means that it’s stored i nthe mrmorty ofcourse but this is the command that will be executed. It’ not as readeable as it shpuld be but we can assume that there is the third command executed okay not assume but observe:net stop
But there are 2 keywords that are interesting: RAYON_NUM_THREADS and RAYON_RS_NUM_CPUS.
As i did some research are some variables that are used in the Rayon library.
Old environment variable: RAYON_NUM_THREADS is a one-to-one replacement of the now deprecated RAYON_RS_NUM_CPUS environment variable. If both variables are specified, RAYON_NUM_THREADS will be preferred.
These are used in struct.ThreadPoolBuilder so it seems that this library is used to work with threads, stack, handlers and so on.
Do you remember about “sub_7FF6D4D97070” that i mentioned earlier, take a look here:
loc_7FF6D4D09857:
lea rax, aSql_0 ; "sql*"
mov [rbp+440h+a1], rax
mov [rbp+440h+a1+8], 4
lea rax, aOracle ; "oracle*"
mov [rbp+440h+var_3C8], rax
mov [rbp+440h+var_3C0], 7
lea rax, aOcssd ; "ocssd*"
mov [rbp+440h+var_3B8], rax
mov [rbp+440h+var_3B0], 6
lea rax, aDbsnmp ; "dbsnmp*"
mov [rbp+440h+var_3A8], rax
mov [rbp+440h+var_3A0], 7
lea rax, aSynctime ; "synctime*"
mov [rbp+440h+var_398], rax
mov [rbp+440h+var_390], 9
lea rax, aAgntsvc ; "agntsvc*"
mov [rbp+440h+var_388], rax
mov [rbp+440h+var_380], 8
lea rax, aIsqlplussvc ; "isqlplussvc*"
mov [rbp+440h+var_378], rax
mov [rbp+440h+var_370], 0Ch
lea rax, aXfssvccon ; "xfssvccon*"
mov [rbp+440h+var_368], rax
mov [rbp+440h+var_360], 0Ah
lea rax, aMydesktopservi ; "mydesktopservice*"
mov [rbp+440h+var_358], rax
mov [rbp+440h+var_350], 11h
lea rax, aOcautoupds ; "ocautoupds*"
mov [rbp+440h+var_348], rax
mov [rbp+440h+var_340], 0Bh
lea rax, aEncsvc ; "encsvc*"
mov [rbp+440h+var_338], rax
mov [rbp+440h+var_330], 7
lea rax, aFirefox ; "firefox*"
mov [rbp+440h+var_328], rax
mov [rbp+440h+var_320], 8
lea rax, aTbirdconfig ; "tbirdconfig*"
mov [rbp+440h+var_318], rax
mov [rbp+440h+var_310], 0Ch
lea rax, aMydesktopqos ; "mydesktopqos*"
mov [rbp+440h+var_308], rax
mov [rbp+440h+var_300], 0Dh
lea rax, aOcomm ; "ocomm*"
mov [rbp+440h+var_2F8], rax
mov [rbp+440h+var_2F0], 6
lea rax, aDben50 ; "dben50*"
mov [rbp+440h+var_2E8], rax
mov [rbp+440h+var_2E0], 7
lea rax, aSqbcoreservice ; "sqbcoreservice*"
mov [rbp+440h+var_2D8], rax
mov [rbp+440h+var_2D0], 0Fh
lea rax, aExcel ; "excel*"
mov [rbp+440h+var_2C8], rax
mov [rbp+440h+var_2C0], 6
lea rax, aInfopath ; "infopath*"
mov [rbp+440h+var_2B8], rax
mov [rbp+440h+var_2B0], 9
lea rax, aMsaccess ; "msaccess*"
mov [rbp+440h+var_2A8], rax
mov [rbp+440h+var_2A0], 9
lea rax, aMspu ; "mspu*"
mov [rbp+440h+var_298], rax
mov [rbp+440h+var_290], 5
lea rax, aOnenote ; "onenote*"
mov [rbp+440h+var_288], rax
mov [rbp+440h+var_280], 8
lea rax, aOutlook ; "outlook*"
mov [rbp+440h+var_278], rax
mov [rbp+440h+var_270], 8
lea rax, aPowerpnt ; "powerpnt*"
mov [rbp+440h+var_268], rax
mov [rbp+440h+var_260], 9
lea rax, aSteam ; "steam*"
mov [rbp+440h+var_258], rax
mov [rbp+440h+var_250], 6
[there are few processes more ]
Is a set of processes and those commands are applied to them. So maybe the sub_7FF6D4D97070
could be i mean it’s related to a rust function to execute cmd code or something like this.
First of all it seems that we load each process name inside a some sort of struct and after that we check if the process is alive or not.
mov rsi, [rbp+440h+var_1D0]
mov r8, [rbp+440h+var_1C8]
mov rbx, [rbp+440h+var_1C0]
mov rax, [rbp+440h+var_1B8]
mov [rbp+440h+var_70], rax
mov r15, [rbp+440h+var_1B0]
; } // starts at 7FF6D4D09CC5
loc_7FF6D4D09D2A:
; try {
lea rcx, [rbp+440h+var_110]
mov [rbp+440h+var_68], rdx
call sub_7FF6D4DA7180
mov r13, r12
mov [rbp+440h+var_5A], 1
; } // starts at 7FF6D4D09D2A
loc_7FF6D4D09D47:
; try {
lea rcx, [rbp+440h+var_F8]
mov r12, rbx
mov rdx, rbx
mov r8, r15
call sub_7FF6D4DA7180
mov rax, [rbp+440h+var_100]
mov [rbp+440h+var_80], rax
movups xmm0, [rbp+440h+var_110]
movaps [rbp+440h+var_90], xmm0
mov rax, [rbp+440h+var_E8]
mov [rbp+440h+var_D0], rax
movups xmm0, [rbp+440h+var_F8]
movaps [rbp+440h+var_E0], xmm0
; } // starts at 7FF6D4D09D47
loc_7FF6D4D09D94:
; try {
lea r15, [rbp+440h+var_90]
mov rcx, r15
mov rbx, rdi
mov rdx, rdi
call sub_7FF6D4DA67D0
...
I'm not going to paste the whole code >:(
This function is quite long, but it’s more like a loop. Imagine you wrote a loop to check if every process exists: if yes, kill it, delete backups, move to the next one, and so on until you’ve gone through every desired process. So we have an array of processes Now this data is based on dynamic analysis and i can’t really upload the same screen of the same function 9000 times. Actually i can sorry :)

Conclusion

How does it work?
- The malware checks if it’s running in a sandbox environment. [False]
As i could observe the program didn’t try to close itself or anything like this.
- IsDebuggerPresent():
The malware may check if it’s being debugged, but I didn’t see any signs of this therefore i could assume that there was no thread to analyze if there is a debugger attached to the process in real time.
- The malware deletes shadow copies.
The malware deletes shadow copies to prevent the user from restoring their files from backups.
- The malware kills processes.
- The malware executes commands to stop VMs[i’m not sure] and kill processes.
- The malware uses the Rayon library to work with threads.
- The VM will crash out because of the encryption 👍🏻[True]
Yeah, I didn’t analyze every function in depth, as there’s a lot to discuss, but I tried my best to generalize them.
Besides that, I’m not sure if GetSystemInfo is used as a sandbox evasion or not. But if that comparison isn’t used for this, it may be used to check if it is a sandbox in order to run without an argument if it’s not a virtual machine. As it seems, the malware is not running without one, at least on my machine.
I will conclude the blog post here. The scope was to analyze the malware and understand its behavior as much as possible. [Yeah, I forgot to break a call to stop the encryption, so my VM crashed.] I didn’t intend to write such a long post, but it seems the malware is much more complex than crackmes, which is understandable. I didn’t go through the process of generating any YARA rules, as there are already pre-existing ones, and I’m actually tired. I will write a blog about how to work with YARA rules in the future.
If you’re a malware analyst, security researcher, or someone who does this as a hobby and have recommendations or feedback, I’m open to them, as this is my first malware-related blog.
Discord: ldmd
References:
Tools: