View previous topic :: View next topic |
Author |
Message |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Fri Oct 19, 2018 2:54 pm Post subject: Freezing when browsing specific memory regions in a game |
|
|
So I was hacking Resonance of Fate/End of Eternity 4K on PC and I encountered this issue which never happened before. There are some specific memory addresses that when shown in the memory browser causes my CPU usage to spike to ~15% for the CE process. CE starts becoming sluggish and even becomes unresponsive when sub-windows open such as a Goto Address prompt. It does not hang completely but pressing buttons to close the sub-window yields no response and so CE becomes stuck because of the modal mode of the prompt.
I tried installing CE6.6 and this issue still persists. It's probably not a bug though but I am curious as to what could be causing this? Is it because the game accesses the memory region too heavily, and thus slows CE from accessing the same region causing a lockup?
UPDATE: So there is this other issue with Tri-Ace games on PC such as Star Ocean and this game where trying to scan for values on a new scan result in "Scan error:thread 7:Access violation". Using CE Kernel "Query memory region routines" solves both this problem and the above one. But not sure if it's a bug with the game or CE? Limiting scan range also solves it though.
UPDATE2: It seems compiling a non-multithreaded build of CE solves the error.
|
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Sun Oct 21, 2018 2:27 am Post subject: |
|
|
It seems the problem is related to memscan.pas, TScanController.firstScan, at line 6139:
Code: | if i=(threadcount-1) then
begin
//if it's the last thread, just give it what's left
scanners[i].stopaddress:=stopaddress;
scanners[i]._stopregion:=memregionpos-1;
//define maxregionsize , go from current till end (since it'll scan everything that's left)
while j<memregionpos do
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
inc(j);
end;
end
|
Both described problems do not present themselves in a non-multithreaded build. The code above is how CE distributes the last portion of a scanning range to its last scanner thread.
What happens in Resonance of Fate is the j counter reaches the end of memregionpos even before the last scanner thread has been initialized. E.g. memregionpos = 300, on the last loop for the last scanner thread, j will have been 299 (the else branch decreases j back down after its loop). memregion[299].MemorySize has a size of only 0x2000 bytes. However, somehow or other further down the code in TScanner.FirstScan, one of the actualread size exceeds this buffer size of 0x2000. The size value is passed along to TScanner.FirstScanMem which then loops past 0x2000 and the respective checkRoutine call tries to query the unallocated memory space, causing a segmentation fault and the Access Violation message.
Copying in some code from the else branch to the code above solves the scanning problem:
Code: | if i=(threadcount-1) then
begin
//if it's the last thread, just give it what's left
scanners[i].stopaddress:=stopaddress;
scanners[i]._stopregion:=memregionpos-1;
currentblocksize:=0;
inc(currentblocksize,memregion[j].MemorySize-offsetincurrentregion);
scanners[i].maxregionsize:=currentblocksize;
//define maxregionsize , go from current till end (since it'll scan everything that's left)
while j<memregionpos do
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
inc(j);
end;
if scanners[i].maxregionsize>buffersize then
scanners[i].maxregionsize:=buffersize;
end
|
This code does not solve the high CPU spike though, I'm assuming a similar problem somewhere else where the memory browser tries to query out of bounds memory and the errors causes some delays between threads resulting in the spike.
Anyway, regarding the j counter for iterating through memregion, is it normal behavior for it to reach the end even before all threads have been initialized? My code above solves the segmentation fault by allocating more memory for the buffer using the same method as the else branch. However, I'm not sure if the bug is the original code above, the strange j counter behavior, or the later parts in TScanner.firstscan having a large actualread size even though the last thread is only assigned the last 0x2000 bytes memRegion.
P.S. The latest source on GitHub cannot be compiled by default because it's missing a debugstructurelist.pas.
EDIT: On second thought, it is cleaner to move line 6157, 6158 and 6161 to before the last thread conditional branch.
Code: | for i:=0 to threadcount-1 do
begin
OutputDebugString(format('Creating scanner %d',[i]));
scanners[i]:=tscanner.Create(true, OwningMemScan.ScanresultFolder);
scanners[i].scannernr:=i;
scanners[i].OwningScanController:=self;
scanners[i]._startregion:=j;
scanners[i].startaddress:=memRegion[j].BaseAddress+offsetincurrentregion;
//scanners[i].maxregionsize:=0; //Original Code, no longer needed
currentblocksize:=0; //Moved Code
inc(currentblocksize,memregion[j].MemorySize-offsetincurrentregion); //Moved Code
scanners[i].maxregionsize:=currentblocksize; //Moved Code
if i=(threadcount-1) then
begin
//if it's the last thread, just give it what's left
scanners[i].stopaddress:=stopaddress;
scanners[i]._stopregion:=memregionpos-1;
//define maxregionsize , go from current till end (since it'll scan everything that's left)
while j<memregionpos do
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
inc(j);
end;
end
else
begin
//not the last thread
inc(j);
while (currentblocksize<blocksize) and (j<memregionpos) do
begin
if scanOption<>soUnknownValue then //not a unknown initial value scan, so it doesn't need overlap
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
end;
inc(currentblocksize,memregion[j].MemorySize);
inc(j);
end; |
Last edited by predprey on Sun Oct 21, 2018 2:16 pm; edited 1 time in total |
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25785 Location: The netherlands
|
Posted: Sun Oct 21, 2018 7:03 am Post subject: |
|
|
thanks i'll check it out.
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Sun Oct 21, 2018 2:06 pm Post subject: |
|
|
Update on the high CPU use issue: to reproduce the issue for Resonance of Fate, browse to this line in the disassembler
Code: | Resonance of Fate.exe+8B0D85 - 48 8B 05 A475F700 - mov rax,["Resonance of Fate.exe"+1828330] |
1. Once this line appears in the panel, CPU usage spikes.
2. There are other lines like this.
3. Issue does not appear in non-multithreaded builds or when kernel query routine is used.
4. Displaying the associated machine code in hex view does not trigger CPU usage spike.
|
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25785 Location: The netherlands
|
Posted: Sun Oct 21, 2018 2:24 pm Post subject: |
|
|
hmm, the disassembler nor anything related there is affected by the cpucount var so not sure that is actually related
are the symbols still being loaded ? (blank, so not 100% or less)
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Sun Oct 21, 2018 6:03 pm Post subject: |
|
|
yea, couldn't find anything related to threads in both the disassembler and hexview sources. but the problem not showing up in a nomt build suggests it has to do with the number of threads. i think i will recompile a nomt build again to confirm i didn't mistake it.
|
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Tue Oct 23, 2018 12:45 am Post subject: |
|
|
predprey wrote: | yea, couldn't find anything related to threads in both the disassembler and hexview sources. but the problem not showing up in a nomt build suggests it has to do with the number of threads. i think i will recompile a nomt build again to confirm i didn't mistake it. |
sorry, it seems i had mistaken it. compiled a new debug 64-bit nomt build and high cpu usage is still present when that offending line is in the disassembler.
to reproduce the issue, open disassembler to that address and enter a battle. cpu usage only spikes during battle. but the line isnt run nor accessed by the game, yet cpu usage spikes. so i decided to nop the code, and cpu usage drops back down to 0-1%. i then switched the ["Resonance of Fate.exe"+1828330] with rax and cpu usage remains at 0-1%. so it's something to do with the routines for autoguessing the value of the memory location, a separate issue from the scan access violation error.
i'm thinking this might be normal behavior when using the winapi version for virtualqueryex? i tried finding what is accessing the location at "Resonance of Fate.exe"+1828330 and there were at least 80 instructions accessing it around 10 times per second, some even 50 times. i'm guessing the windows version gets bogged down by the system scheduler and gets lower priority than the main game? while the kernel version can bypass all these and read directly from the memory so its faster?
|
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25785 Location: The netherlands
|
Posted: Thu Oct 25, 2018 5:48 pm Post subject: |
|
|
I've applied your patch. Though I don't really see a cause for an error (the j indexer is as it should from what I see)
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Thu Oct 25, 2018 7:22 pm Post subject: |
|
|
Dark Byte wrote: | I've applied your patch. Though I don't really see a cause for an error (the j indexer is as it should from what I see) |
just compiled a new release build with the latest source. scans successfully. i concur with you, without knowing the root cause i feel the patch is more of a bandaid now, though it does not hurt performance much either.
i don't fully understand how the j indexer works. is the j indexer supposed to reach its last index before the last thread's iteration? and is it possible that the currentblocksize is larger than the last memRegion size for the last thread.
|
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25785 Location: The netherlands
|
Posted: Fri Oct 26, 2018 12:34 am Post subject: |
|
|
the array goes from 0 to memoryregionpos-1
but if anything goes wrong it likely goes wrong in TScanner.firstNextscan
also don't rule out windows itself. Do you use windows 10 ? It may have had a patch that affected ReadProcessMemory which is why it works now
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Fri Oct 26, 2018 5:46 pm Post subject: |
|
|
Dark Byte wrote: | the array goes from 0 to memoryregionpos-1
but if anything goes wrong it likely goes wrong in TScanner.firstNextscan
also don't rule out windows itself. Do you use windows 10 ? It may have had a patch that affected ReadProcessMemory which is why it works now |
yea i'm using windows 10, but i did also move the currentblocksize code back to its original position in the latest source to make sure that it was not the other commits patching up the issue.
is firstNextscan what executes when pressing next scan? because the issue already occurs on a first scan.
i know the array goes from 0 to memregionpos-1, what i meant is what is the entire block of code doing? is it assigning the memory size based on the workload given to each scanner thread and if so is the workload divided evenly between all threads, or allocated sequentially moving on to the next thread when the thread has been allocated enough memregions whose total size exceeds blocksize.
|
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25785 Location: The netherlands
|
Posted: Sat Oct 27, 2018 2:02 am Post subject: |
|
|
firstNextScan is what gets called on a first scan that isn't an unknown initial value scan or as next scan on a unknown initial value scan (it works on memoryregions) nextnextscan is the one that gets called when there is an addresslist
it divides the total memory by the number of scanners and then distributes it along them in blocks (because there can be a million 4kb regions and one 2GB region)
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
predprey Master Cheater
Reputation: 24
Joined: 08 Oct 2015 Posts: 486
|
Posted: Sat Oct 27, 2018 9:29 am Post subject: |
|
|
ok thanks, when i have the time i will try and debug the issue again. knowing how it divides the memory will help me identify abnormal behavior this time.
|
|
Back to top |
|
 |
|