/*++ Copyright (c) 1998-2001 Klaus P. Gerlicher Module Name: bp.c Abstract: setting, listing and removing breakpoints Environment: LINUX 2.2.X Kernel mode only Author: Klaus P. Gerlicher Revision History: 13-Nov-1999: created 15-Nov-2000: general cleanup of source files Copyright notice: This file may be distributed under the terms of the GNU Public License. --*/ //////////////////////////////////////////////////// // INCLUDES //// #include "remods.h" #include "precomp.h" //////////////////////////////////////////////////// // GLOBALS //// char tempBp[1024]; ULONG OldInt3Handler=0; SW_BP aSwBreakpoints[64]={{0,0,0,0},}; //************************************************************************* // FindSwBp() // //************************************************************************* PSW_BP FindSwBp(ULONG ulAddress) { ULONG i; for(i=0;ibUsed == TRUE && p->bVirtual == TRUE && PICE_strcmpi(p->szModName,ModName)==0 && PICE_strcmpi(p->szFunctionName,szFunctionName)==0) { return p; } } return NULL; } //************************************************************************* // IsSwBpAtAddressInstalled() // //************************************************************************* BOOLEAN IsSwBpAtAddressInstalled(ULONG ulAddress) { ULONG i; for(i=0;ibUsed == TRUE && p->bInstalled == FALSE && p->ulAddress==ulAddress && p->bVirtual==FALSE) { if(IsAddressValid(p->ulAddress)) { DPRINT((0,"NeedToReInstallSWBreakpoint(): [1] found BP\n")); bResult = TRUE; break; } } } else { if(p->bUsed == TRUE && p->bInstalled == FALSE && p->bVirtual == FALSE) { if(IsAddressValid(p->ulAddress)) { DPRINT((0,"NeedToReInstallSWBreakpoint(): [2] found BP\n")); bResult = TRUE; break; } } } } LEAVE_FUNC(); return bResult; } //************************************************************************* // ReInstallSWBreakpoint() // //************************************************************************* BOOLEAN ReInstallSWBreakpoint(ULONG ulAddress) { PSW_BP p; BOOLEAN bResult = FALSE; ULONG i; ENTER_FUNC(); DPRINT((0,"ReInstallSWBreakpoint()\n")); for(i=0;i<(sizeof(aSwBreakpoints)/sizeof(SW_BP));i++) { p = &aSwBreakpoints[i]; if(p->bUsed == TRUE && p->bInstalled == FALSE && p->ulAddress == ulAddress && p->bVirtual == FALSE) { if(IsAddressValid(p->ulAddress)) { BOOLEAN isWriteable; if( !( isWriteable = IsAddressWriteable(p->ulAddress) ) ) SetAddressWriteable(p->ulAddress,TRUE); *(PUCHAR)(p->ulAddress) = 0xCC; if( !isWriteable ) SetAddressWriteable(p->ulAddress,FALSE); p->bInstalled = TRUE; bResult = TRUE; } } } LEAVE_FUNC(); return bResult; } //************************************************************************* // InstallSWBreakpoint() // //************************************************************************* BOOLEAN InstallSWBreakpoint(ULONG ulAddress,BOOLEAN bPermanent,void (*SWBreakpointCallback)(void)) { PSW_BP p; BOOLEAN bResult = FALSE; ENTER_FUNC(); DPRINT((0,"InstallSWBreakpoint()\n")); // check if page is present // TODO: must also check if it's a writable page if(IsAddressValid(ulAddress) ) { DPRINT((0,"InstallSWBreakpoint(): %.8X is valid, writable? %d\n",ulAddress,IsAddressWriteable(ulAddress))); DPRINT((0,"pde: %x, pte: %x\n", *(ADDR_TO_PDE(ulAddress)), *(ADDR_TO_PTE(ulAddress)))); if((p = FindSwBp(ulAddress))==NULL) { DPRINT((0,"InstallSWBreakpoint(): %.8X is free\n",ulAddress)); if( (p=FindEmptySwBpSlot()) ) { BOOLEAN isWriteable; DPRINT((0,"InstallSWBreakpoint(): found empty slot\n")); DPRINT((0,"InstallSWBreakpoint(): %x value: %x", ulAddress, *(PUCHAR)ulAddress)); p->ucOriginalOpcode = *(PUCHAR)ulAddress; //allow writing to page if( !( isWriteable = IsAddressWriteable(ulAddress) ) ) SetAddressWriteable(ulAddress,TRUE); DPRINT((0,"writing breakpoint\n")); *(PUCHAR)ulAddress = 0xCC; DPRINT((0,"restoring page access\n")); if( !isWriteable ) SetAddressWriteable(ulAddress,FALSE); p->bUsed = TRUE; p->bInstalled = TRUE; // find next address p->ulAddress = ulAddress; Disasm(&ulAddress,(PUCHAR)&tempBp); p->ulNextInstr = ulAddress; p->bPermanent = bPermanent; if(bPermanent) p->Callback = SWBreakpointCallback; else p->Callback = NULL; bResult = TRUE; } } else { DPRINT((0,"InstallSWBreakpoint(): %.8X is already used\n",ulAddress)); if(p->bPermanent) { DPRINT((0,"InstallSWBreakpoint(): %.8X is a permanent breakpoint\n",ulAddress)); } } } LEAVE_FUNC(); return bResult; } //************************************************************************* // InstallVirtualSWBreakpoint() // //************************************************************************* BOOLEAN InstallVirtualSWBreakpoint(LPSTR ModName,LPSTR FunctionName) { PSW_BP p; BOOLEAN bResult = FALSE; ENTER_FUNC(); DPRINT((0,"InstallVirtualSWBreakpoint(%s!%s)\n",ModName,FunctionName)); if( (p=FindEmptySwBpSlot()) ) { DPRINT((0,"InstallVirtualSWBreakpoint(): found empty slot\n")); p->bUsed = TRUE; p->bInstalled = TRUE; p->bVirtual = TRUE; p->Callback = NULL; PICE_strcpy(p->szModName,ModName); PICE_strcpy(p->szFunctionName,FunctionName); bResult = TRUE; } LEAVE_FUNC(); return bResult; } //************************************************************************* // TryToInstallVirtualSWBreakpoints() // //************************************************************************* void TryToInstallVirtualSWBreakpoints(void) { ULONG i,ulAddress; PDEBUG_MODULE pMod; PSW_BP p; DPRINT((0,"TryToInstallVirtualSWBreakpoints()\n")); for(i=0;i<(sizeof(aSwBreakpoints)/sizeof(SW_BP));i++) { p = &aSwBreakpoints[i]; if(p->bUsed == TRUE && p->bVirtual) { if((pMod = IsModuleLoaded(p->szModName))) { if((ulAddress = FindFunctionInModuleByName(p->szFunctionName,pMod))) { if((p = FindVirtualSwBp(p->szModName,p->szFunctionName))) { ULONG ulAddressWithOffset = ulAddress+p->ulAddress; DPRINT((0,"TryToInstallVirtualSWBreakpoints(): ulAddressWithOffset = %x (offset = %x)\n",ulAddressWithOffset,p->ulAddress)); if(IsAddressValid(ulAddressWithOffset)) { BOOLEAN isWriteable; DPRINT((0,"TryToInstallVirtualSWBreakpoints(): installing...\n")); p->ucOriginalOpcode = *(PUCHAR)ulAddressWithOffset; //allow writing to page if( !( isWriteable = IsAddressWriteable(ulAddressWithOffset) ) ) SetAddressWriteable(ulAddressWithOffset,TRUE); *(PUCHAR)ulAddressWithOffset = 0xCC; if( !isWriteable ) SetAddressWriteable(ulAddressWithOffset,FALSE); p->bUsed = TRUE; p->bInstalled = TRUE; p->bVirtual = FALSE; // find next address p->ulAddress = ulAddressWithOffset; Disasm(&ulAddressWithOffset,(PUCHAR)&tempBp); p->ulNextInstr = ulAddressWithOffset; p->bPermanent = FALSE; p->Callback = NULL; } else { DPRINT((0,"TryToInstallVirtualSWBreakpoints(): not valid address\n")); PICE_memset(p,0,sizeof(*p)); } } } } } } } //************************************************************************* // RemoveSWBreakpoint() // // removes breakpoint from breakpoint list //************************************************************************* BOOLEAN RemoveSWBreakpoint(ULONG ulAddress) { PSW_BP p; BOOLEAN bResult = FALSE; ENTER_FUNC(); DPRINT((0,"RemoveSWBreakpoint()\n")); if( (p = FindSwBp(ulAddress)) ) { if(IsAddressValid(ulAddress) && p->bInstalled == TRUE && p->bVirtual==FALSE) { BOOLEAN isWriteable; if( !( isWriteable = IsAddressWriteable(ulAddress) ) ) SetAddressWriteable(ulAddress,TRUE); // restore original opcode *(PUCHAR)(p->ulAddress) = p->ucOriginalOpcode; if( !isWriteable ) SetAddressWriteable(ulAddress,FALSE); } PICE_memset(p,0,sizeof(*p)); bResult = TRUE; } LEAVE_FUNC(); return bResult; } //************************************************************************* // DeInstallSWBreakpoint() // //************************************************************************* BOOLEAN DeInstallSWBreakpoint(ULONG ulAddress) { PSW_BP p; BOOLEAN bResult = FALSE; ENTER_FUNC(); DPRINT((0,"DeInstallSWBreakpoint()\n")); if( (p = FindSwBp(ulAddress)) ) { if(IsAddressValid(ulAddress) && p->bInstalled == TRUE && p->bVirtual==FALSE) { BOOLEAN isWriteable; if( !( isWriteable = IsAddressWriteable(ulAddress) ) ) SetAddressWriteable(ulAddress,TRUE); // restore original opcode *(PUCHAR)(p->ulAddress) = p->ucOriginalOpcode; if( !isWriteable ) SetAddressWriteable(ulAddress,FALSE); } p->bInstalled = FALSE; bResult = TRUE; } LEAVE_FUNC(); return bResult; } //************************************************************************* // RemoveAllSWBreakpoints() // //************************************************************************* BOOLEAN RemoveAllSWBreakpoints(BOOLEAN bEvenPermanents) { PSW_BP p; BOOLEAN bResult = FALSE; ULONG i; ENTER_FUNC(); DPRINT((0,"RemoveAllSWBreakpoint()\n")); for(i=0;i<(sizeof(aSwBreakpoints)/sizeof(SW_BP));i++) { p = &aSwBreakpoints[i]; if(p->bUsed == TRUE) { if(bEvenPermanents) { if(IsAddressValid(p->ulAddress) && p->bVirtual==FALSE) { BOOLEAN isWriteable; if( !( isWriteable = IsAddressWriteable(p->ulAddress) ) ) SetAddressWriteable(p->ulAddress,TRUE); *(PUCHAR)(p->ulAddress) = p->ucOriginalOpcode; if( !isWriteable ) SetAddressWriteable(p->ulAddress,FALSE); bResult = TRUE; } PICE_memset(p,0,sizeof(*p)); } else { if(!p->bPermanent) { if(IsAddressValid(p->ulAddress) && p->bVirtual==FALSE) { BOOLEAN isWriteable; if( !( isWriteable = IsAddressWriteable(p->ulAddress) ) ) SetAddressWriteable(p->ulAddress,TRUE); *(PUCHAR)(p->ulAddress) = p->ucOriginalOpcode; if( !isWriteable ) SetAddressWriteable(p->ulAddress,FALSE); bResult = TRUE; } PICE_memset(p,0,sizeof(*p)); } } } } LEAVE_FUNC(); return bResult; } //************************************************************************* // IsPermanentSWBreakpoint() // //************************************************************************* PSW_BP IsPermanentSWBreakpoint(ULONG ulAddress) { PSW_BP p; ULONG i; ENTER_FUNC(); DPRINT((0,"IsPermanentSWBreakpoint(%.8X)\n",ulAddress)); for(i=0;i<(sizeof(aSwBreakpoints)/sizeof(aSwBreakpoints[0]));i++) { p = &aSwBreakpoints[i]; if(p->ulAddress == ulAddress && p->bUsed == TRUE && p->bPermanent == TRUE) { LEAVE_FUNC(); return p; } } LEAVE_FUNC(); return NULL; } //************************************************************************* // ListSWBreakpoints() // //************************************************************************* void ListSWBreakpoints(void) { PSW_BP p; ULONG i; LPSTR pSymbolName; PDEBUG_MODULE pMod; ENTER_FUNC(); DPRINT((0,"ListSWBreakpoints()\n")); for(i=0;i<(sizeof(aSwBreakpoints)/sizeof(SW_BP));i++) { p = &aSwBreakpoints[i]; if(p->bUsed == TRUE && p->bVirtual == FALSE) { if((pSymbolName = FindFunctionByAddress(p->ulAddress,NULL,NULL)) ) { pMod = FindModuleFromAddress(p->ulAddress); PICE_sprintf(tempBp,"[%u] %.8X (%S!%s) %s\n",i,p->ulAddress,pMod->name,pSymbolName,p->bPermanent?"PERMANENT":""); } else { if(ScanExportsByAddress(&pSymbolName,p->ulAddress)) PICE_sprintf(tempBp,"[%u] %.8X (%s) %s\n",i,p->ulAddress,pSymbolName,p->bPermanent?"PERMANENT":""); else PICE_sprintf(tempBp,"[%u] %.8X (no symbol) %s\n",i,p->ulAddress,p->bPermanent?"PERMANENT":""); } Print(OUTPUT_WINDOW,tempBp); } else if(p->bUsed == TRUE) { PICE_sprintf(tempBp,"[%u] xxxxxxxx (%s!%s) VIRTUAL\n",i,p->szModName,p->szFunctionName); Print(OUTPUT_WINDOW,tempBp); } } LEAVE_FUNC(); } //************************************************************************* // RevirtualizeBreakpointsForModule() // //************************************************************************* void RevirtualizeBreakpointsForModule(PDEBUG_MODULE pMod) { ULONG i,start,end; PSW_BP p; char temp[DEBUG_MODULE_NAME_LEN]; DPRINT((0,"RevirtualizeBreakpointsForModule(%x)\n",(ULONG)pMod)); if(IsRangeValid((ULONG)pMod,sizeof(DEBUG_MODULE)) ) { start = (ULONG)pMod->BaseAddress; end = (ULONG)pMod->BaseAddress+pMod->size; DPRINT((0,"RevirtualizeBreakpointsForModule(): module %x (%x-%x)\n",(ULONG)pMod,start,end)); // go through all breakpoints for(i=0;i<(sizeof(aSwBreakpoints)/sizeof(SW_BP));i++) { p = &aSwBreakpoints[i]; // if it's used and installed and not virtual if(p->bUsed && p->bInstalled && p->bVirtual == FALSE) { // make sure we're in module's bound if(p->ulAddress>=start && p->ulAddressulAddress)) { // from now on it's virtual again p->bVirtual = TRUE; if(IsAddressValid(p->ulAddress) ) { BOOLEAN isWriteable; if( !( isWriteable = IsAddressWriteable(p->ulAddress) ) ) SetAddressWriteable(p->ulAddress,TRUE); DPRINT((0,"RevirtualizeBreakpointsForModule(): restoring original opcode @ %x\n",p->ulAddress)); *(PUCHAR)(p->ulAddress) = p->ucOriginalOpcode; if( !isWriteable ) SetAddressWriteable(p->ulAddress,FALSE); } else { DPRINT((0,"RevirtualizeBreakpointsForModule(): could not restore original opcode @ %x\n",p->ulAddress)); } // skip past the module separator while(*pFind!='!')pFind++; pFind++; // remember the function and the module for reinstallation CopyWideToAnsi(temp,pMod->name); PICE_strcpy(p->szModName,temp); PICE_strcpy(p->szFunctionName,pFind); DPRINT((0,"RevirtualizeBreakpointsForModule(): %s!%s\n",p->szModName,p->szFunctionName)); // if function name contains a '+' it's an offset pFind = p->szFunctionName; while(*pFind!=0) { DPRINT((0,"RevirtualizeBreakpointsForModule(): [1] %s\n",pFind)); // found any offset to function if(*pFind=='+') { *pFind=0; break; } pFind++; } DPRINT((0,"RevirtualizeBreakpointsForModule(): [2] %s\n",p->szFunctionName)); if(ScanExports(p->szFunctionName,&ulFunctionAddress)) { p->ulAddress -= ulFunctionAddress; DPRINT((0,"RevirtualizeBreakpointsForModule(): [1] function @ %x offset = %x\n",ulFunctionAddress,p->ulAddress)); } else { if((ulFunctionAddress = FindFunctionInModuleByName(p->szFunctionName,pMod)) ) { p->ulAddress -= ulFunctionAddress; DPRINT((0,"RevirtualizeBreakpointsForModule(): [2] function @ %x offset = %x\n",ulFunctionAddress,p->ulAddress)); } else { DPRINT((0,"RevirtualizeBreakpointsForModule(): Breakpoint %u could not be virtualized properly!\n",i)); PICE_sprintf(tempBp,"Breakpoint %u could not be virtualized properly!\n",i); Print(OUTPUT_WINDOW,tempBp); } } } else { DPRINT((0,"RevirtualizeBreakpointsForModule(): function for %x not found!\n",p->ulAddress)); PICE_memset(p,0,sizeof(*p)); } } } } } } //************************************************************************* // NewInt3Handler() // //************************************************************************* __asm__ ("\n\t \ NewInt3Handler:\n\t \ pushl $" STR(REASON_INT3) "\n\t \ // call debugger loop\n\t \ jmp NewInt31Handler\n\t \ "); //************************************************************************* // InstallInt3Hook() // //************************************************************************* void InstallInt3Hook(void) { ULONG LocalInt3Handler; ENTER_FUNC(); DPRINT((0,"enter InstallInt3Hook()...\n")); MaskIrqs(); if(!OldInt3Handler) { PICE_memset(aSwBreakpoints,0,sizeof(aSwBreakpoints)); __asm__("mov $NewInt3Handler,%0" :"=r" (LocalInt3Handler) : :"eax"); OldInt3Handler=SetGlobalInt(0x03,(ULONG)LocalInt3Handler); } UnmaskIrqs(); DPRINT((0,"leave InstallInt3Hook()...\n")); LEAVE_FUNC(); } //************************************************************************* // DeInstallInt3Hook() // //************************************************************************* void DeInstallInt3Hook(void) { ENTER_FUNC(); DPRINT((0,"enter DeInstallInt3Hook()...\n")); MaskIrqs(); if(OldInt3Handler) { RemoveAllSWBreakpoints(TRUE); SetGlobalInt(0x03,(ULONG)OldInt3Handler); OldInt3Handler=0; } UnmaskIrqs(); DPRINT((0,"leave DeInstallInt3Hook()...\n")); LEAVE_FUNC(); }