Page 1 of 2

Script search

PostPosted: Sat Sep 16, 2006 12:33 pm
by kai
I was wondering, how would I right a script search first, to see if a particular address were a certain value, say 154, then to look at the last charater of the hex address, and if it is not a 0, a C, a 4, or an 8, and to reject the address if it's not.

The reason I am doing this is because, right now I am trying to hack a game where there are a lot of units running around and there is a certain pattern to the addresses of their health and exp when they spawn in, as it is it is very tedious to search for the values over and over again as they change every time they level up, also, they each will have random attributes that will effect their max health or max exp before leveling so if I tried creating a template, I would have to put so many possibilties it would take forever! With their health addresses, they seem to be random, but they always have an ending hex character of a 0 or an 8, with exp, C or 4, I'm trying to do this because, on average, the search will yield tons of addresses when I search for a value in this particular game and with how small of a number their health usually is, they will die within 2 searches and it will have been in vain, I have looked at the found addresses and a lot of the useless values or always non 0,C,4, 0r 8 at the end, so if I was able to right a script search for this it would be very helpful

PostPosted: Sun Sep 17, 2006 1:49 pm
by AlaXul
Another possible way you could accomplish your goal would be use a program such as Olly Debugger or the one that comes with MHS. Simply find the DMA Value that you wish to change, like 154, then tell the assembler to break on access. This will list the static memory code that access the value and uses it in it's procedure. There you can simply modify the logic and set a static value to whatever you wish. This memory area would always be the same on every computer. You would never have to search for it again.

PostPosted: Sun Sep 17, 2006 1:53 pm
by L. Spiro
In fact the phenomena you are observing has to do with alignment.
Alignment forces data values to be on addresses that are multiples of 1, 2, 4, or 8.

Your game either uses a 4-byte alignment or the character structures are simply created in such a way that your health just happens to always be on a 4th-byte address (and this is not a coincidence, and will always be the case).


Regardless of the alignment, when the game needs to allocate some memory, Windows® returns an address that is divisible by 4. This is because data aligned on 4-byte boundaries is faster to access/use.
The address Windows® gives to your game will be used by the game as the base of your player class.

From there, your health will always be the same distance away.
So if your health is 0x180 bytes from the base, it will then always be on an address divisible by 4 (?0, ?4, ?8, or ?C).



Because this is the most common way for data to be stored in games (as it improves speed), I have included an “Aligned” option in every search type (some may call it “Search Every Four Bytes”).


So luckily for you, you do not need to write any scripts just yet (however you are correct that a script can help you solve this problem).
In the Data-Type Search, just check Aligned.

Aligned searches should almost always be used in Data-Type Searches, Group Searches, and Script Searches.
Pointer Search always uses Aligned, so it is not an option.

Using a non-aligned search should only be done if an aligned search fails.


Also note that once you use Aligned searches, your searches will be 4 times faster (more good news for you).



If this does not solve the problem then I have misunderstood exactly what the problem is, so post back with more information and I will write a script function to handle it.


L. Spiro

PostPosted: Mon Sep 18, 2006 1:25 am
by kai
This is what I mean, one guy's health is stored at 012E4720 with his experience at 01DE472C, the next guy might be 016AF9378 with 016AF9384 as is exp, with yet another at 0153BC20 with exp at 0I53BC2C, I have searched many times and found out that health is always at an address ending with a zero or an 8 and exp is always spaced 12 over from that. there are about 20 characters that I may be playing on the game with their addresses scattered everywhere through ram. Also there are anywhere from 15 to 35 enemy units on that map that get values of their oown. In addition to this, every time I start a new scenario or have one of them level up te address changes. They are on every fourth byte, i was just wondering, is there a way to only search 0's, or 8's for health or to be able to search for two values at once almost and only return addresses that meet criteria such as: search for value 105, then search for a value 35, twelve places after that.
I already search only every fourth byte with the 4 byte search if that's what you mean, if I were to search with a one or two byte then I would get results in the hundred thousands.

PostPosted: Mon Sep 18, 2006 10:45 am
by L. Spiro
Code: Select all
INT g_iCusHealth = 100;
INT g_iCusExp = 13000;
INT CustomSearch( LPVOID lpvAddress, LPVOID lpvBuffer, INT iSize ) {
   // Check that lpvAddress is divisible by 8 (change to anything
   //   you need).
   if ( (DWORD)lpvAddress % 0x8 == 0 ) {
      // Create a simple structure that maps the player class.
      // We only know the health and experience for now, and
      //   we know that the experience is 12 (0xC) bytes after
      //   the health.  Use meaningless values to buffer the
      //   space between.
      struct CUSDATA {
         INT iHealth;         // Offset 0x0.
         INT iBuffer0x04;      // Offset 0x4.
         INT iBuffer0x08;      // Offset 0x8.
         INT iExperience;      // Offset 0xC.
      };
      // If the size of the buffer is not large enough to hold
      //   this class, then of course it is not a valid value
      //   (note that if the size of the search data is specified
      //   before the search, iSize will always be large enough
      //   and this check can be removed to improve speed).
      if ( iSize >= sizeof( CUSDATA ) ) {
         // Cast the buffer to our data type.
         CUSDATA * pcdData = lpvBuffer;
         
         // Check the values we know.
         // Change g_iCusHealth and g_iCusExp to whatever values
         //   you like.
         if ( pcdData->iHealth == g_iCusHealth ) {
            if ( pcdData->iExperience == g_iCusExp ) {
               // Return the size of the structure to ensure
               //   compatibility with both types of Script
               //   Searches.
               return sizeof( CUSDATA );
            }
         }
      }
   }
   return 0;
}
// Decode the returned values in the result list.
VOID CustomDecoder( LPVOID lpvAddress, LPVOID lpvBuffer, DWORD dwLength, CHAR * pcReturn, INT iMaxLength ) {
   // Cast the buffer to our data type.
   CUSDATA * pcdData = lpvBuffer;
   // Print health and experience (and more if desired).
   SNPrintF( pcReturn, iMaxLength, "Health: %d.  Exp: %d", pcdData->iHealth, pcdData->iExperience );
}

// Create a function to wrap all of this together.
VOID CustomSearchSetup( INT * piDataSize, INT * piAlign, CHAR ** ppcCallback, CHAR ** ppcDecoder ) {
    // Size of the data is constant (and this allows us to remove the
    //   size check in CustomSearch() to improve speed).
    (*piDataSize) = sizeof( CUSDATA );

    // We can specify an 8-byte alignment here which eliminates the need
    //   for the “if ( (DWORD)lpvAddress % 0x8 == 0 ) {” code in
   //   CustomSearch().
    (*piAlign) = 8;

    // And the rest.
    (*ppcCallback) = "CustomSearch";
    (*ppcDecoder) = "CustomDecoder";
}



Now in the Script Search dialog, all you need to do is set it to Programmatic and specify CustomSearchSetup().
Modify g_iCusHealth and g_iCusExp as you need.
I set these as globals that are not constants because it allows you to change them on-the-fly programmatically.

In the future I will have dialogs boxes that will allow you to prompt the user for input.
You would ask the user for the health and experience values, change g_iCusHealth and g_iCusExp accordingly, and search.

Unfortunately those dialogs aren’t added yet, so there aren’t any good ways to change g_iCusHealth and g_iCusExp except by actually modifying the script and recompiling.



Modify as you please to suit your needs.


L. Spiro

PostPosted: Mon Sep 18, 2006 11:16 am
by kai
Ahh, thank you very much! :D

So the only things I need to modify are the g_iCusHealth and g_CusEXP values?

Just checking so I don't mess anything up.

PostPosted: Mon Sep 18, 2006 11:52 am
by L. Spiro
Probably.

If you want to change the alignment (go back to 4 bytes) you have to change both of the 8’s to 4’s (“if ( (DWORD)lpvAddress % 0x8 == 0 ) {” and “(*piAlign) = 8;”).

You could make it easier to change alignments by changing them to a single global and then modify the global.
That is, if you ever find that the health lands on addresses other than ?0 and ?8.





The script is copy/paste and tested to work as long as you are searching for the correct health and experience.


As you find more values in your player structure, you can modify CUSDATA accordingly.


L. Spiro

PostPosted: Mon Sep 18, 2006 12:19 pm
by kai
hmm... how do I add the search to the list on the script search dialog box? I put the script in the script editor, compiled it, saved it as custom-search in the memhack directory, then added it to the script list.
When I go to the dialog box even after reopening the Memory hacking software it neither shows up in the list for searching in the progammatic nor does it work when I type the name of the file in the box either with the extension or not.
Can you tell me what I am doing wrong? :oops:

PostPosted: Mon Sep 18, 2006 12:25 pm
by L. Spiro
In the help file:
Script Search


Enter “CustomSearchSetup” into the Setup Function, not the name of the script file.


L. Spiro

PostPosted: Mon Sep 18, 2006 12:37 pm
by kai
it keeps giving me this error in the compiling process:

ERROR: Pos: 23 Unable to declare variable “g_iCusHealth” (identifier already declared). File: (null)

And CustomSearchSetup keeps giving me an error.

PostPosted: Mon Sep 18, 2006 2:20 pm
by L. Spiro
Did you post the code into two locations in your script set?


“Unable to declare variable “g_iCusHealth” (identifier already declared).” means just that.
You already declared g_iCusHealth somewhere else.

If this is really the case, you can just remove that line or comment it out.

Code: Select all
//INT g_iCusHealth = 100;
//INT g_iCusExp = 13000;




“File: (null)” is normal; it indicates an error outside of a function before any functions have been compiled (g_iCusHealth happens to be in front of all functions).



And CustomSearchSetup keeps giving me an error.

Everything indicates you posted the code into two locations in the script.


L. Spiro

PostPosted: Tue Sep 19, 2006 7:41 am
by kai
Well, the only other place I can find the variable g_iHealth is:


Code: Select all
    if ( pcdData->iHealth == g_iCusHealth ) {
            if ( pcdData->iExperience == g_iCusExp ) {
               // Return the size of the structure to ensure
               //   compatibility with both types of Script
               //   Searches.
               return sizeof( CUSDATA );


And I only pasted once, so there is really no reason why it should be saying that from my point of view.

PostPosted: Tue Sep 19, 2006 3:03 pm
by L. Spiro
You can mail your Memory Hacking Software directory to yogurtemperor (ach) gmail (doch) com.

Then I can see exactly what the problem is.


L. Spiro

PostPosted: Wed Sep 20, 2006 9:40 am
by kai
Ok here it comes.

PostPosted: Thu Sep 21, 2006 8:31 am
by kai
Did you get the email?

EDIT: Your Gmail won't allow me to send the attachment apparently, I got a message back from the email service that said that it wasn't okay to send the attachment, it probably detected the word hack or something and flipped out.