![]() |
![]() |
#1 |
Registered User
Join Date: Oct 2020
Location: United Kingdom
Posts: 17
|
Help disassembling Wicked
Hello! I'm hoping someone will be kind enough to help nudge me along with disassembling Wicked (Electric Dreams, 1989). I'd like to be able to add a trainer for a level skip/infinite time, as the game is still too hard even with infinite lives. You just can't get past some of the later levels.
I have the original disk, the dual-format Zero #11 disk (in the Zone) and the QTX disk. Wicked used the Rob Northen Copylock protection system, so I don't think I'm able to use the hunk files from the original disk - and I think it was encrypted with incbin, or the Zero disk (as that was also compressed with Rob Northen) for disassembly. I'm trying to disassemble a WinUAE savestate in IDA, but I have no idea where to start analysing from. This is my first time trying to disassemble something with copy protection. Or can I disassemble from @Abaddon's WHDLoad v1.1 as that seems to have patched some things, so I'm sure he must have been able to disassemble it. I've no idea where to begin with that, though. I'd just like to have the asm to look at. I'm so curious about this game, I'd love to understand how it works! I should be grateful for any pointers, tips, (or if some kind person could send a .s file) ![]() Many thanks! |
![]() |
![]() |
#2 |
CaptainM68K-SPS France
|
First check if the game has real files, or if those are hidden on the disk.
Next, if the main code is encrypted, you will have to decrypt it before doing any disassembly of the game. |
![]() |
![]() |
#3 |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,431
|
hooking just before the patch in whdload is the method I use to analyze the code for trainer, second buttons.. etc...
you want a trainer for whdload version? I can do it if you want (and provide the details on how I did it) |
![]() |
![]() |
#4 |
Registered User
Join Date: Oct 2020
Location: United Kingdom
Posts: 17
|
@jotd That would be awesome! I'd love to know how to do that.
I think it starts at $280AA in the snapshot I've loaded in to IDA. Certainly g [280aa] in the debugger takes me back to the title start. Code:
ROM:000280AA sub_280AA: ; CODE XREF: sub_29B9C?j ROM:000280AA ROM:000280AA ; FUNCTION CHUNK AT ROM:00028C86 SIZE 00000592 BYTES ROM:000280AA ROM:000280AA move #$2700,sr ROM:000280AE move.w #$8200,($DFF096).l ROM:000280B6 movea.l #$800,sp ROM:000280BC move.w #1,(word_5E4).l ROM:000280C4 bsr.w sub_29B5C ROM:000280C8 bsr.w sub_29B86 ROM:000280CC bsr.w sub_29976 ROM:000280D0 moveq #0,d0 ROM:000280D2 bsr.w sub_2DCF0 ROM:000280D6 lea (word_5B2).l,a0 ROM:000280DC ROM:000280DC loc_280DC: ; CODE XREF: sub_280AA+3C?j ROM:000280DC move.b #0,(a0)+ ROM:000280E0 cmpa.l #$5E4,a0 ROM:000280E6 bne.s loc_280DC ROM:000280E8 jsr sub_29994 ROM:000280EE ROM:000280EE loc_280EE: ; CODE XREF: sub_280AA+9FA?j ROM:000280EE bsr.w sub_29A5E ROM:000280F2 movea.l #unk_343EA,a0 ROM:000280F8 move.w #$17,d0 ROM:000280FC move.w #0,d1 ROM:00028100 jsr sub_2989C ROM:00028106 clr.w (word_34812).l ROM:0002810C clr.w (word_34838).l ROM:00028112 clr.w (word_34968).l ROM:00028118 clr.w (word_34820).l ROM:0002811E clr.w (word_34846).l ROM:00028124 jsr sub_2BF6E ROM:0002812A move.w #4,(word_476).l ROM:00028132 move.w (word_5B2).l,d0 ROM:00028138 addq.w #2,d0 ROM:0002813A move.w d0,(word_586).l ROM:00028140 move.l #unk_60EE6,d1 ROM:00028146 move.l #unk_5E546,d2 ROM:0002814C cmpi.w #1,(word_5C0).l ROM:00028154 bhi.w loc_28162 ROM:00028158 move.w #1,(word_4BA).l ROM:00028160 exg d1,d2 |
![]() |
![]() |
#5 |
Registered User
Join Date: Oct 2020
Location: United Kingdom
Posts: 17
|
Oh, there are two versions of the game.
IPF-1263 - The Intro/Loader is encrypted, but the game is not IPF-0295 - Both the Loader/Intro and the game are encrypted |
![]() |
![]() |
#6 | |
Going nowhere
Join Date: Oct 2001
Location: United Kingdom
Age: 50
Posts: 9,033
|
Quote:
You can depack that file and all of the nasty encryption will have been removed by Rob/Quartex so you don't even have to deal with that. Or..... tell people here exactly what you would like with regards to trainer options, and i'm sure someone here can oblige, plenty of talented people that can do trainers in their sleep. |
|
![]() |
![]() |
#7 |
Registered User
Join Date: Oct 2017
Location: Sunderland, England
Posts: 2,702
|
I’m almost certain zeus daz already asked for this and i think stingray might have done it already.
|
![]() |
![]() |
#8 | |
Registered User
Join Date: Dec 2013
Location: Halifax
Posts: 226
|
Quote:
![]() |
|
![]() |
![]() |
#9 |
Registered User
Join Date: Oct 2020
Location: United Kingdom
Posts: 17
|
That's interesting.
I think I managed to disassemble an ST memory dump and a lot of the code is similar to the Amiga memory dump. I feel I might have something to study! |
![]() |
![]() |
#10 |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,431
|
okay, here's a small "tutorial" on how to hack a game that is already running on whdload, on WinUAE.
0. installs: - download the whdload package (dev) and wicked.lha on whdload site. Make sure that you have the source code in the package... Or check my github for my slaves. - install vasm on windows - install ira on windows - install notepad++ with a proper asm 68k outline 1. assemble the .slave file. I'm using and vasm cross assembler with the following options: Code:
vasmm68k_mot -DDATETIME -Ipath_to_OS_includes -Ipath_to_whdload_includes -devpac -nosym -Fhunkexe Wicked.slave WickedHD.s I've renamed the file Keith provided as Wicked.asm to WickedHD.s because I'm a maniac and I have a process to update my slaves/manage them with git. 2. annoying part: figure out which parts of the source aren't relocatable barfly optimizes calls like move.l resload,a2 to move.l resload(pc),a2, vasm does not. So it's better to add PC-relative directive where it's missing. WHDload is going to yell at startup without it. Use "hunkfunc" to figure out the number of relocs, or use find_slave_relocs.py from my github tools repo. 3. run the slave (WinUAE) and check that it's working 4. now just before the main "patch" call (where the code is loaded but not patched yet by whdload), put a "blitz" macro instruction. For this game it's going to be here: Code:
_PatchMain ;waitbutton cmp.l #$41fa0022,($25000) beq .notencrypted bsr _Decode295Main bsr _Relocate .notencrypted blitz ; <----------------- add the instruction here lea _PL_MAIN(pc),a0 bsr _Patch 6. Now with the slave source we know that the game starts at $25000. We don't know where it ends. Well, we could but what the hell... We know that it can't be above $80000 for this 512k chip game. We could monitor what the game loads too, and it's even easier when the game uses files as we can guess the size (unless it's packed, where we can guess too ![]() Slave has set WHDLF_ClearMem so we should find a lot of zeroes at one point and guess that the executable ended ($CCCCCCCCC if the flag is not set) Once we have the end (or what we think is the end) just save the binary data using S command Here it appears in _Decode295Main routine that the executable part has $15ED6 longwords so: Code:
> S wicked_25000.bin $25000 $15ED6*4 Wrote 00025000 - 0007CB58 (359256 bytes) to 'wicked_25000.bin'. 7. now the fun begins. Disassemble the file with IRA like this Code:
ira -a -offset=$25000 wicked_25000.bin the patchlist seems to have a zero-base address (all addresses are absolute). It's okay for a hardware banging/no OS game that uses chipmem for code (the easiest ones). in main patchlist, now we can see that Keith already fixed stuff (access fault): Code:
PL_NOP $25ccc,8 ; Fix Access Fault move.b #$8e,($fffa21) Code:
LAB_0037: MOVE.B #$8e,EXT_02f7 ;25ccc: 13fc008e00fffa21 yeah, you're starting to get the gist of it. 8. Trainer: infinite energy Energy is not that easy to find, because as opposed as lives, you don't have a clear "3" or "2" digit and you can't use standard trainers easily. BUT Winuae offers deep trainer. We note that energy decreases, then increases when a life is lost So start game, shift+F12 and start with "D" (init trainer pass). Then continue ("c") and lose some energy and use "Dd" (decrease) to tell winuae that the value you're looking for has decreased. Continue, if you lose a life, energy is resplenished. Use "Di" increase in that case. After 10 or 12 iterations, I find that the value $4B4 (looks good, low address for variables are common in games) contains the energy. To test if it works, just write into $4B4 with $2F Code:
>W $4B4 $2F.B Wrote 2F (47) at 000004B4.B Code:
MOVE.W #$2f00,D1 ;273c2: 323c2f00 LAB_00EC: MOVE.W D1,EXT_00b0 ;273c6: 33c1000004b4 Code:
MOVE.W #$5f00,D0 ;28eac: 303c5f00 SUB.W energy,D0 ;28eb0: 9079000004b4 MOVE.W D0,energy ;28eb6: 33c0000004b4 Code:
dc.b "C1:X:Enable Trainer:0;" Code:
PL_IFC1X 0 PL_NOP $28eb6,4 ; infinite energy PL_ENDIF Ok, I was doing this live, and assuming it was easy enough for a kind of tutorial. The steps above will work for a lot of games, though. To be continued... Last edited by jotd; 28 December 2020 at 16:45. |
![]() |
![]() |
#11 |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,431
|
ok, now we know that energy is in $4B4 all right. Without further disassembly we can use a memory watchpoint to find where the code is changed. Note that JIT MUST BE DISABLED to be able to use watchpoints.
Code:
w 0 $4B4.W 2 W c Code:
Memwatch 0: break at 000004B4.W W 00001F00 PC=0002A3A8 CPUDW (000) >d $2A3A2 0002A3A2 33c1 0000 04b4 MOVE.W D1,$000004b4 [1f00] 0002A3A8 e049 LSR.W #$08,D1 Code:
MOVE.W #$2f00,D1 ;273c2: 323c2f00 LAB_00EC: MOVE.W D1,energy ;273c6: 33c1000004b4 Actually we probably just patched the position of the energy bar but not the real energy... We could find where the energy is really stored by going "upstream" to see how this position is computed. But nevermind since Keith had put a little "infinite energy" trainer, but it didn't work because... he overlooked the fact that his address is the in-game address and not the address when he patches the code. But he had the proper code to change so that saves a LOT of time thanks! The lower addresses patches work, but at some point, code moves to other locations... So using the address $2bf22 in game and what it contains (NEG D6 + a test), we get code that is located in 28f46 Code:
PL_IFC1X 0 PL_NOP $28f46,2 ; Infinite Energy - Neg.w d6 PL_ENDIF But that's not very easy because every code we'll get from the running game will need to be computed back to the original disassembly... I don't like working like that (plus it means that the code copies code in locations, without flushing caches which can cause issues on some machines) ok, I get it, first thing the code does is to relocate itself Code:
ORG $25000 SECSTRT_0: LEA LAB_0001+2(PC),A0 ;25000: 41fa0022 ADDA.L #$00057b2c,A0 ;25004: d1fc00057b2c LEA EXT_02c3,A1 ;2500a: 43f90007fb2c MOVE.L #$00015ecb,D0 ;25010: 203c00015ecb LAB_0000: MOVE.L -(A0),-(A1) ;25016: 2320 SUBQ.L #1,D0 ;25018: 5380 BNE.S LAB_0000 ;2501a: 66fa JMP LAB_0161+4 ;2501c: 4ef9000280aa jmp to $280AA Doing so, the running code addresses match the disassembly addresses. I'm going to do that. As you see there are a lot of minor annoyances in this process. But a lot of games are more straightforward too. Adding levelskip is slightly more difficult. You have to locate the main game loop and see how it exits. Sometimes I use savestates just before completing a level to figure out where it happens (did that for Deliverance for instance). I'm going to do that. Pick another game if you want, open another thread and I'll try to create another tutorial. Last edited by jotd; 28 December 2020 at 17:29. |
![]() |
![]() |
#12 |
Registered User
Join Date: Oct 2020
Location: United Kingdom
Posts: 17
|
Wow, jotd! That is amazing! Thank you so much!
|
![]() |
![]() |
#13 |
Natteravn
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,570
|
|
![]() |
![]() |
#14 | |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,431
|
Quote:
sorry to have been unexact, I don't want to add optimizations, I prefer that the code is okay from the start so it can be assembled without optimizations and work. It's simple enough. (also sometimes I copy some binary trash as code in ripped code and I don't want it optimized it would break it) |
|
![]() |
![]() |
#15 |
Going nowhere
Join Date: Oct 2001
Location: United Kingdom
Age: 50
Posts: 9,033
|
So, is the OP wanting to skip individual levels, or complete an entire Constellation?
|
![]() |
![]() |
#16 |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,431
|
I don't know. I don't have a clue how to play this game.
I've located the mainloop Code:
game_mainloop: JSR LAB_0291 ;28892: 4eb90002d90c TST.W EXT_00ce ;28898: 4a7900000502 Dumped a new disassembly in the zone. This time if you compare the "source" addresses with the addresses when running the game, they match. Last edited by jotd; 28 December 2020 at 18:54. |
![]() |
![]() |
#17 |
Going nowhere
Join Date: Oct 2001
Location: United Kingdom
Age: 50
Posts: 9,033
|
Level skip
Setting one flag doesn't seem to do it, quite convoluted code, but I have something working so people can go from there and experiment!
I used the Oracle cracked version for reference So, once the game is loaded and running do the following: Put in a NOP in at $29e94 Put in a JSR $c0.s at $2890c followed by 3 NOP's At $c0 write the following code: btst #2,$dff016 bne $e0 move.w #1,$516.s tst.w $5ca.s beq $da subq.w #1,$5ca.s jmp $2893e tst.w $516.s bne $e8 rts jmp $2893e When playing the game, press RIGHT mouse button and it should skip the current level. |
![]() |
![]() |
#18 |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,431
|
great. I'm going to add this to the whdload version
|
![]() |
![]() |
#19 |
Registered User
Join Date: Oct 2020
Location: United Kingdom
Posts: 17
|
$5c8 is the lives counter.
ROM:00029916 subq.w #1,($5C8).l ; Subtract Quick SUBQ.W #1,EXT_011e ;29916: 5379000005c8 so EXT_011e is the lives variable Now to try and find the timer... |
![]() |
![]() |
#20 |
Registered User
Join Date: May 2004
Location: Somewhere secret
Age: 50
Posts: 366
|
A good way to find locations for things you don't know (yet), but you do know other locations (lives for example) - put a memory watchpoint on the lives location when you're at the main menu, before you start a game, then start a game and the debugger should pop up... Now you're looking at the "initialise player" code, where there will almost always be a bunch of code to init all the player values to their starting state, eg: ;lives moveq.w #5,$1234 ;energy move.w #99,$1236 etc... you might also see it using some kind of structure instead of lots of fixed addresses, eg: lea $1234,(a0) move.w #5,(a0) move.w #99,2(a0) but you get the general idea! Basically note down all the locations that values are written to during the 'init player' code, then start changing them and see what happens! This is often the fastest way to find less obvious counters such as timers - good luck! |
![]() |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
Disassembling a TAC 2 joystick... please help me | Marty_McFly | support.Hardware | 23 | 01 March 2022 14:18 |
Disassembling an A600 | Revival9001 | support.Hardware | 2 | 31 January 2017 07:01 |
Disassembling resource | copse | Coders. General | 1 | 02 April 2012 03:36 |
Disassembling and reassembling | absence | Coders. General | 7 | 22 September 2009 15:30 |
Disassembling games for fun | crabfists | Coders. General | 69 | 29 October 2008 11:20 |
|
|