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 


{$TRY}/{$EXCEPT} within a {$CCODE}

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

Joined: 17 May 2015
Posts: 22

PostPosted: Fri Aug 30, 2024 1:38 am    Post subject: {$TRY}/{$EXCEPT} within a {$CCODE} Reply with quote

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
View user's profile Send private message
Horse4Horse
Newbie cheater
Reputation: 1

Joined: 17 May 2015
Posts: 22

PostPosted: Sat Aug 31, 2024 8:56 am    Post subject: Reply with quote

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 &lt;windows.h&gt;
#include &lt;stdbool.h&gt;
extern long long WatchAddressStart;
extern long long WatchAddressEnd;

char TestExceptionHandler_ExitThread = false;

void GetAddressOfCCODE(void){
};

char TriggerException = 0;

{$asm}

{$ccode}
WatchAddressStart = &amp;GetAddressOfCCODE;
WatchAddressEnd = &amp;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
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