Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


[SOLVED] Help with Doubles

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking
View previous topic :: View next topic  
Author Message
SirCabby
Newbie cheater
Reputation: 0

Joined: 31 Aug 2020
Posts: 15

PostPosted: Mon Aug 31, 2020 1:13 pm    Post subject: [SOLVED] Help with Doubles Reply with quote

Hey all, I'm new to scripting and I've done a lot of reading and trials before posting. Hoping to continue expanding my understanding but starting to hit a bit of a roadblock on my ability.

I'm trying to do a somewhat tricky script. Here's what's going on:

1) There is a value that I've got a pointer to (multi-level pointer) that I've successfully mapped and can find on new games. I need to know when the value of this pointer is at 6.

2) When the above value is 6, I need to store the value of another pointer into a variable. This other pointer's value is a double type.

3) When the value of the first pointer is NOT 6, I want the script to copy the stored value back into the second pointer's location.

4) Regardless of pointer 1's value, I want the script to cycle on a ~10 second delay loop.

This script will be running continuously on a separate thread.

I've found most of this through various threads, but I've struggled to find posts that explain how to save the value of a register for a double, and then safely restore the register later. I know that different types of values should be handled in different registers. I was also wondering if it would be easier to manage all this through a LUA script instead of manipulating the memory directly. All guidance is greatly appreciated.

Here's a look at what I've come up with so far:

Code:

define(address,"PapersPlease.exe"+0037DC18)

[ENABLE]
globalalloc(mycode,4096,address) // because the script is shared and continuously running, used globalalloc
globalalloc(storedTime,8,address)
globalalloc(mustend,4,address) // saw this as a way to manage disabling of a script when created for threads
globalalloc(cleanup,2048,address)
CREATETHREAD(mycode);

mycode:
  push esi // Get Clock Value, this is my attempt at traversing a multi-level pointer
  mov esi,[["PapersPlease.exe"+0037DC18]+78]
  cmp esi,0
  je cleanup
  mov esi,[esi+7C]
  cmp esi,0
  je cleanup
  mov esi,[esi+80]
  cmp esi,0
  je cleanup
  mov esi,[esi+78]
  cmp esi,0
  je cleanup
  mov esi,[esi+68]
  cmp esi,0
  je cleanup
  cmp esi,6 // If Clock is at 6 we want to store the timer (which is at the following multi-level pointer address)
  jne cleanup
        mov [storedTime],(double)5 // I don't think this works correctly for a double.  I've also tried movsd and comisd
    push edi
    mov edi,[["PapersPlease.exe"+0037DC18]+78]
    cmp edi,0
    je cleanup
    mov edi,[edi+7C]
    cmp edi,0
    je cleanup
    mov edi,[edi+A8]
    cmp edi,0
    je cleanup
    mov edi,[edi+38]
    cmp edi,0
    je cleanup
    movsd [storedTime],xmm0 // I think this is wrong for the register type
    pop edi
    jmp cleanup

cleanup:
  pop esi
  push #1000
  call sleep
  cmp [mustend],1
  jne mycode
  ret

mustend:
  dd 0

storedTime:
  dq (double)0

[DISABLE]
mustend:
  dd 1


This code doesn't produce any errors however it also doesn't behave like I expected. My storedTime remains at 0. I am able to move a value directly into the stored variable at the beginning of the script, but it's immediately set back to 0 when the script tries to update it.[/code]


Last edited by SirCabby on Fri Sep 11, 2020 10:47 am; edited 3 times in total
Back to top
View user's profile Send private message
MMM-304
Expert Cheater
Reputation: 0

Joined: 17 Aug 2020
Posts: 170
Location: Milkey Way

PostPosted: Mon Aug 31, 2020 9:19 pm    Post subject: Reply with quote

1st thing about thread is that every register in a new thread is free ie 0.

also when creating a new thread u dont need to allocate everything that u use inside the thread.


Code:

[ENABLE]
globalalloc(mycode,4096)
CREATETHREAD(mycode)

label(cleanup mustend storedTime)
registersymbol(mustend)

// Assuming ur 1st pointer is: [[[[["PapersPlease.exe"+0037DC18]+78]+7C]+80]+78]+68
// Assuming ur 2nd pointer is: [[[["PapersPlease.exe"+0037DC18]+78]+7C]+A8]+38
mycode:
  mov esi,[["PapersPlease.exe"+0037DC18]+78]
  mov esi,[esi+7C]
  mov ebx,esi // to use in next poiner
  mov esi,[esi+80]
  mov esi,[esi+78]
  cmp [esi+68],6  //By the way it will only work is 6 is an integer or hex otherwise declear type!
  jne cleanup
  mov eax,[storedTime+8]
  mov [storedTime],eax  // moves double five into stored time [althoug i see no use of it]
  // NOTE: I have not registered 'storedTime'. register it in case ur planing to use it outside the script

  mov edi,[["PapersPlease.exe"+0037DC18]+78]
  mov edi,[edi+7C]
  mov edi,[edi+A8]   //although u can use 'mov edi,[ebx+A8]'
  cmp [edi+38],0
  je cleanup
  movsd [storedTime],xmm0 //I have no idea what you want to do here
  {if you want to make storedTime 0 then just do this
  mov [storedTime],00}
  jmp cleanup

cleanup:
  push #10000  //1000 = 1sec
  call sleep  //Sleeps for 10 sec
  cmp [mustend],1
  jne mycode
  ret


storedTime:
  dq (double)0
  dq (double)5

mustend:
  dd 0
[DISABLE]
mustend:
  dd 1

unregistersymbol(mustend)


All registers and stacks (both FPU and normal source) in a new thread are empty even base pointer and stack/store pointers
Back to top
View user's profile Send private message
SirCabby
Newbie cheater
Reputation: 0

Joined: 31 Aug 2020
Posts: 15

PostPosted: Tue Sep 01, 2020 9:49 am    Post subject: Reply with quote

Thank you very much for going through this! I'll experiment and absorb what you suggested and reply in more depth when I've figured it out.
Back to top
View user's profile Send private message
SirCabby
Newbie cheater
Reputation: 0

Joined: 31 Aug 2020
Posts: 15

PostPosted: Tue Sep 08, 2020 3:40 pm    Post subject: Reply with quote

Ok I had time to dig into this. Thanks again for the feedback and help.

I have tweaked it a bit based on your advice, and put response comments as [SC] from me:

Code:

[ENABLE]
globalalloc(mycode,4096)
CREATETHREAD(mycode)

label(cleanup mustend storedTime)
registersymbol(mustend)
registersymbol(storedTime) // [SC] Yes I did want to inspect this outside to make sure it was working, so I went ahead and registered it

// Assuming ur 1st pointer is: [[[[["PapersPlease.exe"+0037DC18]+78]+7C]+80]+78]+68
// Assuming ur 2nd pointer is: [[[["PapersPlease.exe"+0037DC18]+78]+7C]+A8]+38
// [SC] The above assumptions are correct
mycode:
  mov esi,[["PapersPlease.exe"+0037DC18]+78]
  mov esi,[esi+7C]
  mov esi,[esi+80]
  mov esi,[esi+78]
  cmp [esi+68],(Double)6  //By the way it will only work is 6 is an integer or hex otherwise declare type!
                                       // [SC] this is a double type, do doubles work in the 'esi' register type?  This is why I was trying to use 'xmm' type
  jne cleanup
  // moves double five into stored time [although i see no use of it]
  // [SC] this was just me debugging, seeing if I could get a 5 into the stored variable, so I removed this line

  // [SC] if the above pointer was at (Double)6, then we want to store the value of the second pointer into our storedTime variable
  mov edi,[["PapersPlease.exe"+0037DC18]+78]
  mov edi,[edi+7C]
  mov edi,[edi+A8]
  mov edi,[edi+38]
  cmp edi,0
  je cleanup
  mov [storedTime],edi // [SC] this is where I struggled.  I want the value of the double stored in the second pointer to be saved into storedTime.
                                  // [SC] Later on, I'll want to push the value of storedTime back into the value of this pointer (not yet coded)
  jmp cleanup

cleanup:
  push #10000  //1000 = 1sec
  call sleep  //Sleeps for 10 sec
  cmp [mustend],1
  jne mycode
  ret


storedTime:
  dq (double)0
  dq (double)5 // [SC] I didn't need the value of 5 saved from earlier in the script, is this needed?

mustend:
  dd 0
[DISABLE]
mustend:
  dd 1

unregistersymbol(mustend)
unregistersymbol(storedTime)


Basically the logic is meant to be something like this pseudo code:
Code:

if clock == (double) 6 {
    // save the value of time
    storedTime = (double) pLevelTime
} else {
    pLevelTime = (double) storedTime
}
Back to top
View user's profile Send private message
SirCabby
Newbie cheater
Reputation: 0

Joined: 31 Aug 2020
Posts: 15

PostPosted: Wed Sep 09, 2020 3:43 pm    Post subject: Reply with quote

I guess at this point most of my question is really about working with doubles Smile

I'll update the subject to reflect this.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4711

PostPosted: Thu Sep 10, 2020 6:51 pm    Post subject: Reply with quote

This would be far easier with Lua. Anyway...
  • The third parameter to alloc isn't needed for 32-bit processes.
  • You don't need that extra alloc for cleanup.
  • The variables should be aligned. This is fine in the first post (everything has its own alloc), but labels should have an align pseudoinstruction before them.
  • Why mix static and dynamic resolution of pointer paths? If only the first node doesn't change and can't be null, then I guess it's fine.
  • {$try} / {$except} macros are a better alternative to what you're doing with null pointers.
  • You didn't specify the value type of the first value in your first post, but I'm assuming it's a double.
  • I really don't think you need to compare if the value is 0. (this goes against your third requirement)
  • e** registers contain 4 bytes. Doubles take up 8 bytes.
  • cmp is used for integer data, not floating point data.
  • In x86 (32-bit), there is no way to move an 8 byte immediate in a single instruction.
  • movsd can't move immediates, and comisd is used for comparing floating point numbers.
  • You're redoing work by traversing part of the pointer path twice.
  • Volatile registers can be used without being backed up with push/pop (this isn't a code injection)
  • I don't know where you got xmm0 from. Throwing stuff at a wall won't help you much here.
  • Passing 1000 to sleep is 1 second; 10000 would be 10 seconds.
Something like this:
Code:
[ENABLE]
globalalloc(mycode,4096)
CREATETHREAD(mycode)

label(save wait loop noerror cmpvalue mustend storedTime)
registersymbol(mustend)
registersymbol(storedTime)


mycode:
{$try}
  movsd xmm1,[cmpvalue]
  jmp short loop
save:
  movsd xmm0,[edx+38]
  movsd [stored_value],xmm0
  mov eax,[mustend]
  test eax,eax
  jne noerror
wait:
  push #10000
  call sleep
loop:
  mov ecx,["PapersPlease.exe"+0037DC18]
  mov ecx,[ecx+78]
  mov ecx,[ecx+7C]
  mov edx,ecx
  mov ecx,[ecx+80]
  mov ecx,[ecx+78]
  movsd xmm0,[ecx+68]    // [[[[["PapersPlease.exe"+0037DC18]+78]+7C]+80]+78]+68
  mov edx,[edx+A8]       // [[[["PapersPlease.exe"+0037DC18]+78]+7C]+A8]+38
  comisd xmm0,xmm1
  je short save
// load:
  movsd xmm2,[stored_value]
  movsd [edx+38],xmm2
  mov eax,[mustend]
  test eax,eax
  je short wait
  jmp noerror

{$except}
  push 1
  call ExitThread

noerror:
  xor eax,eax
  ret

  align 8 CC
cmpvalue:
  dq (double)6
storedTime:
  dq (double)0
mustend:
  dd 0

[DISABLE]
mustend:
  dd 1

unregistersymbol(mustend)
unregistersymbol(storedTime)

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
SirCabby
Newbie cheater
Reputation: 0

Joined: 31 Aug 2020
Posts: 15

PostPosted: Fri Sep 11, 2020 10:46 am    Post subject: Reply with quote

I really appreciate the time you took to point out all the flaws in my example. It was very constructive and I learned a lot from seeing your example.

I was able to get this working based on your help!

I did have a few follow up questions though if you don't mind.

1) Is there a place for me to see what macros are available and how they work from your {$try} / {$except}? Are these Lua?

2) Since you mentioned it'd be a lot easier with Lua, I'd like to learn more... I found this link to get started but if you have anything better for someone at ground level knowledge can you please share? <snip-because-I-can't-post-urls-yet>cheatengine/index.php?title=Tutorials:Lua:Basics

3) When you say "movsd can't move immediates", from what I've learned immediates are constants in ASM? So I can't do something like "movsd xmm0,(double)6" because the 6 is an immediate? So by your storing it in a variable and then loading it into another register you're able to leverage movsd correct?

4) When you say "I don't know where you got xmm0 from. Throwing stuff at a wall won't help you much here.", I want to make sure I understand what you mean. I read up on register types and learned that xmm registers are used for floating point / double values... I thought I needed those. And I see you used them in your example as well. What was I doing wrong here?

Thanks again, really appreciate your help.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4711

PostPosted: Fri Sep 11, 2020 11:41 am    Post subject: Reply with quote

1) Not really. I guess you can read the source code to see how it works, but that's not exactly easy.
I learned of this in particular by reading stuff on the forums (e.g. this post)

2) First, learn Lua. CE uses Lua 5.3 (Lua 5.4 is out now, but it's not worth switching to it). The best resource I know of is the book "Programming in Lua" (fourth edition), but there are plenty of free resources online.
Then, learn the stuff CE adds to Lua. I don't know of any easy and fast way of doing this. The file celua.txt is good but long. Browsing through the forums and the wiki can be faster but also might give some bad information.

3) Yes, that's exactly correct. If you look at an instruction set reference (e.g. this one), there are three opcodes associated with the movsd mnemonic:
Code:
MOVSD xmm1, xmm2
MOVSD xmm1, m64
MOVSD xmm1/m64, xmm2
...
Ignore all the other AVX stuff on that page.
The first moves a double from an xmm register to an xmm register, the second moves a double from a memory location (something in square brackets) to an xmm register, and the third moves a double in an xmm register into either an xmm register or a memory location.
There is no instruction that lets you move an imm64 (64-bit immediate value) into an xmm register.

4) In the first post, you're reading from xmm0 but never writing anything to it. I don't know what kind of magic you thought was in xmm0- that would never work. The difference in my code is that I write something to xmm0 before reading from it:
Code:
movsd xmm0,[edx+38]        // write to xmm0
movsd [stored_value],xmm0  // read from xmm0

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
SirCabby
Newbie cheater
Reputation: 0

Joined: 31 Aug 2020
Posts: 15

PostPosted: Fri Sep 11, 2020 12:23 pm    Post subject: Reply with quote

Awesome, thanks for the follow up. I don't think my account is old enough to award rep yet but you've earned it either way.

Looking forward to growing more and contributing new tables.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites