Real Executor. Full stack recovered end to end
End to end reverse of Project Real's Roblox script executor. The installer's per download token is in the filename. That token passed /verify and got us the live signed manifest. The manifest gave us every component. We unpacked the injector's outer wrapper by hand, identified the packer as Oreans WinLicense, ran it under Wine 9.0 without any Wine detection tripping. Captured a 1.8 GB core dump of the fully decrypted state. Recovered 455 unique runtime log format strings, 434 Nt/Rtl/Ldr symbols, the injection technique (manual DLL mapping). Then we opened UI.exe and pulled the full auth surface out. 12 backend endpoints. Ed25519 server public key baked into the binary. Complete HWID v4 formula. Device keypair attestation flow. Every error code. Every account field. All of it.
Verdict
Real Executor is a well engineered paid Roblox script executor. The installer is a signed manifest client. The CDN is Cloudflare fronted with an Ed25519 signed component set. The injector is a Themida family packed (WinLicense) manual DLL mapping loader that ships a per Hyperion build patch pack, currently 231 sites for build 69 and 27 for build 67. The executor DLL is a custom Luau VM that fields the full UNC and sUNC executor surface (87 of 105 functions we probed). The UI is a Tauri app with Monaco and a bundled luau-lsp. The backend runs 12 signed endpoints under two hostnames with Discord OAuth. HWID v4 fingerprint. Per device Ed25519 keypair stored in Windows Credential Manager. Ed25519 signed responses verified client side against a baked in server public key.
Any real EDR flags every one of these binaries as HackTool or PUA. The injector openly names Roblox's anti cheat vendor as a PE section (.byfron) and embeds pep talk to it (WE LOVE HYPERION 67 and WE LOVE HYPERION 69) in the log format table. The executor DLL is version stamped as Crypto++ 8.9.0 to fool automated scanners. The auth surface is unusually clean. Injector holds no session state. All identity lives in UI.exe. That is the cleanest thing about the build.
The stack at a glance
Public sample : RealSetup.exe 15 MB unsigned installer
Persistent updater: Update.exe 7 MB extracted from RealSetup RCDATA #101
Executor DLL : Real.dll 2.94 MB 8-byte XOR encrypted at rest
custom Luau VM inside
Hyperion bypass : Injector.exe 4.12 MB WinLicense packed, manual DLL mapping
Front end : UI.exe 10.5 MB Tauri + Monaco, holds all auth state
API host : api.projectreal.live Cloudflare fronted, Ed25519 signed responses
Web host : projectreal.gg Discord OAuth landing
CDN : download.projectreal.live per release opaque slugsBuild we caught: version 1.1.0 build 118 sequence 1783781182 published 2026-07-11 14:46:22 UTC.
File identities
Product identity
| Product | Real Executor by Project Real |
| Marketing | projectreal.gg · discord.gg/projectreal |
| Infrastructure | api.projectreal.live · download.projectreal.live · dl.projectreal.live |
| Current build | 1.1.0 · build 118 · sequence 1783781182 |
| Published | 2026-07-11 14:46:22 UTC |
| Front | Cloudflare on api.projectreal.live |
| Dev machine leaked | C:\Users\Admin\Documents\LUA\GitHub-Repos\Real-Executor |
Injector.exe
| File | Injector.exe (Real.exe in the CDN manifest) |
| SHA-256 | 949ed91c5395e407b3b29c7842bca3c50b61c0df62b9a891211f7297c79f148c |
| Size | 4,118,528 bytes |
| Compile timestamp | 2026-07-11 14:39:03 UTC |
| ImageBase | 0x140000000 |
| EntryPoint RVA | 0x6d0058 |
| Signature | unsigned |
Real.dll
| File | Real.dll (encrypted on disk) |
| Size | 2,940,176 bytes |
| Cipher | 8 byte repeating XOR |
| Key | 2A 27 94 7A E9 EA DA 78 |
| Decrypted ImageBase | 0x180000000 |
| Decrypted EntryPoint | 0x1e16e0 |
| Compile timestamp | 2026-07-11 14:32:14 UTC |
| Version resource lies as | Crypto++ Library DLL 8.9.0 by Wei Dai |
UI.exe
| File | UI.exe (Real.exe in the CDN manifest) |
| Size | 10,549,760 bytes |
| Framework | Tauri 2.11.2 (Rust) + Microsoft Edge WebView2 |
| Editor | Monaco (VS Code engine) + luau-lsp autocomplete |
| HTTP client | reqwest 0.12.28 + cookie_store 0.22.1 |
| Keyring | keyring 3.6.3 (Windows Credential Manager) |
| Version | Real 0.6.4 |
Live protocol we replayed
The sample filename ends in a 64 character hex string. That is the install token. Sent as is to /verify. It came back valid.
POST https://download.projectreal.live/verify
User-Agent: RealLauncher/1.0
Content-Type: application/json
{"token":"c6ea55866cbce887a249a04304efe91696a388f3f28b82eb2e982afce7e097cf"}
200 OK
{"ok":true,"proof":"542845be23d68b5cdcebb7a45abb7d8dcdcfc662d2ee3ca36774dfa3c6e7bdf8"}The Cloudflare fronted api.projectreal.live refused the same probe with a canonical Cloudflare block page. The CDN edge on download.projectreal.live accepted it. Two different fronts with two different policies. That gap tells you how the vendor scopes threat models between control plane and content plane.
The update endpoint required no auth beyond the User Agent match.
GET https://api.projectreal.live/update/check
User-Agent: RealLauncher/1.0
200 OK (JSON manifest, annotated in the next section)Every file URL in the manifest served the actual bytes with no auth. SHA-256 verification matched the manifest values byte for byte.
The signed manifest, annotated
{
"version": "1.1.0",
"buildNumber": 118, // anti rollback
"sequence": 1783781182, // anti replay
"publishedAt": "2026-07-11 14:46:22",
"status": "Working",
"files": {
"dll": {
"url": "https://download.projectreal.live/releases/MkQskkR19VHtJ-MZ/bin/Real.dll",
"sha256": "2756337a38a4525aa9b90b5dd2a843526d79f5b345258f56e8b093b51122b167",
"signature": "w99tPyMIPD15NWw3R1ftK6VXut-VrMnsUX0B7L8qkiCP4uwTMhBC49YIZx4jSIRTJoiEKjIgvv4u0Fr9pAvoBg",
"size": 2940176
},
"injector": {
"url": "https://download.projectreal.live/releases/MkQskkR19VHtJ-MZ/bin/Real.exe",
"sha256": "949ed91c5395e407b3b29c7842bca3c50b61c0df62b9a891211f7297c79f148c",
"signature": "p5OGfUdxZa7zzCf4JZjzjDSKHf-MCu2px4gQKjCh7DtssDIH1pN6oTIjkTFjEicu7IpeCbWZFmVNELZKZ_wjDQ",
"size": 4118528
},
"ui": {
"url": "https://download.projectreal.live/releases/pUpgyTSjP8i6dotu/Real.exe",
"sha256": "11ceebe9c8af83e2aea61c28da7d7e0902daa9eda124f7a946c85b958152bbab",
"signature": "aS3xGeeYqI2eTQaocZ7UaluHvWCqlDK1o-WRgaIaXsrPNLjaRi_6gD23XFQsO3RFpLwYnNMZwhd_Y6H4ol-QAQ",
"size": 10549760
}
}
}Every signature decodes from base64url to exactly 64 bytes. That is Ed25519 wire format. The verification key is the one recovered from UI.exe. See the response signing section for that pubkey.
Release paths use 16 character base64url slugs per release. Enumeration is not trivial from outside because a valid /update/check response is the only place they surface.
Real.dll. The executor
Real.dll ships XOR encrypted. Recovering the 8 byte repeating key took one look at the first 64 bytes lined up against a known PE header pattern (MZ 90 00 03 00 00 00 04). XOR the ciphertext against the plaintext, take modulo 8, done.
Recovered key: 2A 27 94 7A E9 EA DA 78
Decrypted it is a valid PE32+ x64 DLL. ImageBase 0x180000000. EntryPoint 0x1e16e0. Six standard sections. Compile timestamp 2026-07-11 14:32:14 UTC. About 32 minutes before Injector.exe was built. Same build train.
The Crypto++ decoy
The version resource lies. It claims to be Crypto++ Library DLL 8.9.0 by Crypto++ project copyright Wei Dai original filename cryptopp.dll. It is not Crypto++. This stamp exists so any tool that only reads version info sees a known open source library and stops.
What is actually inside
A full custom Luau VM. Namespaces recovered from MSVC RTTI strings:
RealVM::Compile BuiltinVisitor · ConstantVisitor · CostVisitor
ShapeVisitor · TableMutationTracker · TypeMapVisitor · ValueVisitor
RealVM::Compiler ConstUpvalueVisitor · FenvVisitor · FunctionVisitor
ReturnVisitor · UndefinedLocalVisitor · resolveAssignConflicts
Console ConsoleInput (backs rconsoleinput)
Misc HttpGet · RBXMessageBox
Yielding YieldThreadRoblox Luau FastFlag strings are present. LuauCompileInlineDepth, LuauCompileLoopUnrollThreshold, LuauCompileNewTableMutationTracker, LuauCompileStringInterpTargetTop, LuauCostModel, LuauCstAttr, LuauEmitCallFeedback, LuauGcTableStepFix, LuauIntegerBufferFastcalls and a couple dozen more. The fork tracks upstream Luau features closely.
Bytecode key hint
Failed to decode Luau bytecode with encode key 203 or 1. If this file uses a different opcode encoding, it may be from a custom VM.
Key 203 is the shipping Roblox Luau bytecode encode key. Key 1 is the reference Luau default. Real accepts both and prints this diagnostic on a custom VM mismatch.
Identity function
Calling identifyexecutor() from a script returns the string "Real". Script authors commonly branch on this.
Executor API surface
The UI ships three .d.luau type declaration files for Monaco autocomplete. Full UNC and sUNC executor surface. 87 of 105 functions we probed for are present in Real.dll.
Identity identifyexecutor · getexecutorname
Environment getgc(includeTables) · getgenv · getreg · getrenv · getsenv · filtergc
Closures checkcaller · clonefunction · hookfunction · hookmetamethod · newcclosure
newlclosure · restorefunction · replaceclosure · iscclosure · islclosure
isexecutorclosure · isourclosure · loadstring · getfunctionhash
Debug debug.getconstant(s) · getproto(s) · getstack · getupvalue(s)
setconstant · setstack · setupvalue · traceback · profilebegin/end · getinfo
Instances getinstances · getnilinstances · getconnections · firesignal
firetouchinterest · fireproximityprompt · fireclickdetector · cloneref
compareinstances · gethiddenproperty · sethiddenproperty · getproperties
Metatables getrawmetatable · setrawmetatable · setreadonly · isreadonly
getnamecallmethod · setnamecallmethod
Scripts getscripts · getloadedmodules · getcallingscript · decompile · getcallbackvalue
Filesystem readfile · writefile · appendfile · isfile · isfolder · listfiles
makefolder · delfile · delfolder · loadfile · dofile · getcustomasset
Input keypress · keyrelease · mouse1click · mouse2click · mousescroll
mousemoverel · mousemoveabs
Clipboard setclipboard · getclipboard
Network request · http_request · syn_request · WebSocket.connect
Drawing Drawing.new(Line, Text, Image, Circle, Square, Triangle, Quad, Font)
cleardrawcache · isrenderobj · getrenderproperty · setrenderproperty
Encoding base64encode/decode · lz4compress/decompress · crypt.hash
crypt.encrypt/decrypt · crypt.generatebytes/generatekey
Console rconsoleprint · rconsoleinfo · rconsolewarn · rconsoleerr
rconsoleclear · rconsolename · rconsoleinput
Compat syn.request · getsynasset · queue_on_teleport · clearqueueonteleportThird party decompile privacy leak
Real's decompile() ships bytecode off device. The implementation is recovered verbatim from Real.dll .rdata.
local res = request({
Url = "https://api.lua.expert/decompile",
Method = "POST",
Headers = { ["content-type"] = "application/json" },
Body = httpservice:JSONEncode({ script = encoder(bytecode) })
})Every call to decompile uploads the target script's Luau bytecode to api.lua.expert. Anyone running a decompile through Real is handing that script to a third party. This is baked into the executor. Not opt in. Not disclosed at the UI layer.
Injector.exe. Outer layers
4.12 MB console subsystem PE. Only two visible imports. Everything else resolves dynamically at runtime.
| Section | Size (virt / raw) | Entropy | Purpose |
|---|---|---|---|
| .text | 0x43406 / 0x43600 | 6.690 | Visible code plus the unpacker stub at the tail |
| .rdata | 0xfde4 / 0xfe00 | 5.422 | Read only data, log format strings live here |
| .data | 0x1af8 / 0x800 | 3.962 | Initialized data |
| .pdata | 0x1464 / 0x1600 | 5.231 | SEH function tables |
| .byfron | 0x1c70 / 0x1e00 | 3.905 | 960 qword pointer table indexing into .rdata blobs |
| .rsrc | 0x2c8 / 0x400 | 4.109 | Resources |
| .idata | 0x1000 / 0x800 | 3.567 | Visible import table |
| .tuffshi | 0x672000 / 0 | 0.000 | Runtime allocated. 6.7 MB, zero raw. Themida engine writes here. |
| .boot | 0x394e00 / 0x394e00 | 7.966 | aPLib compressed payload |
The layout is diagnostic. High entropy compressed section, huge zero raw section that will receive the decompression output, tiny visible imports, oddly named sections. That is a shipped Themida or WinLicense build with default section randomization on.
Visible imports (that is the whole IAT)
kernel32.dll : GetModuleHandleA ADVAPI32.dll : AdjustTokenPrivileges USER32.dll : MessageBoxA MSVCP140.dll : one std::basic_iostream constructor VERSION.dll : GetFileVersionInfoA ntdll.dll : NtQueryInformationProcess bcrypt.dll : BCryptCloseAlgorithmProvider ncrypt.dll : NCryptCreatePersistedKey CRYPT32.dll : CryptBinaryToStringA WINHTTP.dll : WinHttpCloseHandle VCRUNTIME140.dll · api-ms-win-crt-*.dll : the usual CRT scaffolding
One export per DLL. That is the packer telling the loader what libraries to keep resident. Everything the injector actually uses gets resolved dynamically by walking the PEB and hashing export names.
The aPLib wrapper reversed
Entry point is at RVA 0x6d0058. Inside .boot. The first bytes are Jibz's reference aPLib decompressor, verbatim.
0x1406d0058: e8 82 01 00 00 call 0x1406d01df 0x1406d005d: 41 52 push r10 0x1406d005f: 49 89 e2 mov r10, rsp 0x1406d0062: 41 52 push r10 0x1406d0064: 49 8b 72 10 mov rsi, [r10+16] ; src ptr from stack 0x1406d0068: 49 8b 7a 20 mov rdi, [r10+32] ; dst ptr from stack 0x1406d006c: fc cld 0x1406d006d: b2 80 mov dl, 0x80 ; bit reservoir init 0x1406d006f: 8a 06 mov al, [rsi] ; first literal 0x1406d0071: 48 ff c6 inc rsi 0x1406d0074: 88 07 mov [rdi], al 0x1406d0076: 48 ff c7 inc rdi 0x1406d0079: bb 02 00 00 00 mov ebx, 2 0x1406d007e: 00 d2 add dl, dl ; shift, CF is out bit 0x1406d0080: 75 07 jnz $+9 0x1406d0082: 8a 16 mov dl, [rsi] ; refill 0x1406d0084: 48 ff c6 inc rsi 0x1406d0087: 10 d2 adc dl, dl
The mov dl, 0x80 plus add dl,dl / jnc refill pattern is Jibz's aPLib signature. Bit at a time reservoir read, dispatching literal, short backref or long backref based on prefix bits.
Following the call, the driver at 0x1406d01df pops the return address and computes two constants that fall right out.
rbx = 0x14005E000 ; runtime VA of .tuffshi rbx + 0x36244c = 0x1403C044C ; OEP after decompression rax = 0x1406d005d ; aPLib decompressor entry
The stub uses a self locating call / pop / sub so it works whether relocated or not. Then it reads a segment count from a table at 0x1406d0248 and loops one aPLib call per segment.
Segment count byte : 0x10 (16 segments) Each decompresses to 0x67200 bytes = 421,888 bytes Total : 16 x 0x67200 = 0x672000 = exactly .tuffshi VSize
Perfect end to end match. Running the same aPLib against those 16 streams in Python takes about four seconds and produces the same bytes the packer produces at runtime.
The packer is Oreans WinLicense
Modern Oreans builds do not name their sections .themida or .taz anymore. They pick random names like .tuffshi and .boot. The identification has to be structural.
String evidence. Oreans product family markers
String Meaning WinLicense Oreans product family literal WinLicenseInstance single occurrence WinLicenseDriverVersion kernel driver version field WinLicenseVersion product version field SOFTWARE\WinLicense registry key WinLicense's runtime queries /checkprotection WinLicense debug CLI switch /dumpstatus WinLicense debug CLI switch /bugcheckfull WinLicense debug CLI switch /showinstance WinLicense debug CLI switch /nosplash WinLicense debug CLI switch /bugcheck WinLicense debug CLI switch !ProcOUT Themida VM handler marker
These strings do not appear in generic packers, in Chromium, in Qt, in any common OSS library. They only ship in Oreans engines.
Structural evidence. The VM exit epilogue count
Themida VM handlers exit by popping every register they touched. When a handler is register heavy it pops r8 through r15 with REX-B prefixes. In memory that reads as bytes 41 58 41 59 41 5A 41 5B 41 5C 41 5D 41 5E 41 5F which as ASCII is AXAYAZA[A\A]A^A_. Distinctive. Greppable.
Themida VM exit epilogue pattern
unpacked .tuffshi : 23 occurrences
Real.dll (custom Luau VM, comparison) : 0 occurrencesThe custom Luau VM in Real.dll is not Themida wrapped. Zero hits there. The injector's .tuffshi shows 23. That is the tell.
.byfron. Per Hyperion build metadata index
.byfron is 7,680 bytes on disk. Writable, low entropy, pure data. Contents are 960 qword pointers into .rdata.
Distribution by target section:
.rdata : 910
OUT-OF-IMAGE : 50 (zero quads / trailing padding)
Unique pointer values : 911
Duplicates : ~50Every non null pointer targets one of two blob shapes in .rdata.
Blob shape A. Shared framework (32 blobs)
.rdata+0x49391 (first pointer target): 00: 57 45 20 4c 4f 56 45 20 48 59 50 45 52 49 4f 4e WE LOVE HYPERION 10: 00 01 02 03 32 36 38 00 70 61 74 63 68 69 6e 67 ....268.patching 20: 20 25 58 00 01 02 03 31 31 36 38 00 50 61 74 63 %X....1168.Patc 30: 68 69 6e 67 20 69 6e 74 65 67 72 69 74 79 20 61 hing integrity a 40: 74 20 25 58 2e 2e 2e 00 01 02 03 32 30 36 38 00 t %X.......2068. 50: 70 61 74 63 68 69 6e 67 20 25 58 00 01 02 03 31 patching %X....1
Layout: magic string, delimiter 00 01 02 03, ASCII decimal site ID, null, printf format string, repeat. The format strings are "patching %X" and "Patching integrity at %X...". Successive pointers cycle through site IDs 268 to 576 incrementing by 2.
Blob shape B. Per Hyperion build patch site tables
WE LOVE HYPERION (unversioned) x 32 framework WE LOVE HYPERION 67 x 32 Hyperion build 67 support WE LOVE HYPERION 69 x 256 Hyperion build 69 support (primary) JUSTMAGIC WE LOVE YOU x 256 paired ACK / success marker
32 versus 256 is the tell. Build 67 has partial support left in for backwards compatibility. Build 69 is the current target with 256 patch sites. Every 69 blob is paired with a JUSTMAGIC blob. One operation and one ACK per patched call site.
Not the patch data. The pointer table indexes log metadata that the injector's runtime debug output consumes. The specific memory addresses being patched and the exact byte sequences written are runtime computed. Real signature scans Hyperion's loaded image and generates addresses live. That is why Real can rev support from Hyperion 67 to 68 to 69 without reshipping. Signatures stay stable. Addresses drift per build.
Running the injector under Wine
$ export WINEPREFIX=./wine_prefix $ export DISPLAY=:99 $ Xvfb :99 -screen 0 1024x768x24 & $ wine wineboot --init $ wine Injector.exe $ cat log.txt Roblox not found Roblox not found Roblox not found
The injector runs. Themida bootstraps to completion. Themida does not detect Wine in this build. The injector's actual code executes, scans the process list, fails to find Roblox, writes its own log.txt to cwd, retries three times, exits clean.
The log.txt literal is a durable on host IOC.
Live core dump analysis
While the injector is alive (about three seconds under Wine before it exits), gcore on the actual process PID captures a full core dump. The Wine process names its threads after the running .exe so /proc/*/comm matching Injector.exe gives the right target.
ps polling for /proc/*/comm == "Injector.exe" ... found pid 17520 on tick 3 gcore -o injproc_core 17520 ... Saved corefile injproc_core.17520 (1,812,545,208 bytes)
Memory map at that moment:
140000000-140001000 r--p PE headers 140001000-140045000 r-xp .text 140045000-140055000 r--p .rdata 140055000-140057000 rw-p .data 140057000-140059000 r--p .pdata + .byfron RO half 140059000-14005b000 rw-p .byfron RW half 14005b000-14005d000 r--p .rsrc 14005d000-14005e000 rw-p .idata 14005e000-1406d0000 rwxp .tuffshi (RWX at runtime) 1406d0000-140a65000 r-xp .boot
.tuffshi is RWX at runtime. Self modifying by design. Diffing live .tuffshi against the aPLib output shows 75.2 percent of bytes differ. Themida decodes almost everything under it after initial unpack.
The full log format string catalog
The Themida decrypted heap at file offset 0x68bdd000 in the core contains the fully decoded log format string table. Every format string the injector emits. Every numeric site ID. We parsed 455 unique entries. Grouped by phase.
Phase 1. Framework and setup
| ID range | Count | Format string |
|---|---|---|
| 268 to 2076 | 111 sites | patching %X |
| 576 | 1 site | .byfron |
| 668 to 1086 | 25 sites | JUSTMAGIC WE LOVE YOU |
Phase 2. Integrity patching
| ID range | Count | Format string |
|---|---|---|
| 1128 | 1 site | Roblox not found (observed live) |
| 1178 and 1180 | 2 sites | Patching integrity at %X... |
| 1182 | 1 site | Wrong Roblox Version |
| 1184 | 1 site | Remapped |
| 1186 | 1 site | Successfully Injected! |
Phase 3. Per Hyperion build patch tables
| ID range | Count | Format string |
|---|---|---|
| 1278 to 1330 | 27 sites | WE LOVE HYPERION (framework) |
| 1332 to 1394 | 27 sites | WE LOVE HYPERION 67 |
| 1396 to 1906 | 231 sites | WE LOVE HYPERION 69 (primary target) |
Phase 4. Injection completion
| ID range | Count | Format string |
|---|---|---|
| 1908 to 1938 | 16 sites | remapping done |
| 1940 to 1954 | 8 sites | injected |
| 1956 to 1962 | 4 sites | manual mapping |
The 231 build 69 patch sites is a striking count. This is not a couple of surgical writes. Hyperion's integrity check surface has grown into the hundreds of touch points that the bypass has to neutralize.
The API resolution table
Themida decrypted its full API resolution table at runtime and left it on the heap. Recovered from the core dump at file offset 0x699a0000. 434 Nt, Zw, Rtl, Ldr, Mm, Ps, Cm, Se, Ob, Io symbols plus 70 plus kernel32 exports.
Process handle acquisition
NtOpenProcess open Roblox handle NtOpenProcessToken adjust privileges NtQueryInformationProcess metadata, integrity level
Memory allocation in target
NtAllocateVirtualMemory allocate space in Roblox for the DLL image NtAllocateVirtualMemoryEx Win10+ variant NtProtectVirtualMemory set section permissions post write NtFreeVirtualMemory cleanup
Payload transfer
NtWriteVirtualMemory write DLL headers plus sections NtReadVirtualMemory read for verification NtFlushInstructionCache force i-cache flush before exec NtCreateSection alt path, kernel assisted mapping NtMapViewOfSection / Ex map section into target
Thread manipulation for execution
NtGetContextThread thread hijack context capture CreateThread classic trigger CreateRemoteThreadEx remote trigger NtDelayExecution timing pauses between stages
Self side resolution
LdrGetProcedureAddress manual GetProcAddress LdrGetDllHandleEx manual GetModuleHandle LdrLoadDll last resort fallback LdrResolveDelayLoadedAPI
The Nt heavy preference (Nt everywhere kernel32 would suffice) is the identifying signature of a bypass that expects Hyperion to hook user mode kernel32 exports. Going one layer down to the syscall boundary defeats those hooks. Hook the syscall, not the wrapper. Standard pattern in modern anti cheat bypass loaders.
Injection technique. Manual DLL mapping
The manual mapping and remapping done string family plus the Nt heavy API vocabulary confirm this is a manual DLL mapping injector. Not LoadLibrary. The distinction matters.
Manual DLL mapping steps (reconstructed from string + API evidence):
1. Locate target process (Process32Next enumeration by name)
2. NtOpenProcess with PROCESS_ALL_ACCESS
3. NtAllocateVirtualMemory in target for full image size
- preferred base if free, else relocatable base
4. NtWriteVirtualMemory to copy DLL headers + each section raw bytes
5. For each section: NtProtectVirtualMemory to correct permissions
(this is what "remapping done" x16 marks)
6. Fix relocations in the written image (base delta)
7. Fix imports by resolving with LdrGetProcedureAddress against
target's loaded module set, patch IAT
8. Execute TLS callbacks if any
9. NtCreateThreadEx or SetThreadContext + ResumeThread to call DllMain
10. NtFlushInstructionCache to force i-cache invalidation
11. Log "Successfully Injected!"Because the loaded image never touches LdrLoadDll, it never appears in the target process's PEB.Ldr module list. It never appears in EnumProcessModules output. It is invisible to Hyperion's own module walk based checks. That is the whole point.
Reconstructed injection timeline
Cross referencing log format strings, ID ranges and API vocabulary produces the following ordered sequence. This is what the injector does on a successful run.
[1128] locate Roblox process (failed here under Wine)
[268-576] framework init (patching %X across 111 setup sites)
[576] .byfron section reference
[668-1086] JUSTMAGIC WE LOVE YOU ACK markers along framework path
[1180] check Hyperion image, identify version (67 or 69)
[1182] abort with 'Wrong Roblox Version' if unsupported version
[1178] 'Patching integrity at %X...' phase begins
[1332-1394] apply 27 build 67 specific patches (IF version = 67)
OR
[1396-1906] apply 231 build 69 specific patches (IF version = 69)
[1956-1962] manual mapping (4 stages: alloc, write headers,
write sections, fix imports)
[1908-1938] 16 remapping operations
(section permissions, alignment fixes)
[1940-1954] 8 injection completion markers
(per thread or per callback)
[1184] 'Remapped' terminal
[1186] 'Successfully Injected!'UI.exe. The front end
10.5 MB Tauri app. Rust backend. Microsoft Edge WebView2 for the shell. Monaco (VS Code's editor engine) with bundled Luau LSP for autocomplete.
Attach button flow
UI button "Attach to Roblox"
-> tauri invoke attach
-> rust backend calls into src\auth\commands.rs
-> POST /injector/launch-token (needs valid session)
-> receives real-launch-token-v3 signed payload
-> verify Ed25519 signature (see auth section)
-> CreateProcessW spawn Injector.exe with launch token
(probably passed via env var, IPC, or command line arg)VS Code extension integration
UI.exe ships a bundled VS Code extension package. Commands registered.
Real.attach writes IPC file attach.txt
Real.execute writes IPC file execute.txt with script content
Real.openTab writes IPC file open_tab.txt with { name, path, content }
Real.launch writes IPC file launch.txtThe IPC directory is a shared temp directory whose path gets injected into the extension at load time. Bidirectional sync uses sync_to_vscode.txt and sync_from_vscode.txt. This is how a script written in VS Code gets executed inside Roblox with one click.
Auth surface. Deep dive
The Injector.exe binary itself has zero auth. No cookie handling. No OAuth. No HWID computation. No Ed25519. Search of the 1.8 GB core dump for terms like account_session, launch-token, x-hwid, Bearer, real-executor returns zero hits. All auth lives in UI.exe. What follows is the complete surface recovered from that binary.
Domain topology
projectreal.live API base host Cloudflare fronted
api.projectreal.live control plane
download.projectreal.live release CDN
dl.projectreal.live mirror
cdn.projectreal.live static assets
projectreal.gg web / OAuth landing
api.projectreal.gg OAuth token exchange
projectreal.gg marketing + login pagesComplete endpoint table
| Method | Endpoint | Purpose |
|---|---|---|
| GET | api.projectreal.live/status | Service liveness |
| POST | api.projectreal.live/verify | Verify per install token from installer |
| GET | api.projectreal.live/update/check | Signed CDN manifest |
| POST | api.projectreal.live/injector/launch-token | Per launch token before injection |
| POST | api.projectreal.live/account/devices/challenge | Device attestation nonce challenge |
| POST | api.projectreal.live/account/devices | Register a device public key |
| GET | api.projectreal.live/account/me | Authenticated account details |
| POST | api.projectreal.live/account/heartbeat | Session keepalive |
| POST | api.projectreal.live/account/logout | End session |
| POST | api.projectreal.live/account/claim-app-auth-ticket | Claim Roblox auth ticket for linked account |
| GET | api.projectreal.gg/auth/discord?mode=login&app_auth=1&state=<s> | Discord OAuth start |
| GET | projectreal.gg/login/?loggedout=1&app_auth=1&state=<s> | Post logout redirect |
The login flow, step by step
Reconstructed by chaining recovered strings against the reqwest call sites in src\auth\api.rs and src\auth\commands.rs.
Step 1. User clicks Sign in with Discord in the UI.
Step 2. UI (Tauri backend) generates a random state token and opens:
https://api.projectreal.gg/auth/discord?mode=login&app_auth=1&state=<RANDOM>
The app_auth=1 flag tells the server this is a desktop client
that expects a deep link callback, not a browser redirect.
Step 3. Browser opens. User authenticates against discord.com.
Server exchanges Discord OAuth code, mints a per session ticket.
Step 4. Server redirects the browser to a URL of the form:
projectreal://auth?ticket=<TICKET>
The projectreal:// scheme was registered by the installer under
HKCU\Software\Classes\projectreal\shell\open\command.
Windows picks up the deep link, launches UI.exe with it as
argv[1] (if not already running, otherwise sends to existing).
Step 5. UI receives the deep link (event tag 'projectreal://deep-link-received'),
parses out <TICKET>.
Step 6. UI POSTs the ticket to the API. Server sets two cookies on the
reqwest cookie store:
Set-Cookie: account_session=<SESS> HttpOnly, Secure
Set-Cookie: account_csrf=<CSRF> readable, session bound
Both stored under cookie store identifier "session-cookies-v1".
Step 7. UI immediately checks for a stored device keypair in Windows
Credential Manager under service="real-executor" account="device-key".
If missing: run the device registration flow.
If present: run the device challenge flow on the next auth
gated request to prove ownership.
Step 8. UI GETs /account/me. Server returns AuthSuccess.
Now the user is authenticated. Plan tier, balance and free trial
status are all visible in the account object.The standard request shape
Every authenticated request from UI.exe looks like this.
POST https://api.projectreal.live/<endpoint> HTTP/1.1
Content-Type: application/json
User-Agent: RealExecutor/1.0
X-CSRF-Token: <CSRF from account_csrf cookie>
x-hwid: HWIDv4_<hex hash>
Cookie: account_session=<SESS>; account_csrf=<CSRF>
Accept-Language: (whatever the OS default is)
{
"appVersion": "1.1.0",
"osVersion": "windows 11",
... endpoint specific body ...
}Notes on each header. User-Agent is a fixed string, requests from other UAs would look anomalous in access logs. X-CSRF-Token is the value of the account_csrf cookie resent as a header (double submit pattern), server rejects mismatches with CSRF_NOT_AVAILABLE. x-hwid is the HWID v4 hash, server compares against expected value for the session's account, drift causes a refuse. Cookie is standard, sent by the reqwest cookie store.
HWID v4. The full formula
The client computes an HWID v4 fingerprint from four hardware inputs. The formula was recovered from the block of concatenated strings around file offset 0x8a06a0 in UI.exe.
Recovered raw layout in UI.exe .rodata:
"id="
"system_volume="
"smbios_sha256="
"cpu_id="
"RealExecClient-HWID-v4"
"HWIDv4_"
"SOFTWARE\Microsoft\Cryptography" (registry key for MachineGuid)
"MachineGuid" (value name)
"Could not derive HWID from this system"
"x86_64" "windows 11" "Windows 10"Interpretation. The client builds a canonical string of the form:
id=<MachineGuid> &system_volume=<volume serial of SystemDrive C:> &smbios_sha256=<SHA-256 over concatenated SMBIOS strings> &cpu_id=<CPUID vendor + brand + feature bits>
Prefixes with the domain separator "RealExecClient-HWID-v4" then hashes. Final result is prefixed with "HWIDv4_" and used as the x-hwid header value.
Individual input sources
Registry read HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid. Windows Product Activation identifier. Stable across reinstalls of the same Windows install. Changes on a fresh Windows install.
Volume serial number of the drive holding %SystemDrive% (usually C:). Changes when the disk is reformatted or when a new Windows install lands on a different volume.
SHA-256 over concatenated SMBIOS strings. BIOS vendor, BIOS version, mainboard manufacturer, mainboard product, system UUID and system serial. Changes only when the physical mainboard changes or when the BIOS ROM is reflashed with a different SMBIOS payload.
CPUID vendor string, brand string and feature bits. Changes only when the CPU package is replaced.
The combination targets a "same physical PC" definition. Reinstalling Windows keeps three of the four inputs stable. Replacing motherboard or CPU changes it. That is the model.
Device registration flow
First time a customer signs in on a machine, the client generates a fresh Ed25519 keypair, ships the public half to the server and stores the private half in Windows Credential Manager. All names in the flow were recovered verbatim.
Step 1. UI attempts Windows Credential Manager read:
service = "real-executor"
account = "device-key"
On miss (DEVICE_KEYPAIR_LOAD_FAILED), fall into registration.
Step 2. UI calls into the Rust keyring crate to generate a fresh Ed25519
keypair. Errors here surface as DEVICE_KEYGEN.
Step 3. UI POSTs to /account/devices with a JSON body of:
{
"devicePublicKey": "<base64url pubkey>",
"keyProtectionLevel": "software"
}
keyProtectionLevel = "software" is telling. It means the private
key is not held in a TPM. It is DPAPI wrapped and stored via
Credential Manager. That is a deliberate compromise: allows the
client to run without TPM, but means anyone with the user's
Windows login session can extract the device key.
Step 4. Server response (verified server side, signed as always):
{ "device_id": "<stable id>", ...responseSig+Ts+Nonce... }
Server side, the device_id is joined to the account with a fresh
row. Server tracks: (account_id, device_id, pubkey, hwid_hash,
first_seen_at, last_seen_at).
Step 5. UI writes the private key to Windows Credential Manager under
service = "real-executor"
account = "device-key"
Errors here surface as DEVICE_STORE_ID.
Step 6. UI writes device_id into the local encrypted account vault
so it can be referenced without another server call.Device challenge and response
On every auth gated request, the client is expected to prove ownership of the device keypair. This runs before or alongside the actual API call.
Step 1. Client GETs /account/devices/challenge.
Server returns:
{
"nonce": "<fresh random nonce>",
"account_id": "<current account>",
"hwid_hash": "<the current expected hwid v4 hash>",
...responseSig+Ts+Nonce...
}
Step 2. Client checks server responseSig with the baked in Ed25519 pubkey.
If verify fails, abort with INTEGRITY_FAILED.
Step 3. Client checks account_id matches session, nonce is non empty,
hwid_hash matches locally computed HWID v4. On any check fail:
CHALLENGE_NO_ACCOUNT_ID
CHALLENGE_NO_NONCE
CHALLENGE_NO_HWID_HASH
CHALLENGE_PARSE
Step 4. Client loads Ed25519 private key from Windows Credential Manager
under (real-executor, device-key). On failure:
DEVICE_KEYRING: <msg>
DEVICE_KEYPAIR_LOAD_FAILED
Step 5. Client signs the challenge nonce with the private key.
On failure: DEVICE_SIGN_FAILED.
Step 6. Signature attached to the next authenticated request as body
fields deviceChallengeNonce and deviceSignature.
Step 7. Server verifies the signature against the account's registered
pubkey. On success, the actual API call proceeds. On failure,
the request is rejected with 401.This is a proper challenge response, not a static bearer token. A leaked cookie alone will not authenticate. You need the cookie plus the device private key. On the client side, the private key sits in Windows Credential Manager under the user account, unencrypted at the OS level (DPAPI wraps it but any process running as that user can decrypt). So the model is: "same Windows user session as the enrolling one" is treated as equivalent to the device.
The launch token flow
When the user clicks Attach in the UI, the flow is:
Step 1. UI runs a device challenge if the last one is stale.
Step 2. POST /injector/launch-token with:
body: { appVersion, osVersion, deviceChallengeNonce,
deviceSignature, ... }
headers: cookie, csrf, hwid, standard shape.
Step 3. Server side account state check:
NO_SESSION no valid session cookie
SUSPENDED account is suspended
NO_BALANCE no active plan / no free tier remaining
MUST_CHANGE_USERNAME forced credential change
MUST_CHANGE_PASSWORD forced credential change
ROBLOX_NOT_FOUND no attached Roblox account claim
Any of these returns a JSON error. Otherwise proceeds.
Step 4. Server mints a launch token. Payload scheme identifier:
real-launch-token-v3
The "v3" suffix implies at least two prior revisions. Server
signs it with the Ed25519 key documented below.
Step 5. Response:
{ "launch_token": "<opaque>",
"expires_at": <unix ts>,
...responseSig+Ts+Nonce... }
Step 6. UI CreateProcessW spawns Injector.exe. Passes launch_token
forward (mechanism most likely a command line arg or env var,
possibly an inherited handle for a named pipe). Injector consumes
it and does not persist it.Response signing. The baked in Ed25519 pubkey
Every response from api.projectreal.live is signed by the server. The client verifies with a pubkey compiled into UI.exe. Recovered from file offset 0x8a08e0.
Recovered literal in UI.exe .rodata:
responseSig
responseAlg
responseTs
Ed25519
responseNonce
real-response-v1
NzfoKTM_wDqKgse8D0-OWUt6Rmxt4vKj-t6rOTW0dm4
integrity_failed
CLOCK_SKEW
SERVER_PROTOCOL_ERROR
Invalid server responseThe server pubkey is NzfoKTM_wDqKgse8D0-OWUt6Rmxt4vKj-t6rOTW0dm4. base64url, 43 characters, decodes to exactly 32 bytes. Wire format of an Ed25519 public key.
This is the same key used to verify the update manifest signatures. Same key used to verify launch tokens. Same key used to verify every API response envelope. It is Project Real's signing authority. Any signed blob out in the wild that verifies against this key is provably from their infrastructure.
Response wrapper fields
Field Type Notes
responseSig base64url Ed25519 signature over (Ts, Nonce, Body)
responseAlg string Always "Ed25519" in this build
responseTs unix seconds Rejected if drift > CLOCK_SKEW threshold
(typical is 300s)
responseNonce base64url Per response nonce. Server side anti replay.
Also carried as HTTP header X-Response-Nonce.Signature scheme identifier real-response-v1 is the domain separator. It tells the signature verifier which fields to include. In which order. What canonical encoding.
Local credential storage
Three separate stores on the client machine. Together they hold everything an attacker would want to lift a session.
a. Windows Credential Manager (device keypair)
Service : real-executor Account : device-key Value : Ed25519 private key (raw 32 or 64 byte) Storage backend: keyring 3.6.3 (Rust crate) bindings to Windows Credential Manager. DPAPI wrapped by the OS. Any process running as the current user can read it via CredRead + CryptUnprotectData.
b. account-vault.dpapi (persistent session and accounts)
Filename : account-vault.dpapi
Location : somewhere under %APPDATA%\Real\ (path structure recovered
but not exact directory)
Vault key : label "Real-UI account vault key v1R"
key material derived via DPAPI unwrap of a stored blob
Prefix : "dev:v1:C" on every wrapped value
Encryption : AES-GCM most likely (given Rust ecosystem and keyring context)
Contents (inferred from Account struct):
account_session cookie value
account_csrf cookie value
All Roblox account entries (.ROBLOSECURITY per account)
Cookie rotation timestamps
Local device_id
Recent games list (from Roblox API)c. Discord account state
discord_connected discord_username discord_avatar_url Held in memory during the session, persisted to the vault when written to disk.
Vault key error taxonomy
"Wrong password or data corrupted" "Account vault is locked or belongs to a different Windows profile" "Migrated account vault key has an invalid length" "Legacy account vault key was invalid and has been removed" "Incorrect answers to security questions" "Incorrect current password"
The security question fallback means there is an escrow path. If a user forgets their vault password and cannot use DPAPI (say they moved the file to another machine), they can answer their security questions to recover. That means the server holds enough state to validate those answers. It also means the vault contents can be reopened on a different machine with those answers plus a server call.
Roblox account claim flow
Real can attach multiple Roblox accounts. Each attach uses the customer's own .ROBLOSECURITY cookie plus a Roblox authentication ticket flow.
Step 1. User pastes .ROBLOSECURITY into the UI's Add Account dialog.
Stored in the encrypted vault above.
Step 2. UI performs GET https://auth.roblox.com/v1/authentication-ticket/<>
with the cookie attached.
Roblox returns a ticket via response header rbx-authentication-ticket.
On failure, one of these surfaces:
"Could not obtain security token from Roblox.
Is your account cookie still valid?"
"Failed to get auth-ticket (HTTP %d). Cookie may have expired,
please re-add the account."
"Auth-ticket missing from response headers"
Step 3. UI POSTs to /account/claim-app-auth-ticket with:
{ "ticket": "<from roblox header>",
"target_username": "<the roblox account name>", ... }
Step 4. Real's server holds the mapping (account_id -> roblox_account_id).
Real's server can now issue Roblox launcher URLs on the customer's
behalf, enabling one click launch of arbitrary Roblox accounts.
Step 5. AuthSuccess now includes the account under an entries list
(Account struct fields: username, user_id, display_name,
avatar_url, added_at, alias, sort_index, cookie_rotated_at).Complete account object
The AuthSuccess response from /account/me returns this shape. Every field recovered as a JSON key string in UI.exe.
| Field | Type / meaning |
|---|---|
| account_id | opaque server side id |
| user email | |
| hwidHash | server side hwid v4 hash for the current session |
| discord_connected | bool |
| discord_username | string |
| discord_avatar_url | cdn.discordapp.com/avatars/... |
| plan | string label |
| plan_tier | free | basic | premium | ... |
| plan_active | bool |
| plan_expires_at | unix seconds |
| effective_tier | string |
| effective_tier_label | string |
| balance_active | bool |
| balance_paused | bool |
| free_phase_ends_at | unix seconds |
| free_trial_expires_at | unix seconds |
| booster_active | bool |
| linked_devices | count |
| max_devices | device cap for the account |
Complete client side error taxonomy
| Code | Meaning |
|---|---|
| LAUNCH_TOKEN_HTTP_<code> | server returned non 2xx |
| LAUNCH_TOKEN_NET | network layer failed |
| LAUNCH_TOKEN_PARSE | response body malformed |
| LAUNCH_TOKEN_INVALID | signature verify failed |
| CSRF_NOT_AVAILABLE | no csrf token in session |
| CHALLENGE_HTTP_<code> | challenge request failed |
| CHALLENGE_NET | network layer failed |
| CHALLENGE_PARSE | response malformed |
| CHALLENGE_NO_ACCOUNT_ID | server did not include account_id |
| CHALLENGE_NO_NONCE | server did not include nonce |
| CHALLENGE_NO_HWID_HASH | server did not include hwid_hash |
| DEVICE_SIGN_FAILED | ed25519 signing failed |
| DEVICE_REG_HTTP_<code> | device registration HTTP fail |
| DEVICE_REG_NET | device registration net fail |
| DEVICE_REG_PARSE | device registration parse fail |
| DEVICE_REG_NO_ID | server did not return device_id |
| DEVICE_STORE_ID | Credential Manager write failed |
| DEVICE_KEYGEN | ed25519 keypair generation failed |
| DEVICE_KEYRING | keyring read failure |
| DEVICE_KEYPAIR_LOAD_FAILED | device keypair not present |
| NO_ACCOUNT_SESSION | no session cookie |
| NO_SESSION | server rejected session |
| SUSPENDED | account suspended |
| NO_BALANCE | no active plan / no free tier remaining |
| ROBLOX_NOT_FOUND | no attached Roblox account claim |
| MUST_CHANGE_USERNAME | forced credential change |
| MUST_CHANGE_PASSWORD | forced credential change |
| INTEGRITY_FAILED | signature did not verify against pubkey |
| CLOCK_SKEW | responseTs was too far from local clock |
| SERVER_PROTOCOL_ERROR | wrapper shape mismatch |
| COOKIE_STORE_UNAVAILABLE | reqwest cookie store not available |
GitHub Gist integration (side flow)
UI.exe carries a full GitHub Device Auth OAuth flow. Recovered strings:
https://github.com/login/device/code start
https://github.com/login/oauth/access_token poll for token
Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28
User-Agent: Real-UI GitHub Gists
scope: gist read:user
Errors:
authorization_pending
slow_down
expired_token
GITHUB_DEVICE_AUTH_FAILED
GITHUB_DEVICE_CODE_EXPIRED
GITHUB_RATE_LIMITED
GIST_NOT_FOUND
GITHUB_FORBIDDEN_OR_RATE_LIMITED
GITHUB_BAD_CREDENTIALS
GITHUB_VALIDATION_FAILED
GITHUB_NO_ACCESS_TOKEN
Content hosts:
raw.githubusercontent.com
gist.githubusercontent.comScript sharing flow. Users can save scripts to Gists and load scripts from Gists. Separate from the Project Real auth surface. Uses the GitHub Device Auth flow so the client never sees a client secret.
Injector auth boundary
Worth stating explicitly. The Injector.exe binary does not carry any of the auth surface above. The 1.8 GB core dump from the live Wine run contains zero hits for:
account_session 0 hits launch-token 0 hits x-hwid 0 hits Bearer 0 hits Ed25519 0 hits (as a string; the actual crypto lives in UI.exe) real-executor 0 hits
The injector receives only the launch token from the UI, presumably via command line arg, env var or inherited handle. If it fails to consume it, no auth. If the token is invalid or expired, the launch is rejected at the injector's own gate before it does anything to Roblox's process.
This is a clean design boundary. An isolated injector fails safe. It cannot forge tokens without the UI's session state. Even a stolen injector binary alone cannot inject.
Extra API parameters recovered from a second packed field blob
There is a second concatenated field name run in UI.exe at file offset 0x89c008. Splitting it produces the parameter names for endpoints that are not surfaced by URL constants directly. Together they imply half a dozen more routes we did not enumerate.
Recovered raw run:
userIdsaccountIdlistTypelimituserIdmultiRobloxmaxAgeDaysoldFilename
newFilenameisPubliccursoranswer1answer2newPasswordquerysortFilter
sessionIdknownUpdatedAtoverwritecurrentPassword
Grouped by likely endpoint:
Multi Roblox account listing / management:
userIds accountId userId multiRoblox listType limit
File / script sharing:
oldFilename newFilename isPublic overwrite
Pagination / query:
cursor limit query sortFilter maxAgeDays knownUpdatedAt
Security question recovery for the vault:
answer1 answer2 newPassword currentPassword
Session management:
sessionId overwriteAlso recovered nearby: a second batch of auth error codes not in the earlier taxonomy. These sit in src\auth\device.rs alongside src\auth\api.rs and src\auth\commands.rs.
OAuth callback errors:
STATE_MISMATCH Discord OAuth state token mismatch (CSRF against browser)
INVALID_DEEP_LINK projectreal:// URL malformed
INVALID_URL url parse failed
Device key errors:
DEVICE_KEY_LOAD_FAILED could not read the raw bytes from Credential Manager
DEVICE_KEY_DECODE_FAILED raw bytes did not decode as Ed25519 pubkey
DEVICE_KEY_NOT_FOUND no entry present
DEVICE_NOT_REGISTERED server responded that this device_id is not on the account
DEVICE_KEYGEN_FAILED Ed25519 keypair generation failed
DEVICE_STORE_KEY private key write to Credential Manager failedAlso new: device-key-v1 as the Credential Manager schema version tag. Suggests a v0 existed at some point and a v2 is planned.
Live infrastructure recon
Everything you can see from outside. All API hosts are Cloudflare fronted so there is no raw origin IP visible without CT log historical trawling or a leaked backend cert. That is a materially better opsec posture than the FiveM cheat ecosystem, where sellers like Anxiety and ClubXJefe hardcode bare IP:port pairs into their loaders. Real does not.
DNS resolution as observed
Zone: projectreal.live (Cloudflare)
api.projectreal.live 104.26.10.42, 104.26.11.42, 172.67.68.127
download.projectreal.live same set (shared Cloudflare edge)
dl.projectreal.live same set
projectreal.live same set
cdn.projectreal.live NXDOMAIN (referenced in binary, not deployed)
IPv6 2606:4700:20::681a:a2a, ::681a:b2a, ::ac43:447f
Zone: projectreal.gg (different Cloudflare tenant)
api.projectreal.gg 104.21.16.24, 172.67.165.250
status.projectreal.gg 104.21.16.24
IPv6 2606:4700:3037::6815:1018, ::ac43:a5fa
projectreal.gg (marketing site, NOT Cloudflare)
A records 185.199.108.153, 185.199.109.153,
185.199.110.153, 185.199.111.153
PTR cdn-185-199-108-153.github.com etc
Verdict hosted on GitHub Pages
projectreal.net (Cloudflare, purchase redirect)
A records 172.67.209.109, 104.21.37.145
Behavior /pricing/ 301 redirects to projectreal.gg/pricing/Two Cloudflare tenants (one per zone). Marketing on GitHub Pages. Purchase host acting as a URL shortener. No exposed origin IP anywhere in this map.
Origin egress port
Everything terminates at Cloudflare on standard 443/tcp (HTTPS) and 443/udp (HTTP/3). No non-standard ports observed. For contrast: the ClubXJefe loader we documented in a prior post ships a hardcoded literal 185.242.3.132:41327. Real Executor has no such wire IOC. Any network detection has to be by SNI or by TLS fingerprint.
CSP declared dependency graph
The marketing site (projectreal.gg) ships its own Content Security Policy as a meta tag (since GitHub Pages does not serve custom headers). This is a verbatim disclosure of every backend the browser side is allowed to talk to:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com;
connect-src 'self'
https://api.projectreal.gg
https://status.projectreal.gg
https://discord.com
https://api.rubis.app;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
frame-src https://challenges.cloudflare.com;
object-src 'none';
base-uri 'self';Fresh finding from CSP: the marketing site directly talks to api.rubis.app and uses Cloudflare Turnstile (challenges.cloudflare.com) as its bot filter. See the next two sections for what happens on those hosts.
Live /status endpoint response
Both api.projectreal.live/status and api.projectreal.gg/status return the same JSON to any unauthenticated caller. No cookies. No API key. Just a valid User Agent. We captured this against the live host at time of publication.
GET https://api.projectreal.live/status HTTP/2
200 OK content-type: application/json; charset=utf-8
{
"title": "Real",
"version": "1.1.0",
"updatedDate": "07/11/2026 at 2:46 PM UTC",
"free": true,
"detected": false,
"rbxversion": "version-36a2600cebf1487d",
"updateStatus": true,
"websitelink": "https://projectreal.gg/",
"discordlink": "https://discord.gg/projectreal/",
"purchaselink": "https://projectreal.net/pricing/",
"beta": true,
"platform": "Windows",
"extype": "wexecutor",
"cost": "Key System",
"reseller": true,
"decompiler": true,
"multiInject": true,
"myriadPercentage": 85,
"suncPercentage": 100,
"uncPercentage": 99,
"sunc": {
"suncScrap": "k597N6wevNbuwVdT",
"suncKey": "gHwa20vz7NZF18BMHf82et0shmtSxmky"
},
"update": {
"version": "1.1.0",
"pub_date": "2026-07-11T14:46:22.000Z",
"changelog":
"* Luau 0.728 -> 0.729\n"
"* Fixed hookmetamethod not working\n"
"* Added Real-Decompiler (no more LE)\n"
"* Fixed a vulnerability regarding opening log files (FileManagerService)"
}
}What every field means
| Field | Meaning |
|---|---|
| rbxversion | The Roblox client version the current build is tuned for. Client polls this to warn when Roblox pushed an update ahead of Real. |
| detected | Server side kill switch. Set to true to disable execution across every customer at once. |
| free | Free tier is currently enabled. Used by the front page to show or hide the free key ad flow. |
| beta | Build is a beta channel release. |
| extype | 'wexecutor' identifies as a Windows executor. Rubis and similar aggregators consume this. |
| cost | 'Key System'. Real Executor uses a per key model rather than a subscription. |
| reseller | Reseller program is active. Third parties can sell keys. |
| decompiler | Real Decompiler feature is available. Note: paired with the changelog line 'no more LE', meaning they moved off Lua Expert. |
| multiInject | Multi Roblox process injection is supported (attach to more than one Roblox at a time). |
| myriadPercentage | Compatibility score against the Myriad executor test bench. Range 0-100. |
| suncPercentage | Compatibility score against the sUNC test bench. 100 means all sUNC tests pass. |
| uncPercentage | Compatibility score against the classical UNC test. 99 means one test fails. |
| sunc.suncScrap | Rubis text sharing scrap id. Public. Used as URL parameter. |
| sunc.suncKey | Rubis access key for the same scrap. Public. Anyone can fetch the raw test result. |
Real's 1.1.0 changelog contains the literal line "Added Real-Decompiler (no more LE)". LE is Lua Expert. That is the third party api.lua.expert/decompile service that Real's decompile() was silently shipping customer bytecode to every call. This entry in the changelog is Real acknowledging that leak and switching to a local decompiler on the same day (2026-07-11 14:46:22 UTC). If you are one of the customers who used decompile() on a build before 1.1.0, your target scripts were on api.lua.expert.
The api.rubis.app dependency
api.rubis.app (numelon rubiš) is a third party paste bin and text sharing service. Search of UI.exe strings around src\sunc.rs shows Real uses two URL templates against it.
https://api.rubis.app/v2/scrap/{scrap}/raw?accessKey={key} (raw JSON API)
https://sunc.rubis.app/?scrap={scrap}&key={key} (HTML viewer)Substituting the suncScrap and suncKey we recovered from /status returns Real's sUNC compatibility test result live.
GET https://api.rubis.app/v2/scrap/k597N6wevNbuwVdT/raw?accessKey=gHwa20vz7NZF18BMHf82et0shmtSxmky
200 OK
{
"timestamp": 1779207595.061,
"version": "2.1.5",
"bibip": false,
"timeTaken": 18.58,
"tests": {
"passed": [
"getrawmetatable", "makefolder", "getscriptbytecode", "setthreadidentity",
"delfile", "request", "Drawing.Fonts", "isscriptable", "iscclosure",
"debug.setconstant", "debug.getprotos", "lz4compress", "getscripts",
"isfolder", "sethiddenproperty", "getthreadidentity", "readfile",
"getscriptclosure", "delfolder", "setscriptable", "Drawing.new",
"debug.getupvalues", "hookmetamethod", "debug.getproto", "getrunningscripts",
"checkcaller", "debug.setupvalue", "setrawmetatable", "gethiddenproperty",
"writefile", "setrenderproperty", "getnamecallmethod", "isfile",
"fireclickdetector", "getnilinstances", "getcustomasset", "getconnections",
"islclosure", "restorefunction", "loadstring", "cache.iscached",
"cache.invalidate", "cloneref", "cache.replace", "getgc", "compareinstances",
"base64_encode", "getrenv", "hookfunction", "debug.getupvalue", "setreadonly",
"getloadedmodules", "debug.getinfo", "getscriptfromthread", "fireproximityprompt",
"WebSocket.connect", "listfiles", "gethui", "isreadonly", "getrenderproperty",
"lz4decompress", "appendfile", "loadfile", "getinstances", "isexecutorclosure",
"getcallbackvalue", "getfunctionhash", "replicatesignal", "cleardrawcache",
"decompile", "filtergc", "identifyexecutor", "getscripthash", "firesignal",
"firetouchinterest", "debug.setstack", "isrenderobj", "getcallingscript",
"debug.getstack", "getsenv", "clonefunction", "debug.getconstant", "getgenv",
"newcclosure", "base64_decode", "debug.getconstants"
],
"failed": []
},
"__SUNC": true,
"executor": "Real/V0.2.6"
}85 functions passed. Zero failed. The executor: "Real/V0.2.6" field is not the current 1.1.0 build. It is the version the sUNC run was executed on. Real does not rotate the scrap for every release. The bibip flag is a rubiš internal marker (paste protection bit). The whole result is public to anyone with the scrap plus key. The client fetches it once and displays the result inside the UI.
/status reports suncPercentage: 100. The live sUNC result confirms it. 85 passed, 0 failed. That is the pitch Real makes to prospective customers who care about script compatibility.
Public pricing tiers
Recovered from the live pricing page at projectreal.gg/pricing/. Four tiers. Free is genuinely free and gated on watching ads for daily keys. The paid tiers unlock quotas and features.
Tier hierarchy (planTier value shown in the client):
free Free (default)
booster Booster (Server Booster on Discord)
boost Boost ($2.99 tier observed)
plus Plus best value ($9.99 tier observed)
Feature matrix (from pricing page cells):
Free Booster Boost Plus
Start balance +30d
Free keys / 24h 1 2 4 5 (or 10)
No ad-steps needed - - yes yes
Ad-free download - - yes yes
Device reset cooldown long medium short fastest
Spoofer cooldown long medium short fastest
Alt generator - Standard Priority High priority
Aged accounts (+30d) - - yes yes
Full executor access yes yes yes yes
Discord role - Server B Boost Plus
Image permissions - - yes yes
Support std std Priority High priority
Downtime compensation - - yes yesThe Booster tier is unlocked by boosting the Discord server rather than a paid transaction. So the actual paid tiers are Boost and Plus. The Free tier gates on ads through work.ink (recovered as a ?wt= return token handler in the front page HTML).
Discord and free key generation
Discord invite discord.gg/projectreal
Free key ad handler work.ink AdWall handler (wt return token snapshotted before
history.replaceState strips it, per the recovered inline script)
Bot filter Cloudflare Turnstile (challenges.cloudflare.com)Installer artifacts
On disk layout after install
%LOCALAPPDATA%\Real\
Real.exe = UI.exe (Tauri app, 10.5 MB)
Real.dll = encrypted executor DLL
bin\
Injector = Injector.exe (Hyperion bypass, 4.12 MB)
Update.exe = persistent updater (RealLauncher from RCDATA #101)
luau-lsp.exe = JohnnyMorganz/luau-lsp for Monaco autocomplete
EBWebView\x64\
EmbeddedBrowserWebView.dll = Edge WebView2 runtime hostPersistence
Registry autorun:
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Value: "Real Startup" -> path to Real.exe
Scheduled task:
Real\Launch at HIGHEST integrity, /ru "" /it
Real/Delete/TN Real Startup
Custom URL scheme:
HKCU\Software\Classes\projectreal\shell\open\command
Handler: UI.exe with %1Anti defense manipulation
Defender exclusion (from elevated PowerShell):
Add-MpPreference -ExclusionPath "<install path>"
(Tamper Protection may block this; installer logs
"Could not update Defender exclusions".)
DoH (DNS over HTTPS) baked into the installer's DNS layer:
cloudflare-dns.com
dns.google
dns.quad9.net
"Repair" mode (elevated):
Renames C:\Windows\System32\catroot2 to catroot2.REAL.old
Edits %SystemRoot%\System32\drivers\etc\hosts
netsh winsock reset
Sets DNS on every up network adapter to 1.1.1.1 / 1.0.0.1
via Get-NetAdapter | Set-DnsClientServerAddress
"Could not resolve Real directories" is a soft failure
Token stealing helpers (recovered strings):
DuplicateTokenEx
OpenProcessToken
CreateProcessWithTokenW
explorer.exe target for token elevationCryptographic stack
| Primitive | Present | Actually used for |
|---|---|---|
| Ed25519 | Yes (WinCNG + OpenSSL) | Server response signing, launch token signing, manifest signing. Client device keypair for attestation. |
| SHA-256 | Yes (BCrypt + OpenSSL) | Manifest integrity. HWID v4 SMBIOS hashing. Response signature payload hashing. |
| XOR (8 byte repeat) | Yes (custom) | Real.dll on disk encryption. Key 2A 27 94 7A E9 EA DA 78. |
| DPAPI | Yes (Windows crypt32) | Wrapping account-vault key. Wrapping device private key in Credential Manager. |
| AES | Yes (BCrypt + OpenSSL) | TLS. Exposed to Luau via crypt.encrypt / crypt.decrypt. |
| AES-GCM | Present in deps | Local vault (dev:v1:C prefix consistent with GCM). |
| ML-DSA 44 / 65 / 87 | Linked in OpenSSL 3 | Not observed in wire format. Future proofing. |
| SLH-DSA (SPHINCS+) | Linked | Not observed. Future proofing. |
| X25519MLKEM768 | Linked | Hybrid PQ + classical TLS group. Available if CDN offers it. |
| Trufo C2PA chain | Bundled root + intermediate | C2PA / JUMBF verification. Not live. |
| Authenticode (WinTrust) | Yes | Checked on incoming Update.exe before atomic swap. |
Tooling
The stack that makes sense for a job like this. Reverser oriented, Windows leaning, packer aware.
Disassembly and decompilation
IDA Pro 9.0 Hex-Rays x64 decompiler for the outer wrapper Binary Ninja 4.x cross reference sanity on the aPLib stub and VM shape Ghidra 11.1 second pass decompilation on Real.dll's Luau VM Cutter (Rizin backend) RTTI extraction on the RealVM namespace retdec hand off findings to peers
Packer identification and unpacking
Detect It Easy (DIE) 3.10 first pass packer triangulation PE-bear section characteristics review CAPA (Mandiant) behavioral rule matching on the unpacked code MagicMida / Themidie reference behavior for WinLicense stubs TitanHide Themida VM entry survey ScyllaHide anti anti debug layer for the Windows side runs
Windows debugging
x64dbg (nightly) primary Windows debugger with ScyllaHide + xAnalyzer WinDbg Preview kernel side introspection and Time Travel Debugging API Monitor v2 (rohitab) WinHTTP + BCrypt + NCrypt call auditing on UI.exe Process Hacker 2 live process view with KProcessHacker driver System Informer same, newer fork PE-sieve detect reflectively loaded DLLs inside targets HollowsHunter detect process hollowing artifacts Moneta memory anomaly scanning against the injected image
Dynamic instrumentation and emulation
Frida 16.x hooked WinHTTP and curl_easy_setopt on UI.exe Unicorn Engine x86-64 CPU emulation for isolated function replay Qiling Framework Windows userland emulation of the outer stub Speakeasy (Mandiant) packed sample emulation as a sanity check Dumpulator minidump driven replay for the VM handler traces
Network capture and interception
Wireshark ambient packet capture during runs mitmproxy TLS interception of UI to backend traffic Burp Suite Professional request replay and body fuzzing Fiddler Classic + CertMaker Windows side TLS decryption reference openssl s_client cert chain confirmation on the Cloudflare fronts
Cryptography
CyberChef base64url and Ed25519 signature verify Python cryptography 43.x Ed25519 verify against the manifest signatures YARA signature drafts (see IOC bundle)
Environment
QEMU + KVM (Ubuntu host) Windows 11 guest for the injector's real target VMware Workstation Pro second isolated environment for sandbox runs Windows Sandbox short lived analysis snapshots Sysinternals suite Process Explorer, Autoruns, Handle, VMMap, ProcMon
Miscellaneous
010 Editor with PE template hex level surgery CFF Explorer quick PE structure edits ImHex byte pattern visualisation 7-zip + innounp installer archive extraction
IOC bundle
File hashes
| Injector.exe | SHA-256 | 949ed91c5395e407b3b29c7842bca3c50b61c0df62b9a891211f7297c79f148c |
| Real.dll (enc) | SHA-256 | 2756337a38a4525aa9b90b5dd2a843526d79f5b345258f56e8b093b51122b167 |
| UI.exe | SHA-256 | 11ceebe9c8af83e2aea61c28da7d7e0902daa9eda124f7a946c85b958152bbab |
| Real.dll XOR key | 8 bytes | 2A 27 94 7A E9 EA DA 78 |
Cryptographic identity
| Server Ed25519 pubkey (base64url) | NzfoKTM_wDqKgse8D0-OWUt6Rmxt4vKj-t6rOTW0dm4 |
| Server Ed25519 pubkey (hex) | 3737e829333fc03a8a82c7bc0f4f8e594b7a466c6de2f2a3fadeab3935b4766e |
| SHA-256 of pubkey | ae5f80e44b8a29361ee38991d35a1a6a8c010c9e9424bcd581b2bf1c737ce635 |
| Signature scheme identifier | real-response-v1 |
| Launch token scheme identifier | real-launch-token-v3 |
| HWID scheme label | RealExecClient-HWID-v4 (prefix HWIDv4_) |
HTTP fingerprint
| User-Agent | RealExecutor/1.0 |
| Session cookie names | account_session, account_csrf |
| CSRF header | X-CSRF-Token |
| HWID header | x-hwid |
| Response nonce header | X-Response-Nonce |
| Cookie store schema id | session-cookies-v1 |
Host artifacts
| Install directory | %LOCALAPPDATA%\Real\ |
| Persistent updater | %LOCALAPPDATA%\Real\bin\Update.exe |
| Registry autorun | HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\Real Startup |
| Scheduled task | Real\Launch (HIGHEST integrity) |
| URL scheme handler | HKCU\Software\Classes\projectreal |
| Local vault file | account-vault.dpapi |
| Vault key label | Real-UI account vault key v1R |
| Credential Manager entry | service=real-executor account=device-key |
| Injector log file | log.txt in cwd |
Behavioral indicators (runtime)
Injector process comm Injector.exe
Distinctive log lines "Roblox not found"
"Wrong Roblox Version"
"Patching integrity at %X..."
"Successfully Injected!"
"Remapped"
"manual mapping"
"remapping done"
"injected"
Distinctive string in memory "WE LOVE HYPERION 69" x 256 sites
"WE LOVE HYPERION 67" x 27 sites
"JUSTMAGIC WE LOVE YOU" x 256 sites
Injection technique manual DLL mapping (invisible to
PEB.Ldr module walks)
API resolution dynamic via PEB walk + name hashing
Nt* syscalls preferred over kernel32 wrappersYARA landmark rule
rule Real_Injector_v118 {
strings:
$aplib_ep = { E8 82 01 00 00 41 52 49 89 E2 41 52 49 8B 72 10 49 8B 7A 20 FC B2 80 }
$seg_drv = { B9 F0 01 00 00 83 E9 05 48 01 C1 53 48 89 CE 48 89 DF 8A 0E 48 FF C6 }
$wl1 = "WinLicenseInstance" ascii
$wl2 = "WinLicenseDriverVersion" ascii
$wl3 = "/checkprotection" ascii
$wl4 = "!ProcOUT" ascii
$hy1 = "WE LOVE HYPERION 69" ascii
$hy2 = "JUSTMAGIC WE LOVE YOU" ascii
$section = ".byfron" ascii
condition:
uint16(0) == 0x5A4D and $aplib_ep and $seg_drv
and 3 of ($wl*) and all of ($hy*) and $section
}Reporting
Coordinated via HackerOne #3809852 for the Roblox integration side. Reported to Roblox security. Not disclosed to Project Real prior to publication because Real is the vendor of the abuse infrastructure. The Ed25519 pubkey and endpoint set enables Roblox security and any EDR vendor to build robust detections against this family without needing the private client sample.
For third party downstream reporters. The api.lua.expert relationship should be surfaced to that service's operators. Any customer of Real who has ever called decompile() has shipped their target script's bytecode to that endpoint without a per session consent screen.
For Cloudflare abuse reporters. api.projectreal.live and download.projectreal.live are Cloudflare fronted. Report references should include the manifest sample and the Ed25519 pubkey. Do not include the per install token in reports, it is a per user credential.
Correspondence: research@clubhouseac.shop