Quantcast

Jump to content

» «
Photo

manipulating game memory for fun and profit

20 replies to this topic
Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#1

Posted 30 September 2005 - 10:48 PM Edited by Demarest, 30 September 2005 - 11:10 PM.

For those of you that do not remember this, here are page 1 and page 2 of the original topic from Google cache (damn pruning grumble grumble). In my quest to maximize SCM usefulness, I decided I wanted to master this technique too. It's something I've been discussing with CyQ. At the time, he was okay with me digging the topic up. I thought I'd make this public so we can all have fun, especially now that I've made it work in MB. The code I'm about to post is for MB 0.22 and as of right now, the MB you use HAS to support DMA. Note: this is only possible on VC for now and possibly always. You will need to turn scm.ini off in MB by going to the Compiler menu and selecting disable custom IDs. Below is a stripped SCM. I'm sharing the code in this manner becuase since DMA is required and these slots are in use by original code. So this means no compatibility at this time. I've already tried moving the block--I originally tried starting with 0?? and so on and backing the addresses off the same 8 bytes, but the game crashed. Hopefully we will be able to move the block. If so, I will do what I can to incoporate it into savegame compatibility. Don't get your hopes up as I've already been told this isn't possible. I'm just stubborn and have been learning lately that there's no reason to accept no for an answer wink.gif

Anyways, go into your MB folder and open up scm.ini. BACK IT UP FIRST! Add the lines
CODE
0124=2,write_mem_address %1d% value %2d%
0125=2,read_mem_address %1d% into %2d%
and save. These changes won't be usable in MB until the next time you launch it. And now the stripped SCM:
CODE
DEFINE VERSION VICE 0.22

0002: jump ��Label008620
DEFINE MEMORY  97

:Label008620
0002: jump ��Label008644
DEFINE OBJECTS  1
DEFINE OBJECT (no name)                   \\ This is an unused object. You can put anything here.

:Label008644
0002: jump ��Label008658
DEFINE MISSIONS  0

;-------------MAIN---------------


:Label008658
016A: fade  0? ()  0? ms
01F0: set_max_wanted_level_to  6?
0111: set_wasted_busted_check_to  0? (disabled)
00C0: set_current_time  12?  0?
0007:  1@ = -444!  \\ floating-point values
0007:  2@ = -486!  \\ floating-point values
0007:  3@ =  10!  \\ floating-point values
04E4: unknown_refresh_game_renderer_at  1@  2@
03CB: set_camera  1@  2@  3@
0053: $MYPLAYER_CHAR = create_player #NULL at  1@  2@  3@
01F5: $MYPLAYER_ACTOR = create_emulated_actor_from_player $MYPLAYER_CHAR
0001: wait  0? ms
01B6: set_weather  0?
0352: set_actor $MYPLAYER_ACTOR skin_to "PLAYER"
038B: load_requested_models
0353: refresh_actor $MYPLAYER_ACTOR
016A: fade  1? (back)  1000& ms
04BB: select_interiour  0?  \\ select render area
01B4: set_player $MYPLAYER_CHAR frozen_state  1? (unfrozen)
01B7: release_weather
004F: create_thread ��LabelMEMHACKING

:LabelMAIN3
0001: wait  5000& ms
0002: jump ��LabelMAIN3

:LabelMEMHACKING
0004:  8?? = -1995422835&&
0004:  12?? =  1342335705&&
0004:  16?? = -1023575064&&
0004:  20?? =  1949868543&&
0004:  24?? =  495648893&&
0004:  28?? =  8221756&&
0004:  32?? = -1070589815&&
0004:  36?? =  10011777&&
0004:  40?? =  1599930368&&
0004:  44?? =  79846238&&
0004:  48?? =  272862464&&
0004:  52?? =  23779721&&
0004:  56?? = -44898224&&
0004:  60?? =  950140866&&
0004:  64?? = -1962902156&&
0004:  68?? =  1949868800&&
0004:  72?? =  1133314173&&
0004:  76?? =  1792641296&&
0004:  80?? =  2045267969&&
0004:  84?? =  822067963&&
0004:  88?? = -1731952192&&
0004:  92?? =  1560281088&&
0004:  96?? = -1034199457&&
0004:  100?? =  4&&
03FD: set_player -5035& handling_responsiveness  8524424&&
0124: write_mem_address  6850752&& value  8524465&&

:LabelMEMHACKINGTRAP
0001: wait  5000& ms
0002: jump ��LabelMEMHACKINGTRAP

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#2

Posted 30 September 2005 - 11:06 PM

As both Dem and I were working on this at the same time, talking as we did it, I have a few pieces of code to demonstrate it (nothing exciting, just some bits to mess up your radar). Add these as threads (note: they both use the same labels, im lazy, so one at once):

CODE
:Labelmemhack                                  
03A4: name thread "RADAR"                        
0007:  0@ = -0.01!  \\ floating-point values
0125: read_mem_address  6880572&& value  1@
 
:labelmemloop
0001: wait  100? ms
0125: read_mem_address  6880572&& value  $radar
005f: $radar += 0@  \\ floating-point values (never used in VC or GTA 3)
0124: write_mem_address  6880572&& value  $radar
 
00D6: if  0?  
0032:    0! >=  $radar  \\ floating-point values
004D: jump if false labelmemloop2
0007:  0@ = 0.01!  \\ floating-point values
 
:labelmemloop2
00D6: if  0?  
0036:   $radar >= 1@  \\ floating-point values (never used in VC or GTA 3)
004D: jump if false labelmemloop3
0007:  0@ = -0.01!  \\ floating-point values
                                   
:labelmemloop3                                    
0002: jump labelmemloop


CODE
:Labelmemhack                                  
03A4: name thread "RADAR"                        
0007:  0@ = 0.01!  \\ floating-point values
0124: write_mem_address  6880572&& value  1000!
 
 
0125: read_mem_address  6880552&& value  1@
 
:labelmemloop
0001: wait  10? ms
0125: read_mem_address  6880552&& value  $radar  
0125: read_mem_address  6880564&& value  $radar2  
005f: $radar += 0@  \\ floating-point values (never used in VC or GTA 3)  
0013: 0@ *= 200!  \\ floating-point values (never used in VC or GTA 3)
005f: $radar2 += 0@  \\ floating-point values (never used in VC or GTA 3)
0017: 0@ /= 200!  \\ floating-point values (never used in VC or GTA 3)
0124: write_mem_address  6880552&& value  $radar
0124: write_mem_address  6880564&& value  $radar2  
0011:  $radar *=  100!  \\ floating-point values
008C:  2@ = float_to_integer  $radar
0015:  $radar /=  100!  \\ floating-point values
01E4: text 1number lowpriority "HJ_IS"  2@  2000& ms  1?  
 
00D6: if  0?  
8032:    1! >=  $radar  \\ floating-point values
004D: jump if false labelmemloop2
0007:  0@ = -0.01!  \\ floating-point values
 
:labelmemloop2
00D6: if  0?  
8036:   $radar >= 1@  \\ floating-point values (never used in VC or GTA 3)
004D: jump if false labelmemloop3
0007:  0@ = 0.01!  \\ floating-point values
                                   
:labelmemloop3                                    
0002: jump labelmemloop

random_download
  • random_download

    :o

  • Members
  • Joined: 07 Mar 2004

#3

Posted 30 September 2005 - 11:16 PM

CODE
@0 = 8524416&&
@0 += label
@0 += <bytesfromlabel>
0124: write_mem_address  @0 value <code>

Would doing that be an easier way to do self-modifying code? Currently I think you store the modifing bit in global vars, then gosub to those, which is a little confusing tounge.gif

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#4

Posted 30 September 2005 - 11:18 PM Edited by Y_Less, 30 September 2005 - 11:23 PM.

Yes that would, but self modifying code was sort of developed (by me anyway - apparently CyQ thought of it long ago, but meh, he never released it) before we looked into this.

Edit: also note that Mission Builder likes to screw with certain DMA variables (the ones defined at the bottom of vicescm.ini). Dems way around was to rename $player_char (which is ALWAYS (even when custom ids are disabled on DECOMILE) COMPILED to 8??) to $myplayer_char. My way, which I much prefer, but its up to you, is to comment out all the global vars at the end of vicescm.ini (just put a semi-colon ( wink.gif at the start of every line under "[variables]" (without the double quotes)).

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#5

Posted 30 September 2005 - 11:25 PM

QUOTE (random_download @ Sep 30 2005, 19:16)
you store the modifing bit in global vars, then gosub to those, which is a little confusing tounge.gif

Nah. MB doesn't allow DMA to anything that's not divisible by 4 and certainly not to anything outside the defined memory. The gosub initially jumps to this command

0084: 61973?? = 96??

...which MB won't touch. If however you store valued in globals that set the bytes to where it looks like that, MB won't know, the game will execute, and everybody lives happily ever after. cool.gif

Un3462
  • Un3462

    Mack Pimp

  • Members
  • Joined: 27 Jun 2002

#6

Posted 06 October 2005 - 10:51 AM

ok. so. you want to move this around. it's possible, if you can find a block of 24 free variables. you could even split the two opcode handlers apart quite easily, requiring two separate blocks of only 11 and 13 free variables. (someone with x86 asm knowledge and a bit of time, could even optimize it for less variables, and variables which aren't in a single block.)

the key in moving them around is keeping the relative addressing and the hooking correct. relative addressing occurs in three places:
CODE
e8 7b-fd-c2-ff          call    CollectParameters
e8 52-fd-c2-ff          call    CollectParameters
e8 79-fb-c2-ff          call    StoreParameters

so if you want to move all the code 4 bytes higher, you simply need to subtract 4 from those values (they're negative, and you'd want them to point back 4 bytes further).
the first one for example (e8 is the call opcode, nearly equivalent to gosub in gta):
7b-fd-c2-ff -> ffc2fd7b -> in wincalc subtract 4 -> ffc2fd77 -> 77-fd-c2-ff, and you're done.

when you're done adjusting the addresses, you put all the bytes together again in groups of 4, and convert them to ints, like i did in my original post. then set those in the correct variables.

then you change the following:
CODE
set_player_handling_responsiveness -5035, 8524424; set opcode handler for 0124 at $00000000
write_mem 6850752, 8524465; set opcode handler for 0125 at second byte of $0000000a

..by editing those two addresses (second param for each) accordingly. in this example, you'd simply add 4 to both.

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#7

Posted 06 October 2005 - 01:40 PM

Ahh, I had thought the problem was due to relative addressings (can't you use absolutes for those though?).

Un3462
  • Un3462

    Mack Pimp

  • Members
  • Joined: 27 Jun 2002

#8

Posted 06 October 2005 - 02:28 PM

i don't think so, actually (x86 opcodes are confusing). you might be able to do "mov eax, absaddress / call eax". but relative is how gta does calls to near functions, so i just used that.

small correction to the above: if you split the opcode handlers apart, you need 14 vars for read_mem.

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#9

Posted 06 October 2005 - 02:51 PM Edited by Y_Less, 06 October 2005 - 03:07 PM.

Fair enought then. People interested in this may be interesed in THIS (Im sure CyQ already has something like it, but useful for others. Just enter your HEX string in the top box (with spaces between each byte) and it will generate a load of 32 bit integers for use in your code. I tested it on the start of the code CyQ wrote and it worked fine. One known bug though is that sometimes it adds an extra 0 to the bottom of the list, I know why this is but I can't get rid of it (as you may have entered 00 00 at the end which may end up on a separate needed string, so I cant just not display if the string equals 0 (but thats my problem not yours).

Edit: I also displays 8 numbers a line (assuming 1 space between) in IE, but it displays 9 in FF until you fill the box and the scroll bar appears, filling the box slightly, and then it displays 8 correctly, for one of the first times ever I made my code run better on IE than FF (although if I did it right on FF, it would be wrong on IE AND FF if you had too many, so this is actually better, but anyway).

Edit 2: I THINK I've solved the random extra 0's problem (I set a variable to tell the code that actual numbers have been processed, not just empty spaces (the error occured when you had trailing spaces after a 4 byte boundary, so there should have been data, but wasn't, therefore no data = 0)). But be careful, if you see it again, please tell me (this way WILL display 0 if the last bits entered are 00s, so should technically have a 0 to enter the data.

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#10

Posted 06 October 2005 - 11:55 PM

inlove.gif CyQ! I was just about to prod you for this since it had been a bit. Thank you.

And now a second can of worms. From what I've seen, I'm assuming that your method HAS to write 4 bytes at a time. And I would also assume that your code was made to do exactly that. Fair enough. Keep in mind that the rest of this post is based on those assumptions being true...

I would like a way to be able to write a byte at a time as some memory addresses contain one byte switches/values. I've actually tried a read_mem $var, $var -= 1, write_mem $var and it crashed. I'm assuming that the other 3 bytes I'm involving while trying to change the 1 are the ones crashing either because I'm trying to write to read-onlys or that the data in them move that quickly. Most likely I'm wrong and I'm just implimenting it wrong since mem-hacking is new to me. If this is the case, indulge me if you will for the sake of education/discussion.

I've noticed that quite a few on/off options in the pause menu have one byte switches assigned to them. When you consider an opcode such as 02A3 toggle_widescreen $int, I would assume the opcode handler code for it is something along the lines of "write_mem address value" where address is the memory address tied to the widescreen option being on and off. If all of the above is true, I'm assuming we could "borrow" that opcode for single byte writing by use of write_mem (address of 02A3's target address) (one byte address we'd like to write to), toggle_widescreen (0-255), write_mem (address of 02A3's target address) (original value), eh? I apologize if this is ambiguous in any way. Of course if you know of an easier way to write to a single byte at a time, please share. Or if you're certain that the continuous nature of my failed attempt above is in fact my own misapplication, feel free to say so.

KennyJ
  • KennyJ

    JPCBoy

  • Members
  • Joined: 29 Sep 2005

#11

Posted 07 October 2005 - 03:38 PM

fantastic biggrin.gif rahkstar2.gif

Un3462
  • Un3462

    Mack Pimp

  • Members
  • Joined: 27 Jun 2002

#12

Posted 07 October 2005 - 08:58 PM

the write-only values are called that because the game will mostly ignore them if you change them, not because it would crash or anything. gta is not multithreaded, so things changing inbetween your opcodes shouldn't be a problem either. so, in theory, your approach should work (as long as the value doesn't over- or underflow).

i can sort of see what you're trying to do with the toggle thing, but that really isn't necessary. and indeed, it's possible (likely even, imo) that there's something else you're not quite doing right. it might help to post full code, or some info on the memory value you're trying to manipulate.

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#13

Posted 29 December 2005 - 11:58 AM

Right, this comes from my masses of work recently (I REALLY should be revising for exams but aren't), I have made this darkpact compatible (I just shifted it over a load of variables only used in mission 0 (this overwrites references to things like pickups but they still work):

CODE
0004: 8288?? = -1995422835&&
0004: 8292?? = 1342335705&&
0004: 8296?? = -1025694744&&
0004: 8300?? = 1949868543&&
0004: 8304?? = 495648893&&
0004: 8308?? = 8221756&&
0004: 8312?? = -1070589815&&
0004: 8316?? = 10011777&&
0004: 8320?? = 1599930368&&
0004: 8324?? = 79846238&&
0004: 8328?? = 272862464&&
0004: 8332?? = 23779721&&
0004: 8336?? = -587536304&&
0004: 8340?? = 950140866&&
0004: 8344?? = -1962902156&&
0004: 8348?? = 1949868800&&
0004: 8352?? = 1133314173&&
0004: 8356?? = 1792641296&&
0004: 8360?? = 568872961&&
0004: 8364?? = 822067931&&
0004: 8368?? = -1731952192&&
0004: 8372?? = 1560281088&&
0004: 8376?? = -1034199457&&
0004: 8380?? = 4&&    
03FD: set_player -5035& handling_responsiveness  8532704&&
0124: write_mem_address  6850752&& value  8532745&&


Thats it, just use that instead of the original.

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#14

Posted 29 December 2005 - 02:56 PM

Thank you, sir. I personally wouldn't have reused pickups for good measure, but that's what I get for not getting off my own lazy ass, eh? cool.gif

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#15

Posted 03 January 2006 - 04:50 PM

Err, I've been looking through the disassembly of my copy of the 1.1 EXE and unless I'm very much mistaken (i.e. the EXE I have is not 1.1) the pointers for both 0124 and 0125 are at the same point, so there is no problem using this in VC.

chaosislife
  • chaosislife

    Member title?

  • Members
  • Joined: 01 Feb 2006

#16

Posted 19 June 2006 - 10:30 AM

a quick note; the 'page 1' and 'page 2' links are inactive,Perhaps google pruned them as well?

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#17

Posted 19 June 2006 - 11:25 AM

Hmm, I'll have to see if I have my local copies still and host them somewhere.

random_download
  • random_download

    :o

  • Members
  • Joined: 07 Mar 2004

#18

Posted 03 July 2006 - 07:18 PM

Is there something that stops you writing to addresses in the opcode jump tables in San Andreas? Writing to one through the scm caused the game to crash, even if the value was the same as the original.

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#19

Posted 03 July 2006 - 09:15 PM

I don't know, I was experimenting with it before, using some OpCode in the 500s (IIRC) that fell on a boundary in SA but I could never get it to even write the address (and now I don't have a decompiled version of the EXE).

random_download
  • random_download

    :o

  • Members
  • Joined: 07 Mar 2004

#20

Posted 03 July 2006 - 10:02 PM

I was going for the address 0x466D68 (opcode 0046) by writing -5DBD. Did your game crash when you tried or just didn't write the address? I can't find any reason why it does, the handling_responsiveness opcode does almost exactly the same thing as in VC, as does the CollectParameters function. I have no idea why it doesn't work :s

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#21

Posted 08 September 2006 - 02:13 PM

Bump.

This is an old post from elsewhere but I never finished it and having only just seen randoms post I thought I'd post here in the hope someone better saw it:

The offset for OpCode 03FD in SA is:

CODE
param1*134h+B73458h+74h


Based on the code at 489731h (handler for the opcode) but it doesn't seem to work although SA has 'unk_B73458' (unknown type) as opposed to 'word_7DBCB0' so it could be unsigned.

The problem is, even when I set the command to set_player 1, which should produce an address of B73600h and set the value to 2, it still appears as 0 in memory. However some positive values seem to correctly modify the correct memory addresses, which leads me to believe the formula is correct, but others don't, and no negative numbers I've tried do.

Also, based on that formula, I found OpCode 05DF which is unused and on a correct boundary (using -23414 in the OpCode) and 05E0 which is unused to redirect the read_mem OpCode but when I use -23414, the game crashes.

Finally (this is just extra as I've not got this far yet without the game crashing) could someone check my modified ASM for SA style OpCodes please (note the jump at the end goes to what appears to be a common OpCode end for 1500 range OpCodes), I've never done ASM before:

CODE
Based on CyQs VC write_mem code and existing SA OpCodes:

6a 02   push 2; num pars
8B CE   mov ecx, esi; get ptr to threadstruct
E8 0f-a7-a1-ff  call CollectParmeters; (-6183153)
a1 78-3c-a4-00          mov eax, ds:ScriptParams; get address
8b 1d 7c-3c-a4-00       mov ebx, ds:ScriptParams+4; get value
89 18                   mov [eax], ebx; write mem
E9 0d-91-a4-ff  jmp 0x492AA4; end OpCode

read_mem:

6a 01   push 1; num pars
8B CE   mov ecx, esi; get ptr to threadstruct
E8 f4-a6-a1-ff  call CollectParmeters; (-6183180)
a1 78-3c-a4-00  mov eax, ds:ScriptParams; get address
8b 00   mov eax, [eax]; read mem
a3 78-3c-a4-00  mov ds:ScriptParams, eax; store value
8B CE   mov ecx, esi; get ptr to threadstruct
6a 01   push 1; num pars
50   push eax
E8 ce-a9-a1-ff  call StoreParmeters; (-6182450)
E9 36-91-a4-ff  jmp 0x492AA4; end OpCode


Any help would be much appreciated.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users