Code: Select all
[ENABLE]
{$lua}
if syntaxcheck then return end
if not _customJumpCall then
_pidContext = _pidContext or {now=os.clock()+3; reset=os.clock()+60}
local function pf(...) if indebug then print(string.format(...))end end
--- modify to default +X and allow one result
local function AOBScanEx(aob,s,e,bAllResult,reuseMS,p,a,n,pb)
-- pf('scan '..aob)
local p,a,n,s,e = p or '+X*W',a or fsmNotAligned,n or '0',s or 0x0,e or 0xffffffffffffffff
reuseMS = type(reuseMS)=='userdata'and reuseMS.ClassName:lower():find'scan' and reuseMS
local ms, result = reuseMS or pb and createMemScan(pb) or createMemScan()
ms.OnlyOneResult = not bAllResult
if ms.OnlyOneResult then
ms.firstScan(soExactValue,vtByteArray,nil,aob,nil,s,e,p,a,n,true,false,false,false)
ms.waitTillDone()
local r = ms.Result
if type(r)=='number' and r~=0 then result = r end
else
local fl = createFoundList(ms)
ms.firstScan(soExactValue,vtByteArray,nil,aob,nil,s,e,p,a,n,true,false,false,false)
ms.waitTillDone()
fl.initialize()
if fl.getCount() > 0 then
result = {}
for i=1,fl.getCount() do result[i]=tonumber(fl.getAddress(i-1),16) end
end
fl.destroy()
end
if not reuseMS then ms.destroy() end
return result
end
if not getPidContext then
function getPidContext()
local p, pid, now = _pidContext, getOpenedProcessID(), os.clock()
if now > p.reset then
p.reset = now + 60
local ps = getProcessList()
for k,v in pairs(p)do
if type(k)=='number' and (not ps[k] or ps[k]~=v[1]) then p[k] = nil end
end
end
if now > p.now and not readInteger(process)then
return {}
elseif p[pid] and now<p.now and p.pid == pid and p.process==process then
return p[pid][2]
elseif not readInteger(process) then
return {}
end
p.now, p.pid, p.process = now+3, pid, process
if not p[pid] or p[pid][1]~=process then
local px = enumModules()[1]
p[pid] = {process,{PID=pid,PROCESS=process,ExeBase=px.Address, ExeFile=px.PathToFile,ExeSize=getModuleSize(process)}}
end
return p[pid][2]
end
end
getPidContext()
local EMPTY = -1
local function s2aob(s)
-- print('s2aob:',type(s))
return s:gsub('.',function(c)return string.format(' %02X',c:byte())end):sub(2)
end
local function toInt(n)return type(n)=='number'and math.tointeger(n)end
local function makeKey(target,hint)
return string.format('%x->%x',hint,target)
end
local function makeTrampoline(from,target,hint,t)
local diff = from - hint
-- pf('try make: %X %X %X %X',from,target,hint, diff)
if diff>-0x7ffffffb and diff<0x80000005 then
local bs, r = string.pack('I6I8',0x25ff ,target),{}
for c in bs:gmatch'.'do r[1+#r]=c:byte()end
fullAccess(from,64)
local x = writeBytes(from, r)
local rs = readBytes(from,14,true)
-- pf('written %X', x or -1)
if rs and byteTableToString(rs)==bs then
-- pf('ok write: %X %X %X // %s || %s',from,target,hint,s2aob(bs),s2aob(byteTableToString(rs)))
t[makeKey(target,hint)] = {from,target}
return from
else
return nil,'fail write bytes'
-- pf('fail write: %X %X %X',from,target,hint)
end
end
end
function GetTrampoline(target, hint, noNewAlloc, nearCavePatterns)
if not toInt(target)or not toInt(hint) then return nil end
local p = getPidContext()
p.Trampoline = p.Trampoline or {}
local key = makeKey(target,hint)
local t,tcnt, diff = p.Trampoline,0
if t[key] then return t[key][1] end
if not noNewAlloc and nearCavePatterns then
local cs,ms = nearCavePatterns, createMemScan()
-- print('AOB scan --',#cs)
for i=1,#cs do
local aob,ofs = unpack(cs[i])
local found = AOBScanEx(aob,hint-0x7fff0000,hint+0x7fff0000,false,ms)
-- pf('aob found: %02d %02X %X %s ',i,ofs,found or -1,aob)
if found and makeTrampoline(found+ofs,target,hint,t) then
t[found+ofs] = target
-- pf('ok %X', found+ofs)
ms.Destroy()
return found+ofs
else
-- print'fail'
end
end
ms.Destroy()
end
------
for from, to in pairs(t) do
if to==EMPTY and makeTrampoline(from,target,hint,t) then
-- pf('new alloc: %X %X',from,target)
t[from] = target
return from
end
end
-- no previous allocation, make new one
if not noNewAlloc then
local addr = allocateMemory(0x1000,hint)
diff = addr and addr - hint
if not diff or diff<=-0x7ffffffb or diff>=0x7ffff005 then
if addr then deAlloc(addr)end
return nil,'fail allocate trampoline'
end
p.TrmpAllocCnt = not p.TrmpAllocCnt and 1 or p.TrmpAllocCnt + 1
for i=0,255 do t[addr+i*16]=EMPTY end
return GetTrampoline(target, hint, true)
end
end
if _customJumpCall then
_customJumpCall = nil,unregisterAssembler(_customJumpCall)
end
indebug = true
_customJumpCall = registerAssembler(function (addr, inst)
-- pf('enter: %X - %s',addr, inst)
local force, target, nearCave, isJmp, forceShort, forceNear, forceLong =
inst:match'^%s*[jJ][mM][pP]!(%a*)%s+([^:;]+)%s*(.-)%s*$'
if target then isJmp = true else
force, target, nearCave = inst:match'^%s*[cC][aA][lL][lL]!(%a*)%s+([^:;]+)%s*(.-)%s*$'
end
-- pf('fmt: force[%s] target[%s] nearCave[%s]',force or '?', target or '?',nearCave or '?')
if not target then return end
local nearCavePatterns
if nearCave:len()>0 then -- near cave specificed
local nc, cs, i, tag, _ = nearCave:sub(2), {},0
-- pf('nearCave:',nc)
local pat = '^%s*([%x%?][%x%?]?)(%s*|?[|%*]?|?)'
for aob in nc:gmatch'%s*([^;]+)%s*'do
i = i+1
-- print(i,'aob:',aob)
local as,nxt,ofs,tmp,foundOfs = {},0,1,aob
if aob:sub(1,1)=='|' then foundOfs,aob = 0,aob:sub(2)end
while aob:find(pat,nxt+1)do
_, nxt,as[1+#as],tag = aob:find(pat,nxt+1)
-- pf('---->%d : %s, %s, %s, <<%s>>',i,_, nxt,as[#as],tag)
if tag:find'^%s*%*' then
as[#as] = (as[#as]..' '):rep(14)
ofs = ofs + 13
if tag:find'|' then
if foundOfs then foundOfs = true break else foundOfs = ofs end
end
end
if tag:find'^%s*|' then
if foundOfs then foundOfs = true break else foundOfs = ofs end
if tag:find'%*' then foundOfs = false break end
end
ofs = ofs + 1
end
-- print(i,'>'..table.concat(as,' ')..'<','/',tostring(foundOfs),'/',aob)
if foundOfs == true then
-- pf('err: %s', 'multiple "|" jmp ofs on aob #'..i..': '..tmp)
return nil,'multiple "|" jmp ofs on aob #'..i..': '..tmp
elseif foundOfs == false then
-- pf('err: %s', '"|*" pattern not allowed, aob #'..i..': '..tmp)
return nil,'"|*" pattern not allowed, aob #'..i..': '..tmp
end
-- pf('aob%d- %d , %s %s',i,ofs,aob,foundOfs)
ofs = foundOfs or 0
if #as>0 then
cs[1+#cs] = {table.concat(as,' '):gsub('%s+',' '),ofs}
end
-- print('-->',unpack(cs[#cs]))
end
nearCavePatterns = cs
end
target = target and target:len()>1 and GetAddressSafe(target)
if target and force:len()>1 then
force=force:lower()
if force=='short' then forceShort = true
elseif force=='near' then forceNear = true
elseif force=='long' then forceLong = true
else target = nil end
end
if not target then
return --nil,'invalid :'..inst
else
local cmd = isJmp and {0xeb,0xe9,0x25ff} or {0xe8,0xe8,0x08eb0000000215ff}
local diff, r, bs = target - addr, {}
if isJmp and (forceShort or not forceNear and not forceLong) and diff>-0x7e and diff <0x82 then
bs = string.pack('Bb',cmd[1], diff-2)
elseif not targetIs64Bit() or not forceLong and diff>-0x7ffffffb and diff<0x80000005 then
bs = string.pack('Bi4',cmd[2],diff-5)
elseif not forceNear or addr<0x1000 then
if isJmp then
bs = string.pack('I6I8',cmd[3],target)
else
bs = string.pack('I8I8',cmd[3],target)
end
elseif diff<=-0x7ffffffb or diff>=0x80000005 then
local trmp, errmsg = GetTrampoline(target, addr, nil,nearCavePatterns)
if not trmp then return nil,(errmsg or '!')..', no trampoline:'..inst end
bs = string.pack('Bi4',cmd[2], trmp - addr -5)
end
if bs then
for c in bs:gmatch'.' do r[1+#r]=c:byte()end
return r
end
end
end)
end
{$asm}
aobscan(datt,42 ?? ?? ?? ?? ?? 89 ?? 89 ?? 42 ?? ?? ?? ?? 48 ?? ?? ?? ?? 4C ?? ?? ?? ?? 42 ?? ?? ?? ?? 89 ?? 89 ?? 41 ?? ?? 41 ?? ?? ?? 40 ?? ?? ?? 40 ?? ?? ?? 45 ?? ?? 0F) // should be unique
alloc(newmem,$1000,datt)
alloc(_datt,8)
alloc(dattcopy,6)
registersymbol(datt)
registersymbol(_datt)
registersymbol(dattcopy)
label(code)
label(return)
dattcopy:
readmem(datt,6)
newmem:
mov [_datt],rcx
add [_datt],r8
add [_datt],20
code:
readmem(datt,6)
jmp return
datt:
jmp!near newmem; c3 | 00*
nop
return:
[DISABLE]
datt:
readmem(dattcopy,6)
dealloc(newmem)
dealloc(datt)
dealloc(_datt)
dealloc(dattcopy)
unregistersymbol(datt)
unregistersymbol(_datt)
unregistersymbol(dattcopy)