Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- -- ver 0.1 , 2021-08-02-aug
- -- update to previous custom jump assembler, previous version should be removed
- -- extend jmp!near <address> to
- jmp!near <address> <: or ;> <near_cave_aob_1> ( ; <near_cave_aob_n> )*
- -- before allocate new trampoline persist memory, the <near_cave_aob> is scanned to try find possible trampoline position,
- -- The aob pattern is a bit different from ce's
- -- 1. wildcard must use '?';
- -- 2. cc* means 14x cc bytes for short hand;
- -- 3. 45|67 here | mean the position offset for trampoline, offset 0 if missing (see examples);
- -- example 1
- 00 00* 55
- expand to
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55
- (ie. 15 bytes of 00 then follow 55 (eg push rbp), trampoline offset 0)
- -- example 2
- c3 | 00*
- expand to
- c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- (ie. c3 (ret) then follow 14 bytes 00 , trampoline offset 1, so trampoline will insert just after c3)
- use ':' or ';' for wanting to scan near cave
- use ';' to separate different aob patterns
- only briefly tested, use in your own risk.
- --]]
- 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-- should be redundancy?
- elseif force=='near' then forceNear = true
- elseif force=='long' then forceLong = true
- else target = nil end -- unknown jump distance modifier, error
- 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 -- force near and long jmp trampoline need
- 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 --- cutsomJumpCall
Add Comment
Please, Sign In to add comment