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 


[C++] Hardware Breakpoints On Access/Write From DLL Troubles

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming
View previous topic :: View next topic  
Author Message
KernelMode
How do I cheat?
Reputation: 1

Joined: 13 Oct 2010
Posts: 9
Location: Messing around with bits in you kernel for now...

PostPosted: Tue Oct 26, 2010 11:15 pm    Post subject: [C++] Hardware Breakpoints On Access/Write From DLL Troubles Reply with quote

Hello! I am trying to implement a DLL that is able to set hardware breakpoints on access (type 3) / on write (type 1). I'm aware that type 2 (on read) isn't supported, at least, for windows it isn't, but not a big deal as type 3 can be used for that if you don't mind that writes will show up as well.

The funny thing is I have actually gotten it to work, and it works flawlessly in my test application. It's when I start using it on real world applications / actual games where the trouble comes in.

I think that my test application is a perfect little world where there isn't really anything going on, so it works fine. In a real application though there's alot more going on so I find the it tends to crash in many real applications / games where it doesn't ever in my test app.

One of first obstacles I made it passed, was the DBG_PRINTEXCEPTION_C problem described here: our forums -> viewtopic.php?t=519820

By skipping that exception in my vectored exception handler, debug prints don't cause an infinite loop.

What I don't get though is why does that exception even make it into an if statement like this:
Code:

if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)


If the ExceptionCode IS EXCEPTION_SINGLE_STEP, then how can it also be DBG_PRINTEXCEPTION_C? In other words how can a debug print exception even make it into that if? For me it goes against all known logic.

take the following example:
Code:

int something = 1;

if(something == 1)
{
      //something equals one, therefore if something equals two or anything else, it will never get between here
}


The above example illustrates my frustration. Doing that code, the inside of the if statement will never be reached unless the variable 'something' equals one. So if you made something equal to 2 for example, it will never ever reach the inside. I don't get how that changes and is any different when working with exceptions in an exception handler.

With my understanding of C++ I would expect the following code to only handle EXCEPTION_SINGLE_STEP exceptions, and hand the rest off to the other exception handlers. Is that not how it will behave in practice?
Code:

LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
   if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
   {
      //code here
      //---------
      //code here
      return EXCEPTION_CONTINUE_EXECUTION;
      //We're only handling EXCEPTION_SINGLE_STEP,
   }

   //and passing everything else down the line to the other handlers in place
   return EXCEPTION_CONTINUE_SEARCH;
}



Must I have an if statement for all exceptions and pass them first before getting to my EXCEPTION_SINGLE_STEP if statement?
ex.
Code:

if(ExceptionInfo->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C)
{
        return EXCEPTION_CONTINUE_SEARCH;
}
//etc..
//...
if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
   {
      //code here
      //---------
      //code here
      return EXCEPTION_CONTINUE_EXECUTION;
      //We're only handling EXCEPTION_SINGLE_STEP
   }



My last problem I solved right before getting it working in my test application is resetting the Dr6 register to zero in the exception handler after a breakpoint is hit.

With that last change I was able to have it working without a crash in my test application.

I feel my problem might be in my exception handler code. Maybe I'm not passing certain exceptions down to other handlers when they need to be, causing crashes in certain games/apps that may have all kinds of exceptions happening and things going on.

So if the exception handler code is my problem, how can I make it bulletproof so that it will work in any executable without crashing it? In other words what code can I put in my exception handler so it only handles my set hardware breakpoints and passes everything else along smoothly?

See attached images for what it looks like and how it works...

As you can see it behaves how I would expect and how I want it to behave. The EIP displayed by the DLL in it's memo box for each time a breakpoint is hit, is the address of the instruction after the instruction which did the accessing/writing...

It works like this, upon the user setting a breakpoint through the GUI, it gathers all running threads, and locates a free debug register in the first thread it attempts to set the breakpoint on, by testing the bits in the Dr7 register. Then it set's the proper bits of the Dr7 register to enable that breakpoint for the first thread, and for the rest. The native OpenThread, NtOpenThread was used instead of the standard OpenThread as it wasn't working for me for some reason.

The project source along with binary containing the Hardware Breakpoint Setting DLL, "Project1.dll", and test app, "TestApp.exe" is downloadable here: httpizzle semi colon slash slash localhostr dot com/files/9254de/HWBreakPtr.zip

To test the dll just inject it in any game/application you wish to test it on, or just run the test app if testing it there. (It calls loadlibrary on it so no need to inject)

I'll post most of the code here however so you can see whats going on without a download.

I've contained most of the functionality within the "HWHooker" class.
Note: trying to use the class will result in undefined "HWHandler" as I defined that vectored exception handler in "Unit1.h" and just included it which is a bad way to do it, I'll probably change it so you pass the address of your exception handler to the class object instead.

HWHooker.h
Code:

#ifndef _HWHOOKER_H
#define _HWHOOKER_H

#include "ArrayClass.h"
#include "Unit1.h"
#include <stdio.h>
#include <tlhelp32.h>
#include <Ntsecapi.h>

#define InitializeObjectAttributes( p, n, a, r, s ) {   \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES );          \
    (p)->RootDirectory = r;                             \
    (p)->Attributes = a;                                \
    (p)->ObjectName = n;                                \
    (p)->SecurityDescriptor = s;                        \
    (p)->SecurityQualityOfService = NULL;               \
}

typedef struct _OBJECT_ATTRIBUTES
{
     ULONG Length;
     PVOID RootDirectory;
     PUNICODE_STRING ObjectName;
     ULONG Attributes;
     PVOID SecurityDescriptor;
     PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef struct _CLIENT_ID
{
     DWORD UniqueProcess;
     DWORD UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef NTSTATUS (WINAPI *NTOPENTHREAD)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID);
typedef NTSTATUS (WINAPI *NTGETCONTEXTTHREAD)(HANDLE, LPCONTEXT);

class BREAKPOINT
{
   public:
   LPVOID address;
   DWORD type; //0 = break on execute, 1 = on write, 3 = on access
   DWORD size; //breakpoint size 1, 2, or 4
   DWORD dbgreg; // 0 - 3 //debug registers 1-4

   BREAKPOINT();
   ~BREAKPOINT();
};

class HWHooker
{
   private:
   void GetAllThreads();

   public:
   NTOPENTHREAD NtOpenThread;
   Array *threads;
   Array *breakpoints;
   PVOID ExceptionHandler;
   int handlingexceptions;

   HWHooker();
   ~HWHooker();
   int SetSingleBP(BREAKPOINT *bp, DWORD threadid);
   int SetBreakpoint(DWORD type, DWORD bpsize, LPVOID address);
   //int RemoveBreakpoint(DWORD DbgRegister); //not yet implemented
   int ResetAll(); //unset + delete all breakpoints
};

inline void SETBITS(unsigned long& dw, int lowBit, int bits, int newValue)
{
   int mask = (1 << bits) - 1;
   dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
}

#endif


HWHooker.cpp
Code:

#include "HWHooker.h"

BREAKPOINT::BREAKPOINT()
{
   address = 0;
   type = 0;
   size = 0;
   dbgreg = -1;
}

BREAKPOINT::~BREAKPOINT()
{

}

HWHooker::HWHooker()
{
   NtOpenThread = (NTOPENTHREAD)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenThread");
   threads = new Array;
   breakpoints = new Array;
   ExceptionHandler = 0;
   handlingexceptions = 0;
}

HWHooker::~HWHooker()
{
   breakpoints->ClearAll();
   delete threads;
   delete breakpoints;
}

void HWHooker::GetAllThreads()
{
   THREADENTRY32 *te = new THREADENTRY32;
   HANDLE Snap;

   te->dwSize = sizeof(THREADENTRY32);
   Snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

   if(Snap != INVALID_HANDLE_VALUE)
   {
      if(Thread32First(Snap, te))
      {
         GetNextThread:
         if(te->th32OwnerProcessID == GetCurrentProcessId())
         {
            threads->AddObject((void*)te->th32ThreadID);
         }

         if(Thread32Next(Snap, te))
            goto GetNextThread;
      }

      CloseHandle(Snap);
   }
   delete te;
}

int HWHooker::SetSingleBP(BREAKPOINT *bp, DWORD threadid)
{
   HANDLE hThread;
   OBJECT_ATTRIBUTES ObjAttribs;
   CLIENT_ID cID;
   NTSTATUS Status;

   InitializeObjectAttributes(&ObjAttribs, 0, 0, 0, 0);

   cID.UniqueProcess = 0;
   cID.UniqueThread = threadid;

   Status = NtOpenThread(&hThread, THREAD_ALL_ACCESS, &ObjAttribs, &cID);
   if(Status == 0)
   {
      CONTEXT Context;
      Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
      GetThreadContext(hThread, &Context);

      // find available hardware register
      if(bp->dbgreg == 0xFFFFFFFF)//debug register to use for this breakpoint not gotten yet
      {
         for(DWORD o = 0; o < 4; o++)
         {
            if((Context.Dr7 & (1 << (o * 2))) == 0)
            {
               //found unused debug register
               *(&Context.Dr0 + o) = (DWORD)bp->address;

               SETBITS(Context.Dr7, 16 + o*4, 2, bp->type);
               SETBITS(Context.Dr7, 18 + o*4, 2, bp->size);
               SETBITS(Context.Dr7, o * 2, 1, 1);

               SetThreadContext(hThread, &Context);
               bp->dbgreg = o; //use this debug register for rest of threads
               break;
            }
         }
      }
      else
      {
         DWORD r = bp->dbgreg;
         if((Context.Dr7 & (1 << r * 2)) == 0)
         {
            *(&Context.Dr0 + r) = (DWORD)bp->address;

            SETBITS(Context.Dr7, 16 + r*4, 2, bp->type);
            SETBITS(Context.Dr7, 18 + r*4, 2, bp->size);
            SETBITS(Context.Dr7, r * 2, 1, 1);

            SetThreadContext(hThread, &Context);
         }
      }
      CloseHandle(hThread);
   }

   if(bp->dbgreg >= 0)
      return bp->dbgreg;
   else
      return -1;

}

int HWHooker::SetBreakpoint(DWORD type, DWORD bpsize, LPVOID address)
{
   BREAKPOINT *bp, *tp;
        int successful = 0;

   for(DWORD l = 0; l < breakpoints->numitems; l++)
   {
      tp = (BREAKPOINT*)breakpoints->GetObject(l);
      if(tp->type == type && tp->size == bpsize && tp->address == address)
      {
         //breakpoint already existing... don't set it again.
         return -1;
      }
   }

   bp = new BREAKPOINT;
   bp->address = address;
   bp->type = type;
   bp->size = bpsize;

   breakpoints->AddObject(bp);

   if(!ExceptionHandler) //Add exception handler to the mix, if it isn't already
      ExceptionHandler = AddVectoredExceptionHandler(0x1339, HWHandler);

   GetAllThreads(); //Get all currently running threads in within current executable

   for(DWORD i = 0; i < threads->numitems; i++)
   {
      if(SetSingleBP(bp, (DWORD)threads->GetObject(i)) >= 0)
      {
         //successfully set breakpoint for this thread
      }
   }

        if(successful)
               return 1;
        else
          return -1;
}

int HWHooker::ResetAll()
{
   HANDLE hThread;
   OBJECT_ATTRIBUTES ObjAttribs;
   CLIENT_ID cID;
   NTSTATUS Status;
   BREAKPOINT *bp;

   while(handlingexceptions == 1) //while handling exceptions, wait before removing any breakpoints
   {
        Sleep(10);
   }

   //This isn't really necessary but I thought it might help prevent crashing...
   if(ExceptionHandler)
   {
      RemoveVectoredExceptionHandler(ExceptionHandler);
      ExceptionHandler = 0;
   }

   InitializeObjectAttributes(&ObjAttribs, 0, 0, 0, 0);
   for(DWORD i = 0; i < threads->numitems; i++)
   {
      cID.UniqueProcess = 0;
      cID.UniqueThread = (DWORD)threads->GetObject(i);

      Status = NtOpenThread(&hThread, THREAD_ALL_ACCESS, &ObjAttribs, &cID);
      if(Status == 0)
      {
         CONTEXT Context;
         Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
         GetThreadContext(hThread, &Context);

         //Clear all debug registers for this thread
         Context.Dr0 = 0;
         Context.Dr1 = 0;
         Context.Dr2 = 0;
         Context.Dr3 = 0;
         Context.Dr6 = 0;
         Context.Dr7 = 0;

         SetThreadContext(hThread, &Context);
         CloseHandle(hThread);
      }
   }

   breakpoints->ClearAll(); //delete any breakpoint objects from breakpoints array
   return 1;
}


Unit1.cpp - yes it's a VCL DLL
Code:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
HWHooker *hk = new HWHooker;
BREAKPOINT *Bp;
UnicodeString s;
char msg[1024], total[1024];
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::BPButtonClick(TObject *Sender)
{
   DWORD type;
   DWORD size;
   DWORD regnum;
   long addr;

   size = atoi(SizeBox->Text.t_str());
   s.sprintf(L"%s %u ", BreakAddress->Text.w_str(), size);

   if(AccessRadio->Checked)
   {
      s += "+RW";
      type = 3;
   }
   else if(WriteRadio->Checked)
   {
      s += "+W";
      type = 1;
   }

   addr = strtol(BreakAddress->Text.t_str(), 0, 16);

   regnum = hk->SetBreakpoint(type, size, (void*)addr);
   if(regnum != 0xFFFFFFFF)
   {
      BPTree->Items->Add(0, s);
      OutputDebugString("Breakpoint Set!");
   }
}
//---------------------------------------------------------------------------

LONG CALLBACK HWHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
   hk->handlingexceptions = 1;

   if(ExceptionInfo->ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
   {
      for(DWORD e = 0; e < 4; e++)
      {
         if((ExceptionInfo->ContextRecord->Dr7 & (1 << (e * 2))) == 1)
         {
            ExceptionInfo->ContextRecord->Dr6 = 0; //reset Dr6 if any debug registers are set (otherwise crash will occur)
         }
      }

      DbgPrint("Exception C0DE: %X", 1, ExceptionInfo->ExceptionRecord->ExceptionCode);

      if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
      {
         for(DWORD j = 0; j < hk->breakpoints->numitems; j++)
         {
            Bp = (BREAKPOINT*)hk->breakpoints->GetObject(j);

            if((ExceptionInfo->ContextRecord->Dr7 & (1 << (Bp->dbgreg * 2))) == 1)
            {
               if(ExceptionInfo->ContextRecord->Dr0 == (DWORD)Bp->address || ExceptionInfo->ContextRecord->Dr1 == (DWORD)Bp->address || ExceptionInfo->ContextRecord->Dr2 == (DWORD)Bp->address || ExceptionInfo->ContextRecord->Dr3 == (DWORD)Bp->address)
               {
                  //breakpoint found
                  DbgPrint("EIP: %p; ExceptionAddress: %p", 0, ExceptionInfo->ContextRecord->Eip, ExceptionInfo->ExceptionRecord->ExceptionAddress);
                  //ExceptionInfo->ContextRecord->Dr6 = 0; //putting this here seems to make it crash more often then up there.
                  hk->handlingexceptions = 0;
                  return EXCEPTION_CONTINUE_EXECUTION;
               }
            }
         }
      }
   }

   hk->handlingexceptions = 0;
   return EXCEPTION_CONTINUE_SEARCH;
}

void __fastcall TForm1::ResetAllButtonClick(TObject *Sender)
{
   hk->ResetAll();
   BPTree->Items->Clear();
   AccessedAddressesView->Clear();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
   //Unset and remove any breakpoints existing
   hk->ResetAll();

   //remove vectored exception handler (was here since before I made it remove the handler in "ResetAll" method of HWHooker class, lets just leave it incase I change that)
   if(hk->ExceptionHandler)
      RemoveVectoredExceptionHandler(hk->ExceptionHandler);

   //terminate VCL application + free resources
   Application->Terminate();

   //Free library and exit thread, successfully ejecting DLL and leaving program in a state where it can continue executing normally
   FreeLibraryAndExitThread(GetModuleHandle("Project1.dll"), 0);
}
//---------------------------------------------------------------------------

//Kinda like the kernel mode function with the same name "DbgPrint"
void DbgPrint(char *fmt, int type, ...)
{
   va_list vl;

   char prefix[] = "[BP Hit!] ";

   va_start(vl, type);
   wvsprintfA(msg, fmt, vl);
   va_end(vl);

   lstrcpyA(total, prefix);
   lstrcatA(total, msg);


   if(type == 0)
      Form1->AccessedAddressesView->Lines->Add(total);
   else
       OutputDebugStringA(total);
}


Sorry if this is a long post, but I've been fighting with this all day and just can't seem to figure it out.

Thanks for your help!



WriteCode.png
 Description:
ExceptionAddress given for write
 Filesize:  48.36 KB
 Viewed:  14642 Time(s)

WriteCode.png



HWBreakPtr-1.0.png
 Description:
In test application, flawless execution.
 Filesize:  123.58 KB
 Viewed:  14642 Time(s)

HWBreakPtr-1.0.png


Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Dark Byte
Site Admin
Reputation: 373

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

PostPosted: Wed Oct 27, 2010 12:02 am    Post subject: This post has 1 review(s) Reply with quote

comment out the line "Form1->AccessedAddressesView->Lines->Add" and see if it crashes as often.
VCL doesn't take kindly to multiple threads calling gui code at the same time(And that includes the refresh method call that occurs after and add which then happens from another thread that isn't the normal display thread)
use synchronize if possible, but I never had success with synchronize in dll's, I usually resort to postmessage if it's not the main thread


also,
Quote:
if(ExceptionInfo->ContextRecord->Dr0 == (DWORD)Bp->address || ExceptionInfo->ContextRecord->Dr1 == (DWORD)Bp->address || ExceptionInfo->ContextRecord->Dr2 == (DWORD)Bp->address || ExceptionInfo->ContextRecord->Dr3 == (DWORD)Bp->address)

Once you have set a breakpoint, this will return true 100% of the time, even if it's not caused by your breakpoint (It's better to check the bits in dr6 to see what the cause is)

and
Code:

if((ExceptionInfo->ContextRecord->Dr7 & (1 << (e * 2))) == 1)

this looks like a logic error to me, try != 0 instead (also, setting dr6 to 0 isn't really needed, but if you do set it to something, set it to 0xffff0ff0 )

same for
Code:

if((ExceptionInfo->ContextRecord->Dr7 & (1 << (Bp->dbgreg * 2))) == 1)

use != 0 instead of ==1
lets do the calculation:
dbreg is 1 or higher

assume:
dr=1
d=ExceptionInfo->ContextRecord->Dr7=0xffffffff
d & (1 << (dr*2))=
d & (1 << 2)=
d & 4 =
0xffffffff & 4 = 4

_________________
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
KernelMode
How do I cheat?
Reputation: 1

Joined: 13 Oct 2010
Posts: 9
Location: Messing around with bits in you kernel for now...

PostPosted: Fri Oct 29, 2010 3:27 pm    Post subject: Reply with quote

Thanks alot Dark Byte! I fixed my logic errors, and fixed the VCL GUI accessing from another thread issue. It seems to be running smoother now.

I was unaware that calling GUI code from multiple threads would cause crashing. It seemed to be working fine, but to avoid a crashing bug that I just can't seem to figure out why it's happening later on, I've changed it so I sendmessage/postmessage instead.

I suppose that means not just adding lines to a memo (for example) but also any GUI access? Even just doing a: Form1->AccessedAddressesView->Handle from a separate thread? Well since I wasn't sure I stored the handle to the memo on form create:
Code:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
   MemoHandle = AccessedAddressesView->Handle;
}


and adjusted my DbgPrint function like so:
Code:

void DbgPrint(char *fmt, ...)
{
   va_list vl;

   char suffix[] = "\r\n";

   va_start(vl, fmt);
   wvsprintfA(msg, fmt, vl);
   va_end(vl);

   lstrcpyA(total, msg);
   lstrcatA(total, suffix);

   SendMessage(MemoHandle, EM_REPLACESEL, 0, (LPARAM)total); //good
   //Form1->AccessedAddressesView->Lines->Add(total); //bad
   OutputDebugStringA(total);
}


So that part should be all fine now.

Code:

if((ExceptionInfo->ContextRecord->Dr7 & (1 << (e * 2))) == 1)


Yes that was a logic error on my part. After looking at your example calculation, I was able to understand it, and see where my logic failed. != 0 corrected it.

However I removed that code as you said it's better to test Dr6. And the only reason I thought Dr6 had to be cleared is because that's what I read on wikipedia.
"Note that the bits of DR6 are never cleared by the processor. To avoid any confusion in identifying the next debug exception, the debug handler should move zeros to DR6 immediately before returning."
-->
english wikipedia dot org/wiki/X86_debug_register#DR6_-_Debug_status

Or I may have just interpreted that wrong, and the debug handler they are talking about is not my vectored exception handler.

Yes I'm sure that setting it to zero is wrong though because debug printing the value of the Dr6 reveals that it is actually 0xFFFF0FF0 when there are no breakpoints that have triggered.

My debug handler now looks like this:
Code:

LONG CALLBACK HWHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
   hk->handlingexceptions = 1;

   if(ExceptionInfo->ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
   {
      //DbgPrint("Exception C0DE: %X", ExceptionInfo->ExceptionRecord->ExceptionCode);

      if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
      {
         for(DWORD j = 0; j < hk->breakpoints->numitems; j++)
         {
            Bp = (BREAKPOINT*)hk->breakpoints->GetObject(j);
            if(ExceptionInfo->ContextRecord->Dr6 & (1 << Bp->dbgreg)) //one of our breakpoints triggered the exception, so handle it!
            {
               DbgPrint("EIP: %p; ExceptionAddress: %p", ExceptionInfo->ContextRecord->Eip, ExceptionInfo->ExceptionRecord->ExceptionAddress);

               ExceptionInfo->ContextRecord->Dr6 = 0xFFFF0FF0;
                   hk->handlingexceptions = 0;
               return EXCEPTION_CONTINUE_EXECUTION;
            }
         }
      }
   }

   hk->handlingexceptions = 0;
   return EXCEPTION_CONTINUE_SEARCH;
}


It seems like it's working alot better now, but I still have to fully test the new version it on all the games I tried it on previously to know for sure of its stability.

Also it does still work fine omitting the "ExceptionInfo->ContextRecord->Dr6 = 0xFFFF0FF0;" line like you said, but I chose to leave it in case it does help something.

By testing Dr6, the exception handler now only returns return 'EXCEPTION_CONTINUE_EXECUTION' when the exception is caused by breakpoint set by the dll (at least I think so) and for any other exception returns 'EXCEPTION_CONTINUE_SEARCH'.


For example a calculation:

dbreg is the second debug register, 1

dr = 1
d = ExceptionInfo->ContextRecord->Dr6 = 0xFFFF0FF2

d & (1 << dr) =
d & (1 << 1) =
d & 2 =
0xFFFF0FF2 & 2 = 2

So if that was the caculation going on, the second debug register is what triggered the breakpoint.

So
Code:

if(ExceptionInfo->ContextRecord->Dr6 & (1 << Bp->dbgreg))

is the same as
Code:

if((ExceptionInfo->ContextRecord->Dr6 & (1 << Bp->dbgreg)) ! = 0)


And it tests whether the debug register 'Bp->dbgreg' is what caused the exception.


Also I noticed that the breakpoint size was wrong the way I was going about it. I changed it so that instead of setting the bits the actual value of the size, which is wrong. It sets the proper bits, 00b for 1 byte breakpoint, 01b for 2 bytes, 10b, for 8 bytes, and 11b, for 4 bytes.

I'm not sure if there's a better way to do it, but I just fixed it up like this in my HWHooker::SetBreakpoint method:
Code:

bp = new BREAKPOINT;
   bp->address = address;
   bp->type = type;
   //bp->size = bpsize;

   if(bpsize == 1)
      bp->size = 0;
   else if(bpsize == 2)
      bp->size = 1;
   else if(bpsize == 4)
      bp->size = 3;
   else if(bpsize == 8)
      bp->size = 2;
   else
      bp->size = 3;


Thanks again! Smile

EDIT: actually I thought of a slightly better way to do that last part [less lines]
Code:

if(bpsize == 8)
      bp->size = (bpsize >> 2);
   else if(bpsize >= 1 && bpsize <= 2 || bpsize == 4) //1, 2, 4, 8
      bp->size = (bpsize - 1);
   else
      bp->size = 3; //default to 4 byte break point


A breakpoint of size eight can be shifted right twice, and 1, 2, 4 can just be subtracted by 1 to get their right values to set the bits! Smile
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Dark Byte
Site Admin
Reputation: 373

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

PostPosted: Mon Nov 01, 2010 7:25 am    Post subject: Reply with quote

note that size 8 is only available when the target process is running in 64-bit mode, else you'll need to use 2 bp's
_________________
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
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General programming 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