1
0
Fork 0

Compare commits

...

89 Commits

Author SHA1 Message Date
Duncan Ogilvie caa578a029 Add more descriptive error messages in the trace browser 2024-02-19 03:10:42 +01:00
Duncan Ogilvie d08913bc54 Support copying in the StdTable with multiple items selected 2024-02-19 03:10:17 +01:00
Duncan Ogilvie da43aca116 Fix a few compilation warnings in the GUI 2024-02-19 03:09:53 +01:00
Duncan Ogilvie 7a3851a607 Fix a bug in the vardel command 2024-02-19 03:08:45 +01:00
Duncan Ogilvie a5f73b479f Also implement the syscall.id expression function 2024-02-19 03:08:29 +01:00
Duncan Ogilvie 9e4c1a4d26 Add a syscall.name expression function 2024-02-19 02:29:08 +01:00
Duncan Ogilvie 517a855e9a Refresh the table after reordering the columns 2024-01-07 18:19:59 +01:00
Duncan Ogilvie f3cb3443d1 Print NTSTATUS value in info box 2024-01-06 21:23:08 +01:00
Duncan Ogilvie c3642c35be Fix a bug in zydis_wrapper
Introduced in #3192
2024-01-06 21:21:56 +01:00
Duncan Ogilvie 399b19f847 Keep loaddll running after the DLL is loaded
See #3294
2024-01-06 20:07:18 +01:00
Duncan Ogilvie 9c07d82dc8 Replace broken line breaking in goto dialog with tooltip 2024-01-06 01:31:19 +01:00
Duncan Ogilvie a6e448b598 Truncate first line of disassembly popup to 100 characters 2024-01-06 01:30:36 +01:00
Duncan Ogilvie ec9a2a2af1
Merge pull request #3300 from torusrxxx/patch000000fe
Save module party
2024-01-05 22:25:17 +01:00
Duncan Ogilvie 9f6d396c4e
Merge pull request #3298 from torusrxxx/patch000000fd
Add symbol online search to symbol view
2024-01-05 22:23:01 +01:00
torusrxxx a006d75ee5
fix VS2013 2023-12-30 11:23:07 +08:00
torusrxxx e30a70ad4d
Save module party 2023-12-29 20:36:50 +08:00
torusrxxx 8cb5459629
Add symbol online search to symbol view 2023-12-29 16:26:48 +08:00
Duncan Ogilvie b683fc6a59
Merge pull request #3278 from foralost/3051_Switch__to_thread_handles
#3051: Follow a thread from handles window
2023-12-21 21:33:38 +01:00
Duncan Ogilvie 62129b426e
Merge pull request #3292 from foralost/3134_Highlight_mode_NOP
#3134: Avoiding having space in MnemonicNop instruction
2023-12-21 21:31:23 +01:00
foralost 9ed6033fb6 Avoiding having space in MnemonicNop instruction 2023-12-18 12:25:30 +01:00
Duncan Ogilvie afb5ac45c5 Simplify the showthreadid command 2023-12-18 00:44:24 +01:00
foralost ab9c28549a Switch to Thread from Handles: getCellUserdata fix 2023-12-17 00:03:29 +01:00
foralost 0853cafadc
Merge branch 'x64dbg:development' into 3051_Switch__to_thread_handles 2023-12-16 15:51:39 +01:00
Duncan Ogilvie 81bc337e52 Implement getCellUserdata 2023-12-15 20:32:47 +01:00
Duncan Ogilvie ee13290541 Add support for getCellUserdata in StdSearchListView 2023-12-15 20:32:47 +01:00
Duncan Ogilvie 04c5050015
Merge pull request #3285 from x64dbg/ui-lagging
Fix UI lagging
2023-12-04 17:45:57 +01:00
Duncan Ogilvie e2093d2d89
Merge pull request #3283 from torusrxxx/patch000000fc
Double click to follow wndproc
2023-11-28 08:37:19 +01:00
Duncan Ogilvie 3eb2070c3a Remove some performance bottlenecks from the memory map update
Thanks to @AnthonyPrintup for bringing this to my attention
2023-11-27 23:11:12 +01:00
Duncan Ogilvie f56fa5ce23 Delay at least 100ms between individual UI updates
Thanks to @JustasMasiulis for the help!
2023-11-27 23:09:19 +01:00
torusrxxx aa85488fe5
double click to follow window proc 2023-11-27 11:33:34 +08:00
foralost 911329e8e6 Keeping the style (nice icons) for the CallStackView 2023-11-26 18:16:02 +01:00
foralost 03baaa1ae3 Adding Follow in Threads option and setting the Thread ID 2023-11-26 18:15:45 +01:00
Duncan Ogilvie b17fc86af7
Merge pull request #3281 from foralost/3238_File_does_not_exists_bracket
#3238: Escaping { from any command argument
2023-11-26 17:16:12 +01:00
Duncan Ogilvie 93ac4548f7
Merge pull request #3275 from torusrxxx/patch000000fb
fix No such signal in graph view
2023-11-26 17:13:17 +01:00
Duncan Ogilvie 03c6f3b6a5
Merge pull request #3277 from ZehMatt/optimize-pagerights
Optimize Page Rights UI
2023-11-26 17:12:35 +01:00
foralost 16fccbd2d4 God forgive me: Escaping { from file path for init 2023-11-26 11:02:05 +01:00
ζeh Matt 050c989dc3
Improve performance for deselection a large amount 2023-11-24 17:52:15 +02:00
ζeh Matt 218d784a7e
Optimize select all for the PageMemoryRights UI 2023-11-24 17:52:12 +02:00
torusrxxx f8567b59f5
Modify various functions in graph view with const 2023-11-21 15:51:54 +08:00
torusrxxx eff762460f
fix No such signal 2023-11-21 15:42:14 +08:00
Duncan Ogilvie 570aaea06d
Merge pull request #3272 from GermanAizek/fix-numa
Fixed get count threads for multi-cpu system with NUMA architecture
2023-11-18 19:38:30 +01:00
Duncan Ogilvie d2f6ba72cc Clean up GetThreadCount for XP 2023-11-18 17:47:21 +01:00
Duncan Ogilvie 61814b2da7
Merge pull request #3271 from torusrxxx/patch000000fa
Auto switch thread when following callstack
2023-11-18 02:22:40 +01:00
torusrxxx 715831637a Auto switch thread when following callstack 2023-11-18 02:22:17 +01:00
Duncan Ogilvie 01c239534a
Merge pull request #3260 from foralost/3163-Long_symbol_name
#3163: Long symbol name
2023-11-18 02:16:47 +01:00
Duncan Ogilvie 2747f72f38
Merge pull request #3264 from leetfin/development
Use gender neutral language
2023-11-18 02:15:38 +01:00
Duncan Ogilvie 4193000159
Merge pull request #3263 from habipakyol/patch-1
Fix typo in CPUMultiDump.cpp
2023-11-18 02:15:01 +01:00
Duncan Ogilvie a320227378
Merge pull request #3262 from foralost/3232-follow_rename_thread_callstack
#3232: Providing Rename Thread and etc. for CallStack View
2023-11-18 02:14:25 +01:00
Duncan Ogilvie 7c2276681a Clean up the thread renaming a bit 2023-11-18 02:13:51 +01:00
Duncan Ogilvie b2b104b79f Introduce DbgCmdEscape 2023-11-18 02:13:51 +01:00
Duncan Ogilvie 95b790eab1 Rename addr_text to addrText 2023-11-18 02:13:47 +01:00
German Semenov eeab4c47ed Fixed get count threads for multi-cpu system with NUMA architecture 2023-11-14 17:20:32 +03:00
Duncan Ogilvie 373b3a538f Disable core.autocrlf for all files 2023-11-14 12:50:42 +01:00
Duncan Ogilvie 44c3d39165 Allow strings in the ternary expression function 2023-11-13 01:30:36 +01:00
Duncan Ogilvie 923b894df2 Fix wraparound in disassembly (thanks @shocoman)
Closes #3228
2023-11-12 23:04:19 +01:00
foralost c9d80e5c99 Introducing showthreadid command
Additional options in CallStackView
2023-11-08 18:04:50 +01:00
Duncan Ogilvie 65d57bfd2e Quote the result of a string expression function in the log 2023-11-07 18:18:58 +01:00
Duncan Ogilvie 2e1fd1f289 Add optional count argument to the string expression functions 2023-11-07 18:18:58 +01:00
Duncan Ogilvie b84c293f15 Implement optional arguments for expression functions 2023-11-07 18:18:58 +01:00
Duncan Ogilvie 50d7d988f6 Store the number of expression function arguments in the token 2023-11-07 18:18:58 +01:00
Duncan Ogilvie 735d3ca5f9 Escape logged strings returned from expression functions 2023-11-07 18:18:58 +01:00
Duncan Ogilvie dfda450b41 Allow printing empty strings using the "{s:addr}" format 2023-11-07 18:18:58 +01:00
Duncan Ogilvie 49f9487a59 Allow zero-sized string for utf16/utf8 format type 2023-11-07 18:18:56 +01:00
Duncan Ogilvie 7f9dc7fc04 Allow reading/writing 0 bytes of memory 2023-11-07 18:16:16 +01:00
Duncan Ogilvie 50fc52b0d2 Fix a bug where setTableOffset(0) did nothing
Closes #3254
2023-11-02 17:24:45 +01:00
Duncan Ogilvie 61f99ae5c9 Clean up the code in SearchListView 2023-11-02 17:24:11 +01:00
Duncan Ogilvie e3e6a8a9fa Fix signal connection issues in ReferenceView
Leftovers from the refactor of int -> duint
2023-11-02 17:23:48 +01:00
Duncan Ogilvie 8ae5982502 Delete unused ZydisExportConfig.h 2023-11-02 00:34:20 +01:00
Leet f4821ce331 fix weird character problem 2023-10-31 15:51:32 +00:00
Leet de527241cd
Merge branch 'x64dbg:development' into development 2023-10-31 15:49:53 +00:00
Habip Akyol a2594cf1f9
Fix typo in CPUMultiDump.cpp 2023-10-31 13:30:37 +03:00
Duncan Ogilvie 0f54d5ebcd
Merge pull request #3261 from ruslangaripov/fix_a_typo
Fix a typo
2023-10-30 18:20:49 +01:00
Ruslan Garipov d52466f981
Fix a typo
Signed-off-by: Ruslan Garipov <ruslanngaripov@gmail.com>
2023-10-30 22:00:03 +05:00
Marek Szwajka 4945c36e85 Formatting 2023-10-30 11:18:25 +01:00
foralost 0f39191c9d
Fixing character skipping 2023-10-30 10:54:33 +01:00
foralost 81d72252a8 Formatting 2023-10-29 23:39:57 +01:00
foralost c122eb9e1d Limited window size and dynamic height of errorLabel 2023-10-29 23:26:57 +01:00
Duncan Ogilvie b0392eda1c
Merge pull request #3240 from HayFieldLaps/find-pattern-ranges
Find Pattern searches now begin at start of current region
2023-10-28 15:17:38 +02:00
Duncan Ogilvie d0fb3a0b88
Merge pull request #3250 from shocoman/fix-edit-bp-dialog-bug
Fixed a bug in the Edit Breakpoint dialog that might erase double quotes
2023-10-28 15:17:21 +02:00
Duncan Ogilvie cb31cbd83b
Merge pull request #3251 from lhmouse/development
Make error messages clearer if x96dbg fails to open a file
2023-10-28 15:16:55 +02:00
Duncan Ogilvie 99f5e93592
Merge pull request #3239 from stdust/patch-1
Update HexEditDialog.ui
2023-10-28 15:16:28 +02:00
LIU Hao e9dfe3020e
Make error messages clearer if x96dbg fails to open a file
When x96dbg failed to open a file, it displayed a message box with the path as the message and a reason string as the caption. This looks weird, as the caption conveys useful information but it can usually be truncated.

We should display the reason string as the message, and the path as the caption instead.
2023-10-27 00:17:32 +08:00
shocoman 98d08ce826 Make the Edit Breakpoint dialog resizable horizontally 2023-10-26 02:08:23 +07:00
shocoman 17bd183de2 Fixed a bug in the Edit Breakpoint dialog that might erase double quotes
#3248
2023-10-26 02:08:07 +07:00
Leet 89aee8ad5b Use gender neutral language 2023-10-24 15:59:18 +00:00
dad 7dade58481 Add Start from Selection 2023-10-23 11:10:59 -06:00
dad ca637fad33 Find Pattern searches now begin at start of current region 2023-10-12 10:37:51 -06:00
stdust e5dba0ad63
Update HexEditDialog.ui 2023-10-11 15:43:04 +09:00
stdust 3409444e33
Update HexEditDialog.u
HexEditDialog's tab orders have been assigned to code for less frequently used codepages and to keep size, etc
so unnecessary tab orders should be deleted
2023-10-11 15:25:03 +09:00
100 changed files with 1730 additions and 792 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Disable core.autocrlf (https://stackoverflow.com/a/52996849/1806760)
* -text

View File

@ -1779,6 +1779,12 @@ BRIDGE_IMPEXP void GuiMenuSetEntryHotkey(int hEntry, const char* hack)
_gui_sendmessage(GUI_MENU_SET_ENTRY_HOTKEY, (void*)hEntry, (void*)hack);
}
BRIDGE_IMPEXP void GuiShowThreads()
{
_gui_sendmessage(GUI_SHOW_THREADS, 0, 0);
}
BRIDGE_IMPEXP void GuiShowCpu()
{
_gui_sendmessage(GUI_SHOW_CPU, 0, 0);

View File

@ -1152,6 +1152,7 @@ typedef enum
GUI_GRAPH,
GUI_MEMMAP,
GUI_SYMMOD,
GUI_THREADS,
} GUISELECTIONTYPE;
#define GUI_MAX_LINE_SIZE 65536
@ -1281,6 +1282,7 @@ typedef enum
GUI_SAVE_LOG, // param1=const char* file name,param2=unused
GUI_REDIRECT_LOG, // param1=const char* file name,param2=unused
GUI_STOP_REDIRECT_LOG, // param1=unused, param2=unused
GUI_SHOW_THREADS, // param1=unused, param2=unused
} GUIMSG;
//GUI Typedefs
@ -1423,6 +1425,7 @@ BRIDGE_IMPEXP void GuiMenuSetName(int hMenu, const char* name);
BRIDGE_IMPEXP void GuiMenuSetEntryName(int hEntry, const char* name);
BRIDGE_IMPEXP void GuiMenuSetEntryHotkey(int hEntry, const char* hack);
BRIDGE_IMPEXP void GuiShowCpu();
BRIDGE_IMPEXP void GuiShowThreads();
BRIDGE_IMPEXP void GuiAddQWidgetTab(void* qWidget);
BRIDGE_IMPEXP void GuiShowQWidgetTab(void* qWidget);
BRIDGE_IMPEXP void GuiCloseQWidgetTab(void* qWidget);

View File

@ -1513,7 +1513,7 @@ extern "C" DLL_EXPORT duint _dbg_sendmessage(DBGMSG type, void* param1, void* pa
case DBG_GET_STRING_AT:
{
return disasmgetstringatwrapper(duint(param1), (char*)param2);
return disasmgetstringatwrapper(duint(param1), (char*)param2, true);
}
break;

View File

@ -3,10 +3,12 @@
\brief Implements the global class.
*/
#include <windows.h>
#include "_global.h"
#include <objbase.h>
#include <shlobj.h>
#include <psapi.h>
#include <thread>
#include "DeviceNameResolver/DeviceNameResolver.h"
/**
@ -393,3 +395,53 @@ void WaitForMultipleThreadsTermination(const HANDLE* hThread, int count, DWORD t
for(int i = 0; i < count; i++)
CloseHandle(hThread[i]);
}
// This implementation supports both conventional single-cpu PC configurations
// and multi-cpu system on NUMA (Non-uniform_memory_access) architecture
// Original code from here: https://developercommunity.visualstudio.com/t/hardware-concurrency-returns-an-incorrect-result/350854
duint GetThreadCount()
{
duint threadCount = std::thread::hardware_concurrency();
typedef BOOL(*WINAPI GetLogicalProcessorInformationEx_t)(
LOGICAL_PROCESSOR_RELATIONSHIP,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
PDWORD
);
static auto p_GetLogicalProcessorInformationEx = (GetLogicalProcessorInformationEx_t)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetLogicalProcessorInformationEx");
if(p_GetLogicalProcessorInformationEx == nullptr)
{
return threadCount;
}
DWORD length = 0;
if(p_GetLogicalProcessorInformationEx(RelationAll, nullptr, &length) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return threadCount;
}
std::vector<uint8_t> buffer(length);
if(!p_GetLogicalProcessorInformationEx(RelationAll, (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)buffer.data(), &length))
{
return threadCount;
}
threadCount = 0;
for(DWORD offset = 0; offset < length;)
{
auto info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)(buffer.data() + offset);
if(info->Relationship == RelationProcessorCore)
{
for(WORD group = 0; group < info->Processor.GroupCount; ++group)
{
for(KAFFINITY mask = info->Processor.GroupMask[group].Mask; mask != 0; mask >>= 1)
{
threadCount += mask & 1;
}
}
}
offset += info->Size;
}
return threadCount;
}

View File

@ -65,6 +65,7 @@ bool IsWow64();
bool ResolveShortcut(HWND hwnd, const wchar_t* szShortcutPath, std::wstring & executable, std::wstring & arguments, std::wstring & workingDir);
void WaitForThreadTermination(HANDLE hThread, DWORD timeout = INFINITE);
void WaitForMultipleThreadsTermination(const HANDLE* hThread, int count, DWORD timeout = INFINITE);
duint GetThreadCount();
#ifdef _WIN64
#define ArchValue(x32value, x64value) x64value

View File

@ -240,7 +240,11 @@ typedef enum
{
ValueTypeNumber,
ValueTypeString,
ValueTypeAny, // Cannot be used for values, only for argTypes (to accept any type)
// Types below cannot be used for values, only for registration
ValueTypeAny,
ValueTypeOptionalNumber,
ValueTypeOptionalString,
ValueTypeOptionalAny,
} ValueType;
typedef struct

View File

@ -77,7 +77,7 @@ duint AnalysisPass::IdealThreadCount()
if(m_InternalMaxThreads == 0)
{
// Determine the maximum hardware thread count at once
duint maximumThreads = max(std::thread::hardware_concurrency(), 1);
duint maximumThreads = max(GetThreadCount(), 1);
// Don't consume 100% of the CPU, adjust accordingly
if(maximumThreads > 1)

View File

@ -8,6 +8,26 @@
#include "value.h"
#include "variable.h"
bool cbShowThreadId(int argc, char* argv[])
{
if(argc > 1)
{
duint threadId = 0;
if(!valfromstring(argv[1], &threadId, false))
return false;
SELECTIONDATA newSelection = { threadId, threadId };
if(!GuiSelectionSet(GUI_THREADS, &newSelection))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Invalid thread %s\n"), formatpidtid((DWORD)threadId).c_str());
return false;
}
}
GuiShowThreads();
return true;
}
bool cbDebugDisasm(int argc, char* argv[])
{
duint addr = 0;

View File

@ -2,6 +2,7 @@
#include "command.h"
bool cbShowThreadId(int argc, char* argv[]);
bool cbDebugDisasm(int argc, char* argv[]);
bool cbDebugDump(int argc, char* argv[]);
bool cbDebugStackDump(int argc, char* argv[]);

View File

@ -29,7 +29,8 @@ bool cbBadCmd(int argc, char* argv[])
{
if(evalue.isString)
{
dputs_untranslated(StringUtils::Escape(evalue.data).c_str());
varset("$ans", evalue.data.c_str(), true);
dprintf_untranslated("\"%s\"\n", StringUtils::Escape(evalue.data).c_str());
}
else if(evalue.DoEvaluate(value, silent, baseonly, &valsize, &isvar, &hexonly))
{

View File

@ -51,7 +51,7 @@ bool cbInstrVarDel(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
if(!vardel(argv[1], false))
if(vardel(argv[1], false) != 0)
dprintf(QT_TRANSLATE_NOOP("DBG", "Could not delete variable \"%s\"\n"), argv[1]);
else
dprintf(QT_TRANSLATE_NOOP("DBG", "Deleted variable \"%s\"\n"), argv[1]);

View File

@ -71,6 +71,7 @@ void DbSave(DbLoadSaveType saveType, const char* dbfile, bool disablecompression
EncodeMapCacheSave(root);
TraceRecord.saveToDb(root);
BpCacheSave(root);
ModCacheSave(root);
WatchCacheSave(root);
//save notes
@ -91,7 +92,7 @@ void DbSave(DbLoadSaveType saveType, const char* dbfile, bool disablecompression
//plugin data
PLUG_CB_LOADSAVEDB pluginSaveDb;
// Some plugins may wish to change this value so that all plugins after his or her plugin will save data into plugin-supplied storage instead of the system's.
// Some plugins may wish to change this value so that all plugins after their plugin will save data into plugin-supplied storage instead of the system's.
// We back up this value so that the debugger is not fooled by such plugins.
JSON pluginRoot = json_object();
pluginSaveDb.root = pluginRoot;
@ -283,6 +284,7 @@ void DbLoad(DbLoadSaveType loadType, const char* dbfile)
EncodeMapCacheLoad(root);
TraceRecord.loadFromDb(root);
BpCacheLoad(root, migrateBreakpoints);
ModCacheLoad(root);
WatchCacheLoad(root);
// Load notes
@ -340,6 +342,7 @@ void DbClear(bool terminating)
EncodeMapClear();
TraceRecord.clear();
BpClear();
ModCacheClear();
WatchClear();
GuiSetDebuggeeNotes("");

View File

@ -13,7 +13,7 @@ void disasmget(unsigned char* buffer, duint addr, DISASM_INSTR* instr, bool getr
void disasmget(duint addr, DISASM_INSTR* instr, bool getregs = true);
bool disasmispossiblestring(duint addr, STRING_TYPE* type = nullptr);
bool disasmgetstringat(duint addr, STRING_TYPE* type, char* ascii, char* unicode, int maxlen);
bool disasmgetstringatwrapper(duint addr, char* text, bool cache = true);
bool disasmgetstringatwrapper(duint addr, char* text, bool cache);
int disasmgetsize(duint addr, unsigned char* data);
int disasmgetsize(duint addr);

View File

@ -13,6 +13,7 @@ static std::unordered_map<unsigned int, String> NtStatusNames;
static std::unordered_map<unsigned int, String> ErrorNames;
static std::unordered_map<String, unsigned int> Constants;
static std::unordered_map<unsigned int, String> SyscallIndices;
static std::unordered_map<String, unsigned int> SyscallNames;
static bool UniversalCodeInit(const String & file, std::unordered_map<unsigned int, String> & names, unsigned char radix)
{
@ -253,12 +254,43 @@ bool SyscallInit()
SyscallIndices.insert({ index, syscall.Name });
}
}
ModClear(false);
// Populate the name map
for(const auto & itr : SyscallIndices)
{
SyscallNames.emplace(itr.second, itr.first);
}
// Also allow lookup with only the least significant 14 bits
// Reference: https://alice.climent-pommeret.red/posts/a-syscall-journey-in-the-windows-kernel/
for(const auto & itr : SyscallIndices)
{
auto truncated = itr.first & 0x3FFF;
if(truncated != itr.first)
{
SyscallIndices.emplace(truncated, itr.second);
}
}
// Clear the GUI
ModClear(true);
return result;
}
const String & SyscallToName(unsigned int index)
{
auto found = SyscallIndices.find(index);
auto found = SyscallIndices.find(index & 0x3FFF);
return found != SyscallIndices.end() ? found->second : emptyString;
}
}
unsigned int SyscallToId(const String & name)
{
if(name.find("Zw") == 0)
{
return SyscallToId("Nt" + name.substr(2));
}
auto found = SyscallNames.find(name);
return found != SyscallNames.end() ? found->second : -1;
}

View File

@ -22,5 +22,6 @@ std::vector<CONSTANTINFO> ConstantList();
// To use this function, use EXCLUSIVE_ACQUIRE(LockModules)
bool SyscallInit();
const String & SyscallToName(unsigned int index);
unsigned int SyscallToId(const String & name);
#endif // _EXCEPTION_H

View File

@ -71,7 +71,7 @@ void ExpressionFunctions::Init()
RegisterEasy("mod.offset,mod.fileoffset", valvatofileoffset);
RegisterEasy("mod.headerva", modheaderva);
RegisterEasy("mod.isexport", modisexport);
ExpressionFunctions::Register("mod.fromname", ValueTypeNumber, { ValueTypeString }, Exprfunc::modbasefromname, nullptr);
ExpressionFunctions::Register("mod.fromname", ValueTypeNumber, { ValueTypeString }, Exprfunc::modbasefromname);
//Process information
RegisterEasy("peb,PEB", peb);
@ -81,7 +81,7 @@ void ExpressionFunctions::Init()
//General purpose
RegisterEasy("bswap", bswap);
RegisterEasy("ternary,tern", ternary);
ExpressionFunctions::Register("ternary,tern", ValueTypeAny, { ValueTypeNumber, ValueTypeAny, ValueTypeAny }, ternary);
RegisterEasy("GetTickCount,gettickcount", gettickcount);
RegisterEasy("rdtsc", rdtsc);
@ -110,9 +110,9 @@ void ExpressionFunctions::Init()
RegisterEasy("dis.next", disnext);
RegisterEasy("dis.prev", disprev);
RegisterEasy("dis.iscallsystem", disiscallsystem);
ExpressionFunctions::Register("dis.mnemonic", ValueTypeString, { ValueTypeNumber }, Exprfunc::dismnemonic, nullptr);
ExpressionFunctions::Register("dis.text", ValueTypeString, { ValueTypeNumber }, Exprfunc::distext, nullptr);
ExpressionFunctions::Register("dis.match", ValueTypeNumber, { ValueTypeNumber, ValueTypeString }, Exprfunc::dismatch, nullptr);
ExpressionFunctions::Register("dis.mnemonic", ValueTypeString, { ValueTypeNumber }, dismnemonic);
ExpressionFunctions::Register("dis.text", ValueTypeString, { ValueTypeNumber }, distext);
ExpressionFunctions::Register("dis.match", ValueTypeNumber, { ValueTypeNumber, ValueTypeString }, dismatch);
//Trace record
RegisterEasy("tr.enabled", trenabled);
@ -158,17 +158,20 @@ void ExpressionFunctions::Init()
RegisterEasy("isdebuggeefocused", isdebuggeefocused);
// Strings
ExpressionFunctions::Register("ansi", ValueTypeString, { ValueTypeNumber }, Exprfunc::ansi, nullptr);
ExpressionFunctions::Register("ansi.strict", ValueTypeString, { ValueTypeNumber }, Exprfunc::ansi_strict, nullptr);
ExpressionFunctions::Register("utf8", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf8, nullptr);
ExpressionFunctions::Register("utf8.strict", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf8_strict, nullptr);
ExpressionFunctions::Register("utf16", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf16, nullptr);
ExpressionFunctions::Register("utf16.strict", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf16_strict, nullptr);
ExpressionFunctions::Register("strstr", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strstr, nullptr);
ExpressionFunctions::Register("stristr", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::stristr, nullptr);
ExpressionFunctions::Register("streq", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::streq, nullptr);
ExpressionFunctions::Register("strieq", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strieq, nullptr);
ExpressionFunctions::Register("strlen", ValueTypeNumber, { ValueTypeString }, Exprfunc::strlen, nullptr);
ExpressionFunctions::Register("ansi", ValueTypeString, { ValueTypeNumber, ValueTypeOptionalNumber }, Exprfunc::ansi);
ExpressionFunctions::Register("ansi.strict", ValueTypeString, { ValueTypeNumber, ValueTypeOptionalNumber }, Exprfunc::ansi_strict);
ExpressionFunctions::Register("utf8", ValueTypeString, { ValueTypeNumber, ValueTypeOptionalNumber }, Exprfunc::utf8);
ExpressionFunctions::Register("utf8.strict", ValueTypeString, { ValueTypeNumber, ValueTypeOptionalNumber }, Exprfunc::utf8_strict);
ExpressionFunctions::Register("utf16", ValueTypeString, { ValueTypeNumber, ValueTypeOptionalNumber }, Exprfunc::utf16);
ExpressionFunctions::Register("utf16.strict", ValueTypeString, { ValueTypeNumber, ValueTypeOptionalNumber }, Exprfunc::utf16_strict);
ExpressionFunctions::Register("strstr", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strstr);
ExpressionFunctions::Register("stristr", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::stristr);
ExpressionFunctions::Register("streq", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::streq);
ExpressionFunctions::Register("strieq", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strieq);
ExpressionFunctions::Register("strlen", ValueTypeNumber, { ValueTypeString }, Exprfunc::strlen);
ExpressionFunctions::Register("syscall.name", ValueTypeString, { ValueTypeNumber }, Exprfunc::syscall_name);
ExpressionFunctions::Register("syscall.id", ValueTypeNumber, { ValueTypeString }, Exprfunc::syscall_id);
}
bool ExpressionFunctions::Register(const String & name, const ValueType & returnType, const std::vector<ValueType> & argTypes, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata)
@ -180,10 +183,39 @@ bool ExpressionFunctions::Register(const String & name, const ValueType & return
if(mFunctions.count(aliases[0]))
return false;
// Return type cannot be optional
switch(returnType)
{
case ValueTypeOptionalNumber:
case ValueTypeOptionalString:
case ValueTypeOptionalAny:
return false;
default:
break;
}
// Make sure optional arguments are at the end
bool seenOptional = false;
for(const auto & argType : argTypes)
{
switch(argType)
{
case ValueTypeOptionalNumber:
case ValueTypeOptionalString:
case ValueTypeOptionalAny:
seenOptional = true;
break;
default:
if(seenOptional)
return false;
break;
}
}
Function f;
f.name = aliases[0];
f.argTypes = argTypes;
f.returnType = returnType;
f.argTypes = argTypes;
f.cbFunction = cbFunction;
f.userdata = userdata;
mFunctions[aliases[0]] = f;
@ -228,13 +260,6 @@ bool ExpressionFunctions::Call(const String & name, ExpressionValue & result, st
if(found == mFunctions.end())
return false;
const auto & f = found->second;
if(f.argTypes.size() != int(argv.size()))
return false;
for(size_t i = 0; i < argv.size(); i++)
{
if(argv[i].type != f.argTypes[i] && f.argTypes[i] != ValueTypeAny)
return false;
}
return f.cbFunction(&result, (int)argv.size(), argv.data(), f.userdata);
}

View File

@ -141,6 +141,15 @@ ExpressionParser::ExpressionParser(const String & expression)
mTokens.reserve(r);
mCurToken.reserve(r);
tokenize();
#if 0
// Print the tokens for debugging
dprintf_untranslated("'%s':\n", expression.c_str());
for(const auto & token : mTokens)
{
dprintf_untranslated(" % 2d '%s'\n", token.type(), token.data().c_str());
}
dprintf_untranslated("\n");
#endif
shuntingYard();
}
@ -215,10 +224,10 @@ void ExpressionParser::tokenize()
addOperatorToken(ch, Token::Type::Comma);
break;
case '(':
addOperatorToken(ch, Token::Type::OpenBracket);
addOperatorToken(ch, Token::Type::OpenParen);
break;
case ')':
addOperatorToken(ch, Token::Type::CloseBracket);
addOperatorToken(ch, Token::Type::CloseParen);
break;
case '~':
addOperatorToken(ch, Token::Type::OperatorNot);
@ -367,7 +376,14 @@ void ExpressionParser::addOperatorToken(const String & data, Token::Type type)
{
if(mCurToken.length()) //add a new data token when there is data in the buffer
{
mTokens.push_back(Token(mCurToken, type == Token::Type::OpenBracket ? Token::Type::Function : resolveQuotedData()));
if(type == Token::Type::OpenParen)
{
mTokens.push_back(Token(mCurToken, Token::Type::Function));
}
else
{
mTokens.push_back(Token(mCurToken, resolveQuotedData()));
}
mCurToken.clear();
mCurTokenQuoted.clear();
}
@ -388,7 +404,7 @@ bool ExpressionParser::isUnaryOperator() const
return true;
auto lastType = mTokens.back().type();
//if the previous token is not data or a close bracket, this operator is a unary operator
return lastType != Token::Type::Data && lastType != Token::Type::QuotedData && lastType != Token::Type::CloseBracket;
return lastType != Token::Type::Data && lastType != Token::Type::QuotedData && lastType != Token::Type::CloseParen;
}
void ExpressionParser::shuntingYard()
@ -396,6 +412,7 @@ void ExpressionParser::shuntingYard()
//Implementation of Dijkstra's Shunting-yard algorithm (https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
std::vector<Token> queue;
std::vector<Token> stack;
std::vector<duint> argCount;
auto len = mTokens.size();
queue.reserve(len);
stack.reserve(len);
@ -410,9 +427,18 @@ void ExpressionParser::shuntingYard()
queue.push_back(token);
break;
case Token::Type::Function: //If the token is a function token, then push it onto the stack.
{
stack.push_back(token);
break;
// Unless the syntax is 'fn()' there is always at least one argument
if(i + 2 < mTokens.size() && mTokens[i + 1].type() == Token::Type::OpenParen && mTokens[i + 2].type() == Token::Type::CloseParen)
argCount.push_back(0);
else
argCount.push_back(1);
}
break;
case Token::Type::Comma: //If the token is a function argument separator (e.g., a comma):
{
while(true) //Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
{
if(stack.empty()) //If no left parentheses are encountered, either the separator was misplaced or parentheses were mismatched.
@ -421,16 +447,20 @@ void ExpressionParser::shuntingYard()
return;
}
const auto & curToken = stack.back();
if(curToken.type() == Token::Type::OpenBracket)
if(curToken.type() == Token::Type::OpenParen)
break;
queue.push_back(curToken);
stack.pop_back();
}
break;
case Token::Type::OpenBracket: //If the token is a left parenthesis (i.e. "("), then push it onto the stack.
if(!argCount.empty()) // A comma increases the argument count
argCount.back()++;
}
break;
case Token::Type::OpenParen: //If the token is a left parenthesis (i.e. "("), then push it onto the stack.
stack.push_back(token);
break;
case Token::Type::CloseBracket: //If the token is a right parenthesis (i.e. ")"):
case Token::Type::CloseParen: //If the token is a right parenthesis (i.e. ")"):
{
while(true) //Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
{
@ -441,14 +471,16 @@ void ExpressionParser::shuntingYard()
}
auto curToken = stack.back();
stack.pop_back(); //Pop the left parenthesis from the stack, but not onto the output queue.
if(curToken.type() == Token::Type::OpenBracket) //the bracket is already popped here
if(curToken.type() == Token::Type::OpenParen) //the bracket is already popped here
break;
queue.push_back(curToken);
}
auto size = stack.size();
if(size && stack[size - 1].type() == Token::Type::Function) //If the token at the top of the stack is a function token, pop it onto the output queue.
if(!stack.empty() && stack.back().type() == Token::Type::Function) //If the token at the top of the stack is a function token, pop it onto the output queue.
{
queue.push_back(stack[size - 1]);
// Propagate the argument count as extra information
stack.back().setInfo(argCount.back());
argCount.pop_back();
queue.push_back(stack.back());
stack.pop_back();
}
}
@ -476,7 +508,7 @@ void ExpressionParser::shuntingYard()
while(!stack.empty()) //While there are still operator tokens in the stack:
{
const auto & curToken = stack.back();
if(curToken.type() == Token::Type::OpenBracket || curToken.type() == Token::Type::CloseBracket) //If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
if(curToken.type() == Token::Type::OpenParen || curToken.type() == Token::Type::CloseParen) //If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
{
mIsValidExpression = false;
return;
@ -907,108 +939,203 @@ bool ExpressionParser::Calculate(EvalValue & value, bool signedcalc, bool allowa
else if(token.type() == Token::Type::Function)
{
const auto & name = token.data();
const auto argCount = token.info();
ValueType returnType;
std::vector<ValueType> argTypes;
if(!ExpressionFunctions::GetType(name, returnType, argTypes))
return false;
if(int(stack.size()) < argTypes.size())
return false;
std::vector<ExpressionValue> argv;
argv.resize(argTypes.size());
for(size_t i = 0; i < argTypes.size(); i++)
{
const auto & argType = argTypes[argTypes.size() - i - 1];
auto & top = stack[stack.size() - i - 1];
ExpressionValue arg;
if(top.isString)
if(!silent)
dprintf(QT_TRANSLATE_NOOP("DBG", "No such expression function '%s'\n"), name.c_str());
return false;
}
size_t requiredArguments = 0;
for(const auto & argType : argTypes)
{
switch(argType)
{
arg = { ValueTypeString, 0, StringValue{ top.data.c_str(), false } };
case ValueTypeOptionalNumber:
case ValueTypeOptionalString:
case ValueTypeOptionalAny:
break;
default:
requiredArguments++;
break;
}
else if(top.evaluated)
}
auto typeName = [](ValueType t) -> String
{
switch(t)
{
arg = { ValueTypeNumber, top.value };
case ValueTypeOptionalNumber:
case ValueTypeNumber:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "number"));
case ValueTypeOptionalString:
case ValueTypeString:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "string"));
case ValueTypeOptionalAny:
case ValueTypeAny:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "any"));
}
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "invalid"));
};
auto makeSignature = [&]()
{
String signature = name;
signature += "(";
for(size_t j = 0; j < argTypes.size(); j++)
{
if(j == requiredArguments)
{
signature += "[";
}
if(j > 0)
{
signature += ", ";
}
signature += typeName(argTypes[j]);
}
if(requiredArguments < argTypes.size())
{
signature += "]";
}
signature += ")";
return signature;
};
if(stack.size() < requiredArguments || argCount > argTypes.size())
{
if(!silent)
{
std::string expected;
if(requiredArguments == argTypes.size())
expected = StringUtils::sprintf("%d", (int)requiredArguments);
else
expected = StringUtils::sprintf("%d-%d", (int)requiredArguments, (int)argTypes.size());
dprintf(QT_TRANSLATE_NOOP("DBG", "Bad argument count for expression function %s (expected %s, got %d)!\n"),
makeSignature().c_str(),
expected.c_str(),
(int)argCount
);
}
return false;
}
std::vector<ExpressionValue> argv;
argv.resize(argCount);
for(size_t i = 0; i < argCount; i++)
{
// Get the expected (concrete) argument type
auto argType = argTypes[i];
switch(argType)
{
case ValueTypeOptionalNumber:
argType = ValueTypeNumber;
break;
case ValueTypeOptionalString:
argType = ValueTypeString;
break;
case ValueTypeOptionalAny:
argType = ValueTypeAny;
break;
default:
break;
}
auto & argEval = stack[stack.size() - argCount + i];
ExpressionValue argValue;
if(argEval.isString)
{
argValue = { ValueTypeString, 0, StringValue{ argEval.data.c_str(), false } };
}
else if(argEval.evaluated)
{
argValue = { ValueTypeNumber, argEval.value };
}
else
{
duint result;
if(!top.DoEvaluate(result, silent, baseonly, value_size, isvar, hexonly))
if(!argEval.DoEvaluate(result, silent, baseonly, value_size, isvar, hexonly))
return false;
arg = { ValueTypeNumber, result };
argValue = { ValueTypeNumber, result };
}
if(arg.type != argType && argType != ValueTypeAny)
if(argValue.type != argType && argType != ValueTypeAny)
{
if(!silent)
{
auto typeName = [](ValueType t) -> String
{
switch(t)
{
case ValueTypeNumber:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "number"));
case ValueTypeString:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "string"));
}
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "invalid"));
};
String argValueStr;
if(arg.type == ValueTypeNumber)
if(argValue.type == ValueTypeNumber)
{
argValueStr = StringUtils::sprintf("0x%p", arg.number);
argValueStr = StringUtils::sprintf("0x%p", argValue.number);
}
else if(arg.type == ValueTypeString)
else if(argValue.type == ValueTypeString)
{
argValueStr = "\"" + StringUtils::Escape(arg.string.ptr) + "\"";
argValueStr = "\"" + StringUtils::Escape(argValue.string.ptr) + "\"";
}
else
{
argValueStr = "???";
}
String signature = name;
signature += "(";
for(size_t j = 0; j < argTypes.size(); j++)
{
if(j > 0)
{
signature += ", ";
}
signature += typeName(argTypes[j]);
}
signature += ")";
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression function %s argument %d/%d (%s) type mismatch (expected %s, got %s)!\n"),
signature.c_str(),
argTypes.size() - i,
makeSignature().c_str(),
i + 1,
argTypes.size(),
argValueStr.c_str(),
typeName(argType).c_str(),
typeName(arg.type).c_str()
typeName(argValue.type).c_str()
);
}
return false;
}
argv[argTypes.size() - i - 1] = arg;
argv[i] = argValue;
}
ExpressionValue result = { ValueTypeNumber, 0 };
if(!ExpressionFunctions::Call(name, result, argv))
{
if(!silent)
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression function %s errored!\n"),
makeSignature().c_str()
);
return false;
}
if(result.type == ValueTypeAny)
// Check the return type
switch(result.type)
{
case ValueTypeNumber:
case ValueTypeString:
break;
default:
if(!silent)
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression function %s returned an invalid value!\n"),
makeSignature().c_str()
);
return false;
}
// Pop the arguments off the stack
// NOTE: Do not move, the string pointers are needed during the call
for(size_t i = 0; i < argv.size(); i++)
{
stack.pop_back();
}
// Push the result on the stack
if(result.type == ValueTypeString)
{
stack.push_back(EvalValue(result.string.ptr, true));
stack.emplace_back(result.string.ptr, true);
// We can free the string since it was copied into the EvalValue
if(result.string.isOwner)
BridgeFree((void*)result.string.ptr);
}
else
stack.push_back(EvalValue(result.number));
stack.emplace_back(result.number);
}
else
stack.push_back(EvalValue(token.data(), token.type() == Token::Type::QuotedData));

View File

@ -17,7 +17,7 @@ public:
explicit EvalValue(duint value)
: evaluated(true), value(value) {}
explicit EvalValue(const String & data, bool isString)
EvalValue(const String & data, bool isString)
: evaluated(false), data(data), isString(isString) {}
bool DoEvaluate(duint & result, bool silent = true, bool baseonly = false, int* value_size = nullptr, bool* isvar = nullptr, bool* hexonly = nullptr) const
@ -61,8 +61,8 @@ public:
QuotedData,
Function,
Comma,
OpenBracket,
CloseBracket,
OpenParen,
CloseParen,
OperatorUnarySub,
OperatorUnaryAdd,
@ -133,6 +133,16 @@ public:
return mType;
}
duint info() const
{
return mInfo;
}
void setInfo(duint info)
{
mInfo = info;
}
Associativity associativity() const;
int precedence() const;
bool isOperator() const;
@ -140,6 +150,7 @@ public:
private:
String mData;
Type mType;
duint mInfo = 0;
};
private:

View File

@ -11,6 +11,7 @@
#include "value.h"
#include "TraceRecord.h"
#include "exhandlerinfo.h"
#include "exception.h"
#include <vector>
#include <regex>
#include <string>
@ -189,9 +190,11 @@ namespace Exprfunc
return result;
}
duint ternary(duint condition, duint value1, duint value2)
bool ternary(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
return condition ? value1 : value2;
*result = argv[0].number ? argv[1] : argv[2];
result->string.isOwner = false;
return true;
}
duint memvalid(duint addr)
@ -690,14 +693,24 @@ namespace Exprfunc
template<bool Strict>
bool ansi(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
assert(argc == 1);
assert(argc >= 1);
assert(argv[0].type == ValueTypeNumber);
duint addr = argv[0].number;
std::vector<char> tempStr(MAX_STRING_SIZE + 1);
duint NumberOfBytesRead = 0;
if(!MemRead(addr, tempStr.data(), tempStr.size() - 1, &NumberOfBytesRead) && NumberOfBytesRead == 0 && Strict)
std::vector<char> tempStr;
if(argc > 1)
{
assert(argv[1].type == ValueTypeNumber);
tempStr.resize(argv[1].number + 1);
}
else
{
tempStr.resize(MAX_STRING_SIZE + 1);
}
duint NumberOfBytesRead = -1;