0xresetti

making blog posts and memes about malware

View on GitHub

Reverse Engineering Notes

This is just a page of notes and important things to me to remember while learning Reverse Engineering

PE Structure

image

Windows Architecture

a49e773f13968e92-1

PE Header

= The PE header provides information to the operating system on how to map the file into memory. The executable code has designated regions that require a different memory protection (RWX)

faff0917056c4dc6

PE Header Example

image

The Stack

12ddbf0c041574ca

Threads, Functions and Stacks

Calling a function

image

Local variables on the Stack

image

Heaps, Handles, and Exceptions.

Basic Windows Ring3 Internal Structures.

Windows APIs

Types of Reversing Tools

VA/RVA/Offset

How to calculate offsets:

VA_1 = 0x00400000
VA_2 = 0x00401000
RVA of VA_2 = VA_2 - VA_1 = 0x00001000

Opcodes and Instructions

There are 3 categories of instructions:

Common Instructions

mov, lea (data movement, data access)
add, sub (arithmetic)
or, and, xor (Logic)
shr, shl (Logic)
ror, rol (Logic)
jmp, jne, jnz, jnb (Control Flow)
push, pop, call, leave, enter, ret (Control Flow)
Instruction                Opcode

mov ecx,[0xaaaaaaaa];      8B 0D AA AA AA AA

Top Tips

image

image

image

Patching Lab #1

image

Here we have a regular program that is looking for a valid input, we can see at address 0x402E76 there is a JNZ (jump if not zero) instruction where a decision is taken regarding an output.

Above that we can see TEST EAX,EAX which checks the value of EAX to see if it is zero or not, if it is NOT then the conditional jump that follows will redirect the execution to the “invalid” message. Otherwise the execution will continue with the good message.

To bypass this, we can change the JNZ instruction to JZ using the “Assemble” command

image

After we edit this and run the program, we can input any key and it becomes valid

image

We can also do the same thing by changing the address of the jump condition to jump to the “valid” memory address (0x402E78) instead of the invalid one (0x402E97

image

Finding Byte Offset & Cracking the Program

I want to patch the byte located at VA 0x00402E77, so that the program is now permanently “cracked” and will always give a “valid” answer no matter what key is inputted.

image

Use ALT+M to get to the memory map and find the first section which is at 0x00401000

image

Right click the PE Header (0x00400000) and click Dump in CPU

The bottom part of the interface will now be filled with human-readable information

image

Scroll down a bit more and you’ll find the first section of the PE Header, the .text section.

image

The comments squared in red are the ones we will need to find the byte offset

The below is the formula to calculate the information

Byte_Offset = Byte_VA - (Image_Base + Section_RVA) + PointerToRawData

So it would be:

0x00402E77 - (0x400000 + 0x1000) + 0x600 = 0x2477

Putting the binary in HxD and scrolling down to 0x2477 brings us to the same hex values we saw in Ollydbg!

image

And at the bottom left we can see the offset is 0x2477

Changing the Hex values to 75 00 and saving the file means we have fully patched the binary!

image

image

And we win!

Exploring the Stack with Lab #2

Because strings this time are XOR encrypted and we can’t just search for string references, we must use the stack while the program is running to see data being passed through it when it has been decrypted.

Using F12 or the Pause icon at the top toolbar of Ollydbg, means we can pause execution of the program and analyse the stack at runtime. After triggering a “This code is invalid!!!” I searched through the stack for this string, once i found it, i ensured this was the correct string since there were many different “This code is invalid!!!” strings on the stack, i confirmed this was my target by seeing the CALL that is made to the MessageBoxW WinAPI function.

image

Right click on the function in the stack you want to go to in CPU, and click “Follow in Disassembler”. I can also use CTRL+G to go straight to the memory address location of the function in CPU.

Scrolling up a bit further we can see some JMP conditional calls and more MessageBox’s. This is probably where most of the decision making is happening:

image

I can set a breakpoint just before the comparison is made to check if the right string has been inputted:

image

After running the program with F9 we should hit the breakpoint and have some information in the stack:

image

As you can see, since the code to decrypt the string has been ran, we can see the correct password for the program in the stack clearly!

0019F4EC 0063B26C UNICODE "Fr0m-sma11-b3g1nn1ng5-c0m3-grea7-th1ng5"

Using this code we can check and see that we get the win

image

I also used HxD to permanently patch this program, I used the Byte Offset calculation formula to get the location of the hex values that needed to be edited

image

image

After this, I saved it separately as a way to track my efforts and skills:

image

Anti-Reversing Tricks #1

For example, if an application is being debugged, then the BeingDebugged member of the PEB structure will be equal to 1, if not, it will be 0.

Most Anti-RE tricks are only effective against Ring3 (Userland) debuggers, unless stated otherwise, or they are related to code execution time-elapsed calculation.

1) Direct Debugger Detection

A). Check -> PEB.BeingDebugged

The figure below demonstrates the layout of the PEB structure:

image

The check is commonly used with WinAPIs, in order to “directly” detect if the application is being debugged. For example, a call to the IsDebuggerPresent API which checks the PEB.BeingDebugged member would detect this.

However, someone can instead implement that type of call in his own code instead of calling it, which makes it more difficult to spot. This is actaully the code executed by Windows when you call that API:

mox eax, dword ptr fs:[18h]      <-- Get address of TEB structure
mov eax, dword pty ds:[eax+30h]  <-- Get address of PEB structure
movzx eax, byte ptr ds:[eax+2]   <-- Get value of PEB.BeingDebuggec
test eax, eax                    <-- if not zero, a debugger has been detected
jne_debugger_detected()

B). Check -> PEB.NtGlobalFlag

This has the same concept as the previous one, but this time we check another member of the PEB structure. THis member is not documented in the figure above, but we know that it is localed at PEB_Start + 68h

image

C). CheckRemoteDebuggerPresent API

This Windows API can be used to detect if the calling process is being debugged through a Ring3 debugger, but also if another process is being debugged.

This API is actually a wrapper around the native API ZwQueryInformationProcess

The following lines of code demonstrate how a call to CheckRemoteDebuggerPresent API is normally performed, which then will also call the ZwQueryInformationProcess native API

push pbDebuggerPresent_address        <- push the address of a 32-bit variable
push Process_handle                   <- push the handle to a process (-1 for the calling process)
call CheckRemoteDebuggerPresent       <- call the API
mov eax, [pbDebuggerPresent_address]  <- check the returned result in the variable
test eax,eax
jne_debuggerfound                     <- if not zero, debugger was found

2) Indirect Debugger Detection

A). OutputDebugString API

This is used to send a string to the debugger itself

If the process is not being debugged the return value in EAX will be 1 in Windows XP and 0 in Windows Vista and above, because of the changes made to Windows Internals.

3 ways to detect a debugger:

B). OpenProcess API

Some debuggers enable the SeDebugPrivilege in their access token. The child process inherits the same privilege level as the parent process, and in this case, we refer to the debugee (the process that is being debugged).

Normally, unless specified by the author, an application will not have that privilege enabled, which means that an attempt to obtain a valid handle by using the OpenProcess API to a system process such as services.exe will fail.

On the other hand, if it succeeds, then we know that some other process escalated its privileges and we can assume that it was a debugger.

3) Windows Debugger Detection

The most commonly used API for obtaining a handle to the top-level window of a running application, through either its title or its class name, is the FindWindow API.

Example:

push 0              <- push NULL as title, we want to detect via class name
push "OLLYDBG"      <- push class name, basically pushing the string address
call FindWindow
test eax,eax
jnz_Olly_Detected   <- if eax!=0 Olly is running.

Sometimes though, the EnumWindows API is used, in order to enumerate all top-level windows on the screen and compare each one against a list of well-known application window names beloning to debuggers and or reversing tools.

Anti-Reversing Tricks #2

1) Process Debugger Detection

This trick is used to verify that no known debugger, disassembler or reversing tool is running at the same time with our application, by retrieving a list of all running processes and evaluating their names. Usually the following Windows APIs are involved:

CreateToolhelp32Snapshot

Obtains a snapshot of all running processes, by using the TH32CS_SNAPPROCESS flag.

Process32First

Obtains information about the first process in the snapshot by filling the PROCESSENTRY32 structure.

2) Parent Process Detection

A process can detect if it’s being debugged by checking the name of its parent process. Usually the check is done and expecting “explorer.exe” which is commonly the parent process of a process started by the user.

The same technique is used as in the previous case, but this time we target the parent process of the process we are interested in.

The usual method involves obtaining the PID (Process IDentifier) of our process, enumerating through the processes snapshot list, locating our process and retrieving the PPID (Parent Process Identifier), and finally going once more through the processes snapshot list to see which process the PID belongs to.

A piece of malware could, for example, only accept “explorer.exe” as a legitimate parent process and act differently if it detects a different parent process.

3) Module Debugger Detection

This has the same goal as above (identifying debuggers), but in this case it does so by retrieving a list of all running processes and then a list of all the loaded modules of every process, such as DLLs, which are commonly used as plugins to add extra functionality to many reversing tools.

Usually the following WIndows APIs are involved:

Once again..

Obtains a snapshot of all running processes, by using the TH32CS_SNAPPROCESS flag.

Obtains information about the first module in the snapshot by filling in the MODULEENTRY32 structure.

This is used to go through the loaded modules as listed after the snapshot is taken.

4) Code Execution Time Detection

This is an efficient and easily implemented anti-reversing technique.

It’s purpose is to evaluate the time elapsed for the execution of the instructions in a specific block of code.

image

Other Windows APIs can be used to get time related information and to achieve the same goals. Some examples are the timeGetTime and QueryPerformaceCounter APIs

Anti-Reversing Tricks #3

Software vs Hardware Breakpoints

A software breakpoint is placed by substituting the byte originally located at that memory address by a software interrupt - specifically the INT 3h interrupt of which the opcode is the 0xCC

When the execution hits a INT 3h instruction, a software breakpoint exception is raised (80000003h) and if the process is being debugged, the debugger will force the execution to stop there.

Software breakpoints can only be used for code under execution and not memory access monitoring.

Hardware breakpoints use the debug registers DR0-DR3. They can be used for different types of memory access monitoring and not to just break the execution on a specific instruction. You can set a Hardware Breakpoint On Access on a specific memory area which will be triggered when the virtual address space inside the process is accessed for read/write.

Hardware breakpoints dont do any code modifications in memory, this makes them very suitable to use in cases where self-modifying code is present. (Or other tricks that detect software breakpoints by calculating checksums of code blocks.)

Software Breakpoint Detection

push 'kernel32.dll'
call LoadLibrary            -> get imagebase of kernel32.dll
push 'VirtualProtect'
push eax
GetProcAddress              -> get address of 'VirtualProtect' API inside exported by kernel32.dll
cmp byte ptr ds:[eax], 0xCC -> check if there is a breakpoint set there.

Hardware Breakpoint Detection

The most common way involves the use of 2 Windows APIs:

1). OpenThread -> Get a handle to the desired thread.

2). GetThreadContext -> Read the current thread context and locate the values of the Debug Registers used to store HW BPs, DR0-DR3

NOTE: DR0-DR3 will be zero if no HW BPs are set.

Ring0 Debuggers & System Monitoring Tools Detection

The most common use involves the usage of the CreateFile Windows API

Such types of tools use drivers with which to communicate using their own named devices:

\.\NTICE -> Softice (Windows NT)

\.\FILEM -> FileMon (Windows NT)

\.\REGSYS -> RegMon (Windows NT)

So if we manage to obtain a valid handle to a specifc device like these above, then we can actually reveal the presence of the driver belonging to that tool.

Structured Exception Handling (SEH)

This is debugger detection through exception generation.

This is mainly used for debugger detection but can be used for redirecting the execution flow under certain circumstances; it is a nice logic obfuscation tool.

image

So, what basically happens in this example is that we set our own exception handler and then we attempt to execute an INT 3h instruction which is the equivalant of a software breakpoint.

Depending on the debugger’s settings, genrally a debugger will think that this is a software breakpoint set by the user so it will handle the generated exception itself and set the EIP to point to the next instruction mox eax,1

The point is, if the process is not being debugged, since an exception is raised, the execution should reach the exception handler from where we can set the EIP accordingly.

Basically, 1 if debugger detected, 0 if not detected.

NOTE: Instead of INT 3h, a call to DebugBreak Windows API can be used which executes an INT 3h instruction.

Unhandled Exception Filter

This is another powerful anti-reversing technique that uses a specific exception handler (normally used when there are no appropriate handlers to handle an exception)

In this case, if a process is being debugged, after the execution of the UnhandledExceptionFilter API, the process will exit instead of continuing execution which, in the context of anti-reversing properties, is very useful.

When the UnhandledExceptionFilter API is called it will itself make a call (through a subroutine) to the ZwQueryInformationProcess API asking for ProcessDebugPort information which will return 0xFFFFFFFF if the process is being debugged by a Ring3 debugger, and 0x0 if it is not.

Basically, according to the MSDN documentation, if it returns a non zero value, the process is being debugged by a Ring3 Debugger.

VM Detection

The use of virtualization environments for application testing and malware analysis is basic math at this point.

Some malware authors do not want their malware being analysed in a VM with no juicy info to steal, so they have functions which detect if the malware process is running inside of a Virtual Machine, if it is, it terminates.

There are many ways to achieve this, but some of them are well-known and widely used.

VMware Detection:

mov eax, 'VMXh'    <- magic number
mov ebx, 0
mov ecx, 0Ah       <- set function number / 0Ah = get VMware version
mov edx, 5658h     <- port number used to communicate with VMware
in eax, dx         <- read a dword from that port
cmp eax, ebx       <- if VMware is present EAX == EBX
je__VMware_detected

This piece of code uses a logical port that is used for communication between the VM and VMware itself in order to get information about the VMware version used.

The concept behind this trick is that in the in eax, dx instruction is a privileged one. We can’t usually execute this instruction from usermode (Ring3)

However, the virtual CPU of VMware will allow the execution of this instruction to communicate with VMware itself.

This trick is always used along with an exception handler since if the application is not running inside VMware, a privileged instruction exception will be raised (C0000096h) and will cause the application to crash without the handler in place.

VirtualPC Detection:

mov ebx, 0
mov eax, 1
db 0Fh, 3Fh, 7, 0Bh // VPC Call
cmp ebx, 0
je__VPC_detected

The virtual CPU in VirtualPC can decode this set of bytes 0Fh, 3Fh, 7, 0Bh as special call instructions which of course dont resolve to valid instructions from a native x86 CPU.

Attempting to execute this virtual instruction in a physical system will cause an illegal instruction exception (C000001Dh). For this reason this trick has to be used along with an associated exception handler.

VirtualBox Detection:

A very simple and easy way to detect VirtualBox is through the window class name of a tray icon that it places in the taskbar.

push 0
push 'VBoxTrayToolWndclass'
call FindWindowA
test eax, eax
jnz_VboxDetected ; if we managed to obtain a valid window handle, VBox was detected.

These aren’t the only ways to detect VM environments, do some research.

Code Obfuscation

Logic Flow Obfuscation

The main goal of this type of obfuscation is to make it difficult to predict and understand when program execution should reach certain conditional branches of code. These branches are the core of the logic and determine which blocks of code are executed based on whether certain conditions meet specific requirements.

These are statements like ‘if’, ‘else’, ‘while’, ‘for, etc. In assembly this would be conditional jumps like ‘jne’, ‘je’, ‘jl’, ‘jg’, etc.

Lil Bit of Malware Analysis Tips

You can use CFF Explorer’s Resource Editor to find extra binaries which might be hidden within the file:

image

You can use “X” key in IDA to travel to xrefs to functions

image

You can use “G” key in IDA to travel to memory addresses

You can use “Shift+;” in IDA to add comments

You can use “F5” key in IDA Pro (only) to bring up the decompiled Pseudo-C code of a function

You can use “Space in IDA to change the views from graph view to text view

You can right/left click on pushed hex values and select “R” for the Readable string option, you can also just press R after clicking the value

image

This thing is a reference label, you can click on it and press X to go to the referenced function as mentioned before

image

image

And we are taken to where the string is referenced:

image

Interesting functions that should be monitored:

Opening a file from the resource section
FindResource
SizeofResource
LoadResource
LockResource
Creating a file
GetEnvironmentVariable
CreateFile
WriteFile
CloseHandle
Sleep
Starting a new process
CreateProcess

Using Ollydbg’s command line at the bottom, you can use the bp CreateProcess command to set breakpoints at common ATT&CK functions.

Utilize wireshark &, fakedns, inetsim and httpd start commands on a separate Linux VM to intercept web requests to Domain C2’s, or use HTTP Toolkit if it wants to play fair.

When doing this, make sure to configure the IPv4, Gateway, and DNS to point to the other Linux VM on the Control Panel

image

When monitoring API calls like ReadFile, always check the function Syntax on the MSDN website, for ReadFile, it is:

BOOL ReadFile(
  [in]                HANDLE       hFile,
  [out]               LPVOID       lpBuffer,
  [in]                DWORD        nNumberOfBytesToRead,
  [out, optional]     LPDWORD      lpNumberOfBytesRead,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

Looking at the CPU registers in memory, we can see after this ReadFile breakpoint is hit, we get the value 10C in the rcx register

image

So the HANDLE = 10C

Going into the “Handles” section in x64dbg, hitting refresh and searching for 10C in the handles section, brings us the file path it is trying to read!

image

Call Stack is useful, you can check where the execution path is for a specific function, the To address ending in 2E3F shows where the ReadFile function will return to, the From is where we are presently paused

image

Using “Run to User code” is also very useful, as if we set a breakpoint on a function like ReadFile, we can use “Run to User code” to get to the code that calls that function, also, now we have ran to the START of the user code, AKA “where the ReadFile function will RETURN to”, we can see the addresses match

image

You can use accept-all-ips start and netcat to intercept requests sent directly to IPv4 C2’s. 3127 is the port number to listen on.

image

x64dbg - Common Commands

What to do when you find a malware sample?

Things to do:

Function calls like LoadResource & FindResourceA from KERNEL32.dll and CreateProcessA could indicate that the malware is extracting something from the resources and is creating a new process with it

image

32-bit vs 64-bit

32-bit CPU registers are different, for example the Base Pointer is EBP for 32-bit where 64-bit is RBP.

CFF Explorer Characteristics will indicate a 32-bit binary as a “32 bit word machine” and a 64-bit binary as “App can handle >2gb address space”

32-bit in Optional Headers have BaseOfData, whereas 64-bit doesnt. 32-bits image base is also 4 bits whereas 64-bits image base is 8 bits.

Overall who fuckin cares it’s either RIP or EIP.

Packers And How They Impact Initial Assessments

Difference between a Packed 32-bit Executable file and unpacked 32-bit Executable file:

image

You can also see Entry points + sections are different, and DetectItEasy also detects that it is packed with UPX. WinAPI calls will also be hidden/there will be less of them (6 when packed vs 80 when unpacked):

image

Furthermore, strings are completely encoded:

image

These types of things usually indicate a packer, even if its not being detected by CFF or the header may have been removed, the binary could always be using a custom inbuilt packer.

Monitoring Malicious API Usage with API Monitor (32bit)

You can use API Monitor to monitor different WinAPI usage that programs are using

These API functions can be found in CFF Explorer

image

When these are used in malware, they are usually used for process injection techniques.

Hit CTRL+F to set checkmarks on common WinAPI functions and they will be monitored in the right window

image

You can also set breakpoints when a certain function is called

image

Here is an example of monitoring process hollowing

image

As you can see the notepad.exe process is created using CreateProcessA and we can see that in the screenshot, this process is going to be used for the process hollowing technique. You can also see the WriteProcessMemory calls which write the sections into the process.

image

You can also see the injected strings of the process that was injected when the notepad process is running with any task manager or process hacker

image

64bit version

How Process Hollowing works (+ Visualizing it):

Here we can see the “victim.exe” process getting loaded by the Process Hollowing executable

image

And this would be the flags for the VirtualAllocEx call that ultimately starts the process hollowing:

image

And here we can see the malicious process being loaded:

image

After loading, the new entry point address for the new loaded process is set with SetThreadContext

image

The process and thread is then resumed with ResumeThread

Manipulating Control Flow

In this exe i have “stage2.exe”, there is a buffer check, which is an area of memory being checked for a particular condition.

When the buffer checks are made (with the jne instructions), we can manipulate the ZF Flag (Zero Flag) by stepping into the jne calls.

image

NOTE: In x86 assembly language, the Zero Flag (ZF) is a status flag that is set if the result of an operation is zero. For example if we failed the buffer check, this flag would be 0, same if we failed to supply a valid license key or something.

2a6e5f4d3cefe899

image

Popped the flag for this one!

Analysing Different Files, Borland Delphi

image

Here I have an obvious malicious binary which is impersonating Malwarebytes, you can see the file size is 2.14MB which is quite small for legitimate setup files, usually smaller payloads are used when a loader is incorporated into the malware. However, the reason for the large file size can still be the following reasons:

Why does it matter how the sample was compiled? Because it will determine how the disassembly will be structured.

Imports

Looking at the Imported DLL files, like kernel32.dll, we can see interesting imported functions like VirtualAlloc which as we know is commonly used for code injection.

image

We can also see imports of possible resource manipulation techniques (i.e. FindResourceA, SizeofResource), and possible Anti-Analysis tricks (i.e. Sleep, GetTickCount). With the imports from Advapi32.dll, we can tell it is going to access registry keys (i.e. RegOpenKeyExA).