Jump to content
Moopler
  • 0
Sign in to follow this  
Raymond

Help Thread Spoof and Stat Hook

Question

The current thread spoof in the script database does not work:

Quote

[enable]
Alloc(Hook, 128)
Alloc(ThreadEnv, 4)
Label(Compare)
Label(End)
Label(Return)

00E20710: // CClientSocket::SendPacket
jmp Hook
Return:

ThreadEnv:
dd #0

Hook:
push ebp
mov ebp, esp
mov eax,[ThreadEnv]
test eax, eax
jne Compare
mov eax, fs:[00000018]
mov [ThreadEnv], eax

Compare:
cmp fs:[00000018], eax
je End
mov eax,[ThreadEnv]
mov fs:[00000018], eax

End:
pop ebp
push ebp
mov ebp,esp
push -01
jmp Return

[disable]
DeAlloc(Hook)
DeAlloc(ThreadEnv)

00E20710: // 55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 ?? ?? ?? ?? 50 83 EC 5C A1 ?? ?? ?? ?? 33 C5 89 45 F0 53 56 57 50 8D 45 F4 64 A3 ?? ?? ?? ?? 89 4D C0
push ebp
mov ebp,esp
push -01

 

Enabling this script:

Spoiler

[ENABLE]
alloc(PressKey,128)
CreateThread(PressKey)

PressKey:
mov esi,[02C5AAC8] // TSingletonCWvsContext: 8B 0D ? ? ? ? 50 E8 ? ? ? ? 8D 4D ? E8 ? ? ? ? 8B
mov ecx,[esi+A4]
push 001D0000 // CTRL
push 00
call 02142A30 // CWndMan::OnKey: 55 8B EC 8B 0D ? ? ? ? 85 C9 74 ? 83 C1 [First result]
ret


[DISABLE]

dealloc(PressKey)

 

My character attack once meaning the call went throught, but I disconnect shortly after.

I remember  back then to spoof thread id it was something like this:

__writefsdword(0x24,threadidpointerhere) // current thread id

later it changed to this

__writefsdword(0x6B8,threadidpointerhere) // real thread id

later it changed to this

__writefsdword(0x6B8,DecryptData(threadidpointerhere)) // real thread id is encrypted and we had to use Tsectype SetData

And now I think it should be

__writefsdword(0x18,threadidpointerhere) // TEB

or

__writefsdword(0x18,DecryptData(threadidpointerhere)) // TEB and we had to use Tsectype SetData

but how to find thread id pointer?

fs:[0x00000024] and fs:[0x000006B8] isnt in maplestory anymore but fs:[0x00000018] is

 

and stat hook:

Spoiler

[ENABLE]
alloc(Hook,128)
alloc(HP,4)
alloc(MHP,4)
alloc(MP,4)
alloc(MMP,4)
registersymbol(HP)
registersymbol(MHP)
registersymbol(MP)
registersymbol(MMP)

Hook:
call 02098B30 // Original Opcode
pushad

mov ecx,ebx // CWvsContext: 8D ? ? 53 56 57 50 E8 [mov ecx above]
mov ecx,[ecx+223C] // CWvsContext::GetCharacterData: 8D ? ? 53 56 57 50 E8 [Follow call]

mov esi,[ecx+56+04] // _ZtlSecureTear_nHP[1]
rol esi,05
xor esi,[ecx+56] // _ZtlSecureTear_nHP[0]
mov [HP],esi

mov esi,[ecx+62+04] // _ZtlSecureTear_nMHP[1]
rol esi,05
xor esi,[ecx+62] // _ZtlSecureTear_nMHP[0]
mov [MHP],esi

mov esi,[ecx+6E+04] // _ZtlSecureTear_nMP[1]
rol esi,05
xor esi,[ecx+6E] // _ZtlSecureTear_nMP[0]
mov [MP],esi

mov esi,[ecx+7A+04] // _ZtlSecureTear_nMMP[1]
rol esi,05
xor esi,[ecx+7A] // _ZtlSecureTear_nMMP[0]
mov [MMP],esi

popad
jmp 021C5294+5

021C5294:
jmp Hook

[DISABLE]
021C5294: // CWvsContext::Update: E8 ? ? ? ? 83 ? ? ? ? ? ? 8B ? 89 ? ? 75 ? 89
call 02098B30 // get_update_time

dealloc(Hook)
dealloc(HP)
dealloc(MHP)
dealloc(MP)
dealloc(MMP)
unregisterSymbol(HP)
unregistersymbol(MHP)
unregisterSymbol(MP)
unregistersymbol(MMP)

 

I'm only able to get current hp and mp and max hp(without equip) and max mp (without equip)

 

how to get max hp and max mp with equips and how about exp and max exp

back then it was simple with CUISTATUSBAR::SETNUMBERVALUE but that doesnt work anymore

Share this post


Link to post

15 answers to this question

Recommended Posts

  • 0
1 minute ago, Raymond said:

fs:[0x00000024] and fs:[0x000006B8] isnt in maplestory anymore but fs:[0x00000018] is

Look up what fs:xxx actually does.

https://en.wikipedia.org/wiki/Win32_Thread_Information_Block

But anyway, I looked up how Blight does it for you(just looked at the sendpacket hook), by the looks of it, [[fs:0x18] + 0x6b8], ignore the random scrambled/weird code:

ce5f74b883.png

Share this post


Link to post
  • 0

 

mov eax, fs:[18]
mov eax,[eax+6B8]

 

doesn't seem to work

 

Edit: did you look deeper into blight because i'm pretty sure the thread id pointer is encrypted

Here's an old script the opened new years boxes:

[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
 
define(packet_header, 00809E00)
define(packet_byte, 00418BC0)
define(packet_word, 00439CD0)
define(packet_dword, 00418C10)
define(packet_base,012954A4)
define(packet_send, 004FA350)
define(packet_free, 00438170)
 
label(next)
label(again)
label(times)
 
1f000:
push ebp
mov ebp, esp
sub esp, 10
 
mov ecx, 0129A430
call 0048C8E0
mov ecx, fs:[18]
mov dword ptr [ecx+6B8], eax
 
again:
push 87
lea ecx, dword ptr [ebp-10]
call packet_header
 
call 00D250C0
push eax
lea ecx, dword ptr [ebp-10]
call packet_dword
 
push 01
lea ecx, dword ptr [ebp-10]
call packet_word
 
push 002516C2  // new year's box
//push 002516D0  // valuntine's box
lea ecx, dword ptr [ebp-10]
call packet_dword
 
push next
push ecx
lea eax, dword ptr [ebp-10]
push eax
mov ecx, dword ptr [packet_base]
push 0040C038
jmp packet_send
 
next:
lea ecx, dword ptr [ebp-10]
call 00438170
 
push 100
call Sleep
dec dword ptr [times]
mov eax, dword ptr [times]
test eax, eax
jg again
 
mov esp, ebp
pop ebp
ret
 
times:
DD A
 
CreateThread(1f000)
 
[DISABLE]

 

Edited by Raymond

Share this post


Link to post
  • 0
5 hours ago, Raymond said:

 


mov eax, fs:[18]
mov eax,[eax+6B8]

 

doesn't seem to work

Edit: did you look deeper into blight because i'm pretty sure the thread id pointer is encrypted

Here's an old script the opened new years boxes:

 

Here, this hook allowed me to send packets from a different thread just fine (with my sendpacket implementation faking a ret addr).

b6738d34fc.png

5d3f0e2227.png

 

Just write the script from the screenshot, you have to work for it too.

  • Thanks 1

Share this post


Link to post
  • 0

Actually, the mutations you described in the OP isn't 100% correct.

 

First, let's define what the TEB is. TEB stands for Thread Environment Block. It's a data-structure which is filled out upon thread-scheduled creation in the Windows kernel, and is accessible to the user through the FS-segment. When you use "fs:" before an operand in assembly, you're actually telling it to align the operand with the FS-segment pointer, which points to a kernel structure (_TEB*). The TEB contains a lot of things, but one thing you should completely disregard is the offset 0x18, which is basically a pointer to itself (retarded, right?). In other words, fs:[fs:[0x18]+0x24] is the exact same as fs:[+0x24], so let's pretend it doesn't exist.

Now, what you're describing is the accessing of the two ClientId-structures within the Thread Environment Block: ClientId and RealClientId (of type struct CLIENT_ID, which contains UniqueProcess and UniqueThread).

At first, Nexon simply accessed the TEB.ClientId.UniqueThread data-memeber (offset 0x24), but when people found out about this, they thought they could fuck us up, by changing to an (officially) undocumented data-member of the TEB structure, by instead using TEB.RealClientId.UniqueThread (offset 0x6B8). After this didn't work, they started using BOTH, in some sort of hope that people only attempted one at a time.

Now, after that, they went back to using just the documented version (ClientId) with a TSecType<long> to hold the TID (Thread ID) instead of the previous static value (long), and then they switched the VMProtect protection type to hide the decryption "even better". However, this was once again found, and for some reason they re-attempted the first trick (switching to RealClientId), however still with the decrypted data-type, in a desperate hope that people would just come to their senses and stop hacking such a good game as MapleStory, owned by such good folk as the people at Nexon :( :(

Then after this, they started adding some kind of packet-deallocation fuckery that made packet-sending people crash (I don't remember how it worked exactly, it's too distant), but when even this didn't work, they just said "fuck this, YOLO" and went back to the simple hard-coded static datatype long (albeit still the RealClientId-structure). And now, for a long time it seemed like Nexon was done. Done trying. Done with life. DONE WITH PACKETS.

Untill recently, when they added them dank encrypted headers (which are easily bypass, by the way...), and then when realizing THAT didn't work, they took it even further and started adding the same protection to Recv-packets, and also briefly added it to the Encode/Decode packets (in other versions). I don't know how it worked out, I don't know if it's still there, and I don't know if it'll come to GMS, but I sure hope it does, cause then Nexon will realize just how retarded it is after being broke on the release-day kek.

 

EDIT: Oh, and I just realized there was also a question about Stathook. They don't use the CUIStatusBar::SetNumberValue anymore, after the GUI re-skinning, as they now have different classes. However, the hp bar and the mp bar are both of the same type, so if you're able to read from one, you can (through the same hook) also read from the other. However, currently, you're trying to read data from CWvsContext, which doesn't actually hold the proper data (it is held by the character's stat structure). 

The easiest way to go about this, is to do something like what I did in Firefly:

6b8d4621686a24b992e413871c37145e.png

Edited by NewSprux2.0?
  • Like 2
  • Thanks 1

Share this post


Link to post
  • 0
10 hours ago, Erotica said:

[...]

 

 

Spoiler

[ENABLE]
alloc(Hook,128)
alloc(TEB,16)
label(Spoof)
label(Ending)

TEB:
dd 00000000
dd 00000000
dd 00000000
dd 00000000

Hook:
push ebp
mov ebp,esp
push -01
pushad
mov eax,fs:[00000018]
mov [TEB],eax
mov eax,[eax+000006B8]
mov [TEB+04],eax
mov eax,[TEB]
mov ebx,[TEB+04]
sub eax,ebx
mov [TEB+08],eax
cmp dword ptr [TEB+0C],00
je Spoof
cmp eax,[TEB+0C]
je Ending
mov ebx,[TEB]
mov eax,[TEB+0C]
sub ebx,eax
mov [TEB+04],ebx
mov eax,fs:[00000018]
mov [eax+000006B8],ebx
jmp Ending

Spoof:
mov [TEB+0C],eax

Ending:
popad
jmp 00E20710+5

00E20710:
jmp Hook

[DISABLE]
00E20710:
push ebp
mov ebp,esp
push -01

dealloc(Hook,128)
dealloc(TEB,16)

 

Seems to works, but sometimes it doesn't.

I get this error sometimes when I call a function outside the main thread (yes I have the script enabled)

Tf3

Edited by Raymond

Share this post


Link to post
  • 0
7 hours ago, NewSprux2.0? said:

EDIT: Oh, and I just realized there was also a question about Stathook. They don't use the CUIStatusBar::SetNumberValue anymore, after the GUI re-skinning, as they now have different classes. However, the hp bar and the mp bar are both of the same type, so if you're able to read from one, you can (through the same hook) also read from the other. However, currently, you're trying to read data from CWvsContext, which doesn't actually hold the proper data (it is held by the character's stat structure). 

The easiest way to go about this, is to do something like what I did in Firefly:

6b8d4621686a24b992e413871c37145e.png

The script in OP use GW_CharacterStat Struct:

Tf6

Your hook works to show max hp/max mp with equips that adds hp/mp?

What about exp and max exp?

Share this post


Link to post
  • 0
20 minutes ago, Raymond said:

The script in OP use GW_CharacterStat Struct:

Tf6

Your hook works to show max hp/max mp with equips that adds hp/mp?

What about exp and max exp?

I haven't had MapleStory even updated for about 10 versions, so I'm not actually sure. However, last I checked (and used this, which was with the Firefly Trainer), this worked perfectly.

If you're bothered, you're welcome to read through the topic and see if you can find any complaints or bug-reports about hp/mp:

 

Edited by NewSprux2.0?
  • Like 1

Share this post


Link to post
  • 0
16 hours ago, Erotica said:

Here, this hook allowed me to send packets from a different thread just fine (with my sendpacket implementation faking a ret addr).

This script seems to work with sending packets:

Spoiler

[ENABLE]
alloc(Hook,128)
alloc(TEB,16)
label(Spoof)
label(Ending)

TEB:
dd 00000000
dd 00000000
dd 00000000
dd 00000000

Hook:
push ebp
mov ebp,esp
push -01
pushad
pushfd
mov eax,fs:[00000018]
mov [TEB],eax
mov eax,[eax+000006B8]
mov [TEB+04],eax
mov eax,[TEB]
mov ebx,[TEB+04]
sub eax,ebx
mov [TEB+08],eax
cmp dword ptr [TEB+0C],00
je Spoof
cmp eax,[TEB+0C]
je Ending
mov ebx,[TEB]
mov eax,[TEB+0C]
sub ebx,eax
mov [TEB+04],ebx
mov eax,fs:[00000018]
mov [eax+000006B8],ebx
jmp Ending

Spoof:
mov [TEB+0C],eax

Ending:
popfd
popad
jmp 00E20710+5

00E20710:
jmp Hook

[DISABLE]
00E20710: // CClientSocket::SendPacket: 8B 0D ? ? ? ? 85 C9 74 ? 8D ? ? 50 E8 ? ? ? ? 8D ? ? E8 [Follow call]
push ebp
mov ebp,esp
push -01

dealloc(Hook,128)
dealloc(TEB,16)

 

 

Packet Sending:

Spoiler

[ENABLE]
alloc(SendPacket,128)
alloc(CustomPacket,128)
alloc(Packet,64)

CreateThread(SendPacket)

Packet:
db C5 96 14 00 0A 00 00 00

SendPacket:
push 013F // Unencrypted header here
lea ecx,[CustomPacket]
call 009F2C90 // COutPacket::COutPacket(long): E8 ? ? ? ? 8B ? ? C7 ? ? ? ? ? ? E8 ? ? ? ? ? 8D [Follow call]

push #8 // Size
lea eax, [Packet]
push eax // Data
lea ecx, [CustomPacket]
call 007C5F30 // COutPacket::EncodeBuffer: E8 ? ? ? ? 8B ? ? 8B ? ? 8B ? ? 89 ? ? 85 [Follow call]

mov ecx,[02C73578] // CClientSocketPtr: 8B 0D ? ? ? ? 85 C9 74 ? 8D ? ? 50 E8 ? ? ? ? 8D ? ? E8
push CustomPacket
push 014942A6 // Search for 90 C3 for fake return address
jmp 00E20710 // CClientSocket::SendPacket: Follow call below CClientSocketPtr

[DISABLE]
dealloc(SendPacket)
dealloc(CustomPacket)
dealloc(Packet)

 

 

But with this script, it doesn't seem to work (tf?):

Spoiler

[ENABLE]
alloc(PressKey,128)
CreateThread(PressKey)

PressKey:
mov esi,[02C5AAC8] // TSingletonCWvsContext: 8B 0D ? ? ? ? 50 E8 ? ? ? ? 8D 4D ? E8 ? ? ? ? 8B
mov ecx,[esi+A4]
push 001D0000 // CTRL
push 00
call 02142A30 // CWndMan::OnKey: 55 8B EC 8B 0D ? ? ? ? 85 C9 74 ? 83 C1 [First result]
ret

[DISABLE]

dealloc(PressKey)

 

I get Tf3

 

or ms just crash

Edited by Raymond

Share this post


Link to post
  • 0

@Raymond maybe they have a thread id check in place on other functions too. Try writing the saved thread id from the hook on sendpacket directly in your PressKey thread? Or maybe apply the same type of hook on 02142A30.

Edited by Erotica

Share this post


Link to post
  • 0

No clue about your crash, I've been trying to do the same thing for a few days. I just wanted to mention that (assuming you're on the latest version of ms) the function you are calling

call 02142A30 // CWndMan::OnKey

 isn't a __thiscall function and you don't need to set ecx like that. 

elNcUSF.png

Edited by Ando

Share this post


Link to post
  • 0
11 hours ago, Erotica said:

@Raymond maybe they have a thread id check in place on other functions too. Try writing the saved thread id from the hook on sendpacket directly in your PressKey thread? Or maybe apply the same type of hook on 02142A30.

Spoiler

[ENABLE]
alloc(Hook,128)
alloc(TEB,16)
label(Spoof)
label(Ending)

TEB:
dd 00000000
dd 00000000
dd 00000000
dd 00000000

Hook:
push ebp
mov ebp,esp
mov ecx,[02C7B6E4]
pushad
pushfd
mov eax,fs:[00000018]
mov [TEB],eax
mov eax,[eax+000006B8]
mov [TEB+04],eax
mov eax,[TEB]
mov ebx,[TEB+04]
sub eax,ebx
mov [TEB+08],eax
cmp dword ptr [TEB+0C],00
je Spoof
cmp eax,[TEB+0C]
je Ending
mov ebx,[TEB]
mov eax,[TEB+0C]
sub ebx,eax
mov [TEB+04],ebx
mov eax,fs:[00000018]
mov [eax+000006B8],ebx
jmp Ending

Spoof:
mov [TEB+0C],eax

Ending:
popfd
popad
jmp 02142A30+9

02142A30:
jmp Hook
db 90 90 90 90

[DISABLE]
02142A30: // CWndMan::OnKey: 55 8B EC 8B 0D ? ? ? ? 85 C9 74 ? 83 C1 [First result]
push ebp
mov ebp,esp
mov ecx,[02C7B6E4]

dealloc(Hook,128)
dealloc(TEB,16)

 

Still doesn't work.

I thought CWndMan::OnKey call CClientSocket::SendPacket which then trigger the thread id check?!

 

 

Share this post


Link to post
  • 0
19 hours ago, Ando said:

No clue about your crash, I've been trying to do the same thing for a few days. I just wanted to mention that (assuming you're on the latest version of ms) the function you are calling


call 02142A30 // CWndMan::OnKey

 isn't a __thiscall function and you don't need to set ecx like that. 

elNcUSF.png

Actually, it is a thiscall, it just doesn't use the this-pointer.

Share this post


Link to post
  • 0
4 hours ago, NewSprux2.0? said:

Actually, it is a thiscall, it just doesn't use the this-pointer.

Ah, you are right. I was just looking at the function itself in CE. It cleaned the stack and never read from ecx (only overwrited it immediately) , which seemed to match stdcall. But indeed, the calling function does seem to set ecx before calling.

Share this post


Link to post
  • 0
2 hours ago, Ando said:

Ah, you are right. I was just looking at the function itself in CE. It cleaned the stack and never read from ecx (only overwrited it immediately) , which seemed to match stdcall. But indeed, the calling function does seem to set ecx before calling.

There is virtually no difference between stdcall, thiscall, fastcall and vectorcall except for the amount of registers used to forward initial parameters. The base-convention would be checking who clears the stack (caller or callee). If it's the first, you can be sure a function is of type cdecl. If not, you must check the parameters:

  • If no registers are used, and the arguments are passed exclusively by the stack, the calling convention is stdcall.
  • If ecx is exclusively used, the calling convention is a thiscall.
  • If ecx and edx are exclusively used, the calling convention is fastcall.
  • If ecx, edx, and more registers are used, the calling convention is vectorcall.
  • If other registers are used exclusively, without the presence of the ecx and edx passing registers, the calling convention is most likely custom or compiler-specific (for example, Borland's compiler has their own calling convention that utilizes random registers).
Edited by NewSprux2.0?

Share this post


Link to post
  • 0

A̶n̶y̶ ̶i̶d̶e̶a̶s̶ ̶w̶h̶y̶ ̶c̶a̶l̶l̶i̶n̶g̶ ̶C̶U̶s̶e̶r̶L̶o̶c̶a̶l̶:̶:̶T̶r̶y̶D̶o̶i̶n̶g̶M̶a̶g̶i̶c̶A̶t̶t̶a̶c̶k̶ ̶c̶r̶a̶s̶h̶e̶s̶?̶
̶
̶I̶ ̶h̶a̶v̶e̶ ̶t̶h̶r̶e̶a̶d̶ ̶s̶p̶o̶o̶f̶ ̶e̶n̶a̶b̶l̶e̶d̶.̶
̶
̶ ̶
̶
̶A̶l̶s̶o̶ ̶i̶s̶ ̶i̶t̶ ̶n̶o̶r̶m̶a̶l̶ ̶w̶h̶e̶n̶ ̶I̶ ̶h̶o̶o̶k̶ ̶C̶U̶s̶e̶r̶L̶o̶c̶a̶l̶:̶:̶T̶r̶y̶D̶o̶i̶n̶g̶M̶a̶g̶i̶c̶A̶t̶t̶a̶c̶k̶ ̶a̶n̶d̶ ̶u̶s̶e̶ ̶a̶ ̶m̶a̶g̶i̶c̶ ̶a̶t̶t̶a̶c̶k̶ ̶(̶t̶o̶ ̶r̶e̶t̶r̶i̶e̶v̶e̶ ̶i̶t̶'̶s̶ ̶p̶a̶r̶a̶m̶e̶t̶e̶r̶)̶ ̶m̶y̶ ̶m̶a̶p̶l̶e̶s̶t̶o̶r̶y̶ ̶c̶r̶a̶s̶h̶e̶s̶?̶

 

Forget this question. Just noticed it used an extra parameter from kmst pdb. That's why I was crashing...

I put void* unknown. then it worked xd...

Edited by Raymond
  • Like 1

Share this post


Link to post
Guest
This topic is now closed to further replies.
Sign in to follow this  
×