Register - Login
Views: 99832265
Main - Memberlist - Active users - Calendar - Wiki - IRC Chat - Online users
Ranks - Rules/FAQ - Stats - Latest Posts - Color Chart - Smilies
05-03-22 09:39:03 PM
Jul - Posts by wisk
wisk
Random nobody
Level: 5


Posts: 1/4
EXP: 426
For next: 103

Since: 07-28-14


Since last post: 7.7 years
Last activity: 6.7 years

Posted on 07-28-14 07:24:35 PM, in Shovel Knight - devmenu Link
Hi guys,

I spent some times reversing the game Shove Knight and found a hidden menu:



And a text viewer:



To reach this menu: open ShovelKnight.exe (md5:4efb1caf0dd0b4ba0c71c77c33768605) with an hex editor, and simply modify the byte located at 0x15f4cb from 0x40 to 0x5d. (don't forget to copy/rename the executable first).

I think there's more to find, but most of "interesting" files seem to be missing in the retail version.
wisk
Random nobody
Level: 5


Posts: 2/4
EXP: 426
For next: 103

Since: 07-28-14


Since last post: 7.7 years
Last activity: 6.7 years

Posted on 08-01-14 07:02:56 PM, in Shovel Knight - devmenu (last edited by wisk at 08-01-14 07:05:10 PM) Link
Originally posted by einstein95
Instead of an offset, could you give the 5 hex bytes before and after? That way it might be able to be found in later versions.


Sure, it'll better with the asm code:




; function: 0055FED0
.text:005600A9 D9 EE fldz
.text:005600AB 89 47 08 mov [edi+8], eax
.text:005600AE D9 50 54 fst dword ptr [eax+54h]
.text:005600B1 D9 58 64 fstp dword ptr [eax+64h]
.text:005600B4 D9 80 A0 00 00 00 fld dword ptr [eax+0A0h]
.text:005600BA DC 25 08 FA 61 00 fsub ds:dbl_61FA08
.text:005600C0 D9 58 74 fstp dword ptr [eax+74h]
.text:005600C3 EB 02 jmp short loc_5600C7
.text:005600C5 ; ---------------------------------------------------------------------------
.text:005600C5
.text:005600C5 loc_5600C5: ; CODE XREF: sub_55FED0+187j
.text:005600C5 33 FF xor edi, edi
.text:005600C7
.text:005600C7 loc_5600C7: ; CODE XREF: sub_55FED0+1F3j
.text:005600C7 8B 75 F8 mov esi, [ebp+var_8]
.text:005600CA 6A 40 push 40h ; TitleScreenID
.text:005600CC E8 9F 33 04 00 call SK_SetId ; init the second id
.text:005600CC ; update id
.text:005600D1 8B 4D FC mov ecx, [ebp+var_4]
.text:005600D4
.text:005600D4 loc_5600D4: ; CODE XREF: sub_55FED0+C8j
.text:005600D4 ; sub_55FED0+D5j
.text:005600D4 8D 79 58 lea edi, [ecx+58h]
.text:005600D7 C7 45 F8 07 00 00 00 mov [ebp+var_8], 7
.text:005600DE 8B FF mov edi, edi




Basically, SK_SetId (005A3470) sets the given argument to a global variable. The patch modifies the argument (005600CA + 1) from 0x40 (TitleScreenID) to 0x5D (DevMenuId).




;function: 005A3470 (SK_SetId)
.text:005A34B5 8B 45 08 mov eax, [ebp+DataId]
.text:005A34B8 A3 10 6D 63 00 mov g_SK_NextId



This ID is then copied to another global variable:




; function: 005A3050
.text:005A3152 8B 15 10 6D 63 00 mov edx, g_SK_NextId
.text:005A3158 8B 0D 0C 6D 63 00 mov ecx, g_SK_CurId
.text:005A315E 89 35 10 6D 63 00 mov g_SK_NextId, esi
.text:005A3164 8B 35 2C 42 5F 00 mov esi, ds:SDL_GetTicks
.text:005A316A 89 0D 14 6D 63 00 mov dword_636D14, ecx
.text:005A3170 89 15 0C 6D 63 00 mov g_SK_CurId



Finally, this ID is used to fetch an entry from a global array of structure which contains every "scenes" of the game:




; function: 0048A4E0
.text:0048A4EC A1 0C 6D 63 00 mov eax, g_SK_CurId
.text:0048A4F1 56 push esi ; Args
.text:0048A4F2 8B 35 E4 40 5F 00 mov esi, ds:sprintf
.text:0048A4F8 8D 04 40 lea eax, [eax+eax*2]
.text:0048A4FB 8B 0C C5 08 80 62 00 mov ecx, dword ptr g_DataEntries.Name[eax*8]



In this case, we're looking for 0x5D because it contains the entry "Dev Init".




.data:00628008 dd offset aDevInit ; [5Dh].Name
.data:00628008 dd 0 ; [5Dh].PakPath
.data:00628008 dd offset byte_5FF6FE ; [5Dh].DataPath
.data:00628008 dd offset aDev ; [5Dh].Type
.data:00628008 dd 2 ; [5Dh].anonymous_4
.data:00628008 dd 0 ; [5Dh].anonymous_5



There's more way to access this devmenu, but I think this one is the cleaner.
Tell me if you need further info.

BTW, the PC version contains references to StreetPass:

wisk
Random nobody
Level: 5


Posts: 3/4
EXP: 426
For next: 103

Since: 07-28-14


Since last post: 7.7 years
Last activity: 6.7 years

Posted on 08-03-14 02:10:48 PM, in Shovel Knight - devmenu Link
Regarding the SpotPass/miiverse feature, I didn't find anything about it in the Steam version.
The only reference I found is the path "menus/miiverse.anb" which, of course, is missing.

About the PAK file format, too bad I suck at googling I did one myself in python (available here).
./pak_extract.py -out dump *.pak extracts all files contained in PAK to the folder dump.

Finally, I found what I was looking for: the hidden console!




And the collision viewer (cmd: drawattack 7):



To enable the console, I've made a really hackish patch:




; function: 00E1A660
.text:005BA6C3 66 89 1D 10 E2 62 00 mov word_62E210, bx
.text:005BA6CA D9 45 0C fld [ebp+arg_4]
.text:005BA6CD 89 1D 18 E7 62 00 mov dword_62E718, ebx
.text:005BA6D3 D9 1D 34 15 63 00 fstp flt_631534
.text:005BA6D9 89 1D F0 DF 62 00 mov dword_62DFF0, ebx
.text:005BA6DF D9 45 10 fld [ebp+arg_8]
.text:005BA6E2 88 1D 5A 0C 63 00 mov g_ShowConsole, bl
.text:005BA6E8 D9 1D 38 15 63 00 fstp flt_631538
.text:005BA6EE 88 1D 5B 0C 63 00 mov byte_630C5B, bl
.text:005BA6F4 D9 45 14 fld [ebp+arg_C]
.text:005BA6F7 88 1D 59 0C 63 00 mov byte_630C59, bl
.text:005BA6FD D9 1D 3C 15 63 00 fstp flt_63153C
.text:005BA703 C6 05 64 0C 63 00 01 mov byte_630C64, 1
.text:005BA70A D9 45 18 fld [ebp+arg_10]
.text:005BA70D 89 1D 68 0C 63 00 mov dword_630C68, e



You have to force the value g_ShowConsole to be different from 0. To do so, you can change the byte 005BA6E2 + 1 (MOV Ev, Eg Mod/RM) from 1D to 2D. This modification changes the source register from bl (which is zero) to ch (ECX[16:8]), which contains a pointer. The register ch *should* always be different from 0.

It would be better to bind a key (like ~) to the console.

Happy digging!
wisk
Random nobody
Level: 5


Posts: 4/4
EXP: 426
For next: 103

Since: 07-28-14


Since last post: 7.7 years
Last activity: 6.7 years

Posted on 08-03-14 06:52:27 PM, in Shovel Knight - devmenu (last edited by wisk at 08-03-14 07:01:04 PM) Link
Originally posted by jedivulcan
How do the cheats in the console correlate to cheats that can be entered when I begin a new game? For example, that screenshot you posted shows "butt" to enable butt mode in the console ... but it can be enabled from the name select by typing X&BUTT or WSWWAEAW. I wonder if the console can reveal the effects of codes that are still unknown if they aren't a placeholder for codes that will be utilized in future updates.



That's an interesting question.

All I can tell you is that there's a global variable, say g_ButtMode, which can be enable or disable.
If this variable is enabled, it replaces knight, shovel, health and magic with butt:

(addresses are rebased due to ASLR)



.text:00DC1AD6 mov cl, g_ButtMode
.text:00DC1ADC and cl, 1
.text:00DC1ADF jz butt_mode_is_disabled
.text:00DC1AE5 push offset aButt ; "butt"
.text:00DC1AEA push offset aKnight ; "knight"
.text:00DC1AEF lea esi, [esp+938h+Source]
.text:00DC1AF6 call TextReplace
.text:00DC1AFB push offset aButt_0 ; "Butt"
.text:00DC1B00 push offset aKnight_0 ; "Knight"
.text:00DC1B05 call TextReplace
.text:00DC1B0A push offset aButt_0 ; "Butt"
.text:00DC1B0F push offset aShovel_0 ; "Shovel"
.text:00DC1B14 call TextReplace
.text:00DC1B19 push offset aButt ; "butt"
.text:00DC1B1E push offset aShovel ; "shovel"
.text:00DC1B23 call TextReplace
.text:00DC1B28 push offset aButt_0 ; "Butt"
.text:00DC1B2D push offset aHealth ; "Health"
.text:00DC1B32 call TextReplace
.text:00DC1B37 push offset aButt ; "butt"
.text:00DC1B3C push offset aHealth_0 ; "health"
.text:00DC1B41 call TextReplace
.text:00DC1B46 push offset aButt_0 ; "Butt"
.text:00DC1B4B push offset aMagic ; "Magic"
.text:00DC1B50 call TextReplace
.text:00DC1B55 push offset aButt ; "butt"
.text:00DC1B5A push offset aMagic_0 ; "magic"
.text:00DC1B5F call TextReplace
.text:00DC1B64 add esp, 40h
.text:00DC1B67
.text:00DC1B67 butt_mode_is_disabled: ; CODE XREF: sub_DC14F0+5E0j
.text:00DC1B67 ; sub_DC14F0+5EFj
;...



I can find only one place where this variable is directly written:




.text:00DBB7E0 loc_DBB7E0: ; CODE XREF: ycConsole__Cmd_butt+1Cj
.text:00DBB7E0 A2 D4 45 E9 00 mov g_ButtMode, al
.text:00DBB7E5 84 C9 test cl, cl
.text:00DBB7E7 74 17 jz short loc_DBB800
.text:00DBB7E9 24 01 and al, 1
.text:00DBB7EB 74 13 jz short loc_DBB800
.text:00DBB7ED 68 50 00 E7 00 push offset aButtModeIsOn ; "Butt mode is ON!"
.text:00DBB7F2 E8 49 ED 05 00 call ycConsole__Printf_
.text:00DBB7F7 83 C4 04 add esp, 4
.text:00DBB7FA 8B E5 mov esp, ebp
.text:00DBB7FC 5D pop ebp
.text:00DBB7FD C2 04 00 retn 4



This is the handler of the command "butt".

But! And thank you for giving me these cheat codes, I managed to understand how cheat code are handled in game. Basically, the Shovel Knight uses a hash function (it takes a string in parameter and returns a 32-bit value).
This function is usually used to increase performance (see hash table).
In this case, it's used to hash the player name, which is compared to a list of hash value:




.text:00D46059 push eax
.text:00D4605A call __SK_Hash
.text:00D4605F add esp, 4
.text:00D46062 pop esi
.text:00D46063 test eax, eax
.text:00D46065 jz short loc_D46088
.text:00D46067 xor ecx, ecx
.text:00D46069 lea esp, [esp+0]
.text:00D46070
.text:00D46070 loc_D46070: ; CODE XREF: SK_IsNameCheatCode+46j
.text:00D46070 mov edx, HashedCheatCode[ecx*4]
.text:00D46077 test edx, edx
.text:00D46079 jz short loc_D4607F
.text:00D4607B cmp edx, eax
.text:00D4607D jz short loc_D4608C
.text:00D4607F
.text:00D4607F loc_D4607F: ; CODE XREF: SK_IsNameCheatCode+39j
.text:00D4607F inc ecx
.text:00D46080 cmp ecx, 141h
.text:00D46086 jl short loc_D46070



That's explain why one cheat can be unlocked using multiple names (see hash collision).

Using the index from HashedCheatCode array, a huge switch is used to apply hardcoded cheat (I can't paste the code but I could provide more info if you want to look by yourself).

I can't guarantee all cheat codes have been found, you have to bruteforce them to find them all.

BTW, by messing cheat codes values, I found a challenge mode. I already finished the game once (not game+), but I've never seen it before.
Is this mode hidden?
EDIT: this video answers this question. The challenge mode is unlocked using the cheatcode IM&SGC14.




This game is really awesome: the more you search, the more you find something new.

PS: Sorry for the long post.
Jul - Posts by wisk


Rusted Logic

Acmlmboard - commit 47be4dc [2021-08-23]
©2000-2022 Acmlm, Xkeeper, Kaito Sinclaire, et al.

19 database queries, 9 query cache hits.
Query execution time: 0.075934 seconds
Script execution time: 0.015627 seconds
Total render time: 0.091561 seconds