 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
Dr.Disrespect Grandmaster Cheater
Reputation: 3
Joined: 17 Feb 2016 Posts: 526
|
Posted: Sun Mar 27, 2016 9:33 am Post subject: Random number within a certain range in ASM. |
|
|
I still cannot figure out how to get a random number within a certain range in ASM after one day of research.
ParkourPenguig taught me about this, but I am too stupid to understand it:
Code: |
----------This code is being used to get a random number from 1-10
call rand
xor edx,edx
mov ecx,#10
div ecx
test edx,edx
// do something based on whether edx == 0
My question is: if edx equals 0, what does that mean? What is the value of the randomly generated number? Is it stored in ah?
|
I also did some research on google and found the following snippet:
Code: |
-------This code is being used to generate a random number from 0-9
mov ax, dx
xor dx, dx
mov cx, 10
div cx ; here dx contains the remainder of the division - from 0 to 9
add dl, '0' ; to ascii from '0' to '9'
|
I find it difficult to understand both of them. Say if I want to generate a number from 0-59, how should I do it? Addition to that, all the registers are used in game, so where should I push them first and pop them later?
|
|
Back to top |
|
 |
Zanzer I post too much
Reputation: 126
Joined: 09 Jun 2013 Posts: 3278
|
Posted: Sun Mar 27, 2016 10:34 am Post subject: |
|
|
Take yourself back to elementary school and remember that little concept known as a remainder.
When you have any number in the known or unknown universe and you divide it by another number, it may not divide evenly.
Well, when you divide a number by 10, your possible remainders are 0-9.
When you divide a number by 6, your possible remainders are 0-5.
When you divide a number by 10000, your possible remainders are 0-9999.
There seems to be a pattern here! You can clearly see now how you get a random number between 0 and any other number.
But now lets say you want a number between 100 and 200, inclusive.
So if you divide by 101, you'll get back a value of 0-100. So now all I need to do is add 100 to the result!
Just an observation about your posts... you should probably ask these types of follow-up questions within the same thread.
That way if someone in the future has a similar question, they can do a search and easily find the whole chain of details discussing the topic.
|
|
Back to top |
|
 |
++METHOS I post too much
Reputation: 92
Joined: 29 Oct 2010 Posts: 4197
|
Posted: Sun Mar 27, 2016 10:42 am Post subject: |
|
|
Yes, it's just a 'fancy' way to do simple math.
|
|
Back to top |
|
 |
Dr.Disrespect Grandmaster Cheater
Reputation: 3
Joined: 17 Feb 2016 Posts: 526
|
Posted: Sun Mar 27, 2016 10:42 am Post subject: |
|
|
Zanzer wrote: | Take yourself back to elementary school and remember that little concept known as a remainder.
When you have any number in the known or unknown universe and you divide it by another number, it may not divide evenly.
Well, when you divide a number by 10, your possible remainders are 0-9.
When you divide a number by 6, your possible remainders are 0-5.
When you divide a number by 10000, your possible remainders are 0-9999.
There seems to be a pattern here! You can clearly see now how you get a random number between 0 and any other number.
But now lets say you want a number between 100 and 200, inclusive.
So if you divide by 101, you'll get back a value of 0-100. So now all I need to do is add 100 to the result!
Just an observation about your posts... you should probably ask these types of follow-up questions within the same thread.
That way if someone in the future has a similar question, they can do a search and easily find the whole chain of details discussing the topic. |
Thank you Zaner. I think I need to know how div works in ASM, I will google that. And, yeah, my math sucks...
Edit:
@ ++METHOS, thank you.
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4657
|
Posted: Sun Mar 27, 2016 11:45 am Post subject: |
|
|
From this reference:
Quote: | DIV r/m32 - Unsigned divide EDX:EAX by r/m32, with result stored in EAX = Quotient, EDX = Remainder. |
r/m32 means that this instruction uses a 32-bit register or memory location for this operand. EDX:EAX means that you're using a 64-bit value with the lower 32-bits in EAX and the upper 32-bits in EDX. You should know what a quotient and remainder is from primary school.
Code: | call rand // puts a random number into EAX
xor edx,edx // makes EDX 0 to make sure you're only dividing EAX by a number
mov ecx,#10 // moves 10 (decimal) into ECX.
div ecx // divides EDX:EAX (the random number) by ECX (10)
// remainder is stored in EDX, which is now effectively a pseudo-random value between 0 and 9 inclusive.
// do whatever you want to with it from here. |
If you're ever in doubt of whether or not a register was modified, you can use pushad at the beginning of the script and popad at the end of your script to save and restore all general-purpose registers. Just make sure the stack is balanced between them.
I've attached an example of how I'd go about doing this.
Description: |
Spits out pseudorandom numbers. Attach to any 32-bit process with msvcrt.dll loaded. |
|
 Download |
Filename: |
RandomNumberGenerator.CT |
Filesize: |
2.82 KB |
Downloaded: |
692 Time(s) |
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
Dr.Disrespect Grandmaster Cheater
Reputation: 3
Joined: 17 Feb 2016 Posts: 526
|
Posted: Sun Mar 27, 2016 12:31 pm Post subject: |
|
|
ParkourPenguin wrote: | From this reference:
Quote: | DIV r/m32 - Unsigned divide EDX:EAX by r/m32, with result stored in EAX = Quotient, EDX = Remainder. |
r/m32 means that this instruction uses a 32-bit register or memory location for this operand. EDX:EAX means that you're using a 64-bit value with the lower 32-bits in EAX and the upper 32-bits in EDX. You should know what a quotient and remainder is from primary school.
Code: | call rand // puts a random number into EAX
xor edx,edx // makes EDX 0 to make sure you're only dividing EAX by a number
mov ecx,#10 // moves 10 (decimal) into ECX.
div ecx // divides EDX:EAX (the random number) by ECX (10)
// remainder is stored in EDX, which is now effectively a pseudo-random value between 0 and 9 inclusive.
// do whatever you want to with it from here. |
If you're ever in doubt of whether or not a register was modified, you can use pushad at the beginning of the script and popad at the end of your script to save and restore all general-purpose registers. Just make sure the stack is balanced between them.
I've attached an example of how I'd go about doing this. |
Thank you so much for the explanation and the excellent example, ParkourPenguin. Can I do something to pay you back? You really helped me a lot.
Update:
I have studied your example. Do you mind if I ask couple questions about it?
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4657
|
Posted: Sun Mar 27, 2016 1:40 pm Post subject: |
|
|
Of course you can ask questions about it. There are a few other miscellaneous things in there (i.e. threads, calls to Sleep and VirtualFree), but I'll answer any questions you have.
And don't worry about paying me back; I like messing around with this kind of stuff.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
Dr.Disrespect Grandmaster Cheater
Reputation: 3
Joined: 17 Feb 2016 Posts: 526
|
Posted: Sun Mar 27, 2016 3:24 pm Post subject: |
|
|
ParkourPenguin wrote: | Of course you can ask questions about it. There are a few other miscellaneous things in there (i.e. threads, calls to Sleep and VirtualFree), but I'll answer any questions you have.
And don't worry about paying me back; I like messing around with this kind of stuff. |
ParkourPenguin, you are really a nice guy and it's great to have you here.
My questions are:
1.
Code: |
mov ebx,[lowerBound] //10
mov ecx,[upperBound] //20
cmp ecx,ebx
cmovl ecx,ebx
inc ecx
sub ecx,ebx
idiv ecx
add edx,ebx
mov [myRandomNumber],edx
|
I think this part is the key part and I try to understand it. For example, if ecx == 20 and ebx ==10, after "inc ecx", ecx == 21, then after "sub ecx,ebx", ecx ==11. Then after "idiv ecx", edx:eax is divided by ecx, which is 11. The value in EAX is the random number and EDX is 0, so what is actually happening is EAX/11? Am I right up to this point? So, EDX keeps the remainder of EAX/11, which can be 0-10. Do I understand correctly?
2. Why do I need "[shouldExit]"? I don't see any change to the value of it, it's always 0 and the timer keeps running without being stopped. Update: Oh, i kinda get the idea, it is a global variable used to stop the timer if needed.
3. The "free memory ,return" part. I don't know why you did this:
Code: |
pop eax
push 8000
push 0
push newmem
push eax
jmp kernel32.VirtualFree
db CC CC CC CC CC CC
|
But I think I just need to memorize it as a format to make it work.
4. You push [interval] before calling "kernel32.sleep", I suppose this function takes one argument, which is the interval of the timer, right?
5. I only know how to use the template in "Code Injection", so how do I implement this to "Code Injection"? "shouldExit" kinda confuses me.
6. If I want to generate 2 numbers, how to use an array to do that? Like this?
Code: |
myRandomNumber:
dd 0 0 0 0 <-------------------- Does this define an array?
|
If an array is correctly defined, how to traverse it in ASM? The number of registers are so limited..... I wish there were 16 registers that I can use...
Thanks in advance. Sorry, it's more than a couple of questions.
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 150
Joined: 06 Jul 2014 Posts: 4657
|
Posted: Sun Mar 27, 2016 5:44 pm Post subject: |
|
|
1. When you divide an integer (dividend) by another integer (divisor) and try to get the remainder, the maximum value of the remainder is one less than the divisor. This is because that if it was equal to the divisor, then the divisor would perfectly go into the dividend without any remainder- aka, the remainder resets to 0.
So, in order to make the possible range of values inclusive, you need to add 1 to the maximum value it could return.
Analyzing the code gives:
Code: | mov ebx,[lowerBound] // moves data into registers to work with it
mov ecx,[upperBound] // ^
cmp ecx,ebx // tests whether or not the bounds make sense
cmovl ecx,ebx // corrects upper bound register if needed
inc ecx // allows [upperBound] to be a possible result
sub ecx,ebx // gets the range of values it should produce
idiv ecx // signed division to get the remainder
add edx,ebx // adds lower bound to result
mov [myRandomNumber],edx // edx is now between [upperBound] and [lowerBound] inclusive. Do whatever you want with it. |
2,3,4: This is all the miscellaneous stuff. I'm not hooking any particular instruction, so the process CE is attached to won't ever run this code on it's own. That is, unless I tell it to by creating a thread. Most of the code you referenced in 2-4 deals with thread management; I'll explain it all.
Code: | createthread(newmem) | Tells the process to make a new thread that starts executing the asm at the address newmem.
Code: | push [interval]
call kernel32.Sleep | Calling kernel32.Sleep will suspend the current thread for the time (in milliseconds) you pass to it. This not only lets us see the random number it makes before it makes another one, but it also prevents this thread from hogging all the CPU.
Code: | cmp [shouldExit],0
je asTimeGoesBy | While this isn't important while the script is running, it is very important when you try to disable the script. If you didn't have that at all and instead just had an infinite loop, then the thread would continue running this asm indefinitely (even if you disable the script).
There are a few ways to tell a thread to stop running, but one of the easiest is to execute a ret instruction. This instruction pops a 4-byte value from the stack and jumps to it. In the case of a thread, it returns to a place that destroys the thread. Just make sure the stack is at where it started when you return.
However, if you just simply return, then the memory is still there. When you enable the script again, it will allocate new memory. This is known as memory leak, and it's bad.
One simple solution you may think of is to put dealloc(newmem) in the [DISABLE] section; however, this will crash the process. Remember that the thread is still running when the [DISABLE] section is run. If CE frees that memory remotely and the thread is still using it, then that will result in a segfault, crashing the process. This leads us to...
Code: | pop eax
push 8000
push 0
push newmem
push eax
jmp kernel32.VirtualFree | kernel32.VirtualFree is a subroutine that's meant to be called.
When you call an address, the call first pushes the address of the instruction after it onto the stack, then jumps to the address given to it. It does this because it expects to eventually reach a ret instruction. However, as you can see, I'm jumping to it instead. This means that when it gets to the ret instruction, it won't return to the memory it just freed; instead, it returns to the last thing I pushed onto the stack. In this case, due to the pop eax / push eax, that's the return address of this thread. So, when kernerl32.VirtualFree returns, it will destroy the thread.
All the other things I pushed are specified in this documentation: 8000 is the dwFreeType (MEM_RELEASE), 0 is the dwSize (must be 0 for MEM_RELEASE), and newmem is the address where you want to free memory.
Also, that last line (db CC CC CC...) is just spacing so I know where exactly the code ends and where my variables begin. The byte CC is technically the interrupt 3 instruction (used by debuggers; also used for padding). Putting these here also helps me align the data after it on a 4-byte boundary, but that's usually only for my own tranquility.
5: All you need to understand is what this does:
Code: | mov ebx,#10
mov ecx,#20
sub ecx,ebx
div ecx // note: use idiv for signed values.
add edx,ebx
// edx is now between 10 and 19 (inclusive). | Do whatever you want with edx from that point.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
Dr.Disrespect Grandmaster Cheater
Reputation: 3
Joined: 17 Feb 2016 Posts: 526
|
Posted: Sun Mar 27, 2016 6:58 pm Post subject: |
|
|
ParkourPenguin wrote: | 1. When you divide an integer (dividend) by another integer (divisor) and try to get the remainder, the maximum value of the remainder is one less than the divisor. This is because that if it was equal to the divisor, then the divisor would perfectly go into the dividend without any remainder- aka, the remainder resets to 0.
So, in order to make the possible range of values inclusive, you need to add 1 to the maximum value it could return.
Analyzing the code gives:
Code: | mov ebx,[lowerBound] // moves data into registers to work with it
mov ecx,[upperBound] // ^
cmp ecx,ebx // tests whether or not the bounds make sense
cmovl ecx,ebx // corrects upper bound register if needed
inc ecx // allows [upperBound] to be a possible result
sub ecx,ebx // gets the range of values it should produce
idiv ecx // signed division to get the remainder
add edx,ebx // adds lower bound to result
mov [myRandomNumber],edx // edx is now between [upperBound] and [lowerBound] inclusive. Do whatever you want with it. |
2,3,4: This is all the miscellaneous stuff. I'm not hooking any particular instruction, so the process CE is attached to won't ever run this code on it's own. That is, unless I tell it to by creating a thread. Most of the code you referenced in 2-4 deals with thread management; I'll explain it all.
Code: | createthread(newmem) | Tells the process to make a new thread that starts executing the asm at the address newmem.
Code: | push [interval]
call kernel32.Sleep | Calling kernel32.Sleep will suspend the current thread for the time (in milliseconds) you pass to it. This not only lets us see the random number it makes before it makes another one, but it also prevents this thread from hogging all the CPU.
Code: | cmp [shouldExit],0
je asTimeGoesBy | While this isn't important while the script is running, it is very important when you try to disable the script. If you didn't have that at all and instead just had an infinite loop, then the thread would continue running this asm indefinitely (even if you disable the script).
There are a few ways to tell a thread to stop running, but one of the easiest is to execute a ret instruction. This instruction pops a 4-byte value from the stack and jumps to it. In the case of a thread, it returns to a place that destroys the thread. Just make sure the stack is at where it started when you return.
However, if you just simply return, then the memory is still there. When you enable the script again, it will allocate new memory. This is known as memory leak, and it's bad.
One simple solution you may think of is to put dealloc(newmem) in the [DISABLE] section; however, this will crash the process. Remember that the thread is still running when the [DISABLE] section is run. If CE frees that memory remotely and the thread is still using it, then that will result in a segfault, crashing the process. This leads us to...
Code: | pop eax
push 8000
push 0
push newmem
push eax
jmp kernel32.VirtualFree | kernel32.VirtualFree is a subroutine that's meant to be called.
When you call an address, the call first pushes the address of the instruction after it onto the stack, then jumps to the address given to it. It does this because it expects to eventually reach a ret instruction. However, as you can see, I'm jumping to it instead. This means that when it gets to the ret instruction, it won't return to the memory it just freed; instead, it returns to the last thing I pushed onto the stack. In this case, due to the pop eax / push eax, that's the return address of this thread. So, when kernerl32.VirtualFree returns, it will destroy the thread.
All the other things I pushed are specified in this documentation: 8000 is the dwFreeType (MEM_RELEASE), 0 is the dwSize (must be 0 for MEM_RELEASE), and newmem is the address where you want to free memory.
Also, that last line (db CC CC CC...) is just spacing so I know where exactly the code ends and where my variables begin. The byte CC is technically the interrupt 3 instruction (used by debuggers; also used for padding). Putting these here also helps me align the data after it on a 4-byte boundary, but that's usually only for my own tranquility.
5: All you need to understand is what this does:
Code: | mov ebx,#10
mov ecx,#20
sub ecx,ebx
div ecx // note: use idiv for signed values.
add edx,ebx
// edx is now between 10 and 19 (inclusive). | Do whatever you want with edx from that point. |
Thank you ParkourPenguin, I do understand the crucial part of the code now and generating a random number in ASM is not a problem to me anymore, thanks to you and all the people who have helped me.
Now I am working on another issue: array. I will post a CT after I finish it. If you have time, you are more than welcomed to exam it.
Edit:
@ParkourPenguin, I have made a CT based on your example. It is meant to resolve this issue: http:http://forum.cheatengine.org/viewtopic.php?t=588776[/u]
If you have time to take a look at it, I will be very grateful.
Description: |
|
 Download |
Filename: |
TribeGold.CT |
Filesize: |
5.53 KB |
Downloaded: |
500 Time(s) |
|
|
Back to top |
|
 |
|
|
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
|
|