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 


Porting CEServer for Win64 | CMD_WAITFORDEBUGEVENT

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Source
View previous topic :: View next topic  
Author Message
yeswejam
How do I cheat?
Reputation: 0

Joined: 13 Feb 2020
Posts: 6

PostPosted: Thu Feb 13, 2020 2:47 am    Post subject: Porting CEServer for Win64 | CMD_WAITFORDEBUGEVENT Reply with quote

Hello,

i'm currently working on a Win64 version of the Remote Debugging server and have a question.

I got stuck at the CMD_WAITFORDEBUGEVENT API.
I can successfully create a Debugging Session via WinAPI calls:

DEBUG_EVENT DebugEvent;
DebugActiveProcess(process.pid);
WaitForDebugEvent(&DebugEvent, Timeout)


My Problem: The Client hangs and never responds when responding and sending data back to the API CMD_WAITFORDEBUGEVENT Call


The Client expects following structure;

Code:

typedef struct {
  int debugevent;
  int64_t threadid;
  union
  {
    uint64_t
    struct {
      uint8_t maxBreakpointCount;
      uint8_t maxWatchpointCount;
      uint8_t maxSharedBreakpoints;
    };
  };

} DebugEvent, *PDebugEvent;


When the WinAPI schedules the ProcessCreated event, i fill the DebugEvent.debugevent member with an signed int of "-2"
When the WinAPI schedules the ThreadCreated event, i fill the DebugEvent.debugevent member with an signed int of "-1"


Code:
// send when WinAPI schedules ProcessCreated or ThreadCreated
// if ProcessCreated then debugevent = -2
// if ThreadCreated then debugevent  = -1

*( int32_t*)(writeBuffer->bytes   )   = (int32_t)  -2;
*(uint64_t*)(writeBuffer->bytes + 4 ) = (uint64_t) DebugEvt.dwThreadId;

// send this extended part if the WinAPI schedules ProcessCreated

*(uint64_t*)(writeBuffer->bytes  +  12)   = (uint64_t) DebugEvt.u.CreateProcessInfo.lpThreadLocalBase;
*( uint8_t*)(writeBuffer->bytes   +  20) = (  uint8_t)  4;
*( uint8_t*)(writeBuffer->bytes   +  21) = (  uint8_t)  4;
*( uint8_t*)(writeBuffer->bytes   +  22) = (  uint8_t)  4;



I have tried alot but i never get a feedback back from the Client.
What is wrong here?


The following API's are implemented and the server is running fast, stable and without leaks
Code:
CMD_GETVERSION                             
CMD_CLOSECONNECTION                  
CMD_TERMINATESERVER                    
CMD_OPENPROCESS                        
CMD_CREATETOOLHELP32SNAPSHOT    
CMD_PROCESS32FIRST                   
CMD_PROCESS32NEXT                  
CMD_CLOSEHANDLE                        
CMD_VIRTUALQUERYEX                   
CMD_READPROCESSMEMORY       
CMD_WRITEPROCESSMEMORY       
CMD_STARTDEBUG                  
CMD_STOPDEBUG                          
CMD_GETARCHITECTURE       
CMD_MODULE32FIRST               
CMD_MODULE32NEXT             
CMD_GETSYMBOLLISTFROMFILE       
CMD_LOADEXTENSION               
CMD_ALLOC               
CMD_FREE                  
CMD_CREATETHREAD                  
CMD_VIRTUALQUERYEXFULL         
CMD_GETREGIONINFO            
CMD_GETHEAPLIST            
      



These API's are leftover

Code:
CMD_WAITFORDEBUGEVENT           
CMD_CONTINUEFROMDEBUGEVENT      
CMD_SETBREAKPOINT           
CMD_REMOVEBREAKPOINT            
CMD_SUSPENDTHREAD           
CMD_RESUMETHREAD             
CMD_GETTHREADCONTEXT            
CMD_SETTHREADCONTEXT        
CMD_AOBSCAN      
CMD_LOADMODULE               
CMD_SPEEDHACK_SETSPEED


Last edited by yeswejam on Mon Feb 24, 2020 7:23 pm; edited 1 time in total
Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Thu Feb 13, 2020 4:56 am    Post subject: Reply with quote

So it doesn't call CMD_CONTINUEFROMDEBUGEVENT pretty much immediately after the CMD_WAITFORDEBUGEVENT call?

is your debugging session running in a new thread? (It should be as it created a new socket, but just making sure you didn't group it into one thread for some kind of optimization reason)

perhaps your writeBuffer isn't correct, it should be 20 bytes long

for createProcess it's only debugeventnr, threadid, maxBreakpointCount, maxWatchpointCount, maxSharedBreakpoints

no DebugEvt.u.CreateProcessInfo.lpThreadLocalBase for createProcess

only for signal 5 (single step) you provide the address that triggered it (but then no max* )

_________________
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
View user's profile Send private message MSN Messenger
yeswejam
How do I cheat?
Reputation: 0

Joined: 13 Feb 2020
Posts: 6

PostPosted: Thu Feb 13, 2020 6:01 am    Post subject: Reply with quote

I handle every socket connection in a new Thread and keep each alive until the client socket disconnects explicitly and free the resources then.
It worked well so far.

See Attachment #1

Once the client sends CMD_STARTDEBUG and my dispatcher calls __StartDebug, i mark the current socket thread as DebuggerThread for the rest of the persistent connection.

See Attachment #2

I have not noticed that CMD_CONTINUEFROMDEBUGEVENT got called right after. gotta check it later again.
Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Thu Feb 13, 2020 3:19 pm    Post subject: Reply with quote

yeswejam wrote:
I can't double post yet.

when i send 14 bytes twice before the synchronized recv(), the client responds with the CMD_CONTINUEFROMDEBUGEVENT command Call


*( int32_t*)(writeBuffer->bytes ) = -2;
*(uint64_t*)(writeBuffer->bytes + 4 ) = DebugEvt.dwThreadId;
*( uint8_t*)(writeBuffer->bytes + 12) = 0;
*( uint8_t*)(writeBuffer->bytes + 13) = 0;
*( uint8_t*)(writeBuffer->bytes + 14) = 4;

i don't know why it has to be two calls but as long it's consistent, i don't care

Thanks Darkbyte


As I mentioned in the previous reply, your createprocess command was wrong

The 3th field of the debugevent is a union, not a record, so it's EITHER a debug address(for single step) OR or debugger capabilities(for new process) or else filler(for new thread)
make sure you send exactly 20 (0x14) bytes.

seeing you use visual studio, try this one instead:
Code:

#pragma pack (1)
   typedef struct {
      int debugevent;
      int64_t threadid;
      union
      {
         uint64_t address;
         struct {
            uint8_t maxBreakpointCount;
            uint8_t maxWatchpointCount;
            uint8_t maxSharedBreakpoints;
         };
      };

   } DebugEvent, *PDebugEvent;
#pragma pack()

_________________
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
View user's profile Send private message MSN Messenger
yeswejam
How do I cheat?
Reputation: 0

Joined: 13 Feb 2020
Posts: 6

PostPosted: Thu Feb 13, 2020 9:00 pm    Post subject: Reply with quote

Alright.

Just to make it sure and there is no misunderstanding.

So essentially there are only 3 events which should care the DebuggerThread and the WaitForDebugEvent API.

1. CREATE_PROCESS_DEBUG_EVENT

Code:
      DebugEvent devent;
      memset(&devent,0, sizeof(DebugEvent));
      
      devent.debugevent             = -2
      devent.threadid               = threadId
      devent.maxBreakpointCount      = 0;
      devent.maxWatchpointCount      = 0;
      devent.maxSharedBreakpoints    = 4;

      send(&devent,20);


3. CREATE_THREAD_DEBUG_EVENT

Code:
      DebugEvent devent;
      memset(&devent,0, sizeof(DebugEvent));
      
      devent.debugevent             = -1
      devent.threadid                = threadId
      
      send(&devent,20);


3. EXCEPTION_DEBUG_EVENT

Code:
      DebugEvent devent;
      memset(&devent,0, sizeof(DebugEvent));
      
      devent.debugevent             = SIGTRAP // 5
      devent.threadid                = threadId
      devent.address               = address of exception event
   
      send(&devent,20);




This data is enough information for the Client to advance.


So is the DebuggerThread dedicated to following 4 CEServer API's ?

    CMD_STARTDEBUG
    CMD_STOPDEBUG
    CMD_WAITFORDEBUGEVENT
    CMD_CONTINUEFROMDEBUGEVENT


or should it also invoke the 6 API's below and give feedback to the Client DebuggerSocket ?

    CMD_SETBREAKPOINT
    CMD_REMOVEBREAKPOINT
    CMD_SUSPENDTHREAD
    CMD_RESUMETHREAD
    CMD_GETTHREADCONTEXT
    CMD_SETTHREADCONTEXT



Is the Client DebuggerSocket directly sending commands or do all other connections schedule Jobs for the DebuggerThread by pushing Tasks to a list??


The whole remote debug workflow is making more sense for me now and i'm not as trapped in the dark as i was 48hours ago.

Thank you for your time, though.
Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Fri Feb 14, 2020 2:11 am    Post subject: Reply with quote

only the debugger socket should send debug loop messages, but other threads(/sockets) can call the other API commands

anyhow, just deal with the command in the thread/socket it arrives on.

Unlike android/linux when a process is debugged you don't need to go through the debuggerthread to read memory from that point, so no special handling is needed here

_________________
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


Last edited by Dark Byte on Fri Feb 14, 2020 2:19 am; edited 1 time in total
Back to top
View user's profile Send private message MSN Messenger
yeswejam
How do I cheat?
Reputation: 0

Joined: 13 Feb 2020
Posts: 6

PostPosted: Fri Feb 14, 2020 2:15 am    Post subject: Reply with quote

Thank you for the Infos
Should be everything clear now.
Back to top
View user's profile Send private message
yeswejam
How do I cheat?
Reputation: 0

Joined: 13 Feb 2020
Posts: 6

PostPosted: Sat Feb 15, 2020 9:33 pm    Post subject: Reply with quote

I need help again @ GetThreadContext

Despite sending from the DebuggerEvent CreateProcess the number of breakpoints.
devent.debugevent = -2;
devent.threadId = DebugEvt.dwThreadId;
devent.maxBreakpointCount = 4;
devent.maxWatchpointCount = 4;
devent.maxSharedBreakpoints = 4;

I always get an Error from the client: "All debug registers are used up and this debugger interface does not support software Breakpoints. Remove some and try again"



I've defined the linux x86_64 usermode thread context struct and map the win64 thread context to it and send it to the client.
Code:

struct user_regs_struct
{
   __extension__ unsigned long long int r15;
   __extension__ unsigned long long int r14;
   __extension__ unsigned long long int r13;
   __extension__ unsigned long long int r12;
   __extension__ unsigned long long int rbp;
   __extension__ unsigned long long int rbx;
   __extension__ unsigned long long int r11;
   __extension__ unsigned long long int r10;
   __extension__ unsigned long long int r9;
   __extension__ unsigned long long int r8;
   __extension__ unsigned long long int rax;
   __extension__ unsigned long long int rcx;
   __extension__ unsigned long long int rdx;
   __extension__ unsigned long long int rsi;
   __extension__ unsigned long long int rdi;
   __extension__ unsigned long long int orig_rax;
   __extension__ unsigned long long int rip;
   __extension__ unsigned long long int cs;
   __extension__ unsigned long long int eflags;
   __extension__ unsigned long long int rsp;
   __extension__ unsigned long long int ss;
   __extension__ unsigned long long int fs_base;
   __extension__ unsigned long long int gs_base;
   __extension__ unsigned long long int ds;
   __extension__ unsigned long long int es;
   __extension__ unsigned long long int fs;
   __extension__ unsigned long long int gs;
};



Commented the line where i get the Error.


Code:

ctx.ContextFlags = CONTEXT_ALL;

      if (result = GetThreadContext(threadHandle, &ctx)) {



         lctx.regs.eflags  = static_cast<u64>(ctx.EFlags);
         lctx.regs.ss      = static_cast<u64>(0);
         lctx.regs.cs      = static_cast<u64>(ctx.SegCs);
         lctx.regs.ds      = static_cast<u64>(ctx.SegDs);
         lctx.regs.es      = static_cast<u64>(ctx.SegEs);
         lctx.regs.fs      = static_cast<u64>(ctx.SegFs);
         lctx.regs.gs      = static_cast<u64>(ctx.SegGs);
         lctx.regs.fs_base = static_cast<u64>(ctx.SegFs);
         lctx.regs.gs_base = static_cast<u64>(ctx.SegGs);
         lctx.regs.orig_rax= static_cast<u64>(ctx.Rax);

         lctx.regs.rax     = static_cast<u64>(ctx.Rax);
         lctx.regs.rbx     = static_cast<u64>(ctx.Rbx);
         lctx.regs.rcx     = static_cast<u64>(ctx.Rcx);
         lctx.regs.rdx     = static_cast<u64>(ctx.Rdx);
         lctx.regs.rdi     = static_cast<u64>(ctx.Rdi);
         lctx.regs.rdx     = static_cast<u64>(ctx.Rdx);
         lctx.regs.rip     = static_cast<u64>(ctx.Rip);
         lctx.regs.rsi     = static_cast<u64>(ctx.Rsi);
         lctx.regs.rbp     = static_cast<u64>(ctx.Rbp);
         lctx.regs.rsp     = static_cast<u64>(ctx.Rsp);

           lctx.regs.r8      = static_cast<u64>(ctx.R8);
         lctx.regs.r9      = static_cast<u64>(ctx.R9);
         lctx.regs.r10     = static_cast<u64>(ctx.R10);
         lctx.regs.r11     = static_cast<u64>(ctx.R11);
         lctx.regs.r12     = static_cast<u64>(ctx.R12);
         lctx.regs.r13     = static_cast<u64>(ctx.R13);
         lctx.regs.r14     = static_cast<u64>(ctx.R14);
         lctx.regs.r15     = static_cast<u64>(ctx.R15);

         u32 _structsize = sizeof(LCONTEXT);

         printf("result: %d\n structsize: %d\n, Contextsize: %d\n", result, sizeof(_structsize), _structsize);


         if (!isDebuggerThread) {


            DebuggerSocket->lock();

            DebuggerSocket->send(&gtc, sizeof(gtc));

            DebuggerSocket->recvall(&result, 4);
            DebuggerSocket->recvall(&_structsize, 4);
            DebuggerSocket->recvall(&lctx.regs, _structsize);

            DebuggerSocket->unlock();
         }
         else {

            DebuggerSocket->send(&result, 4);
            DebuggerSocket->send(&_structsize, 4);
            DebuggerSocket->send(&lctx.regs, _structsize);  // After this transmit, the client gives the Error
         }
      }

Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 457

Joined: 09 May 2003
Posts: 25262
Location: The netherlands

PostPosted: Sun Feb 16, 2020 2:02 am    Post subject: Reply with quote

set maxBreakpointCount and maxWatchpointCount to 0


also no need to do that if (!isDebuggerThread) thing in windows
i don't know how send is implemented in your case but I think that may go bad if that send goes to the client as it's on the wrong socket that's not listening for a result
in windows just use the socket that initates the command

_________________
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
View user's profile Send private message MSN Messenger
yeswejam
How do I cheat?
Reputation: 0

Joined: 13 Feb 2020
Posts: 6

PostPosted: Sun Feb 16, 2020 2:49 am    Post subject: Reply with quote

followed the suggestions, but still get the same result.

the user_reg_struct seems to be ok, because the client lists the remote thread registers fine.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Source 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