Quantcast

Jump to content

» «
Photo

SA Memory handling

42 replies to this topic
Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#1

Posted 06 December 2006 - 11:57 AM Edited by Seemann, 06 December 2006 - 03:37 PM.

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...howtopic=211077

All 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 topic
Stat 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.
Download

Advantages:
- 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.mysharefi...ndling.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)
  • MasterHK likes this

ceedj
  • ceedj

    PEDS Creator

  • Feroci Racing
  • Joined: 21 May 2005

#2

Posted 06 December 2006 - 12:58 PM Edited by ceedj, 06 December 2006 - 03:48 PM.

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. smile.gif

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#3

Posted 06 December 2006 - 01:52 PM

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.

Bigun
  • Bigun

    wandering about

  • Members
  • Joined: 10 Jul 2004

#4

Posted 06 December 2006 - 02:30 PM

Splendid post Seemann!
That's nice, modifying thread locals...we can pretty much kiss globals away. O_o Also good for savegame compabillity.

ceedj
  • ceedj

    PEDS Creator

  • Feroci Racing
  • Joined: 21 May 2005

#5

Posted 06 December 2006 - 03:37 PM

I was in fact refering to 00C4. Whoops. smile.gif Anyway, sorry for the confusion. I edited my post with the correct data type for 0181.

This is what I get for replying to topics with little sleep. lol.gif

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#6

Posted 08 December 2006 - 04:07 PM Edited by Seemann, 09 December 2006 - 05:42 AM.

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....les/cheater.rar

This mod allows to enable all cheats by pressing Action Key.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#7

Posted 03 March 2007 - 02:42 AM Edited by Seemann, 04 March 2007 - 04:01 AM.

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

DexX
  • DexX

    Black Hat

  • Feroci Racing
  • Joined: 16 May 2002

#8

Posted 03 March 2007 - 05:16 AM

Seemann, i just want to be sure i understand you correctly, since i'm not a mission coder;
Are you saying we can write anywhere in the game's memory, and write any size data?

i've been using a modified vorbis dll (by delfi) to load .asi files, which alter the game's memory as i wish @ runtime. could we use this code you posted, along with the "mission pack" method of installing scm mods? if so that would really be sweet..

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#9

Posted 03 March 2007 - 07:07 AM

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
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#10

Posted 03 March 2007 - 11:12 AM Edited by PLPynton, 03 March 2007 - 11:16 AM.

that is just great. what is missing is a possibility to decompile a game script whenever hex data input is there. or in fact make a way so code written in such a form will could be decompiled and compiled without loosing its functionality.
as most of the coders will like to use your work here it will be a big minus if their MOD can not be decompiled back.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#11

Posted 03 March 2007 - 12:04 PM

QUOTE (PLPynton @ Mar 3 2007, 19:12)
what is missing is a possibility to decompile a game script whenever hex data input is there.

No, such possibility is there since v2.98, IIRC.
The Sanny Builder console could receive the commands from the input line and you can turn on the option IGNORE_UNKNOWN which allows to decompile almost any protected/broken scm. Read the help files and the console texts


PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#12

Posted 03 March 2007 - 12:32 PM Edited by PLPynton, 03 March 2007 - 12:41 PM.

i mean that in other way:
it is great what you just did!

and the SB returns me
"Thread not found. Base: 194426. Probably Script.img is broken"

edit: i inserted the thread just before mission 0 (added if line that was missing at the beginning), i issued with
create_thread @MemoryProofWrite address 0x00863984 size 4 value 0.002 VirtualProtect 1
from mission 0 code
is there anything i could forget to switch?

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#13

Posted 03 March 2007 - 12:55 PM

QUOTE
and the SB returns me
"Thread not found. Base: 194426. Probably Script.img is broken"

I'm successed to decompile the files with the hex insertions.
BTW, there's no way for exact decompiling. The labels (_____vpsv, _____mwss) will disappear as well when you decompile . So, the way for other people is that the author has to include his source with the mod.

QUOTE
i issued with create_thread @MemoryProofWrite address 0x00863984 size 4 value 0.002 VirtualProtect 1

cant understand the kind of issue: code doesnt compile or the game crashes? I just do the same and all works fine to me (heh, it's too funny play with gravity=2.0)

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#14

Posted 03 March 2007 - 01:45 PM Edited by PLPynton, 03 March 2007 - 02:17 PM.

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.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#15

Posted 03 March 2007 - 03:51 PM Edited by Seemann, 03 March 2007 - 04:20 PM.

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?

SteaVor
  • SteaVor

    Professional investor

  • Members
  • Joined: 17 Dec 2005
  • None

#16

Posted 03 March 2007 - 04:41 PM

Excellent work, Seemann, this is really useful and does work flawlessly for me - it even works with my German v1.0 exe! That's so great!
What's the best way to read a value from memory now?

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#17

Posted 03 March 2007 - 05:00 PM Edited by PLPynton, 03 March 2007 - 05:04 PM.

yes it can be decompiled but that does not proof why it can not on full game script. i see that it does not lead in any good direction, our further discussion about built in source. just forget it.

besides from me too: great work, EU 1.0 NoCD works

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#18

Posted 04 March 2007 - 04:03 AM

QUOTE (SteaVor @ Mar 4 2007, 00:41)
What's the best way to read a value from memory now?

I will write similar code for reading the data, but still don't know the best way how to pass the variable pointer where the read value will be stored

In theory it should be something like:
CODE
create_thread @MemoryProofRead address XXX size XXX variable_offset 20

And the code stores the value to variable $5.

But most likely I will use another way.


QUOTE (PLPynton @ Mar 4 2007, 01:00)
i see that it does not lead in any good direction, our further discussion about built in source. just forget it.

No, thatís good idea. I will implement it for the hex firstly, and when we'll see how that works - for the text sources.


Thanks for your feedbacks, guys.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#19

Posted 04 March 2007 - 05:57 AM Edited by Seemann, 04 March 2007 - 08:09 AM.

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

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#20

Posted 04 March 2007 - 12:51 PM Edited by Seemann, 04 March 2007 - 12:58 PM.

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

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#21

Posted 04 March 2007 - 04:09 PM Edited by Y_Less, 04 March 2007 - 04:22 PM.

I did post a thing a long time ago on hacking based almost entirely on CyQ's method before the stats method was discovered (using the set_player_handling hack), I even found an unused OpCode on an accessible boundary for the write and another unused OpCode nearby for the read, unfortunately I never got it working, although I never tried amazingly hard, but yeah, I posted what I had in Demarest's mem hacking revisited topic, but well done for getting this working.

Also, ceedj, no offence but what's the point of doing that from C++ when you already have a perfectly good handle into memory?

Edit, link to my original SA mem hacking stuff: http://www.gtaforums...dpost&p=3863930

Also, it's great to see people still continuing all the work like this.

ceedj
  • ceedj

    PEDS Creator

  • Feroci Racing
  • Joined: 21 May 2005

#22

Posted 04 March 2007 - 09:25 PM

None taken buddy. As some people know, I am teh suk on memory stuff, so the patch method was a nice, easy way to get what I want. Just recently however, I think I figured out how the function works that op9080 set up for the gravity controls and the various speed/height patches. There was also the issue of controlling it, as with over 100+ keychecks, finding a spot for another one was tricky.

Also bear in mind when I started working with these "hooks" I had NO idea how to program with C++. Some would say I still don't; have a look at Spookie's SCM mod thread here somewhere and watch random_download try to explain asm stuff to me. I, of course, failed spectacularly, but that's another story. wink.gif

@Seemann: at the risk of exposing myself further as the true GTAF-Idiot, I have to ask: is the asm code for that (in SB) a "standard" piece of code? In other words, will I just be able to copy that part and just use the "create_thread" command to do my memory stuff? Although I DO use a hook for most of my SA stuff, I can think of a number of situations where I could have more than one running thread (can't do that with the hooks) and I've even tinkered around with a hybrid setup; using the hook for the keys/selection process and using MB/SB for everything else. Just curious. And BTW, AWESOME JOB coming up with this. You people are friggin genuises. smile.gif

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#23

Posted 05 March 2007 - 08:52 AM Edited by Seemann, 05 March 2007 - 09:04 AM.

QUOTE (ceedj @ Mar 5 2007, 05:25)
is the asm code for that (in SB) a "standard" piece of code?

Yes, but using only the hex insertions is not enough to handle the memory. The whole thread deals with that: one part loads the parameters to the memory, another one runs the asm code. You should copy one of those threads (for v1 only or multiversion thread, as you wish) anywhere to the MAIN and begin to use it with 004F.


Btw, I know the way how to check via the scripts if ANY keyboard key is pressed (I mean ANY key except ESC and PrintScreen ones). Other keys could be checked very easy and it removes the 00E1 opcode limit which allows to check only about 20 pre-defined keys.

Interested?

ceedj
  • ceedj

    PEDS Creator

  • Feroci Racing
  • Joined: 21 May 2005

#24

Posted 05 March 2007 - 01:11 PM

RE: Thread - yeah, that's what I was wondering about. In my case, using the multiversion thread you posted would be better yes. This is probably a few months away (I'm neck-deep in wrapping the second season), but I LOVE getting answers to possible questions early. Makes my life a whole lot nicer in the end. wink.gif

RE: Keypresses - Hmmm. Let's say yes, and we'll see if I can implement it.

Is this related to what Y_Less discussed sometime ago about making your own cheats, or something totally different?

Thanks for the heads up buddy! smile.gif

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#25

Posted 05 March 2007 - 06:27 PM

If you can hook the keypress reader then you can see if a certain sequence of keys is pressed and then act upon it. That action may be something to improve the players stats/situation at that time and it may also display a text box. So yes, creating your own cheats is very possible. You can also do things like text entry and faking GXTs, although I was looking into faking GXTs in SA the other day for unrelated reasons but I'm not sure how to do it atm, it was easy in VC to track down, SA isn't so easy sad.gif .

Anyway, yes, it is possible.

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#26

Posted 05 March 2007 - 10:26 PM Edited by Y_Less, 06 March 2007 - 03:43 AM.

SA cheat processor. To prove the point I've written said cheat processor and designed it to be as easy to use as possible. It uses a very slightly modified version of the mem hacking code to allow it to be called directly or through a create_thread, but this is entirely backwards compatible (basically, if you call it via create_thread just do as you normally would, if you create it directly first set 3@ and 4@ to 1 (4@ is the internal marker to return instead of end_thread but 3@ passes that fact in MemoryProofRead as there's one less parameter)).

I don't know what Seemann's method of getting the keys is but this just reads a pool of last pressed letters at 0x969110, this means we don't need to save the code as they enter it but does mean it's limited a bit.

To create a new cheat simply do:

CODE
create_thread @CHEAT_PROCESSOR @CHEAT_CODE_LABEL


Then create your cheat code at :CHEAT_CODE_LABEL

CODE
:CHEAT_CODE_LABEL
0661: set_cheat_text "ACTIVATE"
03E5: show_text_box 'HELP101'
return


The 0661 OpCode sets the text required to be typed to activate the cheat (the processor reads this itself and acts accordingly). The rest of the section is the code run when the cheat is entered and the return is required instead of an end_thread or other thing.

Also note that in your cheat code 7@ 8@ and 9@ are strictly forbidden, using them will break the cheat!

The full cheat processor code (including modified SINGLE version memory access code):

CODE
{ -----------------------------------------------------------------
 Cheat Processor Thread

 To create your own thread simply call this, passing the cheat's
 processing label as a parameter. Then set the first command
 in the cheat to:
 
 0661: set_cheat_text "<string to trigger the cheat>"
 
 (much like a name_thread but for a cheat)
 
 This string can be up to 255 characters but must be entirely
 capital letters, no other characters at all (spaces, numbers etc)
 
 Also, your cheat thread must be ended with a return,
 not an end thread (unless you want it to be fire once only)
----------------------------------------------------------------- }

:CHEAT_PROCESSOR
0085: 7@ = 0@
0@ += 0xA49963 // SCM offset + 3
1@ = 1 // size
2@ = 0 // virtual protect
3@ = 1 // return, don't end
gosub @MemoryProofRead
008B: 8@ = $MEMORY_PROOF_READ
7@ += 4
005A: 7@ += 8@
9@ = 0

{
   5@ should now hold the length of the string and nothing more
   I REALLY wish we could do && and >> in SCM
   maybe this could be converted to ASM for uber ease...
   
   Important variables at this point:
   0@-6@ - reserverd for processing
   7@ - real cheat label
   8@ - string length
   9@ - old first DWORD of character table
   
   Other notes:
   9867536 - decimal offset of some character table
}

:CP_MAIN_LOOP
wait 1000
0@ = 0x969110
1@ = 4
2@ = 0
3@ = 1
gosub @MemoryProofRead
803C: $MEMORY_PROOF_READ != 9@
jf @CP_MAIN_LOOP  
008B: 9@ = $MEMORY_PROOF_READ
1@ = 1
11@ = 0

:CP_STRCMP
802D: 11@ < 8@ // (!>=)
jf @CP_SUCCESS
0@ = 0x969110
005A: 0@ += 11@
gosub @MemoryProofRead
008B: 10@ = $MEMORY_PROOF_READ
0085: 0@ = 7@
0@ += 0xA4995F // SCM offset - 1
0062: 0@ -= 11@
gosub @MemoryProofRead
003C: $MEMORY_PROOF_READ == 10@
jf @CP_FAILURE
11@ += 1
jump @CP_STRCMP

:CP_SUCCESS
gosub 7@
// Display cheat message
03E5: text box "CHEAT1"
0@ = 0x96918C
1@ = 4
2@ = 1
3@ = 0
4@ = 1
// Mark as having cheated
gosub @MemoryProofWrite
0@ = 0xBAA472
gosub @MemoryProofWrite
// Increase cheat count
0@ = 0xB79044
2@ = 0
3@ = 1
gosub @MemoryProofRead
008B: 2@ = $MEMORY_PROOF_READ
2@ += 1
3@ = 0
gosub @MemoryProofWrite

:CP_FAILURE
jump @CP_MAIN_LOOP

{ -----------------------------------------------------------------
 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
5@ = @MemoryWrite
3@ == 1
jf @_____novp

:_____mpvp
6@ = -429863
&0(6@,1i) =  0xA49960
&0(6@,1i) += @_____vpsv
0052: gap 0 virtual_protect_at_address 0@ size 1@ newprotect 4 gap 0 0
0572: run_VPSV 1
gosub 5@
0052: gap 0 virtual_protect_at_address 0@ size 1@ newprotect -1 gap 0 0
0572: run_VPSV 1
&0(6@,1i) = 0
4@ == 0
jf @MemoryReturn
end_thread

:MemoryProofRead
5@ = @MemoryRead
0085: 4@ = 3@
2@ == 0
jf @_____mpvp

:_____novp
gosub 5@
4@ == 0
jf @MemoryReturn
end_thread

:MemoryWrite
5@ = -429864
&0(5@,1i) =  0xA49960
&0(5@,1i) += @_____mwss
0052: gap 0 target_address 0@ size 1@ value 2@ gap 0 0
0A3D: run_MWSS 1
&0(5@,1i) = 0
return

:MemoryRead
5@ = -429864
&0(5@,1i) =  0xA49960
&0(5@,1i) += @_____mrmm
02EC: run_MRMM 1 read_address 0@ size 1@
hex
3D0A008D0100000000 {store_to} 02 $MEMORY_PROOF_READ
end
&0(5@,1i) = 0
return

:MemoryReturn
0051: 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----------------------------- }


And a few pointless little 'cheats':

CODE
create_thread @CHEAT_PROCESSOR @HEALTH_CHEAT
create_thread @CHEAT_PROCESSOR @CAR_CHEAT


CODE
:HEALTH_CHEAT
0661: set_cheat_text "HELLO"
0256:   player $PLAYER_CHAR defined
jf @HEALTH_CHEAT_END
03E5: show_text_box 'HELP101'  // Respect can be earned be passing certain missions, killing rival gangs members, gaining territory and tagging.
08AF: set_actor $PLAYER_ACTOR max_health_to 255
0223: set_actor $PLAYER_ACTOR health_to 255
00A0: store_actor $PLAYER_ACTOR position_to 10@ 11@ 12@
12@ += 100.0
00A1: put_actor $PLAYER_ACTOR at 10@ 11@ 12@

:HEALTH_CHEAT_END
return

// Spawns a car
:CAR_CHEAT
0661: sat_cheat_text "BURN"
0256:   player $PLAYER_CHAR defined
jf @CAR_CHEAT_END
0247: load_model #INFERNUS
038B: load_requested_models

:CAR_CHEAT_LOAD
wait 0
0248:   model #INFERNUS available
jf @CAR_CHEAT_LOAD
00A0: store_actor $PLAYER_ACTOR position_to 10@ 11@ 12@
10@ += 5.0
00A5: 0@ = create_car #INFERNUS 10@ 11@ 12@
0249: release_model #INFERNUS

:CAR_CHEAT_END
return;


IMPORTANT (AGAIN):

DO NOT USE 7@, 8@ OR 9@ IN YOUR CODE!

PS: Seemann, after reading your code I learnt about the missing if statement trick for 1 big statements, cheers.

Edit: added a bit to the success label, they now behave exactly like real cheats, including displaying the text box and making the game complain when you try save smile.gif .

Edit 2: do you think you could modify these so the initial writing installs the ASM over existing unused OpCodes, instead of reusing existing one. Obviously use that initially but then have that write other OpCode pointers to point at the data so writing memory becomes as easy as:

0D1E: write_memory 0x01234567 size 1 data 0@ protect 1
01DF: 0@ = read_memory $SOME_LABEL size 4 protect 0

(0D1E is my example unknown OpCode for obvious reasons smile.gif )

Shouldn't be too hard to fake the correct OpCode code syntax (that's what I was trying to do originally). Plus it shouldn't be too hard to adjust based on the EXE if you know a constant address in both to use and an address different on both to tell which they're using.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#27

Posted 07 March 2007 - 09:44 AM Edited by Seemann, 07 March 2007 - 01:24 PM.

QUOTE (ceedj @ Mar 5 2007, 21:11)
Is this related to what Y_Less discussed sometime ago about making your own cheats, or something totally different?

No, it's nothing about cheats (essentially, I posted my way of the cheats creating on the first post). It's a checking of the keypresses via the memory addresses. I will post this later.

Edit: custom keypresses

QUOTE (Y_Less @ Mar 6 2007, 02:27)
you can also do things like text entry and faking GXTs, although I was looking into faking GXTs in SA the other day for unrelated reasons but I'm not sure how to do it atm, it was easy in VC to track down, SA isn't so easy

Well, I did that. I have a subroutine to change the existing GXT entries in run-time on my own text.
I could post that later too.

QUOTE (Y_Less @ Mar 6 2007, 06:26)
do you think you could modify these so the initial writing installs the ASM over existing unused OpCodes, instead of reusing existing one

If you mean to do that via the mission scripts (like all in this topic), I should say: there IS a possible way to create a fake opcodes handler and add a hundred of the fake opcodes in range of [0A8C .. 0AEF] doing whatever we want (i like the idea with OD1E but sadly thats impossible). Other opcodes could only be changed with the EXE editing, not in run-time.

But please say, what actually we get from this? We will force then to reuse the fake opcode handler activation in order the added opcodes to work every time the game loads. Why not use the Xieons patch which added such opcodes then?

Btw, I did not change any opcodes in MemoryProof, those 0052 and 02EC are NOPs and I used them only as a good way to load the parameters into the memory (the hex reads the OpcodeParameters[32] array which address is well-known).



Also, in addition of using the MemoryProof: the size parameter is not necessary when it is equals to 4. In other words, DWORD size is default. If you get an access without VirtualProtect you may not specify the size parameter as well:

CODE
create_thread @MemoryProofRead address 100

this will read 4 bytes from the mem addy 100.


And dont forget to add a wait command after using 004F in order a new thread to be created immediately.

Y_Less
  • Y_Less

    629

  • Members
  • Joined: 14 Mar 2004

#28

Posted 08 March 2007 - 12:31 AM

I'm not sure if that whole post was directed at me but anyway.

QUOTE (Seemann @ Mar 7 2007, 09:44)
QUOTE (ceedj @  Mar 5 2007, 21:11)
Is this related to what Y_Less discussed sometime ago about making your own cheats, or something totally different?

No, it's nothing about cheats (essentially, I posted my way of the cheats creating on the first post). It's a checking of the keypresses via the memory addresses. I will post this later.

Edit: custom keypresses


You do know that was the point of my whole last post? tounge.gif

QUOTE (Seemann @ Mar 7 2007, 09:44)
QUOTE (Y_Less @  Mar 6 2007, 02:27)
you can also do things like text entry and faking GXTs, although I was looking into faking GXTs in SA the other day for unrelated reasons but I'm not sure how to do it atm, it was easy in VC to track down, SA isn't so easy

Well, I did that. I have a subroutine to change the existing GXT entries in run-time on my own text.
I could post that later too.


Interesting, I've been doing unrelated work on GXT memory recently and although I've been unable to create my own (for some reason changing the pointer to your own block of CRC names and pointers doesn't seem to work, but changing individual string pointers does, though is obviously less flexible.

QUOTE (Seemann @ Mar 7 2007, 09:44)
QUOTE (Y_Less @  Mar 6 2007, 06:26)
do you think you could modify these so the initial writing installs the ASM over existing unused OpCodes, instead of reusing existing one

If you mean to do that via the mission scripts (like all in this topic), I should say: there IS a possible way to create a fake opcodes handler and add a hundred of the fake opcodes in range of [0A8C .. 0AEF] doing whatever we want (i like the idea with OD1E but sadly thats impossible). Other opcodes could only be changed with the EXE editing, not in run-time.

But please say, what actually we get from this? We will force then to reuse the fake opcode handler activation in order the added opcodes to work every time the game loads. Why not use the Xieons patch which added such opcodes then?


CyQs original VC hack thing changed the pointers for two OpCodes, one for read mem, the other for write mem to make them easier to use, plus it's all SCM based, but it's not essential.

QUOTE (Seemann @ Mar 7 2007, 09:44)
Btw, I did not change any opcodes in MemoryProof, those 0052 and 02EC are NOPs and I used them only as a good way to load the parameters into the memory (the hex reads the OpcodeParameters[32] array which address is well-known).



Also, in addition of using the MemoryProof: the size parameter is not necessary when it is equals to 4. In other words, DWORD size is default. If you get an access without VirtualProtect you may not specify the size parameter as well:

CODE
create_thread @MemoryProofRead address 100

this will read 4 bytes from the mem addy 100.


And dont forget to add a wait command after using 004F in order a new thread to be created immediately.


I didn't use create_thread to access memory, I called your functions directly setting the correct locals first (and it does work).

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia
  • Best Tool 2013 "Sanny Builder"

#29

Posted 08 March 2007 - 03:29 AM

QUOTE (Y_Less @ Mar 8 2007, 08:31)
You do know that was the point of my whole last post?

Well, I think yes smile.gif You made the SCM-based cheat processor using modified MemProof. But to be fair that differs from the thing I posted there, isn't it? wink.gif

QUOTE (Y_Less @ Mar 8 2007, 08:31)
CyQs original VC hack thing changed the pointers for two OpCodes, one for read mem, the other for write mem to make them easier to use, plus it's all SCM based, but it's not essential.

That was possible in VS as the opcodes pointers were stored as simple changeable DATA. In SA these pointers are the CODE thing (the part of the exe functions), and they can NOT be changed in run-time, as it is the part of executed code.

QUOTE (Y_Less @ Mar 8 2007, 08:31)
I didn't use create_thread to access memory, I called your functions directly setting the correct locals first (and it does work).

Sure, thatís what I mean when said: you could modify the code to make it simpier and easier in use for your purpose.



Bigun
  • Bigun

    wandering about

  • Members
  • Joined: 10 Jul 2004

#30

Posted 28 April 2007 - 10:23 AM

Hey (bump!).
I've tried to modify the CLV thread to take parameters, so you can easily modify any thread's any local var. Not very surprisingly, it crashes (I'm pretty clueless about anything memory-related confused.gif ). So I need help with this, and any related info I should know, please.
I've narrowed the crash down to the first 008B opcode; I guess it may have to do with that ADMA (&) thing, which I've no idea what I should do with. Took a shot in changing &0 to &3 like the local var, but that wasn't it.

Heres the code, including the test thread:
CODE

:CLV //0@ == thread_name, 1@ == local var num to modify, 2@ value to set to var
03A4: name_thread 'CLV'
0006: 3@ = 67251

:CLV_LOOP
008B: 3@ = &0(3@,1i)
00D6: if
8039:   not  3@ == 0
004D: jump_if_false @CLV_END
0001: wait 0 ms
0085: 4@ = 3@
0016: 4@ /= 8
000E: 4@ -= 1348395
0016: 3@ /= 4
000E: 3@ -= 2696792
00D6: if
05AD:   &0(4@,1s) == 0@s
004D: jump_if_false @CLV_LOOP

//add to 3@ (local var num + 15)
000A: 1@ += 15
005A: 3@ += 1@ // integer values

// 3333 is a new value of the local
//0004: &0(3@,1i) = 3333 //global=int

008A: &0(3@,1i) = 2@ // integer values and handles

:CLV_END
004E: end_thread

// -------------------------------------------

:debug_var_test
thread 'VCHNG'
2@ = 2
create_thread @CLV thread_name 'vchng' local_num 2 set_value 10
wait 2000
054C: use_GXT_table 'POOL'
01E3: text_1number_styled 'NUM' 2@ 5000 ms 1  // ~1~
end_thread




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users