Choosing a DLL for Read-only Inspection
Choosing a DLL for Read-only Inspection
Start here to learn what a DLL is on disk, how Windows loads it, and where the kernel fits — then pick a practice target. The workflow in Read-only DLL inspection — Part 1 works on any DLL; amsi.dll is the worked example, not the only valid choice.
| Prerequisites | Windows lab VM, tools from Part 1 Step 0 |
| Worked example in vault | amsi.dll — Part 1 walkthrough |
| Lab snapshot | Verified 2026-08-31 on 192.168.50.252 (Win10 Pro 19045) — see Verified lab inventory |
| Deep PE reference | Portable Executable Structure |
| Syscall / kernel path | Syscalls Flow |
Do not rename, patch, replace, or take ownership of system DLLs. Pick targets you will only copy and inspect.
Table of Contents
- Windows DLL structure (on disk)
- How DLLs relate to the kernel
- Where system DLLs live
- Verified lab inventory
- Choosing a practice target
Windows DLL structure (on disk)
0. Purpose
Before you pick amsi.dll vs version.dll, you need a mental model of what any DLL file actually is — a Portable Executable (PE) on disk that the loader maps into process memory.
1. High level (explain like I'm five)
A DLL is a shared instruction book stored as a file. Programs do not copy the whole book into themselves — they borrow pages from it when needed.
On disk, that book has:
- A cover label (headers — file type, CPU architecture, where sections start)
- Chapters (sections — code, read-only data, writable data)
- A table of contents for lending out (exports — functions other programs may call)
- A shopping list of other books (imports — other DLLs and APIs this file needs)
.exe and .dll use the same PE format. The difference is mainly how the loader uses them: an EXE is the program entry point; a DLL is a library module loaded into an already-running process.
2. Deep breakdown — PE layout
Every inbox DLL under %windir%\System32\ follows the same skeleton. When you open a copy in PE-bear or Ghidra, read in this order:
flowchart TD DOS[DOS / MZ header
e_magic = MZ] -->|e_lfanew| NT[NT headers
PE signature] NT --> FH[File header
machine, characteristics] NT --> OH[Optional header
image base, subsystem, entry] OH --> DD[Data directories] DD --> EXP[Export table] DD --> IMP[Import table] DD --> RES[Resources] DD --> SEC[Security / Authenticode] NT --> ST[Section table] ST --> TEXT[.text — executable code] ST --> RDATA[.rdata — read-only constants] ST --> DATA[.data — writable globals] ST --> REL[.reloc / .pdata — loader fixups]
| PE region | What it tells you | Inspection question |
|---|---|---|
| DOS + NT headers | Valid PE? 32- vs 64-bit? Preferred load address? | Does architecture match System32 vs SysWOW64? |
| Sections | Where code and data live; memory permissions | Is .text separate from .rdata? Any unusual empty gaps? |
| Import table | Which DLLs/APIs this module depends on | What facilities does it need? (kernel32, ntdll, ole32, …) |
| Export table | Which functions this module offers to others | What is the public surface? (AmsiScanBuffer, …) |
| Resources | Version info, manifests | Does FileVersion match Get-Item / Windows Update? |
| Security directory | Signing metadata | Does Authenticode match Step 2 in Part 1? |
Key terms:
| Term | Meaning |
|---|---|
| RVA (Relative Virtual Address) | Offset from the module's image base once loaded — not the same as file offset on disk |
| VA (Virtual Address) | Image Base + RVA — actual address in a live process |
| Image base | Preferred load address (ASLR may relocate at runtime) |
Static tools read the file. Process Explorer and debuggers show the mapped module in a process. Exports/imports describe the link contract; the loader + ASLR decide final addresses.
Full header walkthrough: Portable Executable Structure.
How DLLs relate to the kernel
0. Purpose
Understand which DLLs talk to the kernel, which stay in user mode, and why that matters when you pick an inspection target.
1. High level (explain like I'm five)
Windows splits work into two floors:
- User mode (Ring 3) — your apps, PowerShell, browsers, and most DLLs in
System32 - Kernel mode (Ring 0) — the real boss:
ntoskrnl.exe(the Windows kernel), drivers, hardware access
User programs cannot walk upstairs directly. They must ask through a reception desk — a chain of DLLs that ends in a syscall (a controlled door into the kernel).
Most DLLs you will inspect (amsi.dll, version.dll, crypt32.dll) live on the user-mode floor. They may request kernel services, but they are not the kernel.
2. Deep breakdown — the call chain
flowchart TB
subgraph ring3 [User mode — Ring 3]
APP[Application
powershell.exe, your.exe]
SPEC[Specialty DLLs
amsi.dll, crypt32.dll, sspicli.dll]
WIN32[Win32 surface
kernel32.dll, advapi32.dll, user32.dll]
NTDLL[Native API gateway
ntdll.dll]
end
subgraph ring0 [Kernel mode — Ring 0]
KERN[ntoskrnl.exe
Windows kernel]
DRV[Drivers .sys]
end
APP --> SPEC
APP --> WIN32
SPEC --> WIN32
SPEC --> NTDLL
WIN32 --> NTDLL
NTDLL -->|syscall instruction| KERN
KERN --> DRV| Layer | Representative modules | Role |
|---|---|---|
| Application | powershell.exe, mmc.exe |
Calls documented APIs |
| Feature / policy DLLs | amsi.dll, wldp.dll, bcrypt.dll |
Domain logic — scanning, policy, crypto |
| Win32 DLLs | kernel32.dll, advapi32.dll |
Stable documented Win32 API |
| Native API | ntdll.dll |
Thin wrappers + syscall stubs (Nt* / Zw*) |
| Kernel | ntoskrnl.exe |
Memory, processes, objects, I/O — not a DLL you inspect like user modules |
| Drivers | *.sys |
Kernel-mode modules — different analysis path |
Typical path for a privileged operation (allocate memory, open a process, read a file):
- App calls
VirtualAllocinkernel32.dll(Win32) kernel32callsNtAllocateVirtualMemoryinntdll.dll(NT API)ntdllexecutessyscall→ CPU enters kernel modentoskrnl.exeperforms the work and returns anNTSTATUS- Result unwinds back to the application
See: Syscalls Flow and Syscalls Deep dive.
ntdll.dll holds the user-mode stub that triggers the syscall. The actual kernel implementation is in ntoskrnl.exe. When blogs say "resolve syscalls from ntdll," they mean reading the stub / system service number (SSN) — not that the kernel code is inside the DLL file.
How the loader wires DLLs together
When a process starts (or calls LoadLibrary):
- NT loader (
ntdll+ kernel) maps the EXE and required DLLs into the process address space - The PEB (Process Environment Block) records which modules loaded and at which base addresses — see PEB / TEB
- The loader resolves imports — fills in pointers to exported functions from dependency DLLs
- For DLLs with
DllMain, the loader runs attach logic (user-mode code — another reason to inspect read-only first)
Why this matters for target selection:
| If you inspect… | You are learning… | Kernel relationship |
|---|---|---|
version.dll |
Minimal PE + exports | Usually no direct syscalls — simple user helper |
amsi.dll |
Defensive component surface | Calls Win32/NT APIs to reach scanners; stays user mode |
crypt32.dll |
Trust and certificate plumbing | Heavy Win32/CryptoAPI; may reach kernel crypto providers indirectly |
ntdll.dll |
Syscall gateway | Direct kernel boundary — advanced, easy to scope-creep |
kernel32.dll |
Win32 façade over NT APIs | Almost everything eventually touches ntdll |
64-bit vs 32-bit on one machine:
| Path | Bitness | Notes |
|---|---|---|
%windir%\System32\*.dll |
64-bit on x64 Windows | Confusing name — System32 is 64-bit on x64 OS |
%windir%\SysWOW64\*.dll |
32-bit WOW64 copies | Separate PE files — inspect separately (lab .252: amsi.dll is 103,936 vs 78,848 bytes) |
Where system DLLs live
| Location | What lives here | Kernel? |
|---|---|---|
C:\Windows\System32\ |
Inbox OS DLLs (64-bit on x64) | User-mode modules only |
C:\Windows\SysWOW64\ |
32-bit compatibility copies | User-mode |
C:\Windows\System32\drivers\ |
*.sys kernel drivers |
Kernel mode — different playbook |
C:\Windows\System32\ntoskrnl.exe |
Kernel image (not a DLL) | Kernel |
C:\Program Files\...\ |
Vendor components (e.g. MpClient.dll) |
User-mode; may not be in System32 |
Choosing a practice target
Now that you know what a DLL is and where the kernel sits, use the sections below to pick a file that matches your learning goal.
Why not start with kernel32.dll?
Almost every Windows program loads kernel32.dll and ntdll.dll. They are foundational — and enormous. For your first passes you want:
- A small enough export surface to finish in one sitting
- Clear Microsoft documentation for at least some exports
- A DLL you can see load in Process Explorer without exotic setup
- A file that teaches the workflow, not drowning you in unrelated APIs
amsi.dll was chosen for the walkthrough because it hits that sweet spot and connects to script-scanning / defensive-component literacy. It is not automatically the best first DLL for everyone.
Verified lab inventory (192.168.50.252)
Collected 2026-08-31 via SSH + PowerShell on the C2 lab Windows VM (DESKTOP-CR3AD2C, Windows 10 Pro build 19045.6466). Signatures were Valid for every DLL present below.
| DLL | In System32? |
Size (KB) | File version | Last write |
|---|---|---|---|---|
version.dll |
Yes | 31.8 | 10.0.19041.3636 | 2025-06-16 |
amsi.dll |
Yes | 101.5 | 10.0.19041.4355 | 2025-06-16 |
wldp.dll |
Yes | 165.6 | 10.0.19041.1 | 2025-06-16 |
bcrypt.dll |
Yes | 143.5 | 10.0.19041.1 | 2025-06-16 |
sspicli.dll |
Yes | 185.2 | 10.0.19041.6328 | 2026-04-09 |
sechost.dll |
Yes | 627.6 | 10.0.19041.1 | 2026-04-09 |
crypt32.dll |
Yes | 1379.1 | 10.0.19041.1 | 2026-04-09 |
wer.dll |
Yes | 906.5 | 10.0.19041.1 | 2026-04-09 |
dbghelp.dll |
Yes | 1940.0 | 10.0.19041.5848 | 2025-06-16 |
mpclient.dll |
No | — | — | — |
amsi.dll on this host (extra checks):
| Copy | Path | Size (bytes) | Signature |
|---|---|---|---|
| 64-bit | C:\Windows\System32\amsi.dll |
103,936 | Valid — CN=Microsoft Windows |
| 32-bit | C:\Windows\SysWOW64\amsi.dll |
78,848 | Valid (not re-listed; same build family) |
MpClient.dll (Defender) — not in System32:
| Path | Size (bytes) | File version |
|---|---|---|
C:\Program Files\Windows Defender\MpClient.dll |
942,520 | 4.18.1909.6 |
C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.26080.3-0\MpClient.dll |
1,882,136 | 4.18.26080.3 |
System32\mpclient.dll
On this Win10 lab image, lowercase mpclient.dll is absent from System32. Defender ships MpClient.dll (capital M and P) under Program Files\Windows Defender\ and versioned folders under ProgramData. Always resolve the real path with Get-ChildItem -Recurse -Filter MpClient.dll before inspection.
Reproduce on any lab host:
# Quick inventory (same script shape used on .252)
'version.dll','amsi.dll','wldp.dll','crypt32.dll','bcrypt.dll','dbghelp.dll','wer.dll','mpclient.dll' |
ForEach-Object {
$p = Join-Path $env:windir "System32\$_"
[PSCustomObject]@{
DLL = $_
Present = Test-Path $p
SizeKB = if (Test-Path $p) { [math]::Round((Get-Item $p).Length/1KB,1) } else { $null }
Signature = if (Test-Path $p) { (Get-AuthenticodeSignature $p).Status } else { $null }
}
} | Format-Table -AutoSize
Fact-check notes (Microsoft docs + lab)
| Claim | Verdict | Source / correction |
|---|---|---|
| AMSI has six exports | Incorrect | Microsoft documents eight AMSI APIs in Amsi.h: add AmsiNotifyOperation and AmsiResultIsMalware to the list in Part 1 |
| AMSI is Defender-only | Incorrect | AMSI portal: AMSI is vendor-agnostic — any registered antimalware provider can integrate |
mpclient.dll lives in System32 |
Often wrong | Not present on lab .252; use Defender paths above |
wldp.dll = “lockdown policy” |
Partially correct | WLDP APIs cover execution policy; newer WldpCanExecute* functions require Windows 11 build 22621+ — Win10 labs still have WldpGetLockdownPolicy |
| Beginner DLLs are all <500 KB | Mostly true | Tier-1 picks on .252 are 32–166 KB; wer.dll and crypt32.dll in System32 are 900 KB–1.9 MB — keep them in Tier 2+ |
| Ghidra PE view always matches runtime | Caution | Ghidra #9170: malformed or unusual PointerToRawData can desync file vs loaded view — cross-check exports with dumpbin or a second tool |
GitHub and official references (read-only learning)
Curated via agentctl research + manual verification. Use for parsing literacy, not bypass tradecraft.
| Resource | URL | Use for |
|---|---|---|
| pefile (Python) | github.com/erocarrera/pefile | Scriptable PE headers, sections, imports/exports |
| LIEF | github.com/lief-project/LIEF | Cross-platform PE object model; compare with Ghidra |
| pe-parse (Trail of Bits) | github.com/trailofbits/pe-parse | Correctness-focused C++ PE parser for tooling authors |
| PE-bear | github.com/hasherezade/pe-bear | GUI PE tree — matches Part 1 Step 4 |
| Dependencies | github.com/lucasg/Dependencies | Import/export dependency graph |
| Ghidra | github.com/NationalSecurityAgency/ghidra | Disassembly/decompiler; see PE loader issues above |
| AMSI functions (official) | learn.microsoft.com/.../amsi/ | Authoritative export names and signatures |
| WLDP functions (official) | learn.microsoft.com/.../wldp/ | Lockdown / execution-policy APIs |
Prioritization rubric
Score each candidate 0–2 per row (0 = poor fit, 2 = great fit). Prefer DLLs with higher totals for your current goal.
| Criterion | Ask yourself | Weight |
|---|---|---|
| Learning fit | Does this DLL teach the skill I am practicing this week (PE basics, exports, defensive component, crypto, etc.)? | High |
| Size / complexity | Can I review all exports in one session? Tier-1 DLLs on lab .252 are 32–166 KB; under ~200 KB and <30 named exports is ideal for beginners |
High |
| Documentation | Are exports on Microsoft Learn or a clear vendor page? | Medium |
| Runtime visibility | Can I trigger a load in a lab VM and find it with Process Explorer (Ctrl+F)? |
Medium |
| Signature clarity | Is it inbox, Microsoft-signed, and stable on my build? | Medium |
| Track relevance | Does it connect to my path (AMSI/EDR, identity, code integrity, malware analysis)? | Low–medium |
| Sensitivity | Am I only inspecting — not planning to patch or bypass this exact file? | Gate — must pass |
Gate rule: If you are studying a DLL primarily to bypass or weaken it, you are past read-only inspection — use a different lab design and explicit authorization.
Tiered candidates (inbox System32)
All paths below are %windir%\System32\ on 64-bit Windows. On 64-bit, also check the SysWOW64 copy when the architecture matters.
Tier 1 — Best first targets
| DLL | Rough role | Why start here | Tradeoff |
|---|---|---|---|
version.dll |
Version resource queries | Tiny, few exports, fast PE/Ghidra win | Less security-story relevance |
amsi.dll |
Antimalware Scan Interface (script/content scan) | Documented exports, easy to observe in PowerShell, defensive literacy | Security blogs discuss bypass — stay read-only |
wldp.dll |
Windows Lockdown Policy / code-integrity checks | Bridges “policy” and execution trust | Slightly less hand-holding than AMSI docs |
Suggested path: version.dll for pure PE practice → amsi.dll or wldp.dll for defensive components.
Tier 2 — After one full pass
| DLL | Rough role | Why next | Tradeoff |
|---|---|---|---|
crypt32.dll |
Certificates, chains, Authenticode helpers | Pairs with signature verification (Part 1 Step 2) | Large import surface |
bcrypt.dll |
Modern crypto primitives (CNG) | Clean export names (BCrypt*) |
Crypto depth can distract from PE basics |
dbghelp.dll |
Symbol resolution, minidumps, stack walks | Useful for crash/debug literacy | Some exports are sparsely documented |
wer.dll |
Windows Error Reporting client | Medium size, real-world telemetry story | Fewer “hero” exports |
Tier 3 — Specialty / blue-team depth
| DLL | Rough role | Why later | Tradeoff |
|---|---|---|---|
MpClient.dll |
Defender client interface (scan/WMI plumbing) | Ties inspection to endpoint AV | Not in System32 on Win10 .252 — lives under Program Files\Windows Defender\ and ProgramData\...\Platform\<version>\; versioned copies change with Defender updates |
sechost.dll |
Security host / RPC glue for LSASS-adjacent services | Important for Windows security architecture | Large (~628 KB on .252), easy to get lost |
sspicli.dll |
SSPI client (auth packages) | Identity and logon path literacy | Auth state is easy to misread statically |
Tier 4 — Foundational but advanced
| DLL | Rough role | When | Warning |
|---|---|---|---|
kernel32.dll |
Core Win32 API | After you can navigate exports + Ghidra comfortably | Huge — weeks of material |
ntdll.dll |
Native API / syscalls gateway | Same as above | Often tied to EDR/syscall research — scope creep risk |
Quick comparison in PowerShell
Run on your lab VM to compare size and signature before you commit a Saturday to Ghidra:
$candidates = @(
'version.dll',
'amsi.dll',
'wldp.dll',
'crypt32.dll',
'bcrypt.dll',
'dbghelp.dll',
'wer.dll'
# MpClient.dll is NOT in System32 — search separately:
# Get-ChildItem 'C:\Program Files\Windows Defender','C:\ProgramData\Microsoft\Windows Defender' -Recurse -Filter MpClient.dll -EA SilentlyContinue
)
$candidates | ForEach-Object {
$path = Join-Path $env:windir "System32\$_"
if (-not (Test-Path $path)) {
[PSCustomObject]@{ DLL = $_; Present = $false }
return
}
$item = Get-Item $path
$sig = Get-AuthenticodeSignature $path
[PSCustomObject]@{
DLL = $_
SizeKB = [math]::Round($item.Length / 1KB, 1)
FileVersion = $item.VersionInfo.FileVersion
Signature = $sig.Status
LastWriteTime = $item.LastWriteTime.ToString('yyyy-MM-dd')
}
} | Format-Table -AutoSize
Optional — export count when dumpbin is available (Visual Studio Build Tools):
function Get-ExportCount {
param([string]$DllPath)
$dumpbin = Get-Command dumpbin -ErrorAction SilentlyContinue
if (-not $dumpbin) { return $null }
$section = & dumpbin /exports $DllPath 2>$null |
Select-String -Pattern '^\s+\d+\s+[0-9A-F]+\s+[0-9A-F]+\s+\w'
if ($section) { return @($section).Count }
return $null
}
'version.dll','amsi.dll','wldp.dll' | ForEach-Object {
$path = Join-Path $env:windir "System32\$_"
[PSCustomObject]@{
DLL = $_
ExportCount = Get-ExportCount $path
}
} | Format-Table -AutoSize
Prefer DLLs with fewer exports when you are still learning the workflow.
Decision tree
flowchart TD
A[Pick a learning goal] --> B{First time opening PE/Ghidra?}
B -->|Yes| C[version.dll]
B -->|No| D{Defensive / script-scan focus?}
D -->|Yes| E[amsi.dll]
D -->|No| F{Policy / code integrity?}
F -->|Yes| G[wldp.dll]
F -->|No| H{Certificates / trust?}
H -->|Yes| I[crypt32.dll]
H -->|No| J[Re-score candidates with rubric]
C --> K[Run Part 1 workflow on a copy]
E --> K
G --> K
I --> K
J --> KMapping goals → starting DLL
| Your goal this week | Start with | Then |
|---|---|---|
| Learn PE headers/sections/exports | version.dll |
amsi.dll |
| Understand script scanning / AMSI | amsi.dll |
Attack AMSI (after read-only pass) |
| Code integrity / “why is this blocked?” | wldp.dll |
crypt32.dll |
| Certificate and signature literacy | crypt32.dll |
Re-run Part 1 Step 2 on other signed binaries |
| Endpoint AV plumbing | MpClient.dll (Defender path, not System32) |
Defender docs + version pinning on VM snapshot |
| Win32 API breadth | kernel32.dll |
Only after Tier 1–2 complete |
Using a non-amsi.dll target in Part 1
The Part 1 steps are DLL-agnostic. Substitute your chosen name everywhere amsi.dll appears:
| Step | Change |
|---|---|
| 1 — Locate | $path = "$env:windir\System32\<your>.dll" |
| 2 — Signature | Same command, new path |
| 3 — Process Explorer | Search <your>.dll |
| 4 — PE tools | Copy to Desktop as <your>-copy.dll |
| 5 — Ghidra | Import the copy; browse Exports for that DLL's API names |
| 6 — Assembly | Same vocabulary — pick any exported function |
amsi.dll: open PowerShell and run'test' \| Out-Nullversion.dll: almost any GUI app — orpowershell -Command "[System.Diagnostics.FileVersionInfo]::GetVersionInfo('kernel32.dll')"wldp.dll: often loaded by policy-aware hosts; try opening Microsoft Edge or runningGet-AppxPackagein PowerShell, then search ProcExp