[Book] Reverse Engineering Core (리버스 엔지니어링 핵심 원리)

First Post:

Last Update:

Word Count:
10.1k

Read Time:
62 min

El libro

Introduction

This article is used to keep notes and summaries of the book “Reverse Engineering Core”.
The content will be continuously updated as I read through the book.

Reflection

Finally, I have completed this book.

I had always wanted to read this book. Unfortunately, due to personal issues, I was unable to do so for a long time. Therefore, I am glad that I finally finished it.

This book was originally written in Korean. It also has a Simplified Chinese translation. Unfortunately, I have not found an English version. Perhaps this is because there are already many books on reverse engineering written in English.

This book describes the underlying principles of reverse engineering in both theoretical and practical ways.

It introduces how to perform software reverse engineering on the Windows platform using tools such as OllyDbg, WinDbg, and PEView. IDA is rarely discussed. Therefore, this book is especially suitable for readers who want to master OllyDbg.

This book DOES NOT cover kernel debugging.

The book also provides both source code and compiled PE files for all chapters. This is a significant advantage for hands-on practice, because compiling the code yourself may produce different results depending on the environment. In addition, some of the provided code examples are difficult to find on the internet.

This book includes:

  • Reverse engineering on Windows
  • OllyDbg
  • WinDbg
  • PE file format
  • Windows API
  • Techniques for improving reverse engineering skills
  • The author’s experience (the author previously worked for an enterprise developing anti-virus software)
  • Advanced debugging
  • Anti-debugging
  • Advanced anti-debugging
  • Anti-anti-debugging
  • Code injection
  • DLL injection
  • Application patching
  • etc.

This book does not include:

  • Kernel debugging
  • IDA Pro
  • Ghidra
  • Reverse engineering on Linux
  • Reverse engineering of Android APKs
  • Reverse engineering of iOS applications

Prerequisites (Recommended):

  • Basic knowledge of C/C++
  • Basic knowledge of the Win32 API
  • Basic understanding of Windows
  • Passion, patience, and the ability to use Google

You may feel frustrated during the process of reverse engineering. However, the author also mentioned that he experienced similar frustration.

Chapter 1 - Reverse Engineering

Murmur….

Blahblahblah….

This chapter introduces the background and basic knowledge of reverse engineering.

Chapter 2 - Hello World!

2.2 - Debugging

Basic commands of OllyDbg:

Command Hotkey Description
Restart Ctrl+F2 Restart debugging.
Step Into F7 Execute an OP code. If the OP code is CALL, then go into the called code.
Step Over F8 Execute an OP code. If the OP code is CALL, then just execute the code rather then go into the called code.
Execute till Return Ctrl+F9 Keep executing code until it reached RETN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "windows.h"
#include "tchar.h"

int _tmain(int argc, TCHAR *argv[])
{
MessageBox(
NULL,
L"Hello World!",
L"www.reversecre.com",
MB_OK
);

return 0;
}

This is the main() function of HelloWorld.exe

2.3 - Getting Familar with Debugger

Command Hotkey Description
Go to Ctrl+G
Execute till Cursor F4
Comment ;
User-defined comment
Label :
User-defined label
Set/Reset breakpoint F2
Run F9
Show the current EPI *
Show the previous Cursor -
Preview CALL/JMP address Enter

2.4 - Find a Specific Code

  1. Executing Code
  2. Searching with Text: Right click -> Search for -> All referenced text strings
  3. Searching with used APIs: Right click -> Search for -> All intermodular calls

2.5 - Patching the Code

  1. Modifying the Buffer
    Hexdump:

    Modifying, notice that we have to append a null char(00 00) at the end of a string.

    Patched code:

    Save code:

  2. Adding a new string in memory
    Insert a string int memory

    Modify the assembly code

    Patched code:

Chapter 3 - Little Endian

1
2
3
4
BYTE b = 0x12;
WORD w = 0x1234;
DWORD dw = 0x12345678;
char str[] = "abcde";
TYPE Name SIZE Big-Endian Little-Endian
BYTE b 1 [12] [12]
WORD w 2 [12][34] [34][12]
DWORD dw 3 [12][34][56][78] [78][56][34][12]
char[] str 4 [61][62][63][64][65][00] [65][64][63][62][61][00]

main() function:

Hexdump:

Chapter 4 - IA-32 Registers

4.2 - IA-32 Registers

  • General Purpose Registers(32-bit), x8
  • Segment Registers(16-bit), x6
  • Program Status and Control Register(32-bit), x1
  • Instruction Pointer(32-bit), x1

  1. General Purpose Registers
    These type of registers are used for temporary storage, also can be used for arthimetic calculation. Usually, they are used for storing addresses and constants.

    • EAX:
    • EBX:
    • ECX:
    • EDX:
  2. Segment Registers
    Segment is a protection technique in IA-32. The IA-32 architecture divides memory into multiple segments, and allocates start address, range, accessing privilege, etc to them. It also provides paging technique.

    • CS: Code Segment
    • SS: Stack Segment
    • DS: Data Segment
    • ES: Extra (Data) Segment
    • FS: Data Segment
    • GS: Data Segment
  3. Program Status and Control Register

    • EFLAGS

  4. Instruction Pointer

    • EIP

Chapter 5 - Stack

After the PUSH instruction is executed, ESP moves upward ,and its value decreases by 4 bytes.

After the POP EAX instruction is executed, ESP moves downward, and its value increases by 4 bytes.

Thus, we can conclude that:

When data is pushed onto the stack, the value of the stack pointer decreases and it moves upward.\
When data is popped from the stack, the value of the stack pointer increases and it moves downward.

Initially, the stack pointer points to the top of the stack.

Chapter 6 - Analyzing abex’ crackme#1

Patching (Cracking)

Modify the assembly code:
Original

1
JE SHORT 0040103D

Modified
1
JMP 0040103D

Chapter 7 - Stack Frame

7.1 - Stack Frame

A stack frame is a structure used to organize a function’s local variables, parameters, and return address.\
It is accessed using EBP as the base pointer (NOT ESP).

Assembly C Language Type conversion
DWORD PTR SS:[EBP-4] *(DWORD*)(EBP-4) DWORD (4 bytes)
WORD PTR SS:[EBP-4] *(WORD*)(EBP-4) WORD (2 bytes)
BYTE PTR SS:[EBP-4] *(BYTE*)(EBP-4) BYTE

The symbol SS in DWORD PTR SS:\[EBP-4\] stands for Stack Segment.
Although x86 architecture supports segmented memory, modern Windows uses a flat memory model. Therefore, assembly instructions still syntactically specify a segment register, such as SS, DS, or ES.

On 32-bit x86 Windows, these segment registers all refer to the same flat segment, which makes the explicit segment specification effectively meaningless in this context.

Since EBP and ESP are registers that reference the stack, OllyDbg automatically appends the SS segment prefix when displaying stack-based memory operands.

Chapter 8 - abex’ crackme#2

8.2 - VB Engine

abex’ crackme is written in VB (Visual Basic).

A VB (Visual Basic) application is executed by an engine named MSVBVM60.dll (Microsoft Visual Basic Virtual Machine 6.0).

A VB application can be compiled into Native Code (N Code) or Pseudo Code (P Code, or The Thunder Runtime Engine).

All components of a VB application—such as dialogs, controls, forms, modules, and functions—are stored in the form of internal data structures. However, Microsoft has never publicly documented these structures, which makes debugging VB applications more difficult.

8.3 - Debugging

Stub Code:

Indirect Call

1
2
3
00401232   $-FF25 A0104000  JMP DWORD PTR DS:[<&MSVBVM60.#100>]      ;  MSVBVM60.ThunRTMain
00401238 > $ 68 141E4000 PUSH abexcm2-.00401E14 ; => EP
0040123D . E8 F0FFFFFF CALL <JMP.&MSVBVM60.#100> ; call ThunRTMain()

The instruction at 0040123D is used to call the ThunRTMain() function. Instead of calling the function directly, it performs an indirect call through the jump instruction at 00401232.

This is a well-known indirect call technique commonly used by VC++ and the VB compiler.

RT_MainStruct

Microsoft has never publicly documented the RT_MainStruct. However, some experts have fully reverse-engineered it and published their findings online.

ThunRTMain()

Notice that the memory addresses are completely different. This region is inside MSVBVM60.dll

8.4 - Analyzing crackme

Assembly code that it used:

  • TEST: Logical Compare, it is same as AND. However, TEST only changes the values in EFLAGS register.
  • JE: Jump if equal (ZF = 1).

Chapter 9 - Process Explorer

https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer

Chapter 10 - Calling Convention

  • cdecl
  • stdcall
  • fastcall

cdecl is the default method used in C language.

1
2
3
4
5
6
7
8
9
10
11
#include "stdio.h"

int add(int a, int b)
{
return a + b;
}

int main(int argc, char* argv[])
{
return add(1, 2);
}

cdecl


stdcall is commonly used in Win32 API, this is used for clearing the stack by the function caller.

1
2
3
4
5
6
7
8
9
10
#include "stdio.h"

int __stdcall add(int a, int b)
{
return a + b;
}
int main(int argc, char* argv[])
{
return add(1, 2);
}

stdcall

fastcall

Chapter 11 - Lenas Reversing for Newbies

Modify the instruction at 00402FE

Chapter 12 - How to Learn Reverse Engineering

Blah blah blah…

I will write some of the content with my personal idea in “Reflection”

Chapter 13 - PE File Format

13.2 - PE File Format

PE stands for Portable Executable.

A PE file is an executable under x32 platform, it is also know as PE32. A PE+ (or PE32+) is an executable under x64 platform (NOT PE64).

VA & RVA

13.3 - PE Header

DOS Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

//Source: Microsoft Platform SDK - winnt.h (https://github.com/wine-mirror/wine/blob/master/include/winnt.h)
  • e_magic: DOS signature, 4D5A => ASCII: MZ
  • e_lfanew: Offset of NT header.

Here MZ stands for Mark Zbikowski, the engineer who designed DOS executable.

DOS Stub


Run the following command on Windows XP:

1
> debug C:\WINDOWS\NOTEPAD.exe

Although the sentence in the DOS stub states that “This program cannot be run in DOS mode”, notepad.exe can still be executed in a DOS environment. In practice, the DOS loader executes the DOS stub code, which prints the message above and then exits.

The DOS stub is optional in a PE file, but modern development tools usually include it by default. Compilers such as VB, VC++, and Delphi all generate PE files with a DOS stub.

NT Header

1
2
3
4
5
6
7
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

//Source: Microsoft Platform SDK - winnt.h (https://github.com/wine-mirror/wine/blob/master/include/winnt.h)

NT Header: File Header

1
2
3
4
5
6
7
8
9
10
11
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

//Source: Microsoft Platform SDK - winnt.h (https://github.com/wine-mirror/wine/blob/master/include/winnt.h)

There are four important members; file cannot be executed if they are misconfigured.

  1. Machine:\
    Every CPU architecture has its own machine ID.

  2. NumberOfSections:\
    PE file stores code, data and resources in different sections, each with its own attributes. NumberOfSections indicates the total number of sections in the file. This value MUST BE GREATER THAN ZERO.\
    In addition, an executable cannot be loaded if the number of sections does not match the actual section table.
  3. SizeOfOptionalHeader:\
    The last member of structure IMAGE_NT_HEADERS is IMAGE_OPTIONAL_HEADER32. SizeOfOptionalHeader indicates the size of the IMAGE_OPTIONAL_HEADER32 structure.\
    Although IMAGE_OPTIONAL_HEADER is defined as a C structure, the Windows PE loader must still rely on the value of SizeOfOptionalHeader to determine its actual size.\
    In PE32+ files, IMAGE_OPTIONAL_HEADER64 is used instead of IMAGE_OPTIONAL_HEADER32. Since these two structures have different sizes, SizeOfOptionalHeader must explicitly specify the correct size.
  4. Characteristics:\
    This field is used to describe the attributes of the file.

    This value might not contain the 0x0002 (IMAGE_FILE_EXECUTABLE_IMAGE) flags for the files such as *.obj and *.dll.

NT Header: Optional Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//

WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;

//
// NT additional fields.
//

DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

The following members are important. An executable cannot not be loaded if they are misconfigured.

  1. Magic:\
    IMAGE_OPTIONAL_HEADER32 has this value by 10B; IMAGE_OPTIONAL_HEADER64 has this value by 20B.
  2. AddressOfEntry:\
    This field has the RVA value of EP. RVA value indicates the code which is the first priority of executing.
  3. ImageBase:\
    The address range of virtual memory is 0~FFFFFFFF on x32 platform. ImageBase indicates first loaded address of the file.
  4. SectionAlignment, FileAlignment:\

  5. SizeOfImage

  6. SizeOfHeaders
  7. Subsystem
  8. NumberOfRvaAndSizes
  9. DataDirectory

Section Header

13.4 - RVA to RAW

Quiz

Given the following figure, answer the following questions:

  1. RVA = 5000, File Offset = ?\
    Here we assume the ImageBase is 01000000.
    It is located in the first section (.text) sinceThe range of the first section (.text) is: 01001000 ~ 01009000.\
    Thus,
  2. RVA = 13314, File Offset = ?
    RVA = 13314 is located in the third section (.rsrc).\
    Thus,
  3. RVA = ABA8, File Offset = ?
    RVA = ABA8 is located in the second section (.data).\However, RAW address 97A8 is located in the third section. Thus, the given value of RAW by ABA8 is invalid for defining its RAW value. When VirtualSize is larger than SizeOfRawData, the extra region is zero-initialized at load time and does not have a valid file offset.

13.5 - IAT

DLL

Before studying the Import Address Table (IAT), we need to understand DLLs (Dynamic Link Libraries).

In the era of 16-bit DOS, the concept of DLLs did not exist. Instead, applications relied on static libraries, which were linked into the executable at compile time. This approach significantly increased both memory usage and disk space consumption, which was a serious limitation when hardware resources were expensive.

To solve this problem, Windows introduced the concept of DLLs:

  • Instead of embedding library code into the executable, applications import DLLs when needed.
  • Code and resources can be shared among multiple applications through memory mapping.
  • Updating a library only requires replacing the corresponding DLL file.

There are two primary ways to load DLLs:

Explicit Linking:
The application loads the DLL at runtime using functions such as LoadLibrary, and resolves function addresses manually using GetProcAddress. The DLL can be unloaded explicitly when it is no longer needed.

Implicit Linking:
The DLL is loaded automatically by the Windows loader when the application starts, and it remains loaded until the process terminates.

These mechanisms are closely related to the Import Address Table (IAT), which plays a critical role during DLL loading and function resolution.

Notice that the loader calls the API function CreateFileW() via an indirect call rather than a direct call.
The loader obtains the address of the API function from memory address 0x01001104.
The loader calls API functions using this method consistently.

The reason the loader does not call the API function directly (for example, using the instruction CALL 765233B0) is that the author of notepad.exe did not know in advance which platform the program would run on.
This design allows the executable to support multiple platforms and environments.

Another reason is DLL relocation.
Usually, the default ImageBase of a DLL is 0x10000000.
If this address is already occupied by a.dll, then b.dll cannot be loaded at the same ImageBase and must be relocated, for example, to 0x3E000000.

In practice, it cannot be guaranteed that a DLL can be loaded at its preferred ImageBase, because the PE header uses RVAs instead of absolute virtual addresses (VAs).
In contrast, an executable can usually be loaded at its specified ImageBase because it has its own virtual address space.

IMAGE_IMPORT_DESCRIPTOR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; /* 0 for terminating null import descriptor */
DWORD OriginalFirstThunk; /* RVA to original unbound IAT */
} DUMMYUNIONNAME;
DWORD TimeDateStamp; /* 0 if not bound,
* -1 if bound, and real date\time stamp
* in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
* (new BIND)
* otherwise date/time stamp of DLL bound to
* (Old BIND)
*/
DWORD ForwarderChain; /* -1 if no forwarders */
DWORD Name;
/* RVA to IAT (if bound this IAT has actual addresses) */
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

/* Import name entry */
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
char Name[1];
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

The number of imported libraries is equal to the number of IMAGE_IMPORT_DESCRIPTOR

Field Meaning
OriginalFirstThunk Address of INT (RVA)
Name Address of library string name (RVA)
FirstThunk Address of IAT (RVA)

Load into IAT

  1. Obtain the name of the library (for example: “kernel32.dll”) from IID (“IMAGE_IMPORT_DESCRIPTOR”)
  2. LoadLibrary("kernel32.dll)
  3. Read OriginalFirstThunk member from IID, and then obtain the addressof INT (Import Name Table).
  4. Read the values from the array in INT consecutively. Subsequently, obtain the RVA (Relative Virtual Address) of IMAGE_IMPORT_BY_NAME.

Practice

Where is IMAGE_IMPORT_DESCRIPTOR? It is not located in PE header. Instead, it locates in PE body, but its location is stored in PE header.

The value of IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress is the AddressOfEntryPoint (RVA value). IMAGE_IMPORT_DESCRIPTOR is also know as IMAGE_DIRECTORY_TABLE.

  1. Name (Library Name)

  2. OriginalFirstThunk——INT
  3. IMAGE_IMPORT_BY_NAME
  4. FirstThunk——IAT (Import Address Table)

13.6 - EAT

The value of IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress is the RVA value of IMAGE_EXPORT_DIRECTORY

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp; //creation time date stamp
WORD MajorVersion;
WORD MinorVersion;
DWORD Name; //address of library file name
DWORD Base; //ordinal base
DWORD NumberOfFunctions; //number of functions
DWORD NumberOfNames; //number of names
DWORD AddressOfFunctions; //address of function start address array
DWORD AddressOfNames; //address of function name string array
DWORD AddressOfNameOrdinals; //address of ordinal array
} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

Important fields:
| Field | Meaning |
| —- | —- |
| NumberOfFunctions | The total number of exported functions
| NumberOfNames |
| AddressOfFunctions |
| AddressOfNames |
| AddressOfNameOrdinals |

Source: https://baebenzene.tistory.com/5

A PE file obtains the function address from libraries via an API——GetProcAddress()

Chapter 16 - Base Relocation Table

16.1 - PE Relocation

Microsoft introduced ASLR technique since Windows Vista. This technique can improve security of Windows systems.

16.2 - The Operations of PE Relocation

16.3 - The Principles of PE Relocation

1
2
3
4
5
6
typedef struct _IMAGE_BASE_RELOCATION
{
DWORD VirtualAddress;
DWORD SizeOfBlock;
/* WORD TypeOffset[1]; */
} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION;

Chapter 17 - Remove .reloc Section from Executable

Chapter 18 - Analyzing UPackPE Header

Chapter 19 - UPackPE Debugging - Find the OEP

Chapter 20 - Patching

Chapter 21 - Windows Message Hooking

21.2 - Message Hooking

21.3 - SetWindowsHookEx()

1
2
3
4
5
6
HHOOK SetWindowsHookExW(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);

21.4 - Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//Source: https://github.com/reversecore/book
//HookMain.cpp

#include "stdio.h"
#include "conio.h"
#include "windows.h"

#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"

typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();

void main()
{
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
char ch = 0;

//Load KeyHook.dll
hDll = LoadLibraryA(DEF_DLL_NAME);
if( hDll == NULL )
{
printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
return;
}

//Obtain the export functions' addresses.
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);

//Start hooking.
HookStart();

//Wait until user enters 'q'
printf("press 'q' to quit!\n");
while( _getch() != 'q' ) ;

//Stop hooking.
HookStop();

//Unload KeyHook.dll
FreeLibrary(hDll);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//KeyHook.cpp

#include "stdio.h"
#include "windows.h"

#define DEF_PROCESS_NAME "notepad.exe"

HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch( dwReason )
{
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;

case DLL_PROCESS_DETACH:
break;
}

return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szPath[MAX_PATH] = {0,};
char *p = NULL;

if( nCode >= 0 )
{
// bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) )
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');

//Compare to current process. Message will not be passed to the next hook if the current process is notepad.exe.
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
}
}

//If it is not notepad.exe, then the message will be passed to the next hook via calling CallNextHookEx()
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

//Export functions.
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void HookStart()
{
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}

__declspec(dllexport) void HookStop()
{
if( g_hHook )
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif
1
2
3
4
5
LRRESULT CALLBACK KeyboardProc(
int code, //HC_ACTION(0), HC_NOREMOVE(3)
WPARAM wParam, //virtual-key code
LPARAM lParam //extra information
);

Chapter 23 - DLL Injection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:

break;

case DLL_THREAD_ATTACH:
break;

case DLL_THREAD_DETACH:
break;

case DLL_PROCESS_DETACH:
break;
}
}

23.1 - Implementation of DLL Injection

Usually, there are three methods to implement DLL injection:

  • Create remote thread via CreateRemoteThread() API
  • Using registry with modifying the value of AppInit_DLLs
  • SetWindowsHookEx() API

CreateRemoteThread()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//myhack.cpp

#include "windows.h"
#include "tchar.h"

#pragma comment(lib, "urlmon.lib")

#define DEF_URL (L"http://www.naver.com/index.html")
#define DEF_FILE_NAME (L"index.html")

HMODULE g_hMod = NULL;

DWORD WINAPI ThreadProc(LPVOID lParam)
{
TCHAR szPath[_MAX_PATH] = {0,};

if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) )
return FALSE;

TCHAR *p = _tcsrchr( szPath, '\\' );
if( !p )
return FALSE;

_tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME);

URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);

return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
HANDLE hThread = NULL;

g_hMod = (HMODULE)hinstDLL;

switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
OutputDebugString(L"<myhack.dll> Injection!!!");
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
CloseHandle(hThread);
break;
}

return TRUE;
}

Once the DLL is loaded, it prints "myhack.dll Injection!!!". It then calls the function by creating a thread (ThreadProc).

ThreadProc downloads the specified HTML document with the API urlmon!URLDownloadToFile() and saves it as index.html.

Notice that once the DLL file is loaded, DllMain will be called automatically. Hence, once the DLL file above is loaded, the function ThreadProc() will eventually be executed.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//InjectDll.cpp

#include "windows.h"
#include "tchar.h"

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
_tprintf(L"OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
_tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
_tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
_tprintf(L"The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess = NULL, hThread = NULL;
HMODULE hMod = NULL;
LPVOID pRemoteBuf = NULL;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;

// #1. Obtain the Handle of notepad.exe via dwPID.
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
_tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
return FALSE;
}

// #2. Allocate virtual memory in the address space of notepad.exe.
// The size of the allocated memory is based on szDllPath.The size virtual memory space is the size of szDllName
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

// #3. Write the path of myhack.dll (szDllName) into the allocated memory space.
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);

// #4. Obtain the address of LoadLibraryA() API.
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");

// #5. Create a remote thread in notepad.exe.
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);

return TRUE;
}

int _tmain(int argc, TCHAR *argv[])
{
if( argc != 3)
{
_tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]);
return 1;
}

// change privilege
if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
return 1;

// inject dll
if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) )
_tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]);
else
_tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]);

return 0;
}

CreateRemoteThread is used to create a thread in a remote process.
In DLL injection, it is commonly used to make the remote process call LoadLibraryW() so that the DLL is loaded into that process.

1
2
3
4
5
6
7
8
9
HANDLE CreateRemoteThread(
[in] HANDLE hProcess,
[in] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in] LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out] LPDWORD lpThreadId
);

AppInit_DLLs

The principle of this method is that user32.dll automatically loads the DLLs specified in the AppInit_DLLs registry value whenever user32.dll is loaded into a process.

To enable this feature, you need to set LoadAppInit_DLLs to 1 and restart the system.

Warning: This method is powerful because it allows a DLL to be loaded into all processes that load user32.dll. You must be careful with your DLL code; otherwise, your system may become unstable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// myhack2.cpp

#include "windows.h"
#include "tchar.h"

#define DEF_CMD L"c:\\Program Files\\Internet Explorer\\iexplore.exe"
#define DEF_ADDR L"http://www.naver.com"
#define DEF_DST_PROC L"notepad.exe"

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
TCHAR szCmd[MAX_PATH] = {0,};
TCHAR szPath[MAX_PATH] = {0,};
TCHAR *p = NULL;
STARTUPINFO si = {0,};
PROCESS_INFORMATION pi = {0,};

si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;

switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
break;

if( !(p = _tcsrchr(szPath, '\\')) )
break;

if( _tcsicmp(p+1, DEF_DST_PROC) )
break;

wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR);
if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd,
NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi) )
break;

if( pi.hProcess != NULL )
CloseHandle(pi.hProcess);

break;
}

return TRUE;
}
1
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows

SetWindowsHookEx()

Chapter 24 - DLL Ejection

This technique is similar to DLL injection. Instead of using LoadLibrary() to load a DLL, we use FreeLibrary() to unload a DLL via CreateRemoteThread().

Every Windows kernel object has a reference count, which indicates how many references to the object currently exist. For example, the reference count of a.dll becomes 10 if LoadLibrary("a.dll") is called 10 times. Accordingly, FreeLibrary() must be called 10 times to fully unload a.dll.

The reference count is decreased by 1 whenever FreeLibrary() is called. Therefore, we need to pay attention to the value of the reference count.

Chapter 25 - Load DLL File by Modifying the PE File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// EjectDll.exe

#include "windows.h"
#include "tlhelp32.h"
#include "tchar.h"

#define DEF_PROC_NAME (L"notepad.exe")
#define DEF_DLL_NAME (L"myhack.dll")

DWORD FindProcessID(LPCTSTR szProcessName)
{
DWORD dwPID = 0xFFFFFFFF;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;

// Get the snapshot of the system
pe.dwSize = sizeof( PROCESSENTRY32 );
hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

// find process
Process32First(hSnapShot, &pe);
do
{
if(!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
{
dwPID = pe.th32ProcessID;
break;
}
}
while(Process32Next(hSnapShot, &pe));

CloseHandle(hSnapShot);

return dwPID;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
_tprintf(L"OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
_tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
_tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
_tprintf(L"The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
HMODULE hModule = NULL;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;

// dwPID = notepad 프로세스 ID
// TH32CS_SNAPMODULE 파라미터를 이용해서 notepad 프로세스에 로딩된 DLL 이름을 얻음
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);

bMore = Module32First(hSnapshot, &me);
for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
{
if( !_tcsicmp((LPCTSTR)me.szModule, szDllName) ||
!_tcsicmp((LPCTSTR)me.szExePath, szDllName) )
{
bFound = TRUE;
break;
}
}

if( !bFound )
{
CloseHandle(hSnapshot);
return FALSE;
}

if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
_tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
return FALSE;
}

hModule = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, me.modBaseAddr,
0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);

return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
DWORD dwPID = 0xFFFFFFFF;

// find process
dwPID = FindProcessID(DEF_PROC_NAME);
if( dwPID == 0xFFFFFFFF )
{
_tprintf(L"There is no <%s> process!\n", DEF_PROC_NAME);
return 1;
}

_tprintf(L"PID of \"%s\" is %d\n", DEF_PROC_NAME, dwPID);

// change privilege
if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
return 1;

// eject dll
if( EjectDll(dwPID, DEF_DLL_NAME) )
_tprintf(L"EjectDll(%d, \"%s\") success!!!\n", dwPID, DEF_DLL_NAME);
else
_tprintf(L"EjectDll(%d, \"%s\") failed!!!\n", dwPID, DEF_DLL_NAME);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
typedef struct tagMODULEENTRY32 {
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE *modBaseAddr;
DWORD modBaseSize;
HMODULE hModule;
char szModule[MAX_MODULE_NAME32 + 1];
char szExePath[MAX_PATH];
} MODULEENTRY32;

Chapter 26 - PE Tools

Chapter 27 - Code Injection

27.4 - CodeInjection.cpp

MsgBox()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//MsgBox.cpp

#include "windows.h"

DWORD WINAPI ThreadProc(LPVOID lParam)
{
MessageBoxA(NULL, "www.reversecore.com", "ReverseCore", MB_OK);

return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
break;
}

return TRUE;
}

main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(int argc, char *argv[])
{
DWORD dwPID = 0;

if( argc != 2 )
{
printf("\n USAGE : %s <pid>\n", argv[0]);
return 1;
}

// change privilege
if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
return 1;

// code injection
dwPID = (DWORD)atol(argv[1]);
InjectCode(dwPID);

return 0;
}

ThreadProc()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
typedef struct _THREAD_PARAM 
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
char szBuf[4][128]; // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;

typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
LPCSTR lpLibFileName
);

typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
HMODULE hModule,
LPCSTR lpProcName
);

typedef int (WINAPI *PFMESSAGEBOXA)
(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);

DWORD WINAPI ThreadProc(LPVOID lParam)
{
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lParam;
HMODULE hMod = NULL;
FARPROC pFunc = NULL;

// LoadLibrary()
hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); // "user32.dll"
if( !hMod )
return 1;

// GetProcAddress()
pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); // "MessageBoxA"
if( !pFunc )
return 1;

// MessageBoxA()
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

return 0;
}

InjectCode()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
BOOL InjectCode(DWORD dwPID)
{
HMODULE hMod = NULL;
THREAD_PARAM param = {0,};
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
DWORD dwSize = 0;

hMod = GetModuleHandleA("kernel32.dll");

// set THREAD_PARAM
param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
strcpy_s(param.szBuf[0], "user32.dll");
strcpy_s(param.szBuf[1], "MessageBoxA");
strcpy_s(param.szBuf[2], "www.reversecore.com");
strcpy_s(param.szBuf[3], "ReverseCore");

// Open Process
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
dwPID)) ) // dwProcessId
{
printf("OpenProcess() fail : err_code = %d\n", GetLastError());
return FALSE;
}

// Allocation for THREAD_PARAM
dwSize = sizeof(THREAD_PARAM);
if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
dwSize, // dwSize
MEM_COMMIT, // flAllocationType
PAGE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}

if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[0], // lpBaseAddress
(LPVOID)&param, // lpBuffer
dwSize, // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}

// Allocation for ThreadProc()
dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
dwSize, // dwSize
MEM_COMMIT, // flAllocationType
PAGE_EXECUTE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}

if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[1], // lpBaseAddress
(LPVOID)ThreadProc, // lpBuffer
dwSize, // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}

if( !(hThread = CreateRemoteThread(hProcess, // hProcess
NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE)pRemoteBuf[1], // dwStackSize
pRemoteBuf[0], // lpParameter
0, // dwCreationFlags
NULL)) ) // lpThreadId
{
printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
return FALSE;
}

WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);

return TRUE;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;

if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}

if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}

if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}

return TRUE;
}

Chapter 28 - Code Injection via Assembly Code

Chapter 29 - API Hooking

Source: https://maple19out.tistory.com/45

Chapter 30 - API Hooking of Notepad.exe WriteFile()

Debug Events

  • EXCEPTION_DEBUG_EVENT
  • CREATE_THREAD_DEBUG_EVENT
  • CREATE_PROCESS_DEBUG_EVENT
  • EXIT_THREAD_DEBUG_EVENT
  • EXIT_PROCESS_DEBUG_EVENT
  • LOAD_DLL_DEBUG_EVENT
  • UNLOAD_DLL_DEBUG_EVENT
  • OUTPUT_DEBUT_STRING_EVENT
  • RIP_EVENT

EXCEPTION_DEBUG_EVENT

  • EXCEPTION_ACCESS_VIOLATION
  • EXCEPTION_ARRAY_BOUNDS_EXCEEDED
  • EXCEPTION_BREAKPOINT
  • EXCEPTION_DATATYPE_MISALIGNMENT
  • EXCEPTION_FLT_DENORMAL_OPERAND
  • EXCEPTION_FLT_DIVIDE_BY_ZERO
  • EXCEPTION_FLT_INEXACT_RESULT
  • EXCEPTION_FLT_INVALID_OPERATION
  • EXCEPTION_FLT_OVERFLOW
  • EXCEPTION_FLT_STACK_CHECK
  • EXCEPTION_FLT_UNDERFLOW
  • EXCEPTION_FLT_ILLEGAL_INSTRUCTIOIN
  • EXCEPTION_IN_PAGE_ERROR
  • EXCEPTION_INT_DIVIDE_BY_ZERO
  • EXCEPTION_INT_OVERFLOW
  • EXCEPTION_INVALID_DISPOSITION
  • EXCEPTION_NONCONTINUABLE_EXCEPTION
  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_SINGLE_STEP
  • EXCEPTION_STACK_OVERFLOW

30.4 - Practice

30.5 - Principle

WriteFile()

1
2
3
4
5
6
7
BOOL WriteFile(
[in] HANDLE hFile,
[in] LPCVOID lpBuffer,
[in] DWORD nNumberOfBytesToWrite,
[out, optional] LPDWORD lpNumberOfBytesWritten,
[in, out, optional] LPOVERLAPPED lpOverlapped
);


main()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main(int argc, char* argv[])
{
DWORD dwPID;

if( argc != 2 )
{
printf("\nUSAGE : hookdbg.exe <pid>\n");
return 1;
}

//Attach Process
dwPID = atoi(argv[1]);
if( !DebugActiveProcess(dwPID) )
{
printf("DebugActiveProcess(%d) failed!!!\n"
"Error Code = %d\n", dwPID, GetLastError());
return 1;
}

//Debugger loop
DebugLoop();

return 0;
}

DebugLoop()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void DebugLoop()
{
DEBUG_EVENT de;
DWORD dwContinueStatus;

//Waiting for debuggee event.
while( WaitForDebugEvent(&de, INFINITE) )
{
dwContinueStatus = DBG_CONTINUE;

//Debuggee process is created or attached.
if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
OnCreateProcessDebugEvent(&de);
}
//Exception
else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
{
if( OnExceptionDebugEvent(&de) )
continue;
}
//Event of debuggee is terminated.
else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
// debuggee 종료 -> debugger 종료
break;
}

//Continue debuggee.
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
}
}

DEBUG_EVENT structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THREAD_DEBUG_INFO CreateThread;
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
EXIT_THREAD_DEBUG_INFO ExitThread;
EXIT_PROCESS_DEBUG_INFO ExitProcess;
LOAD_DLL_DEBUG_INFO LoadDll;
UNLOAD_DLL_DEBUG_INFO UnloadDll;
OUTPUT_DEBUG_STRING_INFO DebugString;
RIP_INFO RipInfo;
} u;
} DEBUG_EVENT, *LPDEBUG_EVENT;

//Source: https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-debug_event

OnCreateProcessDebugEvent()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
// WriteFile() API 주소 구하기
g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");

// API Hook - WriteFile()
// 첫 번째 byte 를 0xCC (INT 3) 으로 변경
// (orginal byte 는 백업)
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);

return TRUE;
}

OnExceptionDebugEvent()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
CONTEXT ctx;
PBYTE lpBuffer = NULL;
DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;

//BreakPoint exception (INT 3)
if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
{
//if BP address is the address of WriteFile()
if( g_pfWriteFile == per->ExceptionAddress )
{
// #1. Unhook
//0xCC -> original byte
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);

// #2. Thread Context
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(g_cpdi.hThread, &ctx);

// #3. WriteFile(), param 2, 3
// 함수의 파라미터는 해당 프로세스의 스택에 존재함
// param 2 : ESP + 0x8
// param 3 : ESP + 0xC
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
&dwAddrOfBuffer, sizeof(DWORD), NULL);
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
&dwNumOfBytesToWrite, sizeof(DWORD), NULL);

// #4. 임시 버퍼 할당
lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
memset(lpBuffer, 0, dwNumOfBytesToWrite+1);

// #5. WriteFile() 의 버퍼를 임시 버퍼에 복사
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);
printf("\n### original string ###\n%s\n", lpBuffer);

// #6. 소문자 -> 대문자 변환
for( i = 0; i < dwNumOfBytesToWrite; i++ )
{
if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
lpBuffer[i] -= 0x20;
}

printf("\n### converted string ###\n%s\n", lpBuffer);

// #7. 변환된 버퍼를 WriteFile() 버퍼로 복사
WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);

// #8. 임시 버퍼 해제
free(lpBuffer);

// #9. Thread Context 의 EIP 를 WriteFile() 시작으로 변경
// (현재는 WriteFile() + 1 만큼 지나왔음)
ctx.Eip = (DWORD)g_pfWriteFile;
SetThreadContext(g_cpdi.hThread, &ctx);

// #10. Debuggee 프로세스를 진행시킴
ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
Sleep(0);

// #11. API Hook
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);

return TRUE;
}
}

return FALSE;
}

CONTEXT (x86 32-bit)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef struct _CONTEXT {
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;

Chapter 31 - Debugger

OllyDbg

IDA Pro

WinDbg

Chapter 32 - Displaying Korean in calc.exe

1
2
3
4
5
6
BOOL SetWindowTextA(
[in] HWND hWnd,
[in, optional] LPCSTR lpString
);

//Source: https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-setwindowtexta

Here W stands for Wide Character version. A stands for ASCII version.

Important: Notice that Little-endian is used on x32 platform. Thus, you must enter the hexadecimal unicode in reversed. In addition, Unicode is constituted by 2 bytes (16 bits).

32.4 - Practice

1
> InjectDll.exe -e 3192 C:\work\hookiat.dll

32.5 - Code

DllMain()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
// original API 주소 저장
g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"),
"SetWindowTextW");

// # hook
// user32!SetWindowTextW() 를 hookiat!MySetWindowText() 로 후킹
hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
break;

case DLL_PROCESS_DETACH :
// # unhook
// calc.exe 의 IAT 를 원래대로 복원
hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
break;
}

return TRUE;
}

MySetWindowTextW()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
wchar_t* pNum = L"영일이삼사오육칠팔구"; //0123456789 or 零一二三四五六七八九
wchar_t temp[2] = {0,};
int i = 0, nLen = 0, nIndex = 0;

nLen = wcslen(lpString);
for(i = 0; i < nLen; i++)
{
// '수'문자를 '한글'문자로 변환
// lpString 은 wide-character (2 byte) 문자열
if( L'0' <= lpString[i] && lpString[i] <= L'9' )
{
temp[0] = lpString[i];
nIndex = _wtoi(temp);
lpString[i] = pNum[nIndex];
}
}

// user32!SetWindowTextW() API 호출
// (위에서 lpString 버퍼 내용을 변경하였음)
return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}


hook_iat()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
HMODULE hMod;
LPCSTR szLibName;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pThunk;
DWORD dwOldProtect, dwRVA;
PBYTE pAddr;

// hMod, pAddr = ImageBase of calc.exe
// = VA to MZ signature (IMAGE_DOS_HEADER)
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;

// pAddr = VA to PE signature (IMAGE_NT_HEADERS)
pAddr += *((DWORD*)&pAddr[0x3C]);

// dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table
dwRVA = *((DWORD*)&pAddr[0x80]);

// pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

for( ; pImportDesc->Name; pImportDesc++ )
{
// szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name
szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
if( !_stricmp(szLibName, szDllName) )
{
// pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk
// = VA to IAT(Import Address Table)
pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +
pImportDesc->FirstThunk);

// pThunk->u1.Function = VA to API
for( ; pThunk->u1.Function; pThunk++ )
{
if( pThunk->u1.Function == (DWORD)pfnOrg )
{
//Modify the attribute of memory to E/R/W
VirtualProtect((LPVOID)&pThunk->u1.Function,
4,
PAGE_EXECUTE_READWRITE,
&dwOldProtect);

//Modify IAT value (Hooking).
pThunk->u1.Function = (DWORD)pfnNew;

//Redo modification.
VirtualProtect((LPVOID)&pThunk->u1.Function,
4,
dwOldProtect,
&dwOldProtect);

return TRUE;
}
}
}
}

return FALSE;
}


IAT structure

1
2
3
4
5
6
hMod = GetModuleHandle(NULL);       // hMod = ImageBase
pAddr = (PBYTE)hMod; // pAddr = ImageBase
pAddr += *((DWORD*)&pAddr[0x3C]); // pAddr = "PE" signature
dwRVA = *((DWORD*)&pAddr[0x80]); // dwRVA = RVA of IMAGE_IMPORT_DESCRIPTOR

pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

Chapter 33 - Hidden Process

33.3 - Hidden Process

CreateToolhelp32Snapshot()

1
2
3
4
5
6
HANDLE CreateToolhelp32Snapshot(
[in] DWORD dwFlags,
[in] DWORD th32ProcessID
);

//Source: https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot

EnumProcess()

1
2
3
4
5
6
7
BOOL EnumProcesses(
[out] DWORD *lpidProcess,
[in] DWORD cb,
[out] LPDWORD lpcbNeeded
);

//Source: https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses

ZwQuerySystemInformation()

1
2
3
4
5
6
7
8
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);

//Source: https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation

33.4 - Practice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
DWORD dwPID = 0;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;

// Get the snapshot of the system
pe.dwSize = sizeof( PROCESSENTRY32 );
hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

// find process
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;

// 시스템의 안정성을 위해서
// PID 가 100 보다 작은 시스템 프로세스에 대해서는
// DLL Injection 을 수행하지 않는다.
if( dwPID < 100 )
continue;

if( nMode == INJECTION_MODE )
InjectDll(dwPID, szDllPath);
else
EjectDll(dwPID, szDllPath);
}
while( Process32Next(hSnapShot, &pe) );

CloseHandle(hSnapShot);

return TRUE;
}

33.5 - HideProc

Chapter 34 - Global API Hooking

Chapter 35 - How to Choose Your Tools

Chapter 36 - 64-bit

36.1 - 64-bit Processing

Terminology Meaning
AMB64 64-bit processors developed by AMD.
EM64T 64-bit processors developed by Intel. They support AMD64.
Intel64 New name of EM64T.
IA-64 64-bit CPUs developed by Intel and HP.
x86 CPUs of Intel IA-32, IA-16 and IA-8.
x64 AMD64 & Intel64

Chapter 37 - x64 Processor

Chapter 38 - PE32+

IMAGE_NT_HEADERS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; /* "PE"\0\0 */ /* 0x00 */
IMAGE_FILE_HEADER FileHeader; /* 0x04 */
IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

#ifdef _WIN64
typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
#endif

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h


IMAGE_FILE_HEADER

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#define IMAGE_FILE_MACHINE_UNKNOWN      0
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001
#define IMAGE_FILE_MACHINE_I386 0x014c
#define IMAGE_FILE_MACHINE_R3000 0x0162

#define IMAGE_FILE_MACHINE_R4000 0x0166
#define IMAGE_FILE_MACHINE_R10000 0x0168
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169

#define IMAGE_FILE_MACHINE_ALPHA 0x0184
#define IMAGE_FILE_MACHINE_SH3 0x01a2
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4
#define IMAGE_FILE_MACHINE_SH4 0x01a6
#define IMAGE_FILE_MACHINE_SH5 0x01a8
#define IMAGE_FILE_MACHINE_ARM 0x01c0
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01f0

#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200
#define IMAGE_FILE_MACHINE_MIPS16 0x0266
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
#define IMAGE_FILE_MACHINE_TRICORE 0x0520
#define IMAGE_FILE_MACHINE_CEF 0x0cef
#define IMAGE_FILE_MACHINE_EBC 0x0ebc
#define IMAGE_FILE_MACHINE_CHPE_X86 0x3a64
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define IMAGE_FILE_MACHINE_M32R 0x9041
#define IMAGE_FILE_MACHINE_ARM64EC 0xa641
#define IMAGE_FILE_MACHINE_ARM64X 0xa64e
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
#define IMAGE_FILE_MACHINE_RISCV32 0x5032
#define IMAGE_FILE_MACHINE_RISCV64 0x5064
#define IMAGE_FILE_MACHINE_RISCV128 0x5128
#define IMAGE_FILE_MACHINE_CEE 0xc0ee

//Source: //Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h


IMAGE_OPTIONAL_HEADER

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
typedef struct _IMAGE_OPTIONAL_HEADER {

/* Standard fields */

WORD Magic; /* 0x10b or 0x107 */ /* 0x00 */
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint; /* 0x10 */
DWORD BaseOfCode;
DWORD BaseOfData;

/* NT additional fields */

DWORD ImageBase;
DWORD SectionAlignment; /* 0x20 */
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion; /* 0x30 */
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum; /* 0x40 */
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve; /* 0x50 */
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
/* 0xE0 */
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic; /* 0x20b */
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

/* Possible Magic values */
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107

#ifdef _WIN64
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC
#else
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC
#endif

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h


Stack & Heap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString;
ULONGLONG Function;
ULONGLONG Ordinal;
ULONGLONG AddressOfData;
} u1;
} IMAGE_THUNK_DATA64,*PIMAGE_THUNK_DATA64;
#pragma pack(pop)

typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h


IMAGE_IMPORT_DESCRIPTOR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; /* 0 for terminating null import descriptor */
DWORD OriginalFirstThunk; /* RVA to original unbound IAT */
} DUMMYUNIONNAME;
DWORD TimeDateStamp; /* 0 if not bound,
* -1 if bound, and real date\time stamp
* in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
* (new BIND)
* otherwise date/time stamp of DLL bound to
* (Old BIND)
*/
DWORD ForwarderChain; /* -1 if no forwarders */
DWORD Name;
/* RVA to IAT (if bound this IAT has actual addresses) */
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

Chapter 39 - WinDbg

Chapter 40 - 64-bit Debugging

40.1 - Debugger on x64 Platform

OS PE32 PE32+
32-bit OllyDbg, IDA Pro, WinDbg IDA Pro (Disassemble only)
64-bit OllyDbg, IDA Pro, WinDbg IDA Pro, WinDbg

40.2 - x64 Debugging

WOW64Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "stdio.h"
#include "windows.h"
#include "Shlobj.h"
#include "tchar.h"
#pragma comment(lib, "Shell32.lib")

int _tmain(int argc, TCHAR* argv[])
{
HKEY hKey = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
TCHAR szPath[MAX_PATH] = {0,};

////////////////
// system32 folder
if( GetSystemDirectory(szPath, MAX_PATH) )
{
_tprintf(L"1) system32 path = %s\n", szPath);
}

////////////////
// File size
_tcscat_s(szPath, L"\\kernel32.dll");
hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE )
{
_tprintf(L"2) File size of \"%s\" = %d\n",
szPath, GetFileSize(hFile, NULL));
CloseHandle(hFile);
}

////////////////
// Program Files
if( SHGetSpecialFolderPath(NULL, szPath,
CSIDL_PROGRAM_FILES, FALSE) )
{
_tprintf(L"3) Program Files path = %s\n", szPath);
}

////////////////
// Registry
if( ERROR_SUCCESS == RegCreateKey(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\ReverseCore", &hKey) )
{
RegCloseKey(hKey);
_tprintf(L"4) Create Registry Key : HKLM\\SOFTWARE\\ReverseCore\n");
}

return 0;
}

40.3 - WOW64Test_x86.exe

Run code.

Enable "x64 Compatibility-mode [single-step]

Startup

main() function.

40.4 - WOW64Test_x64.exe

We use WinDbg to debug this executable
File -> Launch Executable

Firstly, we need to obtain the EP address.

1
> !dh WOW64Test_x64

Display PE header.

Go into EP:

1
> g WOW64Test_x64 + 142C

Go into EP.

Show more instructions:

1
> u eip L10

Display more instructions.

Trace the jmp instruction at 00000001 40001439

1
2
> g WOW64Test_x64 + 12b4
> u eip L60

Trace function


main() function

1
2
> bp KERNEL32!GetCommandLineW
> g

Set breakpoint.

Obtain the return address from stack

1
2
> r rsp
> dq rsp

View register, and then dump the register.

1
2
> g 00000001`40001381
> u eip L20

Go to the address that we obtained, this is the main() function that we want since the return address is the address of main function. The assembly code above is the assembly code of main().

1
2
> g 00000001`400013ea
> r

Arguments.

1
> db rdx

Dump rdx to show argument.

1
> db rdx

Dump r8 to show stack pointers.

1
> db 023b3ac0 l200

Dump the stack. The address 023b3ac0 is the address of the first element of the array of last result (`db rdx`). Always remember little-endian is used.

Chapter 41 - ASLR

ASLR stands for Address Space Layout Randomization.

Windows OS that apply ASLR:

OS Kernal Version
Windows 2000 5.0
Windows XP 5.1
Windows Server 2003 5.2
Windows Vista 6.0
Windows Server 2008 6.0
Windows Server 2008 R2 6.1
Windows 7 6.1

IMAGE_FILE_HEADER\Characteristics

IMAGE_FILE_RELOCS_STRIPPED

Chapter 42 - Session of Kernel 6

Session 0 Isolation

Chapter 43 - DLL Injection in Kernel 6

Chapter 44 - InjDll.exe——A Tool for DLL Injection

Chapter 45 - TLS Callback Function

From this point on, this book will introduce more advanced topics in reverse engineering.

Here, TLS stands for Thread Local Storage, instead of Transport Layer Security used in networking.

TLS Callback Function is commonly used in anti-debugging.

45.1 - Practice#1 HelloTls.exe

Running HelloTls.exe

Debugging HelloTls.exe

OllyDbg: HelloTls.exe is terminated.

45.2 - TLS

IMAGE_NT_HEADERS\IMAGE-OPTIOAL_HEADER\IMAGE_DATA_DIRECTORY[9] -> TLS Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef struct _IMAGE_TLS_DIRECTORY64 {
ULONGLONG StartAddressOfRawData;
ULONGLONG EndAddressOfRawData;
ULONGLONG AddressOfIndex;
ULONGLONG AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY64, *PIMAGE_TLS_DIRECTORY64;

typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
DWORD AddressOfIndex;
DWORD AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32, *PIMAGE_TLS_DIRECTORY32;

#ifdef _WIN64
typedef IMAGE_TLS_DIRECTORY64 IMAGE_TLS_DIRECTORY;
typedef PIMAGE_TLS_DIRECTORY64 PIMAGE_TLS_DIRECTORY;
#else
typedef IMAGE_TLS_DIRECTORY32 IMAGE_TLS_DIRECTORY;
typedef PIMAGE_TLS_DIRECTORY32 PIMAGE_TLS_DIRECTORY;
#endif

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

IMAGE_TLS_DIRECTORY

AddressOfCallbacks stores an array of pointers which point to callback functions. It is a significant member in structure IMAGE_TLS_DIRECTORY. The array is ended with NULL value.

PEView.exe: AddressOfCallbacks

Notice that we can register multiple TLS callback functions by modifying the program, even though the original program contains only one TLS callback.

45.3 - TLS Callback Function

A TLS callback function is invoked whenever a process thread is created or terminated.
When the main thread is created, the TLS callback is executed before the program’s entry point (EP).
This behavior can be leveraged for anti-debugging purposes.

Notice that the TLS callback function is invoked when the program is terminated.

IMAGE_TLS_CALLBACK

1
2
3
4
5
6
7
typedef VOID (CALLBACK *PIMAGE_TLS_CALLBACK)(
LPVOID DllHandle,
DWORD Reason,
LPVOID Reserved
);

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

Notice that the definition of TLS callback function is similar to DllMain():

1
2
3
4
5
6
7
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
);

//Source: https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain

The TLS callback function and DllMain() have the same parameter order.
For a callback function, DllHandle is the address of the module (its load address), and Reason indicates why the callback function is being invoked.

There are four possible values for Reason:

1
2
3
4
5
6
7
/* Argument 1 passed to the DllEntryProc. */
#define DLL_PROCESS_DETACH 0 /* detach process (unload library) */
#define DLL_PROCESS_ATTACH 1 /* attach process (load library) */
#define DLL_THREAD_ATTACH 2 /* attach new thread */
#define DLL_THREAD_DETACH 3 /* detach thread */

//Source: https://github.com/wine-mirror/wine/blob/master/include/winnt.h

45.4 - Practice#2 TlsTest.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <windows.h>

#pragma comment(linker, "/INCLUDE:__tls_used")

void print_console(char* szMsg)
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

WriteConsoleA(hStdout, szMsg, strlen(szMsg), NULL, NULL);
}

void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
char szMsg[80] = {0,};
wsprintfA(szMsg, "TLS_CALLBACK1() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
print_console(szMsg);
}

void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
char szMsg[80] = {0,};
wsprintfA(szMsg, "TLS_CALLBACK2() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
print_console(szMsg);
}

#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, 0 };
#pragma data_seg()

DWORD WINAPI ThreadProc(LPVOID lParam)
{
print_console("ThreadProc() start\n");

print_console("ThreadProc() end\n");

return 0;
}

int main(void)
{
HANDLE hThread = NULL;

print_console("main() start\n");

hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
WaitForSingleObject(hThread, 60*1000);
CloseHandle(hThread);

print_console("main() end\n");

return 0;
}

The calling order of TLS function:

  1. DLL_PROCESS_ATTACH
  2. DLL_THREAD_ATTACH
  3. DLL_THREAD_DETACH
  4. DLL_PROCESS_DETACH

TlsTest.exe

45.5 - Debugging TLS Callback Function

Enable “System breakpoint”:

Debugging options -> Events -> System breakpoint

Or using Olly Advanced:

Plugin -> Plly Advanced -> Anti-Debug 2 -> Break on TLS Callback

45.6 - Manually Add TLS Callback Function.

Before modification:

Modification:

Modifying .rsrc section header.

HxD: IMAGE_DATA_DIRECTORY

PEView: IMAGE_DATA_DIRECTORY

Write the assembly code:

OllyDbg: Copy to executable -> Selection -> Save file.

OllyDbg: Save file.

Testing

Chapter 46 - TEB

TEB stands for Thread Environment Block.

We can view TEB structure using the following command in WinDbg:

1
> dt _TEB

Members of TEB

There are two important members:

  1. +0x030 ProcessEnvironmentBlock: Ptr32 _PEB\
    This pointer points to PEB (Introduces in next chapter) .
  2. NtTib\
    TIB stands for Thread Information Block, this is the definition of TIB:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    typedef struct _NT_TIB
    {
    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID SubSystemTib;
    union {
    PVOID FiberData;
    DWORD Version;
    } DUMMYUNIONNAME;
    PVOID ArbitraryUserPointer;
    struct _NT_TIB *Self;
    } NT_TIB, *PNT_TIB;

46.2 - Accessing TEB

Ntdll.NtCurrentTeb()

FS Segment Register

FS segment register indicates the TEB structure of the current thread.

  • FS:[0x18] = start address of TEB
  • FS:[0X30] = start address of PEB
  • FS:[0] = start address of SEH

Chapter 47 - PEB

47.1 - PEB

Accessing PEB

PEB stands for Process Environment Block.
PEB structure stores information of process.

We execute the following instruction in OllyDby (F7 or F8) at EP:

1
MOV EAX,DWORD PTR FS:[0x30]

EAX

Definition of PEB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;

Members of PEB

Windows 10

EAX

47.2 - Important Members

1
2
3
4
5
+002 BeginDebugged : UChar
+008 ImageBaseAddress : Ptr32 Void
+00c Ldr : Ptr32 _PEB_LDR_DATA
+018 ProcessHeap : Ptr32 Void
+068 NtGlobalFlag : Uint4B
  1. PEB.BeginDebugged:\
    This value is commonly used in anti-debugging.
  2. PEB.ImageBaseAddress:\
    This value indicates the ImageBase of the process. We use GetModuleHandle() to obtain ImageBase.
  3. PEB.Ldr:\
    This member is a pointer that points to _PEB_LDR_DATA structure. After a DLL has been loaded into a process, we can obtain the ImageBase of the module through PEB.Ldr.
  4. PEB.ProcessHeap & PEB.NtGlobalFlag:\
    These two values are commonly used in anti-debugging.

Chapter 48 - SEH

48.1 - SEH

SEH stands for Structured Exception Handling.

SEH is the exception handling mechanism used in Windows. In practice, we use keywords such as __try, __except, __finally to implement exception handling. Additionally, this mechanism is widely used in anti-debugging techniques.

Notice that the structure of SEH is different from the try / catch mechanism in C++. Windows introduced the SEH mechanism first, and it was later integrated into Visual C++. In other words, SEH is a low-level exception handling mechanism supported by the Windows operating system and the Visual C++ compiler.

Exception.

Memory: Address 0 is undefined.

48.4 - Exception

There are 5 types of common exceptions:

  1. EXCEPTION_ACCESS_VIOLATION(C0000005)
  2. EXCEPTION_BREAKPOINT(80000003)
  3. EXCEPTION_ILLEGAL_INSTRUCTION(C000001D)
  4. EXCEPTION_INT_DIVIDE_BY_ZERO(C0000094)
  5. EXCEPTION_SINGLE_STEP(80000004)

48.5 - Details of SEH

SEH Chain

1
2
3
4
5
6
7
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
PEXCEPTION_REGISTRATION_RECORD Next;
PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

//Source: https://maple19out.tistory.com/72

SEH Chain

Source: https://maple19out.tistory.com/72

Definition of Exception Handler

TEB.NtTib.ExceptionList

Using SEH

48.6 - Practice#2 - seh.exe

Chapter 49 - IA-32 Instruction

49.2 - Terminology

Terminology Description
Machine Language
Instruction
OpCode
Assembly
Assemble
Assembler
Disassemble
Disassembler
Disassembly
*Compile
*Link

49.3 - IA-32 Instruction Format

IA-32 Format

Source: https://www.researchgate.net/figure/A-32-general-instruction-format_fig1_265265229

Instruction Prefix

Opcode

ModR/M

SIB

SIB (Scale-Index-Base) is also an optional field.

Deplacement

Immediate

49.4 - Instruction Analyzing

49.5 - Practice

Chapter 50 - Anti-Debugging

50.1 Anti-Debugging

Anti-Debugging techniques are strongly dependent to OS and debugger.

50.2 - Anti-Anti-Debugging

50.3 - Types of Anti-Debugging

Static Anti-Debugging

Static anti-debugging is used to detect a debugger. If a debugger is detected, the process cannot execute normally. Once the static anti-debugging technique is cracked, the program can execute normally.

Dynamic Anti-Debugging

Even though we have cracked the static anti-debugging technique, we may still encounter difficulties. If a dynamic anti-debugging technique is applied, it becomes difficult to trace instructions because this technique interferes with the debugger.

Chapter 51 - Static Anti-Debugging Techniques

51.2 - PEB

PEB is used for detecting a debugger. It provides reliable and convenient information. This technique is commonly used in anti-debugging.

BeingDebugged (+0x2)

Ldr (+0xC)

Process Heap (+0x18)

  • GetPocessHeap()
  • Flags (+0xC) & Force Flags (+0x10)

NtGlobalFlag (+0x68)

51.3 - NtQueryInformationProcess()

1
2
3
4
5
6
7
8
9
__kernel_entry NTSTATUS NtQueryInformationProcess(
[in] HANDLE ProcessHandle,
[in] PROCESSINFOCLASS ProcessInformationClass,
[out] PVOID ProcessInformation,
[in] ULONG ProcessInformationLength,
[out, optional] PULONG ReturnLength
);

//Source: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
typedef enum _PROCESSINFOCLASS
{
ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX
ProcessIoCounters, // q: IO_COUNTERS
ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX, VM_COUNTERS_EX2
ProcessTimes, // q: KERNEL_USER_TIMES
ProcessBasePriority, // s: KPRIORITY
ProcessRaisePriority, // s: ULONG
ProcessDebugPort, // q: HANDLE
ProcessExceptionPort, // s: PROCESS_EXCEPTION_PORT (requires SeTcbPrivilege)
ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN
ProcessLdtInformation, // qs: PROCESS_LDT_INFORMATION // 10
ProcessLdtSize, // s: PROCESS_LDT_SIZE
ProcessDefaultHardErrorMode, // qs: ULONG
ProcessIoPortHandlers, // s: PROCESS_IO_PORT_HANDLER_INFORMATION // (kernel-mode only)
ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS
ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void
ProcessUserModeIOPL, // qs: ULONG (requires SeTcbPrivilege)
ProcessEnableAlignmentFaultFixup, // s: BOOLEAN
ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS
ProcessWx86Information, // qs: ULONG (requires SeTcbPrivilege) (VdmAllowed)
ProcessHandleCount, // q: ULONG, PROCESS_HANDLE_INFORMATION // 20
ProcessAffinityMask, // qs: KAFFINITY, qs: GROUP_AFFINITY
ProcessPriorityBoost, // qs: ULONG
ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX
ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION
ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND
ProcessWow64Information, // q: ULONG_PTR
ProcessImageFileName, // q: UNICODE_STRING
ProcessLUIDDeviceMapsEnabled, // q: ULONG
ProcessBreakOnTermination, // qs: ULONG
ProcessDebugObjectHandle, // q: HANDLE // 30
ProcessDebugFlags, // qs: ULONG
ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: PROCESS_HANDLE_TRACING_ENABLE[_EX] or void to disable
ProcessIoPriority, // qs: IO_PRIORITY_HINT
ProcessExecuteFlags, // qs: ULONG (MEM_EXECUTE_OPTION_*)
ProcessTlsInformation, // qs: PROCESS_TLS_INFORMATION // ProcessResourceManagement
ProcessCookie, // q: ULONG
ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION
ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION // since VISTA
ProcessPagePriority, // qs: PAGE_PRIORITY_INFORMATION
ProcessInstrumentationCallback, // s: PVOID or PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION // 40
ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX
ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[]; s: void
ProcessImageFileNameWin32, // q: UNICODE_STRING
ProcessImageFileMapping, // q: HANDLE (input)
ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE
ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE
ProcessGroupInformation, // q: USHORT[]
ProcessTokenVirtualizationEnabled, // s: ULONG
ProcessConsoleHostProcess, // qs: ULONG_PTR // ProcessOwnerInformation
ProcessWindowInformation, // q: PROCESS_WINDOW_INFORMATION // 50
ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8
ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION
ProcessDynamicFunctionTableInformation, // s: PROCESS_DYNAMIC_FUNCTION_TABLE_INFORMATION
ProcessHandleCheckingMode, // qs: ULONG; s: 0 disables, otherwise enables
ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION
ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION
ProcessWorkingSetControl, // s: PROCESS_WORKING_SET_CONTROL
ProcessHandleTable, // q: ULONG[] // since WINBLUE
ProcessCheckStackExtentsMode, // qs: ULONG // KPROCESS->CheckStackExtents (CFG)
ProcessCommandLineInformation, // q: UNICODE_STRING // 60
ProcessProtectionInformation, // q: PS_PROTECTION
ProcessMemoryExhaustion, // s: PROCESS_MEMORY_EXHAUSTION_INFO // since THRESHOLD
ProcessFaultInformation, // s: PROCESS_FAULT_INFORMATION
ProcessTelemetryIdInformation, // q: PROCESS_TELEMETRY_ID_INFORMATION
ProcessCommitReleaseInformation, // qs: PROCESS_COMMIT_RELEASE_INFORMATION
ProcessDefaultCpuSetsInformation, // qs: SYSTEM_CPU_SET_INFORMATION[5] // ProcessReserved1Information
ProcessAllowedCpuSetsInformation, // qs: SYSTEM_CPU_SET_INFORMATION[5] // ProcessReserved2Information
ProcessSubsystemProcess, // s: void // EPROCESS->SubsystemProcess
ProcessJobMemoryInformation, // q: PROCESS_JOB_MEMORY_INFO
ProcessInPrivate, // q: BOOLEAN; s: void // ETW // since THRESHOLD2 // 70
ProcessRaiseUMExceptionOnInvalidHandleClose, // qs: ULONG; s: 0 disables, otherwise enables
ProcessIumChallengeResponse, // q: PROCESS_IUM_CHALLENGE_RESPONSE
ProcessChildProcessInformation, // q: PROCESS_CHILD_PROCESS_INFORMATION
ProcessHighGraphicsPriorityInformation, // q: BOOLEAN; s: BOOLEAN (requires SeTcbPrivilege)
ProcessSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
ProcessEnergyValues, // q: PROCESS_ENERGY_VALUES, PROCESS_EXTENDED_ENERGY_VALUES, PROCESS_EXTENDED_ENERGY_VALUES_V1
ProcessPowerThrottlingState, // qs: POWER_THROTTLING_PROCESS_STATE
ProcessActivityThrottlePolicy, // qs: PROCESS_ACTIVITY_THROTTLE_POLICY // ProcessReserved3Information
ProcessWin32kSyscallFilterInformation, // q: WIN32K_SYSCALL_FILTER
ProcessDisableSystemAllowedCpuSets, // s: BOOLEAN // 80
ProcessWakeInformation, // q: PROCESS_WAKE_INFORMATION // (kernel-mode only)
ProcessEnergyTrackingState, // qs: PROCESS_ENERGY_TRACKING_STATE
ProcessManageWritesToExecutableMemory, // s: MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3
ProcessCaptureTrustletLiveDump, // q: ULONG
ProcessTelemetryCoverage, // q: TELEMETRY_COVERAGE_HEADER; s: TELEMETRY_COVERAGE_POINT
ProcessEnclaveInformation,
ProcessEnableReadWriteVmLogging, // qs: PROCESS_READWRITEVM_LOGGING_INFORMATION
ProcessUptimeInformation, // q: PROCESS_UPTIME_INFORMATION
ProcessImageSection, // q: HANDLE
ProcessDebugAuthInformation, // s: CiTool.exe --device-id // PplDebugAuthorization // since RS4 // 90
ProcessSystemResourceManagement, // s: PROCESS_SYSTEM_RESOURCE_MANAGEMENT
ProcessSequenceNumber, // q: ULONGLONG
ProcessLoaderDetour, // qs: Obsolete // since RS5
ProcessSecurityDomainInformation, // q: PROCESS_SECURITY_DOMAIN_INFORMATION
ProcessCombineSecurityDomainsInformation, // s: PROCESS_COMBINE_SECURITY_DOMAINS_INFORMATION
ProcessEnableLogging, // qs: PROCESS_LOGGING_INFORMATION
ProcessLeapSecondInformation, // qs: PROCESS_LEAP_SECOND_INFORMATION
ProcessFiberShadowStackAllocation, // s: PROCESS_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION // since 19H1
ProcessFreeFiberShadowStackAllocation, // s: PROCESS_FREE_FIBER_SHADOW_STACK_ALLOCATION_INFORMATION
ProcessAltSystemCallInformation, // s: PROCESS_SYSCALL_PROVIDER_INFORMATION // since 20H1 // 100
ProcessDynamicEHContinuationTargets, // s: PROCESS_DYNAMIC_EH_CONTINUATION_TARGETS_INFORMATION
ProcessDynamicEnforcedCetCompatibleRanges, // s: PROCESS_DYNAMIC_ENFORCED_ADDRESS_RANGE_INFORMATION // since 20H2
ProcessCreateStateChange, // s: Obsolete // since WIN11
ProcessApplyStateChange, // s: Obsolete
ProcessEnableOptionalXStateFeatures, // s: ULONG64 // EnableProcessOptionalXStateFeatures
ProcessAltPrefetchParam, // qs: OVERRIDE_PREFETCH_PARAMETER // App Launch Prefetch (ALPF) // since 22H1
ProcessAssignCpuPartitions, // s: HANDLE
ProcessPriorityClassEx, // s: PROCESS_PRIORITY_CLASS_EX
ProcessMembershipInformation, // q: PROCESS_MEMBERSHIP_INFORMATION
ProcessEffectiveIoPriority, // q: IO_PRIORITY_HINT // 110
ProcessEffectivePagePriority, // q: ULONG
ProcessSchedulerSharedData, // q: SCHEDULER_SHARED_DATA_SLOT_INFORMATION // since 24H2
ProcessSlistRollbackInformation,
ProcessNetworkIoCounters, // q: PROCESS_NETWORK_COUNTERS
ProcessFindFirstThreadByTebValue, // q: PROCESS_TEB_VALUE_INFORMATION // NtCurrentProcess
ProcessEnclaveAddressSpaceRestriction, // qs: // since 25H2
ProcessAvailableCpus, // q: PROCESS_AVAILABLE_CPUS_INFORMATION
MaxProcessInfoClass
} PROCESSINFOCLASS;

//Source: https://ntdoc.m417z.com/processinfoclass

ProcessDebugPort(0x7)

1
2
3
4
5
6
7
DWORD dwDebugPort = 0;
pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, &dwDebugPort, sizeof(dwDebugPort), NULL);
printf("NtQueryInformationProcess(PorcessDebugPort) = 0x%X\n", dwDebugPort);
if (dwDebugPort != 0x0)
printf("\t=> Debugging.\n");
else
printf("\t=> Not debugging.\n");

CheckRemoteDebuggerPresent()

ProcessDebugObjectHandle(0x1E)

ProcessDebugFlags(0x1F)

51.4 - NtQuerySystemInformation()

51.5 - NtQueryObject()

51.6 - ZwSetInformationThread()

51.7 - TLS Callback Function

51.8 - ETC

Chapter 52 - Dynamic Anti-Debugging Techniques

52.2 - Exception

SEH

SetUnhandledExceptionFilter()

52.3 - Time Checking

Interval

  1. COunter based method
  2. Time based method

RDTSC

52.4 - Trap Flag

52.5 - 0xCC

Chapter 53 - Advanced Anti-Debugging

53.1 - Advanced Anti-Debugging

53.2 - Garbage Code

53.3 - Breaking Code Alignment

53.4 - Encryption/Decription

53.5 - Stolen Bytes (Remove OEP)

Stolen Bytes

53.6 - API Relocation

53.7 - Debug Blocker (Self Debugging)

Chapter 54 - Practice#1: Service

Chapter 55 - Practice#2: Self Creation

Chapter 56 - Practice#3: PE Image Switching

Chapter 57 - Practice#4: Debug Blocker

THANKS FOR READING