DLL | Reverse-Engineering

Ahmet Göker
9 min readOct 2, 2023

--

Hello, everyone! Welcome back to my blog post. Today, I am going to explain how to analyze an EXE file with exported DLLs. I assume that this blog post will not be too complicated. You only need to have basic knowledge of the following:

  • DLL
  • Call conventions
  • Assembly language
  • Programming language

If you are ready to dive deep into this field, let me demonstrate it in a practical manner.

Analyzing the file

It is always important to analyze the file. You should be familiar with the principles of life methodology. Without analyzing the ‘X’ value, you will not be able to understand ‘X’. Therefore, in order to do that

Please be aware: When you see a .exe extension, do not get trapped; you should always analyze the file. Let me do that:

You can drop your target fıle to “detect it easy” to determining types of file.

MZ is used for DOS executable. We can declare that this file is executable… We could also see that the file has not been packed by the author. You might be asking what “packed” is? Let me briefly explaın it:

Executable file packing serves two primary purposes: reducing file size and enhancing protection against reverse engineering. Threat actors often leverage packing techniques for obfuscation, making it considerably more challenging to access and analyze the packed executable file. Consequently, engineers are required to invest additional time and effort in reverse-engineering to understand its functionality and impact.

Awessome! We know that the file is executable as well as not packed. Let me dive into this file…

In order to analyze this file:

  1. Ghidra -> Decompiler
  2. IDA PRO -> Dissassembler
  3. Cutter → Dissassembler

Basic Static analysis

We are going to use basic static analysis technique. Let me explain what basic static analysis is:

Basic static analysis provides a foundation for identifying obvious issues in software, but it may not uncover all possible problems or vulnerabilities. More advanced static analysis tools and techniques, including formal methods and symbolic execution, can be employed for more thorough analysis.

Awesome! As we know, we can execute the program. This goal is finding login name and password. We might be able to find password in the file through string command.

Hmm, it seems that strings are not hidden in this file. Could it be obfuscated? We can use alternative ways to inspect this file. In our research, it is evident that delving into advanced static analysis is imperative for enhancing code comprehension. We should not forget that a file named ‘pwdcheck.dll’ is available

I was about to decompile the file. The code is above and is more readable. I will rename some variables and functions.

FUN_140001589(&DAT_140011000, (float10*)_Str, param_3, param_4);

  • This line is a function call to FUN_140001589 with various arguments, including a pointer to DAT_140011000, a pointer casted to float10* from _Str, and two additional parameters param_3 and param_4.

DLL_check = GetFileAttributesA("pwdcheck.dll");

  • Here, the code uses the GetFileAttributesA function to check the attributes of a file named "pwdcheck.dll" and assigns the result to the variable DLL_check.

Result_check = "The DLL file was not found in the current directory.\n\n";

  • This line initializes the Result_check variable with a string message indicating that the DLL file was not found.

if (DLL_check != 0xffffffff) { ... }

  • This conditional block checks if the DLL_check variable is not equal to the value 0xffffffff. If it's not, it means the "pwdcheck.dll" file was found in the current directory.

password_length = strlen((char*)_Str);

  • This line calculates the length of the string pointed to by _Str (presumably a password) using the strlen function and stores it in the password_length variable.

if (5 < password_length) { ... }

  • This condition checks if the password_length is greater than 5, indicating that the password is at least 6 characters long.

hModule = LoadLibraryA("pwdcheck.dll");

  • If the password is long enough, the code attempts to load the “pwdcheck.dll” library using LoadLibraryA and stores the handle in the hModule variable.

if (hModule == (HMODULE)0x0) { return 0; }

  • This condition checks if the loading of the DLL succeeded. If not, it returns 0, possibly indicating an error.

pwdcheck_proccAddress = GetProcAddress(hModule, "CheckPassword");

  • The code retrieves the address of a function named “CheckPassword” from the loaded DLL using GetProcAddress and stores it in pwdcheck_proccAddress.

IVar1 = (*pwdcheck_proccAddress)((undefined1 (*) [10])local_e0);

  • It calls the “CheckPassword” function with an argument and stores the result in IVar1.

The code then sets the Result_check variable to different messages based on the value of IVar1, suggesting that it's checking the result of the password validation.

  1. FUN_140001540((byte*)Result_check, _Str, param_3, param_4);
  • Another function FUN_140001540 is called with various arguments, including the Result_check.
  1. Finally, the code calls system("pause") to pause the program and then returns 0.

Overall, this code appears to load a DLL (“pwdcheck.dll”), call a function within that DLL to perform a password check, and then display messages based on the result of the check. If the password is too short or if there are issues with loading the DLL or finding the “CheckPassword” function, it displays corresponding error messages.

Hmm, I understand from this code that if the DLL file is not located in the same folder as ‘main.exe,’ it will run, but it cannot be checked. Let me try:

As we expected!

IDA Pro

In this phase, I will try to run my program in debug mode. Using IDA pro and Cutter would be very useful. I am going to disassemble the target program. Let me explain what I mean by “Basic dynamic and advanced dynamic analysis”

Dynamic analysis is a software analysis technique that involves observing the behavior of a program while it is running. It is particularly useful for understanding how a program interacts with its environment, handling data, and responding to various inputs. There are two main levels of dynamic analysis: basic dynamic analysis and advanced dynamic analysis.

In this part, I will use “Advanced dynamic analysis” by running the program, setting breakpoints to see where it will be triggered.

The provided assembly code is from the `ntdll.dll` library, which is a core system library in the Windows operating system. Let’s break down what this code does step by step:

inc rax

This instruction increments the value in the `rax` register by 1.

2. cmp byte ptr [rdx+rax], 0:

It compares the byte at the memory location specified by the sum of `rdx` and `rax` with the value 0.

3. jnz short loc_7FFA9587BDD1:

This is a conditional jump instruction. If the previous comparison (`cmp`) result is not zero, it jumps back to the address `loc_7FFA9587BDD1`, creating a loop. In other words, it continues looping until the byte at the calculated memory location is zero.

4. cmp rax, 0FFFFh:

This instruction compares the value in the rax register with the hexadecimal value `0FFFFh` (65535 in decimal).

5. mov edx, 0FFFEh:

It moves the value 0FFFEh`(65534 in decimal) into the edx register.

6. cmovnb rax, rdx:

This is a conditional move instruction. If the previous comparison (`cmp`) result indicates that `rax` is not below (`nb` stands for “not below”) 0FFFFh, it sets the value of rax to the value in edx. This ensures that rax does not exceed 0FFFFh.

7. mov [rcx], ax:

It moves the lower 16 bits (2 bytes) of the value in the `rax` register into the memory location pointed to by the `rcx` register. This effectively stores the value of rax in memory.

8. inc ax:

This instruction increments the lower 16 bits (2 bytes) of the `rax` register.

9. mov [rcx+2], ax:

It moves the lower 16 bits (2 bytes) of the value in the `rax` register into the memory location pointed to by `rcx` plus 2 bytes. This stores the incremented value in memory.

In summary, this assembly code appears to be a loop that processes data in memory, increments a counter (rax), and stores the counter value in memory. It ensures that the counter value does not exceed 0FFFFh. The loop continues until a byte in memory becomes zero, and then it exits the loop and returns from the function. The exact purpose and context of this code would depend on the larger program or system it is part of.

It is just waiting because of “checkpassword”

Checkpassword DLL | Reverse Engineering

The last step we should take is analyzing the DLL file. I need to reverse DLL files to understand the linking. But before that, let me explain what DLL reverse engineering is:

DLL (Dynamic Link Library) reverse engineering is a process in which individuals or security professionals analyze and understand the inner workings of a dynamic link library. DLLs are commonly used in Windows operating systems to store functions and resources that can be shared among multiple applications. Reverse engineering DLLs can serve various purposes, including security analysis, vulnerability discovery, and software interoperability.

As we know early, the program is unable to use ‘checkpassword’ due to a missing DLL. To understand ‘checkpassword,’ should we reverse the ‘pwdcheck’ DLL?

It has been changed to meaningful name:

I am going to analyze the code:

  1. It calculates the length of the login_name string using the strlen function and stores it in the len_login variable.
  2. It checks if the length of login_name (iVar1) is greater than 0. If not, it directly returns 1, indicating a successful match between an empty login_name and an empty password.
  3. If the length of login_name is greater than 0, it initializes a pointer pcVar2 to the last character of login_name.
  4. It enters a loop that iterates through the characters of login_name in reverse order (from the end of the string to the beginning).
  5. Within the loop, it compares the characters of password and login_name in reverse order. If any characters do not match, it returns 0 (false), indicating a failed password check.
  6. The loop continues until all characters have been compared.
  7. If the loop completes without any mismatches, it returns 1 (true), indicating that the login_name matches the password in reverse order.

In essence, this code checks if the password string matches the login_name string when comparing them character by character in reverse order. If there is any character mismatch or if login_name is empty, it returns 0; otherwise, it returns 1.

To check your password, the program relies on the use of that DLL. Therefore, in order to utilize it, I will enter any login name and the password in reverse order

Awesome!

Thank you for taking the time to explore this technical blog! I hope you found it both informative and engaging. I aimed to provide practical explanations in an accessible manner. Should you have any questions or require further clarification on any topic discussed, please don’t hesitate to reach out to me via social media.

Social Media

Twitter: https://twitter.com/lockpin010_

LinkedIn: https://www.linkedin.com/in/ahmetgoker/

Ahmet | Security Researcher | Sociologist

--

--