core/internal.lua
srift c1bfbaf36a scriptcore: reconstructed from preserved seed files
Reconstructed Lua scripting infrastructure consumed by every per-game
flash script. Two source files preserved at D:\B\Srift\games\Flash\:

  - functional.lua (109 lines): table.from/max/min/find/filter/map
  - internal.lua  (172 lines): Markers, Rule.All/Any sugar, Hex/Addr,
                                print override, gadget enumerators

Surface inventory at D:\B\Srift\games\Flash\SURFACE.md confirms
these two files cover scriptcore's lua-level responsibilities for
CODMW, CODMW2, IW9, DayZ, Template. RB6 may need a Rule.Refs stub.

Server contract (Builder.ts:286-380): pushing this repo to Gitea as
srift/core triggers the standard webhook pipeline and produces
S.Build doc { kind:'script', product:'scriptcore' }.
2026-06-29 21:03:19 +02:00

173 lines
4.2 KiB
Lua

-- Implement the marker table.
--
local markerObjects = {}
_G.Markers = { Clear = function() markerObjects = {} end }
setmetatable(_G.Markers, {
__index = function(_, name)
local marker = markerObjects[name]
return marker and Rule.ReadMarker(marker)
end
})
-- Parses a table of rules and returns a table of valid Rule objects.
--
local function parseRuleList(arg)
local result = {}
local it = 1
while true do
local v = arg[it]
if v == nil then break end
local t = type(v)
if t == "string" then
if v:sub(1,1) == "#" then
v = v:sub(2)
local m = markerObjects[v]
if not m then
m = Rule.Marker()
markerObjects[v] = m
end
result[#result+1] = m
elseif type(arg[it+1]) == "number" then
result[#result+1] = Rule.Instruction(v, arg[it+1])
it = it + 1
else
result[#result+1] = Rule.Pattern(v)
end
elseif t == "table" then
result[#result+1] = Rule.Pad(v[1], v[2])
elseif t == "function" then
result[#result+1] = Rule.Filter(v)
elseif t == "userdata" then
result[#result+1] = v
else
error("Did not expect: " .. t)
end
it = it + 1
end
return result
end
-- Implement Rule.All and Rule.Any.
--
function Rule.All(arg)
return Rule.AllImpl(parseRuleList(arg))
end
function Rule.Any(arg)
return Rule.AnyImpl(parseRuleList(arg))
end
-- Hex helper, override print.
--
function Hex(value) return value and string.format("%x", value) or "nil" end
function Addr(value) return value and string.format("%x", value + Image.base) or "nil" end
function print(...)
local args = table.pack(...)
local msg = ""
for i=1, args.n do
if #msg ~= 0 then
msg = msg .. " " .. tostring(args[i])
else
msg = tostring(args[i])
end
end
Log(msg)
end
local function findAny(patternList)
local output = {0}
local n = 0
for k,v in ipairs(patternList) do
for rva in Image:Find(Rule.Pattern(v[1])) do
local res = rva + ((#v[1]+1)/3) + v[2]
output[n+2] = res
n = n + 1
end
end
output[1] = n
return output
end
-- Implement searchers for user call gadgets.
--
Register("_fast_return_gadgets", R_ARR, function()
return findAny{
{ "E8 ?? 0F 05 00", -3 }, -- call rel32
{ "0F 8? 0F 05 00", -3 }, -- jcc rel32
{ "FF 15 ?? 0F 05", -2 }, -- call [rip+rel32]
{ "48 8D ?? ?? 0F 05", -2 }, -- lea [rip+rel32]
{ "48 89 ?? ?? 0F 05", -2 }, -- mov [rip+rel32]
{ "4C 8D ?? ?? 0F 05", -2 }, -- lea [rip+rel32]
{ "4C 89 ?? ?? 0F 05", -2 }, -- mov [rip+rel32]
}
end)
Register("_fix_arg_gadgets", R_ARR, function()
return findAny{
{ "F3 41 0F 59 C3", -2 }, -- mulss, mulps, vmulss, vmulps
{ "F3 43 0F 59 C3", -2 },
{ "41 0F 59 C3", -2 },
{ "F3 0F 59 C3", -2 },
{ "C4 C1 ?? 59 C3", -2 }
}
end)
Register("_stack_spoof_gadgets", R_ARR, function()
-- Matches a call m64 instruction
-- reg = /2
local callPattern = { "FF 10", "FF 38" }
-- Matches an epilogue
--
local epiPatterns = {
{ "48 83 C4 08 C3", "FF FF FF 8F FF" }, -- [add rsp, imm8], no negative/misaligned immediates
{ "48 81 C4 08 00 00 00 C3", "FF FF FF 0F FF FF FF FF" } -- [add rsp, imm32], no negative/misaligned/huge immediates
}
-- For each epilogue pattern:
--
local results = {}
for _,v in ipairs(epiPatterns) do
-- For each call instruction suffix length:
--
for suf=5,0,-1 do
-- Find the gadget:
--
local value = callPattern[1] .. string.rep(" 00", suf) .. " " .. v[1]
local mask = callPattern[2] .. string.rep(" 00", suf) .. " " .. v[2]
for match in Image:Find(Rule.Pattern(value, mask)) do
-- If valid:
--
if Image:DecodeLen(match) == (2+suf) then
-- Decode the sub instruction, if it is not at arg area, write the result:
--
local subinsn = Image:Decode(match+2+suf)
if subinsn.imm0 >= (8*9) then
results[#results+1] = {
callRva = match,
subRva = match + 2 + suf,
delta = subinsn.imm0 + 8
}
end
end
end
end
end
-- Sort the table based on call complexity.
--
table.sort(results, function(a, b) return (a.subRva-a.callRva)>(b.subRva-b.callRva) end)
-- Get table length, trimming to a maximum of 32 and write the immediates.
--
local len = math.min(32, #results)
local output = {}
output[1] = len
for i=1,len do
output[i*2 + 0] = results[i].subRva
output[i*2 + 1] = results[i].delta
end
return output
end)