Book Image

Windows APT Warfare

By : Sheng-Hao Ma
5 (2)
Book Image

Windows APT Warfare

5 (2)
By: Sheng-Hao Ma

Overview of this book

An Advanced Persistent Threat (APT) is a severe form of cyberattack that lies low in the system for a prolonged time and locates and then exploits sensitive information. Preventing APTs requires a strong foundation of basic security techniques combined with effective security monitoring. This book will help you gain a red team perspective on exploiting system design and master techniques to prevent APT attacks. Once you’ve understood the internal design of operating systems, you’ll be ready to get hands-on with red team attacks and, further, learn how to create and compile C source code into an EXE program file. Throughout this book, you’ll explore the inner workings of how Windows systems run and how attackers abuse this knowledge to bypass antivirus products and protection. As you advance, you’ll cover practical examples of malware and online game hacking, such as EXE infection, shellcode development, software packers, UAC bypass, path parser vulnerabilities, and digital signature forgery, gaining expertise in keeping your system safe from this kind of malware. By the end of this book, you’ll be well equipped to implement the red team techniques that you've learned on a victim's computer environment, attempting to bypass security and antivirus products, to test its defense against Windows APT attacks.
Table of Contents (17 chapters)
1
Part 1 – Modern Windows Compiler
5
Part 2 – Windows Process Internals
9
Part 3 – Abuse System Design and Red Team Tips

Running static PE files as dynamic processes

At this point, you have a general idea of how a minimal program is generated, compiled, and packaged into an executable file by the compiler in the static phase. So, the next question is, What does the OS do to get a static program running?

Figure 1.6 shows the process structure of how an EXE program is transformed from a static to a dynamic process under the Windows system:

Figure 1.6 – Dynamic operation of the process hatching flow

Figure 1.6 – Dynamic operation of the process hatching flow

Note that this is different from the process hatching process in the latest version of Windows. For the sake of explanation, we'll ignore the processes of privilege escalation, the patching mechanism, and kernel generation, and only talk about how a static program is correctly parsed and run from a single execution.

On Windows systems, all processes must be hatched by the parent process by interrupting the system function to jump to the kernel level. For example, a parent process is currently trying to run the cmd.exe /c whoami command, which is an attempt to hatch the cmd.exe static file into a dynamic process and assign its parameters to /c whoami.

So, what happens in the whole process? As shown in Figure 1.6, these are the steps:

  1. The parent process makes a request to the kernel with CreateProcess, specifying to generate a new process (child process).
  2. Next, the kernel will produce a new process container and fill the execution code into the container with file mapping. The kernel will create a thread to assign to this child process, which is commonly known as the main thread or GUI thread. At the same time, the kernel will also arrange a block of memory in Userland’s dynamic memory to store two structural blocks: a process environment block (PEB) for recording the current process environment information and a thread environment block (TEB) for recording the first thread environment information. The details of these two structures will be fully introduced in Chapter 2, Process Memory – File Mapping, PE Parser, tinyLinker, and Hollowing, and in Chapter 3, Dynamic API Calling – Thread, Process, and Environment Information.
  3. The NtDLL export function, RtlUserThreadStart, is the main routing function for all threads and is responsible for the necessary initialization of each new thread, such as the generation of structured exception handling (SEH). The first thread of each process, that is, the main thread, will execute NtDLL!LdrInitializeThunk at the user level and enter the NtDLL!LdrpInitializeProcess function after the first execution. It is the executable program loader, responsible for the necessary correction of the PE module loaded into the memory.
  4. After the execution loader completes its correction, it jumps back to the current execution entry (AddressOfEntryPoint), which is the developer’s main function.

Important note

From a code perspective, a thread can be thought of as a person responsible for executing code, and a process can be thought of as a container for loading code.

The kernel layer is responsible for file mapping, which is the process of placing the program content based on the preferred address during the compiling period. For example, if the image base address is 0x400000 and the .text offset is 0x1000, then the file mapping process is essentially a simple matter of requesting a block of memory at the 0x400000 address in the dynamic memory and writing the actual contents of .text to 0x401000.

In fact, the loader function (NtDLL! LdrpInitializeProcess) does not directly call AddressOfEntryPoint after execution; instead, the tasks corrected by the loader and the entry point are treated as two separate threads (in practice, two thread contexts will be opened). NtDLL!NtContinue will be called after the correction and will hand over the task to the entry to continue execution as a thread task schedule.

The entry point of the execution is recorded in NtHeaders→OptionalHeader.AddressOfEntryPoint of the PE structure, but it is not directly equivalent to the main function of the developer. This is for your understanding only. Generally speaking, AddressOfEntryPoint points to the CRTStartup function (C++ Runtime Startup), which is responsible for a series of C/C++ necessity initialization preparations (e.g., cutting arguments into developer-friendly argc and argv inputs, etc.) before calling the developer’s main function.

In this section, we learned how EXE files are incubated from static to dynamically running processes on the Windows system. With the process and thread, and the necessary initialization actions, the program is ready to run.