// Update state and send info to client: eip module's base address, offset, name HRESULT UpdateState() { bool bRes = FALSE; HRESULT hRes = E_FAIL; DWORD dwRes = 0; ULONG64 PrevBase = g_Base; ULONG NameSize = 0; HANDLE hProcess; g_Offset = GetContextData(UE_CIP); bRes = DbgGetModuleAt((duint)g_Offset, g_NameBuffer); if (!bRes) { _plugin_logprintf("[sync] UpdateState: no module at %p...\n", g_Offset); return hRes; } g_Base = DbgModBaseFromName(g_NameBuffer); if (!g_Base) { _plugin_logputs("[sync] UpdateState: could not get module base..."); return hRes; } // Check if we are in a new module if ((g_Base != PrevBase) & g_SyncAuto) { hProcess = ((PROCESS_INFORMATION*)TitanGetProcessInformation())->hProcess; dwRes = GetModuleBaseNameA(hProcess, (HMODULE)g_Base, g_NameBuffer, MAX_MODULE_SIZE); if (dwRes==0) { _plugin_logputs("[sync] could not get module base name..."); return hRes; } #if VERBOSE >= 2 _plugin_logprintf("[sync] UpdateState: module : \"%s\"\n", g_NameBuffer); #endif hRes = TunnelSend("[notice]{\"type\":\"module\",\"path\":\"%s\"}\n", g_NameBuffer); if (FAILED(hRes)){ return hRes; } } hRes = TunnelSend("[sync]{\"type\":\"loc\",\"base\":%llu,\"offset\":%llu}\n", g_Base, g_Offset); return hRes; }
// Update state and send info to client: eip module's base address, offset, name HRESULT UpdateState() { HRESULT hRes; ULONG64 PrevBase = g_Base; ULONG NameSize=0; /* msdn: GetInstructionOffset method returns the location of the current thread's current instruction. */ hRes=g_ExtRegisters->GetInstructionOffset(&g_Offset); #if VERBOSE >= 2 if(SUCCEEDED(hRes)) dprintf("[sync] GetInstructionOffset 0x%x\n", g_Offset); #endif /* msdn: GetModuleByOffset method searches through the target's modules for one whose memory allocation includes the specified location. */ hRes=g_ExtSymbols->GetModuleByOffset(g_Offset, 0, NULL, &g_Base); #if VERBOSE >= 2 if(SUCCEEDED(hRes)) dprintf("[sync] base address 0x%x\n", g_Base); #endif // Check if we are in a new module if ((g_Base != PrevBase) & g_SyncAuto) { /* Update module name stored in g_NameBuffer msdn: GetModuleNameString method returns the name of the specified module. */ hRes=g_ExtSymbols2->GetModuleNameString(DEBUG_MODNAME_LOADED_IMAGE, DEBUG_ANY_ID, g_Base, g_NameBuffer, MAX_NAME, &NameSize); if(SUCCEEDED(hRes) & (NameSize>0) & (((char) *g_NameBuffer)!=0)) { #if VERBOSE >= 2 dprintf("[sync] DEBUG_MODNAME_LOADED_IMAGE: \"%s\"\n", g_NameBuffer); #endif hRes=TunnelSend("[notice]{\"type\":\"module\",\"path\":\"%s\"}\n", g_NameBuffer); if(FAILED(hRes)) return hRes; } } hRes=TunnelSend("[sync]{\"type\":\"loc\",\"base\":%llu,\"offset\":%llu}\n", g_Base, g_Offset); return hRes; }
HRESULT CALLBACK idbn(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; int NbBytesRecvd; char * msg = NULL; INIT_API(); if (!Args || !*Args) { dprintf("[sync] !idbn <idb num>\n"); return E_FAIL; } #if VERBOSE >= 2 dprintf("[sync] !idbn called\n"); #endif hRes=TunnelSend("[notice]{\"type\":\"idb_n\",\"idb\":\"%s\"}\n", Args); if (FAILED(hRes)){ dprintf("[sync] !idblist failed\n"); return hRes; } hRes=TunnelReceive(&NbBytesRecvd, &msg); if (SUCCEEDED(hRes) & (NbBytesRecvd>0) & (msg != NULL)){ dprintf("%s\n", msg); free(msg); } return hRes; }
HRESULT CALLBACK idblist(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; UNREFERENCED_PARAMETER(Args); INIT_API(); int NbBytesRecvd; char * msg = NULL; #if VERBOSE >= 2 dprintf("[sync] !idblist called\n"); #endif hRes=TunnelSend("[notice]{\"type\":\"idb_list\"}\n"); if (FAILED(hRes)){ dprintf("[sync] !idblist failed\n"); return hRes; } hRes=TunnelReceive(&NbBytesRecvd, &msg); if (SUCCEEDED(hRes) & (NbBytesRecvd>0) & (msg != NULL)){ dprintf("%s\n", msg); free(msg); } return hRes; }
HRESULT CALLBACK syncmodauto(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; char * msg; INIT_API(); #if VERBOSE >= 2 dprintf("[sync] !syncmodauto called\n"); #endif if (!Args || !*Args) goto syncmod_arg_fail; if(strcmp("on", Args)==0) { msg = (char *)Args; g_SyncAuto = true; } else if (strcmp("off", Args)==0) { msg = (char *)Args; g_SyncAuto = false; } else goto syncmod_arg_fail; hRes=TunnelSend("[notice]{\"type\":\"sync_mode\",\"auto\":\"%s\"}\n", msg); return hRes; syncmod_arg_fail: dprintf("[sync] usage !syncmodauto <on|off>\n"); return E_FAIL; }
HRESULT CALLBACK jmpraw(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes; ULONG64 Offset =0; ULONG RemainderIndex; DEBUG_VALUE DebugValue = {}; INIT_API(); if (!Args || !*Args) { dprintf("[sync] !jumpraw <expression>\n"); return E_FAIL; } /* msdn: Evaluate method evaluates an expression, returning the result. */ hRes=g_ExtControl->Evaluate(Args, DEBUG_VALUE_INT64, &DebugValue, &RemainderIndex); if(FAILED(hRes)) { dprintf("[sync] jumpraw: failed to evaluate expression\n"); return E_FAIL; } Offset = (ULONG64)DebugValue.I64; hRes=TunnelSend("[sync]{\"type\":\"loc\",\"offset\":%llu}\n", Offset); return hRes; }
HRESULT TunnelClose() { HRESULT hRes=S_OK; int iResult; if(SUCCEEDED(TunnelIsUp())) { hRes=TunnelSend("[notice]{\"type\":\"dbg_quit\",\"msg\":\"dbg disconnected\"}\n"); if(FAILED(hRes)) return hRes; } if(!(g_Sock == INVALID_SOCKET)){ iResult = closesocket(g_Sock); g_Sock = INVALID_SOCKET; if (iResult == SOCKET_ERROR) dprintf("[sync] closesocket failed with error %d\n", WSAGetLastError()); } dprintf("[sync] sync is off\n"); g_Synchronized=FALSE; WSACleanup(); return hRes; }
HRESULT CALLBACK bc(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; ULONG DwRGB = 0; ULONG RemainderIndex; DEBUG_VALUE DebugValue = {}; char * msg; char * rgb_msg[64] = {0}; INIT_API(); #if VERBOSE >= 2 dprintf("[sync] !bc called\n"); #endif if (!Args || !*Args) { msg = "oneshot"; } else if (strcmp("on", Args) == 0) { msg = (char *)Args; } else if (strcmp("off", Args) == 0) { msg = (char *)Args; } else if (strncmp("set ", Args, 4) == 0) { *((char *)Args+3) = 0; hRes=g_ExtControl->Evaluate((char *) (Args+4), DEBUG_VALUE_INT32, &DebugValue, &RemainderIndex); if(FAILED(hRes)) { dprintf("[sync] failed to evaluate RGB code\n"); return E_FAIL; } DwRGB = (ULONG)DebugValue.I32; _snprintf_s((char *) rgb_msg, 64, _TRUNCATE , "%s\", \"rgb\":%lu, \"reserved\":\"", Args, DwRGB); msg = (char *)rgb_msg; } else { dprintf("[sync] usage !bc <|||on|off|set 0xBBGGRR> >\n"); return E_FAIL; } hRes=TunnelSend("[sync]{\"type\":\"bc\",\"msg\":\"%s\",\"base\":%llu,\"offset\":%llu}\n", msg, g_Base, g_Offset); return hRes; }
STDMETHODIMP StdioOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) { UNREFERENCED_PARAMETER(Mask); HRESULT hRes; errno_t err; size_t cbBinary; LPTSTR pszString; cbBinary = strlen(Text); if (g_OutputCbLocal) { if ((g_CmdBuffer.len + cbBinary) < (MAX_CMD-2)) { err = strcpy_s(g_CmdBuffer.buffer+g_CmdBuffer.len, MAX_CMD-g_CmdBuffer.len, Text); if (err) { g_CmdBuffer.hRes = E_FAIL; g_CmdBuffer.len = 0; } else { g_CmdBuffer.hRes = S_OK; g_CmdBuffer.len += cbBinary; } } } else { hRes = ToBase64((const byte *)Text, (unsigned int)cbBinary, &pszString); if (SUCCEEDED(hRes)) { TunnelSend("[sync] {\"type\":\"cmd\",\"msg\":\"%s\", \"base\":%llu,\"offset\":%llu}\n", pszString, g_Base, g_Offset); free(pszString); } } return S_OK; }
HRESULT CALLBACK fcmt(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; INIT_API(); #if VERBOSE >= 2 dprintf("[sync] !fcmt called\n"); #endif if (!Args || !*Args) { Args = ""; } hRes=TunnelSend("[sync]{\"type\":\"fcmt\",\"msg\":\"%s\",\"base\":%llu,\"offset\":%llu}\n", Args, g_Base, g_Offset); return hRes; }
HRESULT CALLBACK lbl(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; INIT_API(); #if VERBOSE >= 2 dprintf("[sync] !lbl called\n"); #endif if (!Args || !*Args) { dprintf("[sync] !lbl <comment to add>\n"); return E_FAIL; } hRes=TunnelSend("[sync]{\"type\":\"lbl\",\"msg\":\"%s\",\"base\":%llu,\"offset\":%llu}\n", Args, g_Base, g_Offset); return hRes; }
HRESULT sync(PSTR Args) { HRESULT hRes = S_OK; // Reset global state g_Base = NULL; g_Offset = NULL; if (g_Synchronized) { _plugin_logputs("[sync] sync update\n"); UpdateState(); goto Exit; } if (FAILED(hRes = TunnelCreate(g_DefaultHost, g_DefaultPort))) { _plugin_logputs("[sync] sync failed\n"); goto Exit; } _plugin_logputs("[sync] probing sync\n"); hRes = TunnelSend("[notice]{\"type\":\"new_dbg\",\"msg\":\"dbg connect - x64_dbg\",\"dialect\":\"x64_dbg\"}\n"); if (FAILED(hRes)) { _plugin_logputs("[sync] sync aborted\n"); goto Exit; } _plugin_logprintf("[sync] sync is now enabled with host %s\n", g_DefaultHost); UpdateState(); CreatePollTimer(); Exit: return hRes; }
HRESULT CALLBACK jmpto(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes; ULONG64 Base, Offset =0; ULONG NameSize=0; ULONG RemainderIndex; DEBUG_VALUE DebugValue = {}; INIT_API(); if (!Args || !*Args) { dprintf("[sync] !jumpto <expression>\n"); return E_FAIL; } /* msdn: Evaluate method evaluates an expression, returning the result. */ hRes=g_ExtControl->Evaluate(Args, DEBUG_VALUE_INT64, &DebugValue, &RemainderIndex); if(FAILED(hRes)) { dprintf("[sync] jumpto: failed to evaluate expression\n"); return E_FAIL; } Offset = (ULONG64)DebugValue.I64; /* msdn: GetModuleByOffset method searches through the target's modules for one whose memory allocation includes the specified location. */ hRes=g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &Base); if(FAILED(hRes)) { dprintf("[sync] jumpto: failed to get module base for address 0x%x\n", Base); return E_FAIL; } /* Update module name stored in g_NameBuffer msdn: GetModuleNameString method returns the name of the specified module. */ hRes=g_ExtSymbols2->GetModuleNameString(DEBUG_MODNAME_LOADED_IMAGE, DEBUG_ANY_ID, Base, g_NameBuffer, MAX_NAME, &NameSize); if(!(SUCCEEDED(hRes) & (NameSize>0) & (((char) *g_NameBuffer)!=0))) { dprintf("[sync] jumpto: failed to get module name for target address\n"); return E_FAIL; } // Check if we are in a new module if(g_Base != Base) { // Update base address of current active module g_Base = Base; hRes=TunnelSend("[notice]{\"type\":\"module\",\"path\":\"%s\"}\n", g_NameBuffer); if(FAILED(hRes)) return hRes; } hRes=TunnelSend("[sync]{\"type\":\"loc\",\"base\":%llu,\"offset\":%llu}\n", Base, Offset); return hRes; }
HRESULT CALLBACK sync(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; PCSTR Host; PSTR pszId=NULL; INIT_API(); // Reset global state g_Base = NULL; g_Offset = NULL; #if VERBOSE >= 2 dprintf("[sync] sync function called\n"); #endif if(g_Synchronized) { dprintf("[sync] sync update\n"); UpdateState(); goto exit; } if (!Args || !*Args) { dprintf("[sync] No argument found, using default host (%s:%s)\n", g_DefaultHost, g_DefaultPort); Host=g_DefaultHost; }else{ Host=Args; } if(FAILED(hRes=TunnelCreate(Host, g_DefaultPort))) { dprintf("[sync] sync failed\n"); goto exit; } dprintf("[sync] probing sync\n"); if(FAILED(hRes=Identity(&pszId))) { dprintf("[sync] get identity failed\n"); goto exit; } hRes=TunnelSend("[notice]{\"type\":\"new_dbg\",\"msg\":\"dbg connect - %s\"}\n", pszId); if(SUCCEEDED(hRes)) { dprintf("[sync] sync is now enabled with host %s\n", Host); UpdateState(); CreatePollTimer(); } else { dprintf("[sync] sync aborted\n"); } exit: if(!(pszId==NULL)) free(pszId); return hRes; }
HRESULT CALLBACK modcheck(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes; DWORD cbBinary; int NbBytesRecvd = 0; LPSTR pszResString= NULL; CHAR *msg = NULL; CHAR *type; CHAR cmd[64] = {0}; BOOL bUsePdb = TRUE; INIT_API(); if(!g_Synchronized) { dprintf("[sync] please enable sync\n"); return E_FAIL; } if (!(*g_NameBuffer)) { dprintf("[sync] no module\n"); return E_FAIL; } // check args // md5 is accepted only with local debuggee if (!Args || !*Args) { bUsePdb=TRUE; } else if(strcmp("md5", Args)==0) { bUsePdb=FALSE; if (!(IsLocalDebuggee())) { dprintf("[sync] can't use md5 check with non local debuggee\n"); return E_FAIL; } } else dprintf("[sync] unknown argument, defaulting to pdb match\n"); // The debugger does not know if an IDB client // is actually connected to the dispatcher. // First disable tunnel polling for commands (happy race...) ReleasePollTimer(); // default behavior is to used !IToldYouSo command. if (bUsePdb) { type = "pdb"; _snprintf_s(cmd, 64, _TRUNCATE , "!itoldyouso %x", g_Base); // g_CommandBuffer first four bytes should contains // return value for command exec hRes=LocalCmd(Client, cmd); if (FAILED(hRes) || FAILED(*g_CommandBuffer)) { dprintf("[sync] failed to evaluate !ItoldYouSo command\n"); goto Exit; } cbBinary = (DWORD) strlen(g_CommandBuffer+4); if (cbBinary == 0) { dprintf(" ItoldYouSo return empty result\n"); goto Exit; } dprintf("%s\n", g_CommandBuffer+4); hRes=ToBase64((const byte *)g_CommandBuffer+4, cbBinary, &pszResString); if (FAILED(hRes)) { dprintf("[sync] modcheck ToBase64 failed\n"); goto Exit; } } else { type="md5"; hRes=modmd5(&pszResString); if (FAILED(hRes)) { dprintf("[sync] modcheck modmd5 failed\n"); goto Exit; } dprintf(" MD5: %s\n", pszResString); } hRes = TunnelSend("[sync]{\"type\":\"modcheck\",\"%s\":\"%s\"}\n", type, pszResString); if (FAILED(hRes)) { dprintf("[sync] modcheck send failed\n"); goto Exit; } // Let time for the IDB client to reply if it exists Sleep(150); // Poll tunnel hRes=TunnelPoll(&NbBytesRecvd, &msg); if (FAILED(hRes)) { dprintf("[sync] modcheck poll failed\n"); goto Exit; } else { if ((NbBytesRecvd>0) & (msg != NULL)) dprintf("%s\n", msg); else dprintf(" -> no reply, make sure an idb is enabled first\n"); } Exit: // Re-enable tunnel polling CreatePollTimer(); if (pszResString) free(pszResString); if (msg) free(msg); return hRes; }
HRESULT CALLBACK bpcmds(PDEBUG_CLIENT4 Client, PCSTR Args) { HRESULT hRes=S_OK; char *msg, *decoded, *query; LPSTR pszString; int NbBytesRecvd; size_t cbBinary; INIT_API(); #if VERBOSE >= 2 dprintf("[sync] !bpcmds called\n"); #endif if(!g_Synchronized) { dprintf("[sync] please enable sync\n"); return E_FAIL; } if (!Args || !*Args){ msg = "query"; } else { msg = (char *)Args; } if ((strncmp("load", msg, 4)==0) || (strncmp("query", msg, 5)==0)) { dprintf("[sync] query idb for bpcmds\n"); hRes=TunnelSend("[sync]{\"type\":\"bps_get\"}\n"); } else if(strncmp("save", msg, 4)==0) { dprintf("[sync] dumping bpcmds to idb\n"); hRes=LocalCmd(Client, ".bpcmds"); // g_CommandBuffer first four bytes should contains // return value for command exec if (FAILED(hRes) || FAILED(*g_CommandBuffer)) { dprintf("[sync] failed to evaluate .bpcmds command\n"); return E_FAIL; } cbBinary = strlen(g_CommandBuffer+4); dprintf("%s\n", g_CommandBuffer); hRes = ToBase64((const byte *)g_CommandBuffer+4, (unsigned int)cbBinary, &pszString); if (SUCCEEDED(hRes)) { hRes = TunnelSend("[sync]{\"type\":\"bps_set\",\"msg\":\"%s\"}\n", pszString); free(pszString); } } else { dprintf("[sync] usage !bpcmds <||query|save|load|\n"); return E_FAIL; } // Check if we failed to query the idb client if (FAILED(hRes)){ dprintf("[sync] !bpcmds failed\n"); return hRes; } // Get result from idb client hRes=TunnelReceive(&NbBytesRecvd, &query); if (!(SUCCEEDED(hRes) & (NbBytesRecvd>0) & (query != NULL))) { dprintf("[sync] !bpcmds failed\n"); return hRes; } // Handle result if(strncmp("load", msg, 4)==0) { hRes = FromBase64(query, (BYTE **)(&decoded)); if (SUCCEEDED(hRes)) { hRes = ExecCmdList(decoded); free(decoded); } } else if(strncmp("query", msg, 4)==0) { hRes = FromBase64(query, (BYTE **)(&decoded)); if (SUCCEEDED(hRes)) { dprintf("[sync] idb's saved bpcmds:\n %s\n", decoded); free(decoded); } } else { dprintf("%s\n", query); } free(query); return hRes; }