local caption = "NFCTools" local subMenus = { {caption = 'x64bit Function Calling Assistant', name = 'miFunctionCallingAssistant'}, {caption = 'x32bit Function Calling Assistant', name = 'miFunctionCallingAssistant32'}, {caption = 'About', name = 'miSettings'} } local mm = getMemoryViewForm().Menu local menuItem = mm.mnNFCTools if not(mm.mnNFCTools) then menuItem = createMenuItem(mm) menuItem.Caption = caption menuItem.Name = 'mnNFCTools' mm.Items.add(menuItem) end if not(menuItem) then print('Error: Failed to create "NFCTools" menu') return end for i=1,#subMenus do local chk = createMenuItem(menuItem) chk.Caption = subMenus[i].caption chk.Name = subMenus[i].name menuItem.add(chk) end -- ============================================================================ -- Memory Change Monitoring (added in v1.3) -- ---------------------------------------------------------------------------- -- After replaying a recorded function call, the buffers that registers point -- to may already hold different data than they did at the original breakpoint. -- This monitor reads N bytes from each register's address every second and -- flags any register whose backing memory has changed since the previous read. -- * Values < 0x10000 are treated as scalars (not pointers) and skipped. -- * Once a change is seen, the indicator stays red for a few seconds, then -- the current memory state is taken as the new baseline and watching -- resumes. -- * Check size is configurable per-form. Default 4000 bytes. -- ============================================================================ local CHANGE_HOLD_MS = 3000 -- how long "CHANGED" stays red before re-baselining local COLOR_RED = 0x000000FF local COLOR_GREEN = 0x00008000 local COLOR_GRAY = 0x00808080 local function parseAddressValue(text) if not text or text == "" then return 0 end if string.find(text, "0x") then local cleaned = string.gsub(text, "^0x", "") return tonumber(cleaned, 16) or 0 else local ok, val = pcall(getAddress, text) if ok and val then return val end return tonumber(text, 16) or tonumber(text) or 0 end end local function isLikelyPointer(value) return value and type(value) == "number" and value >= 0x10000 end local function safeReadMem(address, count) if not isLikelyPointer(address) then return nil end if not count or count < 1 then return nil end local ok, bytes = pcall(readBytes, address, count, true) if ok and bytes and type(bytes) == "table" and #bytes > 0 then return bytes end return nil end local function computeMemHash(bytes) if not bytes then return nil end -- djb2-style rolling hash, kept inside 32 bits for Lua 5.1 number safety local hash = 5381 for i = 1, #bytes do hash = (hash * 33 + bytes[i]) % 4294967296 end return hash end local function createStatusLabel(form, x, y, width) local lbl = createLabel(form) lbl.Left = x lbl.Top = y lbl.Width = width or 70 lbl.Caption = '' lbl.Font.Style = '[fsBold]' return lbl end local function setupChangeMonitor(form, watchedItems, checkSizeInput, monitorCheckbox) local state = {} local timer = createTimer(form) timer.Interval = 1000 timer.OnTimer = function(sender) -- Form might be closing; bail safely. local visOk, visible = pcall(function() return form.Visible end) if not visOk or not visible then return end if not monitorCheckbox.Checked then for _, item in ipairs(watchedItems) do if item.statusLabel then item.statusLabel.Caption = '' end end return end local checkSize = tonumber(checkSizeInput.Text) or 400 if checkSize < 1 then checkSize = 1 end if checkSize > 1048576 then checkSize = 1048576 end local now = getTickCount() for _, item in ipairs(watchedItems) do local addr = parseAddressValue(item.input.Text) local key = item.input.Name or tostring(item.input) local s = state[key] if not s then s = { baseline = nil, changedUntil = 0, lastAddr = nil } state[key] = s end if now < s.changedUntil then -- Still in the red-hold window; leave the alert visible. item.statusLabel.Caption = 'CHANGED' item.statusLabel.Font.Color = COLOR_RED elseif not isLikelyPointer(addr) then -- Not a plausible pointer: hide indicator and clear baseline. item.statusLabel.Caption = '' s.baseline = nil s.lastAddr = addr else local bytes = safeReadMem(addr, checkSize) local hash = computeMemHash(bytes) if hash == nil then -- Unreadable region. item.statusLabel.Caption = '?' item.statusLabel.Font.Color = COLOR_GRAY s.baseline = nil elseif s.baseline == nil or s.lastAddr ~= addr then -- First check, or the user changed the address: re-baseline. s.baseline = hash s.lastAddr = addr item.statusLabel.Caption = 'OK' item.statusLabel.Font.Color = COLOR_GREEN elseif hash ~= s.baseline then -- Memory differs from the previous baseline. item.statusLabel.Caption = 'CHANGED' item.statusLabel.Font.Color = COLOR_RED -- Removed the baseline update so it never resets to 'OK' -- unless the memory actually changes back to the original values. else item.statusLabel.Caption = 'OK' item.statusLabel.Font.Color = COLOR_GREEN end end end end timer.Enabled = true return timer end -- ============================================================================ -- x64 Function Calling Assistant -- ============================================================================ local fcaForm = nil local allocatedMemoryAddress = nil local function CreateFunctionCallingAssistantForm() if fcaForm then return fcaForm end fcaForm = createForm() fcaForm.Caption = 'x64bit Function Calling Assistant' fcaForm.Width = 620 fcaForm.Height = 500 fcaForm.Position = 'poScreenCenter' fcaForm.WindowState = 'wsNormal' fcaForm.Visible = true fcaForm.BorderStyle = 'bsSizeable' fcaForm.BorderIcons = '[biSystemMenu,biMinimize,biMaximize]' -- Layout constants. Right column shifted right so the left-column -- "CHANGED" status fits without colliding with the right-column labels. local STATUS_LEFT_X = 215 -- after left input (ends at x=210) local RIGHT_LABEL_X = 290 -- (was 250) local RIGHT_INPUT_X = 360 -- (was 320) local STATUS_RIGHT_X = 485 -- after right input (ends at x=480) -- Watched items collected during build, fed to the monitor at the end. local watchedItems = {} local functionAddressLabel = createLabel(fcaForm) functionAddressLabel.Caption = 'Function Address:' functionAddressLabel.Left = 20 functionAddressLabel.Top = 20 functionAddressLabel.Width = 120 functionAddressLabel.Font.Style = '[fsBold]' local functionAddressInput = createEdit(fcaForm) functionAddressInput.Left = 150 functionAddressInput.Top = 18 functionAddressInput.Width = 150 functionAddressInput.Text = '0x0000000000000000' functionAddressInput.Name = 'functionAddressInput' local argCountLabel = createLabel(fcaForm) argCountLabel.Caption = 'Arguments:' argCountLabel.Left = 320 argCountLabel.Top = 20 argCountLabel.Width = 80 argCountLabel.Font.Style = '[fsBold]' local argCountInput = createEdit(fcaForm) argCountInput.Left = 410 argCountInput.Top = 18 argCountInput.Width = 50 argCountInput.Text = '9' argCountInput.Name = 'argCountInput' local yPos = 60 local labelWidth = 60 local inputWidth = 120 local spacing = 30 local raxLabel = createLabel(fcaForm) raxLabel.Caption = 'RAX:' raxLabel.Left = 20 raxLabel.Top = yPos raxLabel.Width = labelWidth local raxInput = createEdit(fcaForm) raxInput.Left = 90 raxInput.Top = yPos - 2 raxInput.Width = inputWidth raxInput.Text = '0' raxInput.Name = 'raxInput' local raxStatus = createStatusLabel(fcaForm, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = raxInput, statusLabel = raxStatus }) yPos = yPos + spacing local rcxLabel = createLabel(fcaForm) rcxLabel.Caption = 'RCX:' rcxLabel.Left = 20 rcxLabel.Top = yPos rcxLabel.Width = labelWidth local rcxInput = createEdit(fcaForm) rcxInput.Left = 90 rcxInput.Top = yPos - 2 rcxInput.Width = inputWidth rcxInput.Text = '0' rcxInput.Name = 'rcxInput' local rcxStatus = createStatusLabel(fcaForm, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = rcxInput, statusLabel = rcxStatus }) yPos = yPos + spacing local rdxLabel = createLabel(fcaForm) rdxLabel.Caption = 'RDX:' rdxLabel.Left = 20 rdxLabel.Top = yPos rdxLabel.Width = labelWidth local rdxInput = createEdit(fcaForm) rdxInput.Left = 90 rdxInput.Top = yPos - 2 rdxInput.Width = inputWidth rdxInput.Text = '0' rdxInput.Name = 'rdxInput' local rdxStatus = createStatusLabel(fcaForm, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = rdxInput, statusLabel = rdxStatus }) yPos = yPos + spacing local r8Label = createLabel(fcaForm) r8Label.Caption = 'R8:' r8Label.Left = 20 r8Label.Top = yPos r8Label.Width = labelWidth local r8Input = createEdit(fcaForm) r8Input.Left = 90 r8Input.Top = yPos - 2 r8Input.Width = inputWidth r8Input.Text = '0' r8Input.Name = 'r8Input' local r8Status = createStatusLabel(fcaForm, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = r8Input, statusLabel = r8Status }) yPos = yPos + spacing local r9Label = createLabel(fcaForm) r9Label.Caption = 'R9:' r9Label.Left = 20 r9Label.Top = yPos r9Label.Width = labelWidth local r9Input = createEdit(fcaForm) r9Input.Left = 90 r9Input.Top = yPos - 2 r9Input.Width = inputWidth r9Input.Text = '0' r9Input.Name = 'r9Input' local r9Status = createStatusLabel(fcaForm, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = r9Input, statusLabel = r9Status }) local yPos2 = 60 local rbxLabel = createLabel(fcaForm) rbxLabel.Caption = 'RBX:' rbxLabel.Left = RIGHT_LABEL_X rbxLabel.Top = yPos2 rbxLabel.Width = labelWidth local rbxInput = createEdit(fcaForm) rbxInput.Left = RIGHT_INPUT_X rbxInput.Top = yPos2 - 2 rbxInput.Width = inputWidth rbxInput.Text = '0' rbxInput.Name = 'rbxInput' local rbxStatus = createStatusLabel(fcaForm, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = rbxInput, statusLabel = rbxStatus }) yPos2 = yPos2 + spacing local rbpLabel = createLabel(fcaForm) rbpLabel.Caption = 'RBP:' rbpLabel.Left = RIGHT_LABEL_X rbpLabel.Top = yPos2 rbpLabel.Width = labelWidth local rbpInput = createEdit(fcaForm) rbpInput.Left = RIGHT_INPUT_X rbpInput.Top = yPos2 - 2 rbpInput.Width = inputWidth rbpInput.Text = '0' rbpInput.Name = 'rbpInput' local rbpStatus = createStatusLabel(fcaForm, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = rbpInput, statusLabel = rbpStatus }) yPos2 = yPos2 + spacing local rsiLabel = createLabel(fcaForm) rsiLabel.Caption = 'RSI:' rsiLabel.Left = RIGHT_LABEL_X rsiLabel.Top = yPos2 rsiLabel.Width = labelWidth local rsiInput = createEdit(fcaForm) rsiInput.Left = RIGHT_INPUT_X rsiInput.Top = yPos2 - 2 rsiInput.Width = inputWidth rsiInput.Text = '0' rsiInput.Name = 'rsiInput' local rsiStatus = createStatusLabel(fcaForm, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = rsiInput, statusLabel = rsiStatus }) yPos2 = yPos2 + spacing local rdiLabel = createLabel(fcaForm) rdiLabel.Caption = 'RDI:' rdiLabel.Left = RIGHT_LABEL_X rdiLabel.Top = yPos2 rdiLabel.Width = labelWidth local rdiInput = createEdit(fcaForm) rdiInput.Left = RIGHT_INPUT_X rdiInput.Top = yPos2 - 2 rdiInput.Width = inputWidth rdiInput.Text = '0' rdiInput.Name = 'rdiInput' local rdiStatus = createStatusLabel(fcaForm, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = rdiInput, statusLabel = rdiStatus }) yPos2 = yPos2 + spacing local rspLabel = createLabel(fcaForm) rspLabel.Caption = 'RSP:' rspLabel.Left = RIGHT_LABEL_X rspLabel.Top = yPos2 rspLabel.Width = labelWidth local rspInput = createEdit(fcaForm) rspInput.Left = RIGHT_INPUT_X rspInput.Top = yPos2 - 2 rspInput.Width = inputWidth rspInput.Text = '0' rspInput.Name = 'rspInput' local rspStatus = createStatusLabel(fcaForm, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = rspInput, statusLabel = rspStatus }) local stackYPos = 240 local stackLabel = createLabel(fcaForm) stackLabel.Caption = 'Stack Arguments:' stackLabel.Left = 20 stackLabel.Top = stackYPos stackLabel.Width = 150 stackLabel.Font.Style = '[fsBold]' stackYPos = stackYPos + 25 local rsp8Label = createLabel(fcaForm) rsp8Label.Caption = 'RSP+28:' rsp8Label.Left = 20 rsp8Label.Top = stackYPos rsp8Label.Width = labelWidth local rsp8Input = createEdit(fcaForm) rsp8Input.Left = 90 rsp8Input.Top = stackYPos - 2 rsp8Input.Width = inputWidth rsp8Input.Text = '0' rsp8Input.Name = 'rsp8Input' local rsp8Status = createStatusLabel(fcaForm, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = rsp8Input, statusLabel = rsp8Status }) stackYPos = stackYPos + spacing local rsp16Label = createLabel(fcaForm) rsp16Label.Caption = 'RSP+30:' rsp16Label.Left = 20 rsp16Label.Top = stackYPos rsp16Label.Width = labelWidth local rsp16Input = createEdit(fcaForm) rsp16Input.Left = 90 rsp16Input.Top = stackYPos - 2 rsp16Input.Width = inputWidth rsp16Input.Text = '0' rsp16Input.Name = 'rsp16Input' local rsp16Status = createStatusLabel(fcaForm, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = rsp16Input, statusLabel = rsp16Status }) stackYPos = stackYPos + spacing local rsp24Label = createLabel(fcaForm) rsp24Label.Caption = 'RSP+38:' rsp24Label.Left = 20 rsp24Label.Top = stackYPos rsp24Label.Width = labelWidth local rsp24Input = createEdit(fcaForm) rsp24Input.Left = 90 rsp24Input.Top = stackYPos - 2 rsp24Input.Width = inputWidth rsp24Input.Text = '0' rsp24Input.Name = 'rsp24Input' local rsp24Status = createStatusLabel(fcaForm, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = rsp24Input, statusLabel = rsp24Status }) stackYPos = stackYPos + spacing local rsp32Label = createLabel(fcaForm) rsp32Label.Caption = 'RSP+40:' rsp32Label.Left = 20 rsp32Label.Top = stackYPos rsp32Label.Width = labelWidth local rsp32Input = createEdit(fcaForm) rsp32Input.Left = 90 rsp32Input.Top = stackYPos - 2 rsp32Input.Width = inputWidth rsp32Input.Text = '0' rsp32Input.Name = 'rsp32Input' local rsp32Status = createStatusLabel(fcaForm, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = rsp32Input, statusLabel = rsp32Status }) local stackYPos2 = 265 local rsp40Label = createLabel(fcaForm) rsp40Label.Caption = 'RSP+48:' rsp40Label.Left = RIGHT_LABEL_X rsp40Label.Top = stackYPos2 rsp40Label.Width = labelWidth local rsp40Input = createEdit(fcaForm) rsp40Input.Left = RIGHT_INPUT_X rsp40Input.Top = stackYPos2 - 2 rsp40Input.Width = inputWidth rsp40Input.Text = '0' rsp40Input.Name = 'rsp40Input' local rsp40Status = createStatusLabel(fcaForm, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = rsp40Input, statusLabel = rsp40Status }) stackYPos2 = stackYPos2 + spacing local rsp48Label = createLabel(fcaForm) rsp48Label.Caption = 'RSP+56:' rsp48Label.Left = RIGHT_LABEL_X rsp48Label.Top = stackYPos2 rsp48Label.Width = labelWidth local rsp48Input = createEdit(fcaForm) rsp48Input.Left = RIGHT_INPUT_X rsp48Input.Top = stackYPos2 - 2 rsp48Input.Width = inputWidth rsp48Input.Text = '0' rsp48Input.Name = 'rsp48Input' local rsp48Status = createStatusLabel(fcaForm, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = rsp48Input, statusLabel = rsp48Status }) stackYPos2 = stackYPos2 + spacing local rsp56Label = createLabel(fcaForm) rsp56Label.Caption = 'RSP+64:' rsp56Label.Left = RIGHT_LABEL_X rsp56Label.Top = stackYPos2 rsp56Label.Width = labelWidth local rsp56Input = createEdit(fcaForm) rsp56Input.Left = RIGHT_INPUT_X rsp56Input.Top = stackYPos2 - 2 rsp56Input.Width = inputWidth rsp56Input.Text = '0' rsp56Input.Name = 'rsp56Input' local rsp56Status = createStatusLabel(fcaForm, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = rsp56Input, statusLabel = rsp56Status }) stackYPos2 = stackYPos2 + spacing local rsp64Label = createLabel(fcaForm) rsp64Label.Caption = 'RSP+72:' rsp64Label.Left = RIGHT_LABEL_X rsp64Label.Top = stackYPos2 rsp64Label.Width = labelWidth local rsp64Input = createEdit(fcaForm) rsp64Input.Left = RIGHT_INPUT_X rsp64Input.Top = stackYPos2 - 2 rsp64Input.Width = inputWidth rsp64Input.Text = '0' rsp64Input.Name = 'rsp64Input' local rsp64Status = createStatusLabel(fcaForm, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = rsp64Input, statusLabel = rsp64Status }) -- Buttons local buttonYPos = 400 local callButton = createButton(fcaForm) callButton.Caption = 'Call Function' callButton.Left = 20 callButton.Top = buttonYPos callButton.Width = 100 callButton.Height = 30 local callButton2 = createButton(fcaForm) callButton2.Caption = 'Call Function + Exit Thread' callButton2.Left = 20 callButton2.Top = 435 callButton2.Width = 150 callButton2.Height = 30 local clearButton = createButton(fcaForm) clearButton.Caption = 'Clear All' clearButton.Left = 140 clearButton.Top = buttonYPos clearButton.Width = 100 clearButton.Height = 30 local loadButton = createButton(fcaForm) loadButton.Caption = 'Load Current' loadButton.Left = 260 loadButton.Top = buttonYPos loadButton.Width = 100 loadButton.Height = 30 -- ---- Monitor controls ---- local monitorCheckbox = createCheckBox(fcaForm) monitorCheckbox.Caption = 'Monitor pointer changes' monitorCheckbox.Left = 380 monitorCheckbox.Top = 405 monitorCheckbox.Width = 200 monitorCheckbox.Checked = true monitorCheckbox.Hint = 'Periodically reads memory at each register address and flags it if its contents have changed.' monitorCheckbox.ShowHint = true local checkSizeLabel = createLabel(fcaForm) checkSizeLabel.Caption = 'Check Size:' checkSizeLabel.Left = 380 checkSizeLabel.Top = 442 checkSizeLabel.Width = 80 local checkSizeInput = createEdit(fcaForm) checkSizeInput.Left = 460 checkSizeInput.Top = 440 checkSizeInput.Width = 70 checkSizeInput.Text = '300' checkSizeInput.Hint = 'Number of bytes hashed at each pointer for change detection (1 - 1048576).' checkSizeInput.ShowHint = true -- Button event handlers local function OnCallFunction(sender) local function parseHexValue(inputText) if inputText == nil or inputText == "" then return 0 end local cleanText = string.gsub(inputText, "^0x", "") return tonumber(cleanText, 16) or 0 end local funcAddress = parseHexValue(functionAddressInput.Text) if funcAddress == 0 then showMessage('Please enter a valid function address!') return end local argCount = tonumber(argCountInput.Text) or 9 if string.find(raxInput.Text, "0x") then raxVal = parseHexValue(raxInput.Text) else raxVal = getAddress(raxInput.Text) end if string.find(rcxInput.Text, "0x") then rcxVal = parseHexValue(rcxInput.Text) else rcxVal = getAddress(rcxInput.Text) end if string.find(rdxInput.Text, "0x") then rdxVal = parseHexValue(rdxInput.Text) else rdxVal = getAddress(rdxInput.Text) end if string.find(r8Input.Text, "0x") then r8Val = parseHexValue(r8Input.Text) else r8Val = getAddress(r8Input.Text) end if string.find(r9Input.Text, "0x") then r9Val = parseHexValue(r9Input.Text) else r9Val = getAddress(r9Input.Text) end if string.find(rsiInput.Text, "0x") then rsiVal = parseHexValue(rsiInput.Text) else rsiVal = getAddress(rsiInput.Text) end if string.find(rdiInput.Text, "0x") then rdiVal = parseHexValue(rdiInput.Text) else rdiVal = getAddress(rdiInput.Text) end if string.find(rspInput.Text, "0x") then rspVal = parseHexValue(rspInput.Text) else rspVal = getAddress(rspInput.Text) end if string.find(rsp8Input.Text, "0x") then arg5Val = parseHexValue(rsp8Input.Text) else arg5Val = getAddress(rsp8Input.Text) end if string.find(rsp16Input.Text, "0x") then arg6Val = parseHexValue(rsp16Input.Text) else arg6Val = getAddress(rsp16Input.Text) end if string.find(rsp24Input.Text, "0x") then arg7Val = parseHexValue(rsp24Input.Text) else arg7Val = getAddress(rsp24Input.Text) end if string.find(rsp32Input.Text, "0x") then arg8Val = parseHexValue(rsp32Input.Text) else arg8Val = getAddress(rsp32Input.Text) end if string.find(rsp40Input.Text, "0x") then arg9Val = parseHexValue(rsp40Input.Text) else arg9Val = getAddress(rsp40Input.Text) end if string.find(rsp48Input.Text, "0x") then arg10Val = parseHexValue(rsp48Input.Text) else arg10Val = getAddress(rsp48Input.Text) end if string.find(rsp56Input.Text, "0x") then arg11Val = parseHexValue(rsp56Input.Text) else arg11Val = getAddress(rsp56Input.Text) end if string.find(rsp64Input.Text, "0x") then arg12Val = parseHexValue(rsp64Input.Text) else arg12Val = getAddress(rsp64Input.Text) end local assemblyScript = string.format([[ alloc(cMem,2000,$process) registersymbol(cMem) label(funcToCall) label(arg1) label(arg2) label(arg3) label(arg4) label(arg5) label(arg6) label(arg7) label(arg8) label(arg9) cMem: push rbp mov rbp,rsp mov rcx,[arg1] mov rdx,[arg2] mov r8,[arg3] mov r9,[arg4] sub rsp,48 mov rax,[arg9] push rax mov rax,[arg8] push rax mov rax,[arg7] push rax mov rax,[arg6] push rax mov rax,[arg5] push rax call [funcToCall] add rsp,48 mov rsp,rbp pop rbp xor rax,rax ret arg1: dq 0x%X arg2: dq 0x%X arg3: dq 0x%X arg4: dq 0x%X arg5: dq 0x%X arg6: dq 0x%X arg7: dq 0x%X arg8: dq 0x%X arg9: dq 0x%X funcToCall: dq 0x%X createThread(cMem) ]], rcxVal, rdxVal, r8Val, r9Val, arg5Val, arg6Val, arg7Val, arg8Val, arg9Val, funcAddress) local success, result = pcall(function() return autoAssemble(assemblyScript) end) if success and result then allocatedMemoryAddress = result autoAssemble('dealloc(cMem)\nunregistersymbol(cMem)') allocatedMemoryAddress = nil --showMessage('Function call executed successfully!') else showMessage('Failed to execute function call. Check console for errors.') print('Assembly script execution failed. Error: ' .. tostring(result)) end end local function OnCallFunction2(sender) local function parseHexValue(inputText) if inputText == nil or inputText == "" then return 0 end local cleanText = string.gsub(inputText, "^0x", "") return tonumber(cleanText, 16) or 0 end local funcAddress = parseHexValue(functionAddressInput.Text) if funcAddress == 0 then showMessage('Please enter a valid function address!') return end local argCount = tonumber(argCountInput.Text) or 9 local raxVal = parseHexValue(raxInput.Text) local rcxVal = parseHexValue(rcxInput.Text) local rdxVal = parseHexValue(rdxInput.Text) local r8Val = parseHexValue(r8Input.Text) local r9Val = parseHexValue(r9Input.Text) local rbxVal = parseHexValue(rbxInput.Text) local rbpVal = parseHexValue(rbpInput.Text) local rsiVal = parseHexValue(rsiInput.Text) local rdiVal = parseHexValue(rdiInput.Text) local rspVal = parseHexValue(rspInput.Text) local arg5Val = parseHexValue(rsp8Input.Text) local arg6Val = parseHexValue(rsp16Input.Text) local arg7Val = parseHexValue(rsp24Input.Text) local arg8Val = parseHexValue(rsp32Input.Text) local arg9Val = parseHexValue(rsp40Input.Text) local arg10Val = parseHexValue(rsp48Input.Text) local arg11Val = parseHexValue(rsp56Input.Text) local arg12Val = parseHexValue(rsp64Input.Text) local assemblyScript = string.format([[ alloc(cMem,2000,$process) registersymbol(cMem) label(funcToCall) label(arg1) label(arg2) label(arg3) label(arg4) label(arg5) label(arg6) label(arg7) label(arg8) label(arg9) cMem: push rbp mov rbp,rsp mov rcx,[arg1] mov rdx,[arg2] mov r8,[arg3] mov r9,[arg4] sub rsp,48 mov rax,[arg9] push rax mov rax,[arg8] push rax mov rax,[arg7] push rax mov rax,[arg6] push rax mov rax,[arg5] push rax call [funcToCall] add rsp,48 mov rsp,rbp pop rbp xor rax,rax call exitThread ret arg1: dq 0x%X arg2: dq 0x%X arg3: dq 0x%X arg4: dq 0x%X arg5: dq 0x%X arg6: dq 0x%X arg7: dq 0x%X arg8: dq 0x%X arg9: dq 0x%X funcToCall: dq 0x%X createThread(cMem) ]], rcxVal, rdxVal, r8Val, r9Val, arg5Val, arg6Val, arg7Val, arg8Val, arg9Val, funcAddress) local success, result = pcall(function() return autoAssemble(assemblyScript) end) if success and result then allocatedMemoryAddress = result autoAssemble('dealloc(cMem)\nunregistersymbol(cMem)') allocatedMemoryAddress = nil --showMessage('Function call executed successfully!') else showMessage('Failed to execute function call. Check console for errors.') print('Assembly script execution failed. Error: ' .. tostring(result)) end end local function OnClearAll(sender) functionAddressInput.Text = '0x0000000000000000' argCountInput.Text = '9' raxInput.Text = '0' rcxInput.Text = '0' rdxInput.Text = '0' r8Input.Text = '0' r9Input.Text = '0' rbxInput.Text = '0' rbpInput.Text = '0' rsiInput.Text = '0' rdiInput.Text = '0' rspInput.Text = '0' rsp8Input.Text = '0' rsp16Input.Text = '0' rsp24Input.Text = '0' rsp32Input.Text = '0' rsp40Input.Text = '0' rsp48Input.Text = '0' rsp56Input.Text = '0' rsp64Input.Text = '0' end local function OnLoadCurrent(sender) local function safeFormatRegister(value) if value == nil then return "0x0" else return string.format("0x%X", value) end end functionAddressInput.Text = safeFormatRegister(RIP) raxInput.Text = safeFormatRegister(RAX) rcxInput.Text = safeFormatRegister(RCX) rdxInput.Text = safeFormatRegister(RDX) r8Input.Text = safeFormatRegister(R8) r9Input.Text = safeFormatRegister(R9) rbxInput.Text = safeFormatRegister(RBX) rbpInput.Text = safeFormatRegister(RBP) rsiInput.Text = safeFormatRegister(RSI) rdiInput.Text = safeFormatRegister(RDI) rspInput.Text = safeFormatRegister(RSP) local function safeReadQword(address) if address == nil then return 0 end local success, value = pcall(readQword, address) if success then return value or 0 else return 0 end end rsp8Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 28) or nil)) rsp16Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 30) or nil)) rsp24Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 38) or nil)) rsp32Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 40) or nil)) rsp40Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 48) or nil)) rsp48Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 56) or nil)) rsp56Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 64) or nil)) rsp64Input.Text = safeFormatRegister(safeReadQword(RSP and (RSP + 72) or nil)) showMessage('Current register values loaded successfully!') end callButton.OnClick = OnCallFunction callButton2.OnClick = OnCallFunction2 clearButton.OnClick = OnClearAll loadButton.OnClick = OnLoadCurrent fcaForm.OnClose = function(sender) fcaForm = nil return caFree end -- Wire up the change monitor. setupChangeMonitor(fcaForm, watchedItems, checkSizeInput, monitorCheckbox) return fcaForm end -- x32bit local fcaForm32 = nil local allocatedMemoryAddress32 = nil local function CreateFunctionCallingAssistantForm32() if fcaForm32 then return fcaForm32 end fcaForm32 = createForm() fcaForm32.Caption = 'x32bit Function Calling Assistant' fcaForm32.Width = 620 fcaForm32.Height = 500 fcaForm32.Position = 'poScreenCenter' fcaForm32.WindowState = 'wsNormal' fcaForm32.Visible = true fcaForm32.BorderStyle = 'bsSizeable' fcaForm32.BorderIcons = '[biSystemMenu,biMinimize,biMaximize]' -- Same shifted layout as the x64 form. local STATUS_LEFT_X = 215 local RIGHT_LABEL_X = 290 local RIGHT_INPUT_X = 360 local STATUS_RIGHT_X = 485 local watchedItems = {} local functionAddressLabel = createLabel(fcaForm32) functionAddressLabel.Caption = 'Function Address:' functionAddressLabel.Left = 20 functionAddressLabel.Top = 20 functionAddressLabel.Width = 120 functionAddressLabel.Font.Style = '[fsBold]' local functionAddressInput = createEdit(fcaForm32) functionAddressInput.Left = 150 functionAddressInput.Top = 18 functionAddressInput.Width = 150 functionAddressInput.Text = '0x00000000' functionAddressInput.Name = 'functionAddressInput32' local conventionLabel = createLabel(fcaForm32) conventionLabel.Caption = 'Convention:' conventionLabel.Left = 320 conventionLabel.Top = 20 conventionLabel.Width = 80 conventionLabel.Font.Style = '[fsBold]' local conventionDropdown = createComboBox(fcaForm32) conventionDropdown.Left = 410 conventionDropdown.Top = 18 conventionDropdown.Width = 100 conventionDropdown.Style = 'csDropDownList' conventionDropdown.Items.add('stdcall') conventionDropdown.Items.add('cdecl') conventionDropdown.Items.add('thiscall') conventionDropdown.Items.add('fastcall') conventionDropdown.ItemIndex = 0 conventionDropdown.Name = 'conventionDropdown32' local argCountLabel = createLabel(fcaForm32) argCountLabel.Caption = 'Arguments Count:' argCountLabel.Left = 20 argCountLabel.Top = 55 argCountLabel.Width = 120 argCountLabel.Font.Style = '[fsBold]' local argCountInput = createEdit(fcaForm32) argCountInput.Left = 150 argCountInput.Top = 53 argCountInput.Width = 50 argCountInput.Text = '4' argCountInput.Name = 'argCountInput32' local noteLabel = createLabel(fcaForm32) noteLabel.Caption = '<--(Important!)' noteLabel.Left = 200 noteLabel.Top = 55 noteLabel.Width = 350 noteLabel.Font.Style = '[fsItalic]' local yPos = 90 local labelWidth = 60 local inputWidth = 120 local spacing = 30 local eaxLabel = createLabel(fcaForm32) eaxLabel.Caption = 'EAX:' eaxLabel.Left = 20 eaxLabel.Top = yPos eaxLabel.Width = labelWidth local eaxInput = createEdit(fcaForm32) eaxInput.Left = 90 eaxInput.Top = yPos - 2 eaxInput.Width = inputWidth eaxInput.Text = '0' eaxInput.Name = 'eaxInput32' local eaxStatus = createStatusLabel(fcaForm32, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = eaxInput, statusLabel = eaxStatus }) yPos = yPos + spacing local ecxLabel = createLabel(fcaForm32) ecxLabel.Caption = 'ECX:' ecxLabel.Left = 20 ecxLabel.Top = yPos ecxLabel.Width = labelWidth local ecxInput = createEdit(fcaForm32) ecxInput.Left = 90 ecxInput.Top = yPos - 2 ecxInput.Width = inputWidth ecxInput.Text = '0' ecxInput.Name = 'ecxInput32' local ecxStatus = createStatusLabel(fcaForm32, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = ecxInput, statusLabel = ecxStatus }) yPos = yPos + spacing local edxLabel = createLabel(fcaForm32) edxLabel.Caption = 'EDX:' edxLabel.Left = 20 edxLabel.Top = yPos edxLabel.Width = labelWidth local edxInput = createEdit(fcaForm32) edxInput.Left = 90 edxInput.Top = yPos - 2 edxInput.Width = inputWidth edxInput.Text = '0' edxInput.Name = 'edxInput32' local edxStatus = createStatusLabel(fcaForm32, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = edxInput, statusLabel = edxStatus }) yPos = yPos + spacing local ebxLabel = createLabel(fcaForm32) ebxLabel.Caption = 'EBX:' ebxLabel.Left = 20 ebxLabel.Top = yPos ebxLabel.Width = labelWidth local ebxInput = createEdit(fcaForm32) ebxInput.Left = 90 ebxInput.Top = yPos - 2 ebxInput.Width = inputWidth ebxInput.Text = '0' ebxInput.Name = 'ebxInput32' local ebxStatus = createStatusLabel(fcaForm32, STATUS_LEFT_X, yPos) table.insert(watchedItems, { input = ebxInput, statusLabel = ebxStatus }) local yPos2 = 90 local esiLabel = createLabel(fcaForm32) esiLabel.Caption = 'ESI:' esiLabel.Left = RIGHT_LABEL_X esiLabel.Top = yPos2 esiLabel.Width = labelWidth local esiInput = createEdit(fcaForm32) esiInput.Left = RIGHT_INPUT_X esiInput.Top = yPos2 - 2 esiInput.Width = inputWidth esiInput.Text = '0' esiInput.Name = 'esiInput32' local esiStatus = createStatusLabel(fcaForm32, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = esiInput, statusLabel = esiStatus }) yPos2 = yPos2 + spacing local ediLabel = createLabel(fcaForm32) ediLabel.Caption = 'EDI:' ediLabel.Left = RIGHT_LABEL_X ediLabel.Top = yPos2 ediLabel.Width = labelWidth local ediInput = createEdit(fcaForm32) ediInput.Left = RIGHT_INPUT_X ediInput.Top = yPos2 - 2 ediInput.Width = inputWidth ediInput.Text = '0' ediInput.Name = 'ediInput32' local ediStatus = createStatusLabel(fcaForm32, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = ediInput, statusLabel = ediStatus }) yPos2 = yPos2 + spacing local ebpLabel = createLabel(fcaForm32) ebpLabel.Caption = 'EBP:' ebpLabel.Left = RIGHT_LABEL_X ebpLabel.Top = yPos2 ebpLabel.Width = labelWidth local ebpInput = createEdit(fcaForm32) ebpInput.Left = RIGHT_INPUT_X ebpInput.Top = yPos2 - 2 ebpInput.Width = inputWidth ebpInput.Text = '0' ebpInput.Name = 'ebpInput32' local ebpStatus = createStatusLabel(fcaForm32, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = ebpInput, statusLabel = ebpStatus }) yPos2 = yPos2 + spacing local espLabel = createLabel(fcaForm32) espLabel.Caption = 'ESP:' espLabel.Left = RIGHT_LABEL_X espLabel.Top = yPos2 espLabel.Width = labelWidth local espInput = createEdit(fcaForm32) espInput.Left = RIGHT_INPUT_X espInput.Top = yPos2 - 2 espInput.Width = inputWidth espInput.Text = '0' espInput.Name = 'espInput32' local espStatus = createStatusLabel(fcaForm32, STATUS_RIGHT_X, yPos2) table.insert(watchedItems, { input = espInput, statusLabel = espStatus }) local stackYPos = 210 local stackLabel = createLabel(fcaForm32) stackLabel.Caption = 'Stack Arguments:' stackLabel.Left = 20 stackLabel.Top = stackYPos stackLabel.Width = 150 stackLabel.Font.Style = '[fsBold]' stackYPos = stackYPos + 25 local stackArg1Label = createLabel(fcaForm32) stackArg1Label.Caption = 'Arg 1:' stackArg1Label.Left = 20 stackArg1Label.Top = stackYPos stackArg1Label.Width = labelWidth local stackArg1Input = createEdit(fcaForm32) stackArg1Input.Left = 90 stackArg1Input.Top = stackYPos - 2 stackArg1Input.Width = inputWidth stackArg1Input.Text = '0' stackArg1Input.Name = 'stackArg1Input32' local stackArg1Status = createStatusLabel(fcaForm32, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = stackArg1Input, statusLabel = stackArg1Status }) stackYPos = stackYPos + spacing local stackArg2Label = createLabel(fcaForm32) stackArg2Label.Caption = 'Arg 2:' stackArg2Label.Left = 20 stackArg2Label.Top = stackYPos stackArg2Label.Width = labelWidth local stackArg2Input = createEdit(fcaForm32) stackArg2Input.Left = 90 stackArg2Input.Top = stackYPos - 2 stackArg2Input.Width = inputWidth stackArg2Input.Text = '0' stackArg2Input.Name = 'stackArg2Input32' local stackArg2Status = createStatusLabel(fcaForm32, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = stackArg2Input, statusLabel = stackArg2Status }) stackYPos = stackYPos + spacing local stackArg3Label = createLabel(fcaForm32) stackArg3Label.Caption = 'Arg 3:' stackArg3Label.Left = 20 stackArg3Label.Top = stackYPos stackArg3Label.Width = labelWidth local stackArg3Input = createEdit(fcaForm32) stackArg3Input.Left = 90 stackArg3Input.Top = stackYPos - 2 stackArg3Input.Width = inputWidth stackArg3Input.Text = '0' stackArg3Input.Name = 'stackArg3Input32' local stackArg3Status = createStatusLabel(fcaForm32, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = stackArg3Input, statusLabel = stackArg3Status }) stackYPos = stackYPos + spacing local stackArg4Label = createLabel(fcaForm32) stackArg4Label.Caption = 'Arg 4:' stackArg4Label.Left = 20 stackArg4Label.Top = stackYPos stackArg4Label.Width = labelWidth local stackArg4Input = createEdit(fcaForm32) stackArg4Input.Left = 90 stackArg4Input.Top = stackYPos - 2 stackArg4Input.Width = inputWidth stackArg4Input.Text = '0' stackArg4Input.Name = 'stackArg4Input32' local stackArg4Status = createStatusLabel(fcaForm32, STATUS_LEFT_X, stackYPos) table.insert(watchedItems, { input = stackArg4Input, statusLabel = stackArg4Status }) local stackYPos2 = 235 local stackArg5Label = createLabel(fcaForm32) stackArg5Label.Caption = 'Arg 5:' stackArg5Label.Left = RIGHT_LABEL_X stackArg5Label.Top = stackYPos2 stackArg5Label.Width = labelWidth local stackArg5Input = createEdit(fcaForm32) stackArg5Input.Left = RIGHT_INPUT_X stackArg5Input.Top = stackYPos2 - 2 stackArg5Input.Width = inputWidth stackArg5Input.Text = '0' stackArg5Input.Name = 'stackArg5Input32' local stackArg5Status = createStatusLabel(fcaForm32, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = stackArg5Input, statusLabel = stackArg5Status }) stackYPos2 = stackYPos2 + spacing local stackArg6Label = createLabel(fcaForm32) stackArg6Label.Caption = 'Arg 6:' stackArg6Label.Left = RIGHT_LABEL_X stackArg6Label.Top = stackYPos2 stackArg6Label.Width = labelWidth local stackArg6Input = createEdit(fcaForm32) stackArg6Input.Left = RIGHT_INPUT_X stackArg6Input.Top = stackYPos2 - 2 stackArg6Input.Width = inputWidth stackArg6Input.Text = '0' stackArg6Input.Name = 'stackArg6Input32' local stackArg6Status = createStatusLabel(fcaForm32, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = stackArg6Input, statusLabel = stackArg6Status }) stackYPos2 = stackYPos2 + spacing local stackArg7Label = createLabel(fcaForm32) stackArg7Label.Caption = 'Arg 7:' stackArg7Label.Left = RIGHT_LABEL_X stackArg7Label.Top = stackYPos2 stackArg7Label.Width = labelWidth local stackArg7Input = createEdit(fcaForm32) stackArg7Input.Left = RIGHT_INPUT_X stackArg7Input.Top = stackYPos2 - 2 stackArg7Input.Width = inputWidth stackArg7Input.Text = '0' stackArg7Input.Name = 'stackArg7Input32' local stackArg7Status = createStatusLabel(fcaForm32, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = stackArg7Input, statusLabel = stackArg7Status }) stackYPos2 = stackYPos2 + spacing local stackArg8Label = createLabel(fcaForm32) stackArg8Label.Caption = 'Arg 8:' stackArg8Label.Left = RIGHT_LABEL_X stackArg8Label.Top = stackYPos2 stackArg8Label.Width = labelWidth local stackArg8Input = createEdit(fcaForm32) stackArg8Input.Left = RIGHT_INPUT_X stackArg8Input.Top = stackYPos2 - 2 stackArg8Input.Width = inputWidth stackArg8Input.Text = '0' stackArg8Input.Name = 'stackArg8Input32' local stackArg8Status = createStatusLabel(fcaForm32, STATUS_RIGHT_X, stackYPos2) table.insert(watchedItems, { input = stackArg8Input, statusLabel = stackArg8Status }) local buttonYPos = 400 local callButton = createButton(fcaForm32) callButton.Caption = 'Call Function' callButton.Left = 20 callButton.Top = buttonYPos callButton.Width = 100 callButton.Height = 30 local callButton2 = createButton(fcaForm32) callButton2.Caption = 'Call Function + Exit Thread' callButton2.Left = 20 callButton2.Top = 435 callButton2.Width = 150 callButton2.Height = 30 local clearButton = createButton(fcaForm32) clearButton.Caption = 'Clear All' clearButton.Left = 140 clearButton.Top = buttonYPos clearButton.Width = 100 clearButton.Height = 30 local loadButton = createButton(fcaForm32) loadButton.Caption = 'Load Current' loadButton.Left = 260 loadButton.Top = buttonYPos loadButton.Width = 100 loadButton.Height = 30 -- ---- Monitor controls ---- local monitorCheckbox = createCheckBox(fcaForm32) monitorCheckbox.Caption = 'Monitor pointer changes' monitorCheckbox.Left = 380 monitorCheckbox.Top = 405 monitorCheckbox.Width = 200 monitorCheckbox.Checked = true monitorCheckbox.Hint = 'Periodically reads memory at each register address and flags it if its contents have changed.' monitorCheckbox.ShowHint = true local checkSizeLabel = createLabel(fcaForm32) checkSizeLabel.Caption = 'Check Size:' checkSizeLabel.Left = 380 checkSizeLabel.Top = 442 checkSizeLabel.Width = 80 local checkSizeInput = createEdit(fcaForm32) checkSizeInput.Left = 460 checkSizeInput.Top = 440 checkSizeInput.Width = 70 checkSizeInput.Text = '300' checkSizeInput.Hint = 'Number of bytes hashed at each pointer for change detection (1 - 1048576).' checkSizeInput.ShowHint = true local function OnCallFunction32(sender) if conventionDropdown.Items[conventionDropdown.ItemIndex] == 'stdcall' then local function parseHexValue(inputText) if inputText == nil or inputText == "" then return 0 end local cleanText = string.gsub(inputText, "^0x", "") return tonumber(cleanText, 16) or 0 end local funcAddress = parseHexValue(functionAddressInput.Text) if funcAddress == 0 then showMessage('Please enter a valid function address!') return end if string.find(eaxInput.Text, "0x") then eaxVal = parseHexValue(eaxInput.Text) else eaxVal = getAddress(eaxInput.Text) end if string.find(ecxInput.Text, "0x") then ecxVal = parseHexValue(ecxInput.Text) else ecxVal = getAddress(ecxInput.Text) end if string.find(edxInput.Text, "0x") then edxVal = parseHexValue(edxInput.Text) else edxVal = getAddress(edxInput.Text) end if string.find(ebxInput.Text, "0x") then ebxVal = parseHexValue(ebxInput.Text) else ebxVal = getAddress(ebxInput.Text) end if string.find(ebpInput.Text, "0x") then ebpVal = parseHexValue(ebpInput.Text) else ebpVal = getAddress(ebpInput.Text) end if string.find(esiInput.Text, "0x") then esiVal = parseHexValue(esiInput.Text) else esiVal = getAddress(esiInput.Text) end if string.find(ediInput.Text, "0x") then ediVal = parseHexValue(ediInput.Text) else ediVal = getAddress(ediInput.Text) end if string.find(espInput.Text, "0x") then espVal = parseHexValue(espInput.Text) else espVal = getAddress(espInput.Text) end if string.find(stackArg1Input.Text, "0x") == 1 then arg1Val = parseHexValue(stackArg1Input.Text) else arg1Val = getAddress(stackArg1Input.Text) end if string.find(stackArg2Input.Text, "0x") == 1 then arg2Val = parseHexValue(stackArg2Input.Text) else arg2Val = getAddress(stackArg2Input.Text) end if string.find(stackArg3Input.Text, "0x") == 1 then arg3Val = parseHexValue(stackArg3Input.Text) else arg3Val = getAddress(stackArg3Input.Text) end if string.find(stackArg4Input.Text, "0x") == 1 then arg4Val = parseHexValue(stackArg4Input.Text) else arg4Val = getAddress(stackArg4Input.Text) end if string.find(stackArg5Input.Text, "0x") == 1 then arg5Val = parseHexValue(stackArg5Input.Text) else arg5Val = getAddress(stackArg5Input.Text) end if string.find(stackArg6Input.Text, "0x") == 1 then arg6Val = parseHexValue(stackArg6Input.Text) else arg6Val = getAddress(stackArg6Input.Text) end if string.find(stackArg7Input.Text, "0x") == 1 then arg7Val = parseHexValue(stackArg7Input.Text) else arg7Val = getAddress(stackArg7Input.Text) end if string.find(stackArg8Input.Text, "0x") == 1 then arg8Val = parseHexValue(stackArg8Input.Text) else arg8Val = getAddress(stackArg8Input.Text) end function onBPFunc() EAX = eaxVal ECX = ecxVal EDX = edxVal EBX = ebxVal EDI = ediVal ESI = esiVal end debug_setBreakpoint(funcAddress, 1, 0, 0, onBPFunc) if tonumber(argCountInput.Text) == 1 then executeCodeEx(0,100,funcAddress,arg1Val) elseif tonumber(argCountInput.Text) == 2 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val) elseif tonumber(argCountInput.Text) == 3 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val) elseif tonumber(argCountInput.Text) == 4 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val) elseif tonumber(argCountInput.Text) == 5 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val) elseif tonumber(argCountInput.Text) == 6 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val) elseif tonumber(argCountInput.Text) == 7 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val,arg7Val) elseif tonumber(argCountInput.Text) == 8 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val,arg7Val,arg8Val) else showMessage('Argument count exceeds 8 or is invalid for thiscall!') end debug_removeBreakpoint(funcAddress) elseif conventionDropdown.Items[conventionDropdown.ItemIndex] == 'cdecl' then local function parseHexValue(inputText) if inputText == nil or inputText == "" then return 0 end local cleanText = string.gsub(inputText, "^0x", "") return tonumber(cleanText, 16) or 0 end local funcAddress = parseHexValue(functionAddressInput.Text) if funcAddress == 0 then showMessage('Please enter a valid function address!') return end if string.find(eaxInput.Text, "0x") then eaxVal = parseHexValue(eaxInput.Text) else eaxVal = getAddress(eaxInput.Text) end if string.find(ecxInput.Text, "0x") then ecxVal = parseHexValue(ecxInput.Text) else ecxVal = getAddress(ecxInput.Text) end if string.find(edxInput.Text, "0x") then edxVal = parseHexValue(edxInput.Text) else edxVal = getAddress(edxInput.Text) end if string.find(ebxInput.Text, "0x") then ebxVal = parseHexValue(ebxInput.Text) else ebxVal = getAddress(ebxInput.Text) end if string.find(ebpInput.Text, "0x") then ebpVal = parseHexValue(ebpInput.Text) else ebpVal = getAddress(ebpInput.Text) end if string.find(esiInput.Text, "0x") then esiVal = parseHexValue(esiInput.Text) else esiVal = getAddress(esiInput.Text) end if string.find(ediInput.Text, "0x") then ediVal = parseHexValue(ediInput.Text) else ediVal = getAddress(ediInput.Text) end if string.find(espInput.Text, "0x") then espVal = parseHexValue(espInput.Text) else espVal = getAddress(espInput.Text) end if string.find(stackArg1Input.Text, "0x") == 1 then arg1Val = parseHexValue(stackArg1Input.Text) else arg1Val = getAddress(stackArg1Input.Text) end if string.find(stackArg2Input.Text, "0x") == 1 then arg2Val = parseHexValue(stackArg2Input.Text) else arg2Val = getAddress(stackArg2Input.Text) end if string.find(stackArg3Input.Text, "0x") == 1 then arg3Val = parseHexValue(stackArg3Input.Text) else arg3Val = getAddress(stackArg3Input.Text) end if string.find(stackArg4Input.Text, "0x") == 1 then arg4Val = parseHexValue(stackArg4Input.Text) else arg4Val = getAddress(stackArg4Input.Text) end if string.find(stackArg5Input.Text, "0x") == 1 then arg5Val = parseHexValue(stackArg5Input.Text) else arg5Val = getAddress(stackArg5Input.Text) end if string.find(stackArg6Input.Text, "0x") == 1 then arg6Val = parseHexValue(stackArg6Input.Text) else arg6Val = getAddress(stackArg6Input.Text) end if string.find(stackArg7Input.Text, "0x") == 1 then arg7Val = parseHexValue(stackArg7Input.Text) else arg7Val = getAddress(stackArg7Input.Text) end if string.find(stackArg8Input.Text, "0x") == 1 then arg8Val = parseHexValue(stackArg8Input.Text) else arg8Val = getAddress(stackArg8Input.Text) end function onBPFunc() EAX = eaxVal ECX = ecxVal EDX = edxVal EBX = ebxVal EDI = ediVal ESI = esiVal end debug_setBreakpoint(funcAddress, 1, 0, 0, onBPFunc) if tonumber(argCountInput.Text) == 1 then executeCodeEx(1,100,funcAddress,arg1Val) elseif tonumber(argCountInput.Text) == 2 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val) elseif tonumber(argCountInput.Text) == 3 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val,arg3Val) elseif tonumber(argCountInput.Text) == 4 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val) elseif tonumber(argCountInput.Text) == 5 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val) elseif tonumber(argCountInput.Text) == 6 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val) elseif tonumber(argCountInput.Text) == 7 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val,arg7Val) elseif tonumber(argCountInput.Text) == 8 then executeCodeEx(1,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val,arg7Val,arg8Val) else showMessage('Argument count exceeds 8 or is invalid for thiscall!') end debug_removeBreakpoint(funcAddress) elseif conventionDropdown.Items[conventionDropdown.ItemIndex] == 'thiscall' then local function parseHexValue(inputText) if inputText == nil or inputText == "" then return 0 end local cleanText = string.gsub(inputText, "^0x", "") return tonumber(cleanText, 16) or 0 end local funcAddress = parseHexValue(functionAddressInput.Text) if funcAddress == 0 then showMessage('Please enter a valid function address!') return end if string.find(eaxInput.Text, "0x") then eaxVal = parseHexValue(eaxInput.Text) else eaxVal = getAddress(eaxInput.Text) end if string.find(ecxInput.Text, "0x") then ecxVal = parseHexValue(ecxInput.Text) else ecxVal = getAddress(ecxInput.Text) end if string.find(edxInput.Text, "0x") then edxVal = parseHexValue(edxInput.Text) else edxVal = getAddress(edxInput.Text) end if string.find(ebxInput.Text, "0x") then ebxVal = parseHexValue(ebxInput.Text) else ebxVal = getAddress(ebxInput.Text) end if string.find(ebpInput.Text, "0x") then ebpVal = parseHexValue(ebpInput.Text) else ebpVal = getAddress(ebpInput.Text) end if string.find(esiInput.Text, "0x") then esiVal = parseHexValue(esiInput.Text) else esiVal = getAddress(esiInput.Text) end if string.find(ediInput.Text, "0x") then ediVal = parseHexValue(ediInput.Text) else ediVal = getAddress(ediInput.Text) end if string.find(espInput.Text, "0x") then espVal = parseHexValue(espInput.Text) else espVal = getAddress(espInput.Text) end if string.find(stackArg1Input.Text, "0x") == 1 then arg1Val = parseHexValue(stackArg1Input.Text) else arg1Val = getAddress(stackArg1Input.Text) end if string.find(stackArg2Input.Text, "0x") == 1 then arg2Val = parseHexValue(stackArg2Input.Text) else arg2Val = getAddress(stackArg2Input.Text) end if string.find(stackArg3Input.Text, "0x") == 1 then arg3Val = parseHexValue(stackArg3Input.Text) else arg3Val = getAddress(stackArg3Input.Text) end if string.find(stackArg4Input.Text, "0x") == 1 then arg4Val = parseHexValue(stackArg4Input.Text) else arg4Val = getAddress(stackArg4Input.Text) end if string.find(stackArg5Input.Text, "0x") == 1 then arg5Val = parseHexValue(stackArg5Input.Text) else arg5Val = getAddress(stackArg5Input.Text) end if string.find(stackArg6Input.Text, "0x") == 1 then arg6Val = parseHexValue(stackArg6Input.Text) else arg6Val = getAddress(stackArg6Input.Text) end if string.find(stackArg7Input.Text, "0x") == 1 then arg7Val = parseHexValue(stackArg7Input.Text) else arg7Val = getAddress(stackArg7Input.Text) end if string.find(stackArg8Input.Text, "0x") == 1 then arg8Val = parseHexValue(stackArg8Input.Text) else arg8Val = getAddress(stackArg8Input.Text) end function onBPFunc() EAX = eaxVal ECX = ecxVal EDX = edxVal EBX = ebxVal EDI = ediVal ESI = esiVal end debug_setBreakpoint(funcAddress, 1, 0, 0, onBPFunc) if tonumber(argCountInput.Text) == 1 then executeCodeEx(0,100,funcAddress,arg1Val) elseif tonumber(argCountInput.Text) == 2 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val) elseif tonumber(argCountInput.Text) == 3 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val) elseif tonumber(argCountInput.Text) == 4 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val) elseif tonumber(argCountInput.Text) == 5 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val) elseif tonumber(argCountInput.Text) == 6 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val) elseif tonumber(argCountInput.Text) == 7 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val,arg7Val) elseif tonumber(argCountInput.Text) == 8 then executeCodeEx(0,100,funcAddress,arg1Val,arg2Val,arg3Val,arg4Val,arg5Val,arg6Val,arg7Val,arg8Val) else showMessage('Argument count exceeds 8 or is invalid for thiscall!') end debug_removeBreakpoint(funcAddress) elseif conventionDropdown.Items[conventionDropdown.ItemIndex] == 'fastcall' then local function parseHexValue(inputText) if inputText == nil or inputText == "" then return 0 end local cleanText = string.gsub(inputText, "^0x", "") return tonumber(cleanText, 16) or 0 end local funcAddress = parseHexValue(functionAddressInput.Text) if funcAddress == 0 then showMessage('Please enter a valid function address!') return end if string.find(eaxInput.Text, "0x") then eaxVal = parseHexValue(eaxInput.Text) else eaxVal = getAddress(eaxInput.Text) end if string.find(ecxInput.Text, "0x") then ecxVal = parseHexValue(ecxInput.Text) else ecxVal = getAddress(ecxInput.Text) end if string.find(edxInput.Text, "0x") then edxVal = parseHexValue(edxInput.Text) else edxVal = getAddress(edxInput.Text) end if string.find(ebxInput.Text, "0x") then ebxVal = parseHexValue(ebxInput.Text) else ebxVal = getAddress(ebxInput.Text) end if string.find(ebpInput.Text, "0x") then ebpVal = parseHexValue(ebpInput.Text) else ebpVal = getAddress(ebpInput.Text) end if string.find(esiInput.Text, "0x") then esiVal = parseHexValue(esiInput.Text) else esiVal = getAddress(esiInput.Text) end if string.find(ediInput.Text, "0x") then ediVal = parseHexValue(ediInput.Text) else ediVal = getAddress(ediInput.Text) end if string.find(espInput.Text, "0x") then espVal = parseHexValue(espInput.Text) else espVal = getAddress(espInput.Text) end if string.find(stackArg1Input.Text, "0x") == 1 then arg1Val = parseHexValue(stackArg1Input.Text) else arg1Val = getAddress(stackArg1Input.Text) end if string.find(stackArg2Input.Text, "0x") == 1 then arg2Val = parseHexValue(stackArg2Input.Text) else arg2Val = getAddress(stackArg2Input.Text) end if string.find(stackArg3Input.Text, "0x") == 1 then arg3Val = parseHexValue(stackArg3Input.Text) else arg3Val = getAddress(stackArg3Input.Text) end if string.find(stackArg4Input.Text, "0x") == 1 then arg4Val = parseHexValue(stackArg4Input.Text) else arg4Val = getAddress(stackArg4Input.Text) end if string.find(stackArg5Input.Text, "0x") == 1 then arg5Val = parseHexValue(stackArg5Input.Text) else arg5Val = getAddress(stackArg5Input.Text) end if string.find(stackArg6Input.Text, "0x") == 1 then arg6Val = parseHexValue(stackArg6Input.Text) else arg6Val = getAddress(stackArg6Input.Text) end local assemblyScript = string.format([[ alloc(cMem,1000,$process) registersymbol(cMem) label(funcToCall) label(arg1) label(arg2) label(arg3) label(arg4) label(arg5) label(arg6) label(arg7) label(arg8) cMem: push ebp mov ebp,esp and esp,FFFFFFF0 // 16 bit alignment for stack sub esp,10 mov ecx,[arg1] mov edx,[arg2] mov eax,[arg8] push eax mov eax,[arg7] push eax mov eax,[arg6] push eax mov eax,[arg5] push eax mov eax,[arg4] push eax mov eax,[arg3] push eax call [funcToCall] mov esp,ebp // restore original stack pop ebp xor eax,eax ret arg1: dd 0x%X arg2: dd 0x%X arg3: dd 0x%X arg4: dd 0x%X arg5: dd 0x%X arg6: dd 0x%X arg7: dd 0x%X arg8: dd 0x%X funcToCall: dd 0x%X createThread(cMem) ]], ecxVal, edxVal, arg1Val, arg2Val, arg3Val, arg4Val, arg5Val, arg6Val, funcAddress) local success, result = pcall(function() return autoAssemble(assemblyScript) end) if success and result then allocatedMemoryAddress = result autoAssemble('dealloc(cMem)\nunregistersymbol(cMem)') allocatedMemoryAddress = nil --showMessage('Function call executed successfully!') else showMessage('Failed to execute function call. Check console for errors.') print('Assembly script execution failed. Error: ' .. tostring(result)) end end end local function OnCallFunction2_32(sender) showMessage('not implemented yet.') end local function OnClearAll32(sender) functionAddressInput.Text = '0x00000000' eaxInput.Text = '0' ecxInput.Text = '0' edxInput.Text = '0' ebxInput.Text = '0' esiInput.Text = '0' ediInput.Text = '0' ebpInput.Text = '0' espInput.Text = '0' stackArg1Input.Text = '0' stackArg2Input.Text = '0' stackArg3Input.Text = '0' stackArg4Input.Text = '0' stackArg5Input.Text = '0' stackArg6Input.Text = '0' stackArg7Input.Text = '0' stackArg8Input.Text = '0' end local function OnLoadCurrent32(sender) local function safeFormatRegister(value) if value == nil then return "0x0" else return string.format("0x%X", value) end end functionAddressInput.Text = safeFormatRegister(EIP) eaxInput.Text = safeFormatRegister(EAX) ecxInput.Text = safeFormatRegister(ECX) edxInput.Text = safeFormatRegister(EDX) ebxInput.Text = safeFormatRegister(EBX) esiInput.Text = safeFormatRegister(ESI) ediInput.Text = safeFormatRegister(EDI) ebpInput.Text = safeFormatRegister(EBP) espInput.Text = safeFormatRegister(ESP) local function safeReadDword(address) if address == nil then return 0 end local success, value = pcall(readDword, address) if success then return value or 0 else return 0 end end if ESP then stackArg1Input.Text = string.format("0x%X", readInteger(ESP + 0x4)) stackArg2Input.Text = string.format("0x%X", readInteger(ESP + 0x8)) stackArg3Input.Text = string.format("0x%X", readInteger(ESP + 0xC)) stackArg4Input.Text = string.format("0x%X", readInteger(ESP + 0x10)) stackArg5Input.Text = string.format("0x%X", readInteger(ESP + 0x14)) stackArg6Input.Text = string.format("0x%X", readInteger(ESP + 0x18)) stackArg7Input.Text = string.format("0x%X", readInteger(ESP + 0x24)) stackArg8Input.Text = string.format("0x%X", readInteger(ESP + 0x28)) end if conventionDropdown.Items[conventionDropdown.ItemIndex] == 'thiscall' then showMessage("Current register values loaded successfully!\nNote: For 'thiscall', ensure ECX is set to the 'this' pointer value.\nDont add ECX to the argument count.") else showMessage('Current register values loaded successfully!') end end callButton.OnClick = OnCallFunction32 callButton2.OnClick = OnCallFunction2_32 clearButton.OnClick = OnClearAll32 loadButton.OnClick = OnLoadCurrent32 fcaForm32.OnClose = function(sender) fcaForm32 = nil return caFree end -- Wire up the change monitor. setupChangeMonitor(fcaForm32, watchedItems, checkSizeInput, monitorCheckbox) return fcaForm32 end local function OnFunctionCallingAssistant(sender) if fcaForm and fcaForm.Visible then fcaForm.BringToFront() return end fcaForm = CreateFunctionCallingAssistantForm() fcaForm.Position = 'poScreenCenter' fcaForm.WindowState = 'wsNormal' fcaForm.Visible = true fcaForm.Show() fcaForm.BringToFront() end local function OnFunctionCallingAssistant32(sender) if fcaForm32 and fcaForm32.Visible then fcaForm32.BringToFront() return end fcaForm32 = CreateFunctionCallingAssistantForm32() fcaForm32.Position = 'poScreenCenter' fcaForm32.WindowState = 'wsNormal' fcaForm32.Visible = true fcaForm32.Show() fcaForm32.BringToFront() end local function OnSettings(sender) showMessage('Made by NightfallCT.\n\nFunction Calling Assistant v1.3\n\nSpecial thanks to Icew0lf for the idea.') end menuItem.miFunctionCallingAssistant.OnClick = OnFunctionCallingAssistant menuItem.miFunctionCallingAssistant32.OnClick = OnFunctionCallingAssistant32 menuItem.miSettings.OnClick = OnSettings -- v1.3 - per-register memory change monitor with adjustable check size