|
 |
|
|
|
|
|
GTA Modification Forums
SA Memory handling some tricks using SA arrays
 |
|
 |
| |
Seemann  |
Posted: Wednesday, Dec 6 2006, 11:57
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
I've decided to create a new topic to collect all examples how to handle GTASA memory via SCM. If you don't understand what is written there, maybe it's not yours. I'll try to explain more things later by editing this post. All codes are written in Sanny Builder v2.99. To get it working, you should download the latest version of SB. http://www.gtaforums.com/index.php?showtopic=211077All codes are tested in San Andreas v1.0 US. Memory addresses could be different in other versions. If something is not working, be sure you use the version I said. 1. Now, we have three ways to handle the game memory. 1. Initial memory handling way was published in this topicStat opcodes provide limited memory access to locations near the stat pools. It allows to make things like changing the player's money, for example. Advantages:- the only opcode is using Disadvantages- memory range is very limited, many useful addresses are inaccesible. 2. Second way: using the Xieon's patch, that changes three opcodes in gta-sa.exe and provides extremely wide possibilities for game memory handling. DownloadAdvantages:- all game addresses are accessible - possibility to protect a memory region with VirtualProtect to rewrite it Disadvantages- requires exe patching; may not work with different versions (but in fact, I did not see any messages that the patch is working incorrectly). 3. Third way: using the SA arrays to get an access to any addresses in range of 0..FFFFFFFF. Initially was posted there. Advantages:- all game addresses are accessible - easy to use - nothing especial required; scm-based solution Disadvantages- some of addresses still coudn't be rewritable (because of AccessViolation Error). 2. For the last way, there are three routines to read/write values to the specified address: (briefly, so far). Memory Handling Routines (san andreas) 1 MemoryWrite: write new value with specified length into the memory Params: 0@ = address; 2@ = new value; 3@ - value length (1, 2, 3, 4) 2 MemoryWrite_DWORD: write new DWord value into the memory Params: 0@ = address; 1@ = new value; 3 MemoryRead: read DWord value from the memory Params: 0@ = address; 1@ = returned value; note that some address could be unreadable/unrewriteable!!! to change such addresses try Xieon's MemPatch: ..\tools\Sa Memory Patch\
| CODE | //--write specified number of bytes into memory
:MemoryWrite 0085: 5@ = 0@ 0@ /= 4 0@ *= 4 // memory address 0062: 5@ -= 0@ // offset (0, 1, 2, 3) :_GetInitValue // if you specify mem offset in 5@, you're able to gosub here gosub @MemoryRead // get initial value 3@ *= 8 // bytes -> bits 5@ *= 8 dec(3@) for 6@ = 0 to 3@ if 08B6: test 2@ bit 6@ then 08BF: set 1@ bit 5@ // 1 else 08C5: clear 1@ bit 5@ // 0 end inc(5@) // next memory bit end 008A: &0(0@,1i) = 1@ // write new value return
//--write 32-bit value into memory-----------
:MemoryWrite32bit 0@ -= 0xA49960 0@ /= 4 008A: &0(0@,1i) = 1@ return
//--read 32-bit value from memory-----------
:MemoryRead 0@ -= 0xA49960 0@ /= 4 008B: 1@ = &0(0@,1i) return
|
3. I wrote some examples using these routines. Most of examples were published at gtaf, some of them was out of there. Now, they are: -------------------------------------------------
EXAMPLE 1. Make extremely long trains, 15+ carriages!
Original (in Russian) Screenshot -------------------------------------------------
| CODE | :LONGTRAINS thread 'TRAINS' for 0@ = -382229 to -382216 wait 0 &0(0@,1i) = #STREAKC end // type0 changed!
// load models #FREIGHT.Load #FREIFLAT.Load #STREAKC.Load
while true if and Model.Available(#FREIGHT) Model.Available(#FREIFLAT) Model.Available(#STREAKC) then Break end wait 0 end // create train with new carriages 06D8: 1@ = create_train_at 2278.1771 -1144.8823 27.5108 type 0 direction 1
#FREIGHT.Destroy #FREIFLAT.Destroy #STREAKC.Destroy end_thread
|
-------------------------------------------------
EXAMPLE 2. New cheats in run-time
Original -------------------------------------------------
| CODE | :NEWCHEATS // EXAMPLE 1: TEST 1 key press (space) thread 'CHEATS' 0@ = -229908 while true 008B: 1@ = &0(0@,1i) // get last keypresses 0085: 2@ = 1@ div(1@, 0x 1 00) // 1char: 0x100, 2chars: 0x10000: 3chars: 0x1000000 mul(1@, 0x 1 00) // same 0062: 2@ -= 1@ // get needed number of chars (1) if 2@ == 0x20 // test if it's SPACE then 03E5: text_box 'CHEAT1'// Cheat activated Break end wait 1000 end
// EXAMPLE 2: TEST 2 keys ('NO') 0@ = -229908 while true 008B: 1@ = &0(0@,1i) // get last keypresses 0085: 2@ = 1@ div(1@, 0x 1 00 00) // 1char: 0x100, 2chars: 0x10000: 3chars: 0x1000000 mul(1@, 0x 1 00 00) // same 0062: 2@ -= 1@ // get needed number of chars (2) if 2@ == 0x4e4f // test if player typed NO then 03E5: text_box 'CHEAT1' // Cheat activated Break end wait 1000 end
// EXAMPLE 3: TEST 3 keys ('WOW') 0@ = -229908 while true 008B: 1@ = &0(0@,1i) // get last keypresses 0085: 2@ = 1@ div(1@, 0x 1 00 00 00) // 1char: 0x100, 2chars: 0x10000: 3chars: 0x1000000 mul(1@, 0x 1 00 00 00) // same 0062: 2@ -= 1@ // get needed number of chars (3) if 2@ == 0x574f57 // test if player typed WOW then 03E5: text_box 'CHEAT1'// Cheat activated Break end wait 1000 end
// EXAMPLE 4: TEST 4 keys ('HACK') 0@ = -229908 while true if &0(0@,1i) == 0x4841434B // test if player typed HACK then 03E5: text_box 'CHEAT1' // Cheat activated Break end wait 1000 end
// EXAMPLE 5: TEST 5 keys ('SANNY') // test 5th char 's' from address +4b, then test 'anny' from the beginning; // addr keys // -229908: X X X X | -229908: A N N Y // -229907: X X X O <- | -229907: _ _ _ S while true 0@ = -229907 // +32bits; next 4 bytes/chars in the last keypresses block 008B: 1@ = &0(0@,1i) 0085: 2@ = 1@ div(1@, 0x100) mul(1@, 0x100) 0062: 2@ -= 1@ // 5th char is the last in the second block: if 2@ == 0x53 // test if S is the 5th pressed key then 0@ = -229908 008B: 3@ = &0(0@,1i) if 3@ == 0x414E4E59 // test if player also typed ANNY after that then 03E5: text_box 'CHEAT1' // Cheat activated Break end end wait 1000 end // EXAMPLE 6: TEST 16 keys: '1234567812345678' 0@ = -57477 while true if &0(0@,1v) == "8765432187654321" // test if last pressed key combo is "1234567812345678" then 03E5: text_box 'CHEAT1' // Cheat activated Break end wait 1000 end end_thread
|
-------------------------------------------------
EXAMPLE 3. Changing of the local variables of any thread
Original (in Russian) -------------------------------------------------
| CODE | // --------------------------------------------- // This thread searches the one named TEST // and changes its local variable 10@ // --------------------------------------------- :CHANGELOCALVAR thread 'CLV' { 0@ = thread address 1@ = temp } 0@ = 0xA8B42C // FIND_THREAD_LOOP while true gosub @MemoryRead // 1@ = first active thread if 1@ == 0 then Break // no threads, search failed end 0085: 0@ = 1@ // save the pointer // get thread name magic address div(1@, 8) dec(1@, 1 348 395) { IMPORTANT NOTE: thread names are stored in lowercase registry, so we've to compare it in lowercase as well. SB option 'Case Converting' has to be set to 'As is'! } if &0(1@,1s) == 'test' // check if this thread's name is "test" then // well, we've found it, can do everything with it // 0@ contains that thread address
// get address of 10@ inc(0@, 0x3c) // thread locals pool inc(0@, 40) // local var name * 4; i.e. set to 36 to change 9@ // MEMORY WRITE DWORD 1@ = 3333 // new value of thread('test').10@ gosub @MemoryWrite32bit
Break // end the LOOP else // no, that thread has another name // check the next one wait 0 end // go to while_begin end // variable changed, end_thread end_thread
// --------------------------------------------- // This thread shows a number after 1 sec // after activated. The number is stored in 10@ // --------------------------------------------- :TEST1 thread 'TEST' 10@ = 10000 wait 3000 054C: use_GXT_table 'POOL' 01E3: text_1number_styled 'NUM' 10@ 5000 ms 1 // ~1~ end_thread
|
optimized version of CLV thread (see above):| CODE | :CLV 03A4: name_thread 'CLV' 0006: 0@ = 67251
:CLV_LOOP 008B: 0@ = &0(0@,1i) 00D6: if 8039: not 0@ == 0 004D: jump_if_false @CLV_END 0001: wait 0 ms 0085: 1@ = 0@ 0016: 1@ /= 8 000E: 1@ -= 1348395 0016: 0@ /= 4 000E: 0@ -= 2696792 00D6: if 05AD: &0(1@,1s) == 'test' 004D: jump_if_false @CLV_LOOP
// 25 is the local number + 15 // i.e. set to 24 to change 9@ 000A: 0@ += 25
// 3333 is a new value of the local 0004: &0(0@,1i) = 3333
:CLV_END 004E: end_thread
|
-------------------------------------------------
EXAMPLE 4. Remove the message "To stop Carl..." when the player first time stealing a car
Original -------------------------------------------------
| CODE | :MSGREMOVE thread 'NOMSG' 0@ = 0xC0BC15 // ADDRESS 2@ = 1 // VALUE 3@ = 1 // LENGTH (Byte) gosub @MemoryWrite end_thread
|
4. You can grab a main.scm with all examples from here: http://www.mysharefile.com/v/7409206/memhandling.rar.html- Readme included - source included changes history: 06 dec 2006:- added link to the Xieon's patch - added optimized version of CLV thread (example #3) This post has been edited by Seemann on Wednesday, Dec 6 2006, 15:37
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
ceedj  |
Posted: Wednesday, Dec 6 2006, 12:58
|
PEDS Creator

Group: Members
Joined: May 21, 2005



|
Excellent post Seemann! I have applied the #2 method (Xieon's Patch) and have gotten it working using the SA loader by op9080 (a C++ hook, and the foundation for SA Studios). Here's the opcode setups: | CODE | //Xieon's Memory Patch
DEFINE_OPCODE(read_mem_addy, 0x00C3, "iii"); DEFINE_OPCODE(write_mem_addy, 0x00C4, "iii"); DEFINE_OPCODE(virtual_protect_change_at, 0x0181, "iii"); |
And here's his example from the Read Me that comes with the patch (moon gravity on jumps) | CODE | theScript << virtual_protect_change_at << 8796548 << 4 << 4; theScript << write_mem_addy << 8796548 << 2 << 0.002; theScript << virtual_protect_change_at << 8796548 << 4 << -1; |
Two things: 1) On 0181, the last parameter is a float in the example. Though it works now, suppose I use an address that needs an integer? How would I solve this?2) Maybe add a link to the patch in your first post. My only gripe with the patch method is that you'd have to include it in any mods that use it (is he allowing this? It's not really clear in the Read Me file), and get your end user to patch it. Although nifty that an auto-find and backup were included in the patch. This post has been edited by ceedj on Wednesday, Dec 6 2006, 15:48
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
Posted: Wednesday, Dec 6 2006, 13:52
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
| QUOTE (ceedj @ Dec 6 2006, 21:58) | 1) On 0181, the last parameter is a float in the example. Though it works now, suppose I use an address that needs an integer? How would I solve this?
|
sorry, but why do you speak about 0181? This one has all the parameters integer. Do you mean 00C3/00C4? | QUOTE (ceedj @ Dec 6 2006, 21:58) | My only gripe with the patch method is that you'd have to include it in any mods that use it (is he allowing this? It's not really clear in the Read Me file), and get your end user to patch it. |
yes, it's free to distribute, the only you'd have to do is to include Readme.txt with the patch.
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
Main.scm Got Cracked!Finally, I figured out a way to run an assembler code from a main.scm without any patches or whatever. From now, we have no limits for the coding ever. It's similar to the CyQ's method of an injection of the asm code to change opcode handlers. I found two opcodes in SA that could make the same function. They are: 0572 and 0A3D. So, how it works. 1. In San Andreas we have 92 cheats. Some of them are null, some are well-known and often used. 2. These opcodes are well known too. 0572 gives to all taxis the nitro, 0A3D makes the prostitutes pay you (it doen't matter, I use 0572). But not anybody knows that this opcodes works as the cheats like. They enables one of the 92 flags and game begins to think that you use a cheat (but without affecting cheats history, only implying what the cheat does). 3. 0A3D referring to the cheat #90, 0572 - cheat #91 4. All the cheats routines addresses are stored as simple Dword values and COULD be changed in run-time via array's method. 5. So, what this code does: it changes 91st cheat routine address with a value INSIDE main.scm (all adresses are well-known). Then we run this cheat and game goes to the address we set, in other words it goes inside main.scm and begins work with the code as with an assembler. Using hex..end struct in Sanny Builder it's possible to execute ANY code you want. Personally I'm not good ASM'er, so my example is a kind of useless (I just give 1000 buxes to the player), but it's only the beginning. Somebody who knows asm well could implement VirtualProtect routine and then we even do not need to patch the game with the Xieon's patch to work with the addresses that have to be protected (a gravity one, for example). The code: | CODE | // ---------------------------------- // Asm code injector // ---------------------------------- // set new handler address 0@ = -429863 &0(0@,1i) = 0xA49960 &0(0@,1i) += @__AsmInjection // Run Asm Injection 0572: 1 // restore handler &0(0@,1i) = 0 return
:__AsmInjection hex // here goes ASM code!
//My example: Add $1000 to the player money B8 E8030000 // mov eax, 1000 01 05 50CEB700 // add [0xB7CE50], eax C3 // return end
|
This code adds 1000 to the value stored at 0xB7CE50 (player.money). Bit useless, but works! Pay attention: that was tested in San Andreas v1.0 US. If you have v1.01 please test if it works. well, well... There's another example: http://sannybuilder.com/files/cheater.rarThis mod allows to enable all cheats by pressing Action Key. This post has been edited by Seemann on Saturday, Dec 9 2006, 05:42
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
Posted: Saturday, Mar 3 2007, 02:42
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
I could run the VirtualProtect procedure from the main.scm, and now there's no need for the Xieon's patch: we can do the same through the SCM. Worked on US, EUR, GER 1.0 That's the full code how to write the value with the specified length (1,2,4 bytes) to the specified address protected with VP.| CODE | :MemoryProofWrite 3@ == 1 jf @_____novp 4@ = -429863 &0(4@,1i) = 0xA49960 &0(4@,1i) += @_____vpsv 0052: gap 0 virtual_protect_at_address 0@ size 1@ newprotect 4 gap 0 0 0572: 1 gosub @MemoryWrite 0052: gap 0 virtual_protect_at_address 0@ size 1@ newprotect -1 gap 0 0 0572: 1 &0(4@,1i) = 0 end_thread
:_____novp gosub @MemoryWrite end_thread
:MemoryWrite 3@ = -429864 &0(3@,1i) = 0xA49960 &0(3@,1i) += @_____mwss 0052: gap 0 target_address 0@ size 1@ value 2@ gap 0 0 0A3D: 1 &0(3@,1i) = 0 return
:_____vpsv hex 68 F4 3C A4 00 83 3D 84 3C A4 00 FF 75 08 FF 35 F4 3C A4 00 EB 06 FF 35 84 3C A4 00 FF 35 80 3C A4 00 FF 35 7C 3C A4 00 FF 15 2C 80 85 00 C3 end
:_____mwss hex 8B 15 7C 3C A4 00 8B 05 84 3C A4 00 83 3D 80 3C A4 00 01 75 03 88 02 C3 83 3D 80 3C A4 00 02 75 04 66 89 02 C3 89 02 C3 end
|
It is a thread that executed once and stopped. Place it to your scripts as you did with other threads. When you create it with 004F, pass four parameters to it: | CODE | create_thread @MemoryProofWrite address (DWORD) size (Byte: 1,2,4) value (DWORD) VirtualProtect (BOOL)
|
then the local variables of the thread will be initialized with these values. The variables as parameters are allowed. If you only need to write values (without the VirtualProtect) use
| CODE | create_thread @MemoryProofWrite address XXXX size XXXX value XXXX VirtualProtect 0
|
or without VirtualProtect parameter ever
Hello, world!
Example1. Changing the gravity (the patch example) /with VirtualProtect| CODE | create_thread @MemoryProofWrite address 0x00863984 size 4 value 0.002 VirtualProtect 1
| Compiled SCM and source Example2. Remove annoying message about entering car (from the first post) /without VirtualProtect| CODE | | create_thread @MemoryProofWrite address 0xC0BC15 size 1 value 1 | This post has been edited by Seemann on Sunday, Mar 4 2007, 04:01
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
Posted: Saturday, Mar 3 2007, 07:07
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
| QUOTE (DexX @ Mar 3 2007, 13:16) | | Are you saying we can write anywhere in the game's memory, and write any size data? |
The memory address could be any, but the value size could only be 1, 2 or 4 bytes. The asm code there contains something like: | CODE | cmp size, 1 ja @2 mov byte ptr [address], value ret
:2 cmp size, 2 ja @4 mov word ptr [address], value ret
:4 mov dword ptr [address], value ret
|
I could implement the BlockWrite routine for writing the data with any size, need that?
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
PLPynton  |
Posted: Saturday, Mar 3 2007, 13:45
|
Player Hater

Group: Members
Joined: Jul 9, 2005


|
well, i still can not decompile it but as you say we have to include source ok, not a problem i guess.
i do not want to mess up too much: there is a way to place any kind of informations within script after jump command and that information is ignored by the game, while that kind of information can be interpreted by decompiler after it was created by compiler. additional marking of START, END and TXTCODE (3 nops) will be also required for code that is extraordinary when compiling. nop1 for start of code, nop2 for end code and nop3 somewhere in script after jump (to avoid main part consumption). well but that is just an idea and i understand how unnecesary it might be. ignore that please.
ignore that, i have had an issue because my game crashed after i quit and dis not want to start again. did because i messed to much in memory.
edit: he i can imagine what happens with 2.0, deadly gravity hehe
EDIT2: listen, do not ignore that idea with txt code included in main.scm. what if you do that: i can write a mod then mark my code as "SPECIAL" and it decompiles it after all EXACTLY AS SOURCE- with custom labels, globals, and descriptions. you just place text code after jump when our special nop is there (within main.scm code). then decompile using this text and putting it between our 2 special nops. (even using one nop with several parameters). i think people can like have SOURCE after decompiling right? if the game accepts it of course.
This post has been edited by PLPynton on Saturday, Mar 3 2007, 14:17
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
Posted: Saturday, Mar 3 2007, 15:51
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
| QUOTE (PLPynton @ Mar 3 2007, 21:45) | | i think people can like have SOURCE after decompiling right? |
The decompiler don't even need for the special marks (nops or whatever) in the code to detect if there's an in-built source code. The compiler just adds the source and the offsets where the text has to be insterted and its length at very end of the file. That also save the space. So, the decompiler checks that and inserts this plain text instead of the byte code (it wont even decompile it as there's a source ready-to-go, yes, just skip?). Very easy. But lets imagine: for example, I have two custom threads. I decide to include one of them as source in main.scm. But it uses custom variables and labels from another one. What happen: when the decompiler put the source, it still use such names, but those thread will have another names after the decompiling. So, that source will be uncompilable. And to avoid that is too hard, I think. Ideas? I think the using of such sources for now only necessary for the hex..end construct. I could make the compiler store all the hex insertions offsets and its length to allow then the decompiler parse them and it should solve the problem with the undecompilable sources. | QUOTE (PLPynton @ Mar 3 2007, 20:32) | | added if line that was missing at the beginning |
For the single ifs there's no need for 00D6. I just save some space in the main.scm Is the compiled scm from the gravity.rar working for you? This post has been edited by Seemann on Saturday, Mar 3 2007, 16:20
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
Well, that's it: | CODE | { ----------------------------------------------------------------- Memory Access Thread
to write a value with the specified length to the memory address use create_thread @MemoryProofWrite address X:DWord size X:BYTE value X:DWORD VirtualProtect X:BOOL to read a value with the specified length by the memory address use create_thread @MemoryProofRead address X:DWord size X:BYTE VirtualProtect X:BOOL The read value will be stored to the variable $MEMORY_PROOF_READ
The possible Size values: 1 (byte), 2 (word), 4 (dword) The VirtualProtect parameter is optional ----------------------------------------------------------------- }
:MemoryProofWrite 4@ = @MemoryWrite 3@ == 1 jf @_____novp
:_____mpvp 5@ = -429863 &0(5@,1i) = 0xA49960 &0(5@,1i) += @_____vpsv 0052: gap 0 virtual_protect_at_address 0@ size 1@ newprotect 4 gap 0 0 0572: run_VPSV 1 gosub 4@ 0052: gap 0 virtual_protect_at_address 0@ size 1@ newprotect -1 gap 0 0 0572: run_VPSV 1 &0(5@,1i) = 0 end_thread
:MemoryProofRead 4@ = @MemoryRead 2@ == 0 jf @_____mpvp
:_____novp gosub 4@ end_thread
:MemoryWrite 4@ = -429864 &0(4@,1i) = 0xA49960 &0(4@,1i) += @_____mwss 0052: gap 0 target_address 0@ size 1@ value 2@ gap 0 0 0A3D: run_MWSS 1 &0(4@,1i) = 0 return
:MemoryRead 4@ = -429864 &0(4@,1i) = 0xA49960 &0(4@,1i) += @_____mrmm 02EC: run_MRMM 1 read_address 0@ size 1@ hex 3D0A008D0100000000 {store_to} 02 $MEMORY_PROOF_READ end &0(4@,1i) = 0 return
:_____vpsv hex 68 F4 3C A4 00 83 3D 84 3C A4 00 FF 75 08 FF 35 F4 3C A4 00 EB 06 FF 35 84 3C A4 00 FF 35 80 3C A4 00 FF 35 7C 3C A4 00 FF 15 2C 80 85 00 C3 end
:_____mwss hex 8B 15 7C 3C A4 00 8B 05 84 3C A4 00 8B 0D 80 3C A4 00 83 F9 01 75 03 88 02 C3 83 F9 02 75 04 66 89 02 C3 89 02 C3 end
:_____mrmm hex 31 C0 BA 78 3C A4 00 89 02 8B 0D 80 3C A4 00 8B 05 7C 3C A4 00 83 F9 01 75 05 8A 00 88 02 C3 83 F9 02 75 07 66 8B 00 66 89 02 C3 8B 00 89 02 C3 end
{ ---------end of memory access thread----------------------------- }
| That thread is the more complex way of getting memory access. For some cases you may need for something shorter. Feel free to modify it.Example: decrease a gravity for 5 sec, then restore to normal| CODE | create_thread @MemoryProofRead address 0x863984 size 4 VirtualProtect 1 wait 0 create_thread @MemoryProofWrite address 0x863984 size 4 value 0.002 VirtualProtect 1 wait 5000 create_thread @MemoryProofWrite address 0x863984 size 4 value $MEMORY_PROOF_READ VirtualProtect 1
|
edit: optimized mrmm This post has been edited by Seemann on Sunday, Mar 4 2007, 08:09
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
Seemann  |
|
Ruhe

Group: Members
Joined: Sep 3, 2004



|
That's the multiversion of the memory access thread. You could pass two addresses: one for v1, second for v1.01. The code detects what version is running now and works with that correctly. Parameters: | CODE | create_thread @MemoryProofMultiWrite/Read address for v1.0 (X:DWord) address for v1.01 (X:DWord) size (X:BYTE) value (X:DWORD) VirtualProtect (X:BOOL)
|
| CODE | { ----------------------------------------------------------------- Memory Access Thread MultiVersion :: supports GTA SA v1 and v1.01
to write a value with the specified length to the memory address use create_thread @MemoryProofMultiWrite address for version1 X:DWord address for version1.01 X:DWord size X:BYTE value X:DWORD VirtualProtect X:BOOL
Example: create_thread @MemoryProofMultiWrite address_V1 0xC0BC15 address_V2 0xC0E295 size 1 value 1 to read a value with the specified length by the memory address use create_thread @MemoryProofMultiRead address for version1 X:DWord address for version1.01 X:DWord size X:BYTE VirtualProtect X:BOOL The read value will be stored to the variable $MEMORY_PROOF_READ Example: create_thread @MemoryProofMultiRead address_V1 0x863984 address_V2 0 size 4 VirtualProtect 1
The possible Size values: 1 (byte), 2 (word), 4 (dword) The VirtualProtect parameter is optional ----------------------------------------------------------------- }
:MemoryProofMultiWrite gosub @_____gsvo01 7@ = @MemoryWrite 4@ == 1 jf @_____novp
:_____mpvp 0085: 8@ = 10@(9@,2i) 008A: &0(8@,1i) = 14@(9@,2i) 005E: &0(8@,1i) += 16@(9@,2i) 0052: gap 0 virtual_protect_at_address 0@(9@,2i) size 2@ newprotect 4 gap 0 0 0572: run_VPSV 1 gosub 7@ 0052: gap 0 virtual_protect_at_address 0@(9@,2i) size 2@ newprotect -1 gap 0 0 0572: run_VPSV 1 &0(8@,1i) = 0 end_thread
:MemoryProofMultiRead gosub @_____gsvo01 7@ = @MemoryRead 3@ == 0 jf @_____mpvp
:_____novp gosub 7@ end_thread
:MemoryWrite 0085: 7@ = 12@(9@,2i) 008A: &0(7@,1i) = 14@(9@,2i) 005E: &0(7@,1i) += 18@(9@,2i) 0052: gap 0 target_address 0@(9@,2i) size 2@ value 3@ gap 0 0 0A3D: run_MWSS 1 &0(7@,1i) = 0 return
:MemoryRead 0085: 7@ = 12@(9@,2i) 008A: &0(7@,1i) = 14@(9@,2i) 005E: &0(7@,1i) += 20@(9@,2i) 02EC: run_MRMM 1 read_address 0@(9@,2i) size 2@ hex 3D0A008D0100000000 {store_to} 02 $MEMORY_PROOF_READ end &0(7@,1i) = 0 return
:_____gsvo01 8@ = -429566 &0(8@,1i) == 4611680 jf @_____gsvo012 // 1.0 // 9@ = 0 10@ = -429863 12@ = -429864 14@ = 0xA49960 16@ = @_____vpsv 18@ = @_____mwss 20@ = @_____mrmm return :_____gsvo012 // 1.01 9@ = 1 11@ = -431117 13@ = -431118 15@ = 0xA4BFE0 17@ = @_____vpsv2 19@ = @_____mwss2 21@ = @_____mrmm2 return
:_____vpsv2 hex 68 74 63 A4 00 83 3D 04 63 A4 00 FF 75 08 FF 35 74 63 A4 00 EB 06 FF 35 04 63 A4 00 FF 35 FC 62 A4 00 FF 35 F8 62 A4 00 FF 15 2C 90 85 00 C3 end
:_____vpsv hex 68 F4 3C A4 00 83 3D 84 3C A4 00 FF 75 08 FF 35 F4 3C A4 00 EB 06 FF 35 84 3C A4 00 FF 35 80 3C A4 00 FF 35 7C 3C A4 00 FF 15 2C 80 85 00 C3 end
:_____mwss2 hex 8B 15 FC 62 A4 00 8B 05 04 63 A4 00 8B 0D 63 00 A4 00 EB 12 end
:_____mwss hex 8B 15 7C 3C A4 00 8B 05 84 3C A4 00 8B 0D 80 3C A4 00 83 F9 01 75 03 88 02 C3 83 F9 02 75 04 66 89 02 C3 89 02 C3 end
:_____mrmm2 hex 31 C0 BA F8 62 A4 00 89 02 8B 0D 00 63 A4 00 8B 05 FC 62 A4 00 EB 15 end
:_____mrmm hex 31 C0 BA 78 3C A4 00 89 02 8B 0D 80 3C A4 00 8B 05 7C 3C A4 00 83 F9 01 75 05 8A 00 88 02 C3 83 F9 02 75 07 66 8B 00 66 89 02 C3 8B 00 89 02 C3 end
{ ---------end of memory access thread----------------------------- }
| Example: how to remove the message showed when Carl first time stealing a car in the both versions| CODE | create_thread @MemoryProofMultiWrite address_V1 0xC0BC15 address_V2 0xC0E295 size 1 value 1
| This post has been edited by Seemann on Sunday, Mar 4 2007, 12:58
|
|
|
|
|
 |
|
 |
 |
|
 |
| |
0 User(s) are reading this topic (0 Guests and 0 Anonymous Users)
0 Members:
Pages:
(3) [1] 2 3
Track this topic
Receive email notification when a reply has been made to this topic and you are not active on the board.
Subscribe to this forum
Receive email notification when a new topic is posted in this forum and you are not active on the board.
Download / Print this Topic
Download this topic in different formats or view a printer friendly version.
| |
 |
|
 |
|
|
|
|