 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
Horse4Horse Newbie cheater
Reputation: 1
Joined: 17 May 2015 Posts: 22
|
Posted: Fri Aug 30, 2024 1:38 am Post subject: {$TRY}/{$EXCEPT} within a {$CCODE} |
|
|
Hi.
I'm making a {$CCODE} loop for manipulating the data of the objects in the game.
Everything is fine, but I can't really check if the object is still valid. Maybe the user moved it out of the game world or the map changed. For the later some checks can be used, but they will still have a possibility of a race condition.
I tried many things, but I can't produce reliable result.
So I took a glance at try/except AA feature and it was perfect for my case - because at the worst situation all I want is to not to crash the game at the cost of the exited {$CCODE} thread.
But the problem is - {$CCODE} section is allocated outside of the main newmem part of the injection.
So if I try: Code: |
ExObjectMover_NewMem:
{$try}
{$C}
My header code
{$ASM}
{$CCODE}
My main loop code
{$ASM}
{$except}
mov rcx,ExObjectMover_NewMem
xor rdx,rdx //Size to 0 bc we are using MEM_RELEASE
mov r8d,8000 //MEM_RELEASE
jmp kernel32.VirtualFree |
It results in an exception handler for a single instruction at the top level of ExObjectMover_NewMem, which contains a single call for the {$ccode} allocated section.
So my question is: How it could be implemented? Theoretically I can edit the List and ListSize addresses of the AA's exceptionhandler. But they are not registered and I would need to get the pointer to the exceptionhandler, add some bytes to it until I reach the pointer to the entries and edit them, by registering an address of the CCODE loop like: Code: |
{$C}
My header code
{$ASM}
mov [EXCEPTIONHANDLER_LIST_ADDRESS],RIP
{$CCODE}
My main loop code
{$ASM} |
And hardwriting the size, but this results in the mov [EXCEPTIONHANDLER_LIST_ADDRESS],RIP instruction located before the {$CCODE} function call, right at the first byte of the ExObjectMover_NewMem.
That's the single way I found for this case. I also tried making the custom crude variation of autoassemblerexeptionhandler.pas, but I got stuck at the proper de-initialization and deallocation stages.
Upd:
I think I could write an exception handler function in the {c} part, assign it with AddVectoredExceptionHandler(1, myexceptionhandler); in {$CCODE} and inside set a rip to the desired exit address(if we are between our loop address and loop address + size), which will also have a RemoveVectoredExceptionHandler(MyExceptionHandle);. But EXCEPTION_POINTERS* info->ContextRecord->Rip = &CustomExit; for some reason gives an offset 16 instead of 8 for the ContextRecord. So I have to write it like *(long long*) (*(long long*) (info+1)+0xF8 ) = &CustomExit;.
|
|
Back to top |
|
 |
Horse4Horse Newbie cheater
Reputation: 1
Joined: 17 May 2015 Posts: 22
|
Posted: Sat Aug 31, 2024 8:56 am Post subject: |
|
|
I think I made something more or less reliable, at least for my purposes.
You can always write an another {$CCODE} section which will handle your exceptions, but for me it is enough.
Can handle only one region.
Only for a {$CCODE} zone exception protection.
Parts of the TopLevelHandler are borrowed from the Dark Byte's autoassemblerexeptionhandler.pas file for official AA {$TRY}/{$EXCEPT} function.
Code: |
<?xml version="1.0" encoding="utf-8"?>
<CheatTable>
<CheatEntries>
<CheatEntry>
<ID>3391</ID>
<Description>"RegisterExceptionHandler"</Description>
<Options moDeactivateChildrenAsWell="1"/>
<LastState Activated="1"/>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{ Game :
Version:
Date : 2024-08-30
Author : Horse4Horse
}
[ENABLE]
alloc(RegisterExceptionHandler_NewMem,1000)
registersymbol(RegisterExceptionHandler_NewMem)
label(WatchAddressStart)
label(WatchAddressEnd)
label(WatchAddressJumpTo)
label(MyExceptionHandle)
label(TopLevelHandler)
label(RegisterH)
label(UnRegisterH)
RegisterExceptionHandler_NewMem:
dq 0 //pad, not needed.
WatchAddressStart:
dq 0
WatchAddressEnd:
dq 0
WatchAddressJumpTo:
dq 0
MyExceptionHandle:
dq 0
TopLevelHandler:
sub rsp,28
//free to edit: RCX, RDX, R8, R9, R10, R11
//rsp+28=return address
//rsp+30=local scratchspace 1 (ExceptionInfo)
mov [rsp+30],rcx
cmp [WatchAddressStart],0
je ExceptionHandler_exit
cmp [WatchAddressEnd],0
je ExceptionHandler_exit
cmp [WatchAddressJumpTo],0
je ExceptionHandler_exit
mov rax,[rsp+30] //rax=ExceptionInfo pointer
mov rax,[rax+8] //rax=ContextRecord
lea rax,[rax+f8] //rax points to RIP
mov r8,WatchAddressStart
next:
mov r9,[r8] //try address
mov r10,[r8+8] //end of try address
cmp [rax],r9
jb nomatch //if before the try instruction, it's not a match
cmp [rax],r10
jb match //if after try, but before end, then it is match
nomatch:
//add r8,10 //[try another address pair] - we have only one pair, it will hang the thread here until a game crash.
//loop next
xor rax,rax //EXCEPTION_CONTINUE_SEARCH, this is not our target code fault
jmp ExceptionHandler_exit
match:
mov r10,[WatchAddressJumpTo]
mov [rax],r10 //change RIP to the exception handler address (or new RIP)
mov eax,ffffffff
ExceptionHandler_exit:
add rsp,28
ret
RegisterH:
sub rsp,28
mov rcx,1
mov rdx,TopLevelHandler
call AddVectoredExceptionHandler
mov [MyExceptionHandle],rax
add rsp,28
ret
UnRegisterH:
mov rcx,MyExceptionHandle
test rcx,rcx
je SkipHandlerRemove //If MyExceptionHandle is undefined - skip
mov rcx,[rcx]
test rcx,rcx
je SkipHandlerRemove //If we don't have a handle - skip
sub rsp,28
call RemoveVectoredExceptionHandler
add rsp,28
SkipHandlerRemove:
mov rcx,WatchAddressStart
xor rdx,rdx //Size to 0 bc we are using MEM_RELEASE
mov r8d,8000 //MEM_RELEASE
jmp kernel32.VirtualFree
createthreadandwait(RegisterH)
registersymbol(WatchAddressStart)
registersymbol(WatchAddressEnd)
registersymbol(WatchAddressJumpTo)
registersymbol(MyExceptionHandle)
registersymbol(TopLevelHandler)
registersymbol(UnRegisterH)
[DISABLE]
createthread(UnRegisterH)
unregistersymbol(WatchAddressStart, WatchAddressEnd, WatchAddressJumpTo, MyExceptionHandle, TopLevelHandler, UnRegisterH)
</AssemblerScript>
<CheatEntries>
<CheatEntry>
<ID>3396</ID>
<Description>"Addresses"</Description>
<LastState Value="" RealAddress="00000000"/>
<GroupHeader>1</GroupHeader>
<CheatEntries>
<CheatEntry>
<ID>3392</ID>
<Description>"WatchAddressStart"</Description>
<LastState Value="0000000000000000" RealAddress="10BE0008"/>
<ShowAsHex>1</ShowAsHex>
<ShowAsSigned>0</ShowAsSigned>
<VariableType>8 Bytes</VariableType>
<Address>WatchAddressStart</Address>
</CheatEntry>
<CheatEntry>
<ID>3393</ID>
<Description>"WatchAddressEnd"</Description>
<LastState Value="0000000000000000" RealAddress="10BE0010"/>
<ShowAsHex>1</ShowAsHex>
<ShowAsSigned>0</ShowAsSigned>
<VariableType>8 Bytes</VariableType>
<Address>WatchAddressEnd</Address>
</CheatEntry>
<CheatEntry>
<ID>3397</ID>
<Description>"WatchAddressJumpTo"</Description>
<LastState Value="0000000000000000" RealAddress="10BE0018"/>
<ShowAsHex>1</ShowAsHex>
<ShowAsSigned>0</ShowAsSigned>
<VariableType>8 Bytes</VariableType>
<Address>WatchAddressJumpTo</Address>
</CheatEntry>
</CheatEntries>
</CheatEntry>
<CheatEntry>
<ID>3394</ID>
<Description>"TestExceptionHandler"</Description>
<LastState/>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>{ Game :
Version:
Date : 2024-08-30
Author : Horse4Horse
}
[ENABLE]
{$lua}
if syntaxcheck then return end
TestExceptionHandler_MemrecID = 3394
TestExceptionHandler_MemrecDisable_Timer = createTimer(getMainForm(), false)
TestExceptionHandler_MemrecDisable_Timer.Interval = 1000
TestExceptionHandler_MemrecDisable_Timer.OnTimer = function(timer)
if destroyalltimers == 1 then timer.Destroy() end
if not readByte("TestExceptionHandler_NewMem") then
local Memrec = getAddressList().getMemoryRecordByID(TestExceptionHandler_MemrecID)
if not Memrec and MemrecName then
Memrec = getAddressList().getMemoryRecordByDescription("TestExceptionHandler")
if not Memrec then
showMessage(string.format("setMemrec Error: Can't find memory record for the \"%s\"(ID %d)!",MemrecName,MemrecID))
return
end
elseif not Memrec then
showMessage(string.format("setMemrec Error: Can't find memory record for the ID %d",MemrecID))
return
end
Memrec.setActive(false)
TestExceptionHandler_MemrecDisable_Timer = nil
showMessage("TestExceptionHandler","TestExceptionHandler just caused an exception, it's memory is freed.")
timer.Destroy()
end
end
TestExceptionHandler_MemrecDisable_Timer.Enabled = true
{$asm}
alloc(TestExceptionHandler_NewMem,1000)
registerSymbol(TestExceptionHandler_NewMem)
label(NormalExit)
label(ExceptionExit)
label(Exit)
TestExceptionHandler_NewMem:
mov rax,ExceptionExit
mov [WatchAddressJumpTo],rax
mov [RSPBackup],rsp //Save RSP address so we can restore it later for return to the BaseThreadInitThunk if we encounter an exception and RSP is not reset from the CCODE init.
{$c}
#define NO_OLDNAMES
#include <windows.h>
#include <stdbool.h>
extern long long WatchAddressStart;
extern long long WatchAddressEnd;
char TestExceptionHandler_ExitThread = false;
void GetAddressOfCCODE(void){
};
char TriggerException = 0;
{$asm}
{$ccode}
WatchAddressStart = &GetAddressOfCCODE;
WatchAddressEnd = &TestExceptionHandler_ExitThread; //First "shared" local is at the end of the CCODE part, we can use it or set end for the whole allocated block.
while(!TestExceptionHandler_ExitThread) {
if (TriggerException){
int *ptr = 0;
*ptr = 1;
}
sleep(100);
}
{$asm}
NormalExit: //Redundant, we can safely set the RSP even when we are not recovering from an exception. But I'm leaving it here for the custom de-init code on a normal exit.
jmp Exit
ExceptionExit:
mov rsp,[RSPBackup]
Exit:
xor rcx,rcx
mov [WatchAddressStart],rcx
mov [WatchAddressEnd],rcx
mov [WatchAddressJumpTo],rcx
mov rcx,TestExceptionHandler_NewMem
xor rdx,rdx //Size to 0 bc we are using MEM_RELEASE
mov r8d,8000 //MEM_RELEASE
jmp kernel32.VirtualFree
RSPBackup:
dq 0
createthread(TestExceptionHandler_NewMem)
[DISABLE]
{$lua}
if syntaxcheck then return end
if TestExceptionHandler_MemrecDisable_Timer then
TestExceptionHandler_MemrecDisable_Timer.destroy()
TestExceptionHandler_MemrecDisable_Timer = nil
end
writeByte("TestExceptionHandler_ExitThread", 1)
unregisterSymbol("TestExceptionHandler_NewMem")
{$asm}
</AssemblerScript>
<CheatEntries>
<CheatEntry>
<ID>3395</ID>
<Description>"Trigger an Exception"</Description>
<ShowAsSigned>0</ShowAsSigned>
<VariableType>Byte</VariableType>
<Address>TriggerException</Address>
</CheatEntry>
</CheatEntries>
</CheatEntry>
</CheatEntries>
</CheatEntry>
</CheatEntries>
</CheatTable>
|
|
|
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
|
|