Well I got bored and couldn't concentrate on my assignments with this on my mind so Ive done it
And its led to some interesting finds as I thought.
Yes I tried to money hack with this method too and nope it didnt work. but we can still get to places for free.
We can even make return scrolls that take us back to our town map without getting consumed.
Making NPC transport free
First of all I find invintory silver in cheat engine
Find what accesses it
Look for the 1 access during moving to Saigo
Found it
Closed game down and opened in ollydbg and checked it out and worked out what stuff does.
Code:
004D78F7 |> \813D 784C1601>CMP DWORD PTR DS:[1164C78],3E8 ; Check Money against 1000
004D7901 |. 7D 26 JGE SHORT 004D7929 ; if ( Player->Invintory.Silver >= 1000 )
004D7903 |. 8B0D 44816000 MOV ECX,DWORD PTR DS:[608144] ; Player does not have enough silver
004D7909 |. 51 PUSH ECX ; /Arg2
004D790A |. 68 D6000000 PUSH 0D6 ; |/Arg1 = 000000D6
004D790F |. B9 C8AC5B00 MOV ECX,005BACC8 ; ||Get not enough silver message
004D7914 |. E8 F749F5FF CALL 0042C310 ; |\TwelveSk.0042C310
004D7919 |. 50 PUSH EAX ; |Arg1
004D791A |. B9 604C2E01 MOV ECX,012E4C60 ; |Output it to info
004D791F |. E8 4CBC0400 CALL 00523570 ; \TwelveSk.00523570
004D7924 |. E9 15010000 JMP 004D7A3E
004D7929 |> 8D55 EC LEA EDX,DWORD PTR SS:[EBP-14] ; Player has enough silver
004D792C |. 52 PUSH EDX ; /Arg2
004D792D |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8] ; |
004D7930 |. 50 PUSH EAX ; |Arg1 = 00000025
004D7931 |. B9 58EAFD00 MOV ECX,00FDEA58 ; |Check Goto MapID
004D7936 |. E8 655FF7FF CALL 0044D8A0 ; \Game_CheckGotoMap( unsigned int MapID, void* Buffer )
Now ill breakpoint 004D78F7 and bp send and see if there is a packet that deducts zeny seperate to zone change packet by stepping through with F8 on the JGE 004D7901 ill double click the A flag or I can change it to a JMP
I find no such function but I do find the zone change packet
const unsigned char PACKETID_CLIENT_ZONE_CHANGE = 0x14;
Code:
struct PACKET_CLIENT_ZONECHANGE
{
unsigned int Type;
unsigned int MapID;
char Username[17];
char CharacterName[13];
};
I now know the first paramater Type
I didnt know this previously
Here are all the types ive found so far
Code:
const unsigned int ZONECHANGE_TYPE_LOADWORLD = 0x01; // When just loaded ??
const unsigned int ZONECHANGE_TYPE_PORTAL = 0x04; // Portal
const unsigned int ZONECHANGE_TYPE_GATEMASTER = 0x05; // The gate master for 1000 silver
const unsigned int ZONECHANGE_TYPE_GUARDCAPTIAN = 0x06; // When you use the guard captian he is free..
const unsigned int ZONECHANGE_TYPE_RETURNSCROLL = 0x08; // Return Scroll
const unsigned int ZONECHANGE_TYPE_ENTERPALACE = 0x0A; // Or Elder
Doing a scan for array of bytes on this "C7 05 40 64 16 01" shows me every moving of a integer into the type address theres 17 moves
Ones I have not figured out yet are
0x01 ?
0x02
0x03
0x07
0x09
0x0B
All my comments are here on the code block
Code:
004D78F7 |> \813D 784C1601>CMP DWORD PTR DS:[1164C78],3E8 ; Check Money against 1000
004D7901 |. 7D 26 JGE SHORT 004D7929 ; if ( Player->Invintory.Silver >= 1000 )
004D7903 |. 8B0D 44816000 MOV ECX,DWORD PTR DS:[608144] ; Player does not have enough silver
004D7909 |. 51 PUSH ECX ; /Arg2
004D790A |. 68 D6000000 PUSH 0D6 ; |/Arg1 = 000000D6
004D790F |. B9 C8AC5B00 MOV ECX,005BACC8 ; ||Get not enough silver message
004D7914 |. E8 F749F5FF CALL 0042C310 ; |\char* GetGameMessage( unsigned int MessageID )
004D7919 |. 50 PUSH EAX ; |Arg1
004D791A |. B9 604C2E01 MOV ECX,012E4C60 ; |Output it to info
004D791F |. E8 4CBC0400 CALL 00523570 ; \Game_OutputInfo( char* Message, unsigned int TextColor )
004D7924 |. E9 15010000 JMP 004D7A3E
004D7929 |> 8D55 EC LEA EDX,DWORD PTR SS:[EBP-14] ; Player has enough silver
004D792C |. 52 PUSH EDX ; /Arg2
004D792D |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8] ; |
004D7930 |. 50 PUSH EAX ; |Arg1
004D7931 |. B9 58EAFD00 MOV ECX,00FDEA58 ; |Check Goto MapID
004D7936 |. E8 655FF7FF CALL 0044D8A0 ; \Game_CheckGotoMap( unsigned int MapID, void* Buffer )
004D793B |. 85C0 TEST EAX,EAX
004D793D |. 75 05 JNZ SHORT 004D7944
004D793F |. E9 FA000000 JMP 004D7A3E
004D7944 |> 833D 3C641601>CMP DWORD PTR DS:[116643C],0
004D794B |. 0F85 E5000000 JNZ 004D7A36 ; Check if map is invalid?
004D7951 |. C705 40641601>MOV DWORD PTR DS:[1166440],5 ; Set Type
004D795B |. C705 44641601>MOV DWORD PTR DS:[1166444],3E8 ; Set Cost
004D7965 |. 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8]
004D7968 |. 890D 50641601 MOV DWORD PTR DS:[1166450],ECX ; Set MapID
004D796E |. 6A 34 PUSH 34
004D7970 |. 6A 00 PUSH 0
004D7972 |. 68 54641601 PUSH 01166454
004D7977 |. E8 34780700 CALL 0054F1B0
004D797C |. 83C4 0C ADD ESP,0C
004D797F |. 8A15 54641601 MOV DL,BYTE PTR DS:[1166454]
004D7985 |. 80E2 F8 AND DL,0F8
004D7988 |. 8815 54641601 MOV BYTE PTR DS:[1166454],DL
004D798E |. C605 55641601>MOV BYTE PTR DS:[1166455],1
004D7995 |. C705 58641601>MOV DWORD PTR DS:[1166458],0
004D799F |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14]
004D79A2 |. A3 5C641601 MOV DWORD PTR DS:[116645C],EAX
004D79A7 |. 8B4D F0 MOV ECX,DWORD PTR SS:[EBP-10]
004D79AA |. 890D 60641601 MOV DWORD PTR DS:[1166460],ECX
004D79B0 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
004D79B3 |. 8915 64641601 MOV DWORD PTR DS:[1166464],EDX
004D79B9 |. E8 26750700 CALL 0054EEE4
004D79BE |. 99 CDQ
004D79BF |. B9 68010000 MOV ECX,168
004D79C4 |. F7F9 IDIV ECX
004D79C6 |. 8955 E4 MOV DWORD PTR SS:[EBP-1C],EDX
004D79C9 |. DB45 E4 FILD DWORD PTR SS:[EBP-1C]
004D79CC |. D91D 74641601 FSTP DWORD PTR DS:[1166474]
004D79D2 |. 8B15 74641601 MOV EDX,DWORD PTR DS:[1166474]
004D79D8 |. 8915 78641601 MOV DWORD PTR DS:[1166478],EDX
004D79DE |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
004D79E1 |. 50 PUSH EAX
004D79E2 |. B9 38D41501 MOV ECX,0115D438
004D79E7 |. E8 5411F7FF CALL 00448B40
004D79EC |. 83F8 01 CMP EAX,1
004D79EF |. 75 23 JNZ SHORT 004D7A14
004D79F1 |. 68 6B035600 PUSH 0056036B ; /Arg3 = 0056036B
004D79F6 |. 68 39030000 PUSH 339 ; |/Arg1 = 00000339
004D79FB |. B9 C8AC5B00 MOV ECX,005BACC8 ; ||
004D7A00 |. E8 0B49F5FF CALL 0042C310 ; |\TwelveSk.0042C310
004D7A05 |. 50 PUSH EAX ; |Arg2
004D7A06 |. 6A 25 PUSH 25 ; |Arg1 = 00000025
004D7A08 |. B9 80EA2B01 MOV ECX,012BEA80 ; |Get Name of map?
004D7A0D |. E8 EE9FFEFF CALL 004C1A00 ; \TwelveSk.004C1A00
004D7A12 |. EB 22 JMP SHORT 004D7A36
004D7A14 |> C705 3C641601>MOV DWORD PTR DS:[116643C],1
004D7A1E |. 8B0D 50641601 MOV ECX,DWORD PTR DS:[1166450] ; MapID
004D7A24 |. 51 PUSH ECX ; /Arg2 => 00000000
004D7A25 |. 8B15 40641601 MOV EDX,DWORD PTR DS:[1166440] ; |Type
004D7A2B |. 52 PUSH EDX ; |Arg1 => 00000000
004D7A2C |. B9 A0FA5900 MOV ECX,0059FAA0 ; |
004D7A31 |. E8 1AB4F4FF CALL 00422E50 ; \ChangeZone( unsigned int Type, unsigned int MapID )
004D7A36 |> 8B4D E8 MOV ECX,DWORD PTR SS:[EBP-18] ; Jumped here if map invalid
004D7A39 |. E8 E274FFFF CALL 004CEF20 ; Change to map loading screen?
004D7A3E |> 5E POP ESI
004D7A3F |. 8BE5 MOV ESP,EBP
004D7A41 |. 5D POP EBP
004D7A42 \. C2 0800 RETN 8
To get there for no cost we can simply change
004D7901 |. 7D 26 JGE SHORT 004D7929
to
004D7901 |. E9 26 JMP SHORT 004D7929
So that we allways have enough
Change the type to be as if it were a portal or maybe another number *This is actally optional if you just set the cost to 0
*
004D7951 |. C705 40641601>MOV DWORD PTR DS:[1166440],5 ; Set Type
to
004D7951 |. C705 40641601>MOV DWORD PTR DS:[1166440],4 ; Set Type // Other numbers might work too
And set the cost to 0
004D795B |. C705 44641601>MOV DWORD PTR DS:[1166444],3E8 ; Set Cost
To
004D795B |. C705 44641601>MOV DWORD PTR DS:[1166444],0 ; Set Cost
A prehaps better option to the last two changes would be to detour the ChangeZone function at 00422E50 to set these addresses our selves in there.
Making the change in the function its self allows us to be certian that it will work for every ZoneChange call also we may be able to have infinite return scrolls
By making it use type 4 because return scroll is another type all together.
Ill just bp inside the change zone function and find out the Type for return scroll.
If you use a return scroll on town map it sets your X Z Y to town center.
Freezing 1166440 to 4 at 1ms with cheat engine lets us use return scrolls for free meaning non are consumed.
Freezing cost when I dont have that much money and setting the type to 5 resulted in the map loading but me disconnecting after it did. No ban just disconnected.
How can these sort of hacks be patched on pserver?
Well by checking X Z Y for one. If the player is not near a portal then you know its not a portal change.
If the player is within range of an npc that costs to transport then put charge on them.
If using return scroll. Have a use item packet rather than a ZoneChange straight away and manage the zone change server side.
Making return scroll not a return scroll
Find what writes too zone change type 01166440 when using return scroll
004C2008 C705 40641601>MOV DWORD PTR DS:[1166440],8
Change to
004C2008 C705 40641601>MOV DWORD PTR DS:[1166440],4
Lets find zone change type for some more things as im missing some.
Code:
const unsigned int ZONECHANGE_TYPE_LOADWORLD = 0x01; // When just loaded ??
const unsigned int ZONECHANGE_TYPE_UNKNOWN02 = 0x02;
const unsigned int ZONECHANGE_TYPE_UNKNOWN03 = 0x03;
const unsigned int ZONECHANGE_TYPE_PORTAL = 0x04; // Portal
const unsigned int ZONECHANGE_TYPE_GATEMASTER = 0x05; // The gate master for 1000 silver
const unsigned int ZONECHANGE_TYPE_GUARDCAPTIAN = 0x06; // When you use the guard captian he is free..
const unsigned int ZONECHANGE_TYPE_UNKNOWN07 = 0x07;
const unsigned int ZONECHANGE_TYPE_RETURNSCROLL = 0x08; // Return Scroll
const unsigned int ZONECHANGE_TYPE_UNKNOWN09 = 0x09;
const unsigned int ZONECHANGE_TYPE_ENTERPALACE = 0x0A; // Or Elder
const unsigned int ZONECHANGE_TYPE_UNKNOWN0B = 0x0B;
Calling Game Function
void Game_ChangeZone( unsigned int MapID )
{
__asm
{
PUSHAD
MOV [01166444],00000000 // Set cost to 0
MOV ECX,MapID
PUSH ECX ; /Arg2 => 00000000
MOV EDX,00000004 // Type
PUSH EDX ; |Arg1 => 00000000
MOV ECX,0059FAA0 // Buffer
CALL 00422E50 //ChangeZone( unsigned int Type, unsigned int MapID )
POPAD
}
}
Or the typedef function pointer version you would need to make signitures for Cost and the game function.
Code:
typdef void* ( _cdecl* TGame_ChangeZone)( unsigned int Type,unsigned int MapID )
TGame_ChangeZone Game_ChangeZone = (TGame_ChangeZone*)0x00422E50;
DWORD* Cost = (DWORD*)0x01166444;
void ChangeZone( unsigned int MapID )
{
*Cost=0;
Game_ChangeZone( 0,MapID );
}
Then you could go ChangeZone(1); to be teleported to kalian fortress instantly providing your not already on it.
If you get the address of current map ID you can do a check before you do anything that the map your going to is one your not already on and that your allowed on it.
Whilst this is an interesting find dont abuse it to annoy enemy's. Use it to help grinding or doing quests ya be sensible