Skip to main content
18 min read Beginner Desktop

Thick Client Fundamentals

A beginner-friendly guide to the basic concepts behind thick client applications. Read this before the testing checklist. The goal is to understand how these apps are built and how Windows runs them, so the testing steps make sense instead of feeling like magic.

No prior knowledge is assumed. Each concept is explained in plain language with small examples.


Table of Contents

  1. What Is a Thick Client
  2. Application Architecture (Tiers)
  3. How Programs Are Built and Run
  4. Managed vs Native Code
  5. The PE File Format
  6. Processes, Threads, and Memory
  7. DLLs and Linking
  8. The Windows API and Its Layers
  9. Where Applications Store Data
  10. The Windows Registry
  11. Users, Privileges, and Integrity Levels
  12. Windows Services
  13. Inter-Process Communication
  14. Networking Basics
  15. Cryptography Basics
  16. Authentication and Authorization
  17. Binary Protections
  18. Obfuscation Basics
  19. Debugging, Disassembly, and Decompilation
  20. Why Thick Clients Are Interesting to Test
  21. Glossary

1. What Is a Thick Client

A thick client (also called a fat client or rich client) is an application that is installed on a user's computer and does most of its work locally. It can often run without a constant internet connection.

Compare this to a thin client, which is basically a web browser. With a thin client, almost all the logic and data live on a server, and the browser just displays results.

Examples of thick clients: desktop email apps, banking and trading terminals, chat tools such as Teams and Slack, games, and internal business software.

The key point for security: because a large part of a thick client runs on hardware the user controls, the user (or an attacker) can inspect the program, read its files and memory, and change how it behaves. This is a much bigger local attack surface than a web app.


2. Application Architecture (Tiers)

Thick clients are usually described by how many tiers (layers) they have.

Two-tier:

[ Thick client on the user's PC ] <--> [ Database or server ]

The client talks directly to a database or server, often on the same network. There is no middle layer. Because the client connects straight to the database, the database credentials and queries are often inside the client. This makes two-tier apps a rich target.

Three-tier:

[ Thick client ] <--> [ Application server ] <--> [ Database ]

A middle application server holds the business logic. The client sends requests (often over HTTP/S) and the server decides what to do and talks to the database. The client no longer holds the database password, but you can still test how the client talks to the server.

N-tier just means more layers (for example a separate authentication service or API gateway). The idea is the same: identify where logic and data live, and test each boundary.


3. How Programs Are Built and Run

A program starts as source code (text a human writes). To run, it must be turned into instructions the computer understands. There are two broad paths.

Compiled to machine code (native):

Source code (C/C++) -> Compiler -> Machine code (the .exe) -> CPU runs it directly

The result is a binary full of CPU instructions. To read it later you must disassemble it (turn machine code back into assembly) because the original source is gone.

Compiled to an intermediate form (managed):

Source code (C#/Java) -> Compiler -> Bytecode / IL -> Runtime (CLR/JVM) runs it

Languages like C# and Java compile to a middle format (Intermediate Language for .NET, bytecode for Java). A runtime engine (the .NET CLR or the Java JVM) reads that format and runs it. This middle format keeps a lot of structure, so it can be decompiled back to near-original source.

This difference matters a lot for testing, which is the next topic.


4. Managed vs Native Code

Managed code runs inside a runtime that manages memory and execution for it.

  • .NET (C#, VB.NET) runs on the Common Language Runtime (CLR). It compiles to Intermediate Language (IL) stored in assemblies (.exe or .dll files).
  • Java runs on the Java Virtual Machine (JVM). It compiles to bytecode stored in .class files, often bundled in a .jar.

Managed code is easy to reverse because tools can turn IL or bytecode back into readable C# or Java. Tools: dnSpyEx, ILSpy, dotPeek for .NET; JADX, JD-GUI for Java. Because of this, managed apps are often obfuscated to slow analysis.

Native code (unmanaged) is compiled straight to machine code.

  • C and C++ produce native binaries. There is no runtime managing memory, so the programmer manages it (which is where bugs like buffer overflows come from).
  • Native code does not decompile cleanly. You disassemble it with tools like Ghidra, IDA, or radare2, and read assembly or a rough decompilation.

Quick way to tell them apart: open the file in Detect It Easy or CFF Explorer. A .NET assembly shows a .NET header; a native binary does not. Electron apps are native wrappers around JavaScript, and Python apps are often native wrappers around Python bytecode.


5. The PE File Format

On Windows, executable files (.exe, .dll, .sys) use the PE format, which stands for Portable Executable. Understanding its parts helps you know what a tool is showing you.

Main parts:

  • DOS header: a small legacy header at the very start. It contains the classic "This program cannot be run in DOS mode" text and a pointer to the real PE header.
  • PE header: identifies the file as a PE and records the target architecture (32-bit or 64-bit) and flags.
  • Optional header: holds the entry point (where execution starts), the preferred load address, and the data directories.
  • Sections: named blocks of the file. Common ones:
    • .text holds the code (instructions).
    • .data holds initialized global variables.
    • .rdata holds read-only data such as strings and the import table.
    • .rsrc holds resources such as icons and version info.
  • Import table: the list of functions the program borrows from other DLLs (for example CreateFileW from kernel32.dll).
  • Export table: for DLLs, the list of functions this file offers to others.

Why testers care: the imports hint at what the app does (crypto, network, file access). The entry point and sections matter when unpacking. High entropy (randomness) in a section can indicate packing or encryption. Tools: CFF Explorer, PE-bear, Detect It Easy.


6. Processes, Threads, and Memory

When you launch a program, Windows creates a process for it.

  • A process is a container. It has its own private memory space, a list of handles to resources, and a security token that says who it runs as.
  • A thread is what actually runs the code. A process has one or more threads. Each thread has its own stack and its own set of CPU register values.

Memory inside a process is divided into regions:

  • Stack: holds local variables and keeps track of function calls. It is small and fast.
  • Heap: holds data whose size or lifetime is decided at runtime (objects, buffers). It is larger.
  • Code and data sections: the loaded program image (from the PE file).
  • Registers: tiny fast storage inside the CPU (not in RAM) that hold the values being worked on right now.

A key security fact: secrets such as passwords and keys must exist in memory in plaintext at the moment the app uses them, even if they are encrypted on disk. That is why memory analysis can recover them.

Each process has a private virtual address space. Addresses in one process do not point to the same data in another. To read another process's memory you must ask the operating system through special functions.

For a deeper walkthrough of memory and how to inspect it, see the memory analysis guide in this repository.


7. DLLs and Linking

A DLL (Dynamic Link Library) is a file of code and data that programs load and share at runtime. Windows itself is built from DLLs such as kernel32.dll (core functions) and ntdll.dll (low-level functions).

Linking is how a program connects to the functions it uses.

  • Static linking copies the needed code into the .exe at build time. Fewer external files, bigger binary.
  • Dynamic linking keeps the code in separate DLLs that are loaded when needed. Smaller binary, shared code.

When a program starts, the Windows loader finds and loads the DLLs it imports. It searches a specific order of folders (the application directory, system folders, and the PATH). This search order is important: if an attacker can place a malicious DLL earlier in the search order than the real one, the app may load the wrong file. This is called DLL hijacking or DLL search-order hijacking, and it is a common thick client finding.


8. The Windows API and Its Layers

When a program wants the operating system to do something (open a file, send data, allocate memory), it calls a Windows API function. These calls pass through layers:

Your program
|
kernel32.dll / user32.dll / advapi32.dll (documented Win32 API)
|
ntdll.dll (native API, closer to the kernel)
|
system call into the Windows kernel

Most apps use the documented top layer (for example CreateFileW, VirtualAlloc, send). Underneath, those call the native Nt functions in ntdll.dll, which then enter the kernel.

Why this matters: watching which API functions an app calls tells you what it is doing. Tools like API Monitor and Procmon show these calls. If you see calls to crypto functions, you know encryption is happening and you can breakpoint them to read keys. Advanced tools sometimes call the lower ntdll layer directly to avoid security hooks placed on the top layer.


9. Where Applications Store Data

Thick clients store settings, cache, and sometimes secrets on the local machine. Knowing the common locations saves time.

Common folders (with their environment variable):

  • Install folder: C:\Program Files\App or C:\Program Files (x86)\App.
  • Per-user data: %APPDATA% (roaming) and %LOCALAPPDATA% (local), usually under C:\Users\<name>\AppData.
  • All-users data: %PROGRAMDATA% at C:\ProgramData.
  • Temp files: %TEMP%.

Common file types to inspect:

  • Config files: .config (for example app.config), .ini, .xml, .json.
  • Logs: .log, .txt.
  • Local databases: .db, .sqlite (open with DB Browser for SQLite).

What to look for: hardcoded passwords, API keys, connection strings, tokens, and any cleartext personal data. Also check the file permissions. If a low-privilege user can write to the install folder or a config file, that can lead to code execution or tampering.


10. The Windows Registry

The registry is a hierarchical database that Windows and applications use to store settings. Think of it as a giant tree of keys (folders) and values (settings).

Main root keys (hives):

  • HKEY_LOCAL_MACHINE (HKLM): settings for the whole machine and all users.
  • HKEY_CURRENT_USER (HKCU): settings for the current user.
  • HKEY_CLASSES_ROOT (HKCR): file associations and registered COM objects and protocol handlers.

Apps often store configuration here, and sometimes secrets, license data, or trial timestamps. Testers do three things with the registry:

  1. Read it, to find stored secrets or config (reg query HKCU\Software\App /s).
  2. Diff it, to see what an app changes when it runs (Regshot: snapshot, run app, snapshot, compare).
  3. Check permissions, because a key that a normal user can write to may allow bypassing a check (accesschk).

11. Users, Privileges, and Integrity Levels

Windows controls what code can do through a few related ideas.

  • User account: who you are logged in as. A normal user has limited rights; an administrator has more.
  • Access token: every process carries a token that lists the user, group memberships, and privileges. It decides what the process is allowed to do.
  • Privileges: specific rights a token may hold, such as SeDebugPrivilege (open almost any process) or SeBackupPrivilege. Some are dangerous if granted.
  • Integrity level: a label on a process (Low, Medium, High, System). A lower-integrity process cannot modify a higher-integrity one. A normal user app runs at Medium; an elevated (admin) app runs at High.
  • UAC (User Account Control): the prompt that asks for permission to run something as administrator. It separates normal actions from admin actions.

Why this matters: if a thick client runs with more privilege than it needs, or if a low-privilege part can influence a high-privilege part (for example a writable service binary), an attacker can escalate from a normal user to admin or SYSTEM.


12. Windows Services

A service is a program that runs in the background, often without a user logged in, and frequently with high privilege (the SYSTEM account). Windows starts services automatically.

Thick clients sometimes install a helper service (for updates, drivers, or privileged tasks). Services are a common escalation path because they often run as SYSTEM. Testers check:

  • Unquoted service paths: if a service path has spaces and no quotes, Windows may run the wrong executable.
  • Binary permissions: if a normal user can replace the service's .exe, they run code as the service account.
  • Configuration rights: if a user can change the service's settings, they can point it at their own program.

Commands: sc qc <service> shows the config; accesschk -uwcqv "Users" <service> shows who can modify it.


13. Inter-Process Communication

Inter-Process Communication (IPC) is how separate processes talk to each other on the same machine. A thick client and its helper service often use IPC.

Common IPC methods on Windows:

  • Named pipes: a named channel two processes can read and write. Permissions decide who can connect.
  • COM and DCOM: a system for one program to call objects exposed by another.
  • RPC (Remote Procedure Call): lets a program call functions in another process or machine.
  • Shared memory and memory-mapped files: a block of memory two processes both see.
  • Window messages (such as WM_COPYDATA) and mailslots: simpler message passing.

Why it matters: if an IPC endpoint does not check who is calling, a low-privilege process may send commands to a high-privilege one and escalate. Tools: pipelist, OleViewDotNet (COM), RpcView (RPC).


14. Networking Basics

Most thick clients talk to a server. A few terms make traffic testing clearer.

  • IP address and port: the address of a server and the numbered door on it (for example 443 for HTTPS).
  • TCP and UDP: TCP is reliable and ordered (used by most apps); UDP is faster but does not guarantee delivery.
  • Protocol: the language spoken over the connection. HTTP/S is common, but thick clients also use raw TCP, FTP/S, or custom protocols.
  • TLS (Transport Layer Security): encryption for network traffic. It protects data on the wire and lets the client verify the server's certificate. HTTPS is HTTP over TLS.
  • Certificate pinning: the client only trusts a specific certificate, which makes interception harder.

Why it matters: if traffic is unencrypted, anyone on the network can read it. If TLS is weak, or the client accepts invalid certificates, an attacker can intercept and change traffic (a man-in-the-middle attack). For non-HTTP traffic you need tools beyond a normal web proxy, such as Echo Mirage or a relay into Burp.


15. Cryptography Basics

Cryptography protects data. A few core ideas:

  • Encoding is not encryption. Base64 and hex just change the format; anyone can reverse them. Do not treat encoded data as protected.
  • Hashing turns data into a fixed fingerprint that cannot be reversed. Used for passwords and integrity. Weak hashes (MD5, SHA1) should not be used for security.
  • Symmetric encryption uses one shared key to encrypt and decrypt (for example AES). Fast, but both sides need the same secret key.
  • Asymmetric encryption uses a public key to encrypt and a private key to decrypt (for example RSA). Used to set up secure channels and for signatures.
  • Key and IV: the key is the secret; the IV (initialization vector) adds randomness so the same data does not always encrypt to the same output.

Why it matters: thick clients often encrypt local data or traffic. If the key is hardcoded in the app, the encryption gives little protection because anyone can recover the key. Weak algorithms, reused IVs, and predictable random numbers are common findings. Because the app must decrypt data to use it, you can often read keys in memory or by breakpointing the crypto functions.


16. Authentication and Authorization

These two words are often confused.

  • Authentication is proving who you are (logging in with a username and password).
  • Authorization is what you are allowed to do once logged in (are you an admin or a normal user).

A core rule: security decisions must be made where the user cannot tamper with them, which usually means the server. A thick client runs on the user's machine, so anything it decides locally can be changed.

Common weaknesses:

  • Client-side authentication: the app checks the password locally, so you can patch the check or edit memory to log in.
  • Client-side authorization: the app hides admin features but the server does not enforce the restriction, so unlocking the feature grants real access.
  • Hardcoded or default credentials stored in the client.

The test is always: is this check enforced on the server, or only in the client that I control?


17. Binary Protections

Compilers can add defenses that make attacks harder. These do not fix bugs, but they raise the difficulty. Testers check whether they are enabled.

  • ASLR (Address Space Layout Randomization): loads code at random addresses each run, so attackers cannot rely on fixed addresses.
  • DEP or NX (Data Execution Prevention): marks data areas as non-executable, so injected data cannot run as code.
  • Stack canaries (/GS): a secret value placed before the return address on the stack; if an overflow changes it, the program stops.
  • SafeSEH and SEHOP: protect the structured exception handling chain from being hijacked.
  • CFG (Control Flow Guard): checks that indirect calls go to valid targets.
  • High Entropy VA: stronger ASLR for 64-bit programs.
  • Code signing: a digital signature proving who published the file and that it was not changed.

A binary missing these protections is weaker. Tools: winchecksec, PESecurity, and Sigcheck for signatures.


18. Obfuscation Basics

Obfuscation makes code harder to read without changing what it does. It is common in managed apps (.NET, Java) because they decompile so cleanly.

Common techniques:

  • Renaming: meaningful names become a, b, c or random strings.
  • String encryption: text is stored encrypted and decrypted at runtime.
  • Control flow obfuscation: the logic is reshaped so it is hard to follow.
  • Anti-debug and anti-tamper: checks that try to stop debugging or detect changes.
  • Packing: the real code is compressed or encrypted and unpacked in memory at runtime.

Important idea: obfuscation is not real security. The app must turn the code and strings back into a usable form to run, so you can recover them, most reliably at runtime. Tools: de4dot for .NET, Java Deobfuscator for Java, and running the app under a debugger to read decrypted values.


19. Debugging, Disassembly, and Decompilation

These three words describe different ways to look at a program. They are easy to mix up.

  • Disassembly turns machine code into assembly language (a low-level, human-readable form of the instructions). You use it on native binaries. Tools: Ghidra, IDA, radare2.
  • Decompilation tries to rebuild higher-level source code. It works well for managed code (near-original C# or Java) and roughly for native code. Tools: dnSpyEx and ILSpy for .NET, JADX for Java, Ghidra's decompiler for native.
  • Debugging runs the program under a tool that can pause it, step through instructions, read and change memory and registers, and set breakpoints. You do this on a live process. Tools: WinDbg, x64dbg, and dnSpyEx (for .NET).

A simple way to remember: disassembly and decompilation are static (you read the code without running it). Debugging is dynamic (you watch and control the code while it runs). Real assessments use both.


20. Why Thick Clients Are Interesting to Test

Pulling the concepts together, thick clients are a rich target for a few reasons:

  • Code runs on hardware the user controls, so it can be read, debugged, and changed.
  • Secrets often live locally: in files, the registry, or memory.
  • Logic that should be on the server is sometimes done in the client, so it can be bypassed.
  • They use many channels (files, registry, memory, network, IPC, services), each a possible weakness.
  • Managed apps decompile easily, and obfuscation does not truly hide secrets.

The testing checklist in this repository turns these ideas into concrete steps. Read this fundamentals guide first, then use the checklist to test methodically.


21. Glossary

  • API: a set of functions a program can call to ask the system or a library to do something.
  • Assembly (.NET): a compiled .NET file (.exe or .dll) containing Intermediate Language.
  • Bytecode: the intermediate instructions Java compiles to, run by the JVM.
  • CLR: the Common Language Runtime, the engine that runs .NET code.
  • DLL: a shared library of code loaded at runtime.
  • Endianness: the byte order used to store multi-byte numbers (x86/x64 is little-endian, least significant byte first).
  • Handle: a reference to a system object such as a file or process.
  • Heap: the memory region for dynamically allocated data.
  • IL: Intermediate Language, the compiled form of .NET code.
  • Integrity level: a trust label on a process (Low, Medium, High, System).
  • IPC: Inter-Process Communication, how processes talk on one machine.
  • JVM: the Java Virtual Machine, the engine that runs Java bytecode.
  • Managed code: code that runs inside a runtime that manages memory (.NET, Java).
  • Native code: code compiled directly to machine instructions (C, C++).
  • PE: Portable Executable, the Windows format for .exe and .dll files.
  • Pointer: a value that is the address of another location in memory.
  • Process: a running program with its own memory and resources.
  • Registry: the Windows database of settings.
  • Stack: the memory region for local variables and function calls.
  • Thread: the unit inside a process that runs instructions.
  • TLS: Transport Layer Security, encryption for network traffic.
  • Token: the object describing a process's security identity and rights.