mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-07 11:17:46 +00:00
231 lines
6.6 KiB
Lua
231 lines
6.6 KiB
Lua
-- init global variable
|
|
g_dump = io.open(os.date("dump_%Y-%m-%d-%H-%M-%S.txt"), "a")
|
|
g_dump:write("ptr, name\n")
|
|
g_count = 0
|
|
|
|
-- relative virtual address(rva) to register_whatever function
|
|
-- this assume ffxiv sb benchmark
|
|
-- YOU MUST EDIT THIS TO RIGHT VALUE TO WORK
|
|
-- check http://imgur.com/a/nJCef for disasm
|
|
g_addr = {}
|
|
|
|
g_override_path = {
|
|
-- {"01_roc_r2", "01_gyr_g3"},
|
|
-- {}
|
|
-- {"ex1/01_roc_r2/twn/r2t2/level/r2t2", "ex2/01_gyr_g3/twn/g3t2/level/g3t2"},
|
|
-- {"ex1/01_roc_r2/twn/r2t2", "ex2/01_gyr_g3/twn/g3t2"},
|
|
|
|
--
|
|
-- {"ffxiv/wil_w1/twn/w1t1/level/w1t1", "ex2/02_est_e3/fld/e3f1/level/e3f1"},
|
|
-- {"ffxiv/wil_w1/twn/w1t1", "ex2/02_est_e3/fld/e3f1"},
|
|
|
|
-- kugane
|
|
{"ffxiv/wil_w1/twn/w1t1/level/w1t1", "ex2/02_est_e3/twn/e3t1/level/e3t1"},
|
|
{"ffxiv/wil_w1/twn/w1t1", "ex2/02_est_e3/twn/e3t1"},
|
|
|
|
-- last cutscene dungeon ex2/02_est_e3/dun/e3d1
|
|
--{"ffxiv/wil_w1/twn/w1t1/level/w1t1", "ex2/02_est_e3/dun/e3d1/level/e3d1"},
|
|
--{"ffxiv/wil_w1/twn/w1t1", "ex2/02_est_e3/dun/e3d1"},
|
|
|
|
}
|
|
|
|
-- g_addr.load_master = 0x162210 -- looks like below function is called from this
|
|
g_addr.load1 = 0x186BB0 -- mostly loads gfx stuff but does calc in it?
|
|
g_addr.load2 = 0x186CE0 -- mostly loads gfx stuff
|
|
g_addr.load3 = 0x1868A0 -- mostly loads exd data
|
|
g_addr.load4 = 0x186E00 -- from this on found it because there are next to each other
|
|
g_addr.load5 = 0x186EE0 -- umm, vfx?
|
|
g_addr.kugane_workaround = 0xE61416
|
|
|
|
g_loadseen = {}
|
|
|
|
g_xiv = {}
|
|
g_xiv.proc_name = "ffxiv_dx11.exe"
|
|
g_xiv.path = "C:\\Users\\Mino\\Games\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\" .. g_xiv.proc_name
|
|
-- because I don't want to login and out every single try
|
|
g_xiv.arg = "DEV.TestSID=3a3f77cc5f7c88136d4a66e36bd27f8535d773d0378b521275468785 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=1 DEV.GMServerHost=localhost"
|
|
g_mode = "attach"
|
|
|
|
function init()
|
|
-- attach debugger
|
|
if g_mode == "create" then
|
|
print(string.format("Launching ffxiv.exe w/ arg %s", g_xiv.arg))
|
|
createProcess(g_xiv.path, g_xiv.arg, true, true)
|
|
elseif g_mode == "attach" then
|
|
print("Looking for "..g_xiv.proc_name)
|
|
while not openProcess(g_xiv.proc_name) do sleep(1) end
|
|
print("Attaching...")
|
|
debugProcess()
|
|
while not getAddress(g_xiv.proc_name) do sleep(1) end
|
|
print("Module loaded")
|
|
else
|
|
print("Unsupported mode!")
|
|
return
|
|
end
|
|
|
|
for k, v in pairs(g_addr) do
|
|
-- k = name
|
|
-- v = rva
|
|
print(string.format("Attaching %s breakpoint on 0x%X", k, v))
|
|
debug_setBreakpoint(get_va(v))
|
|
end
|
|
|
|
print("Now waiting for breakpoints..")
|
|
end
|
|
|
|
function debugger_onBreakpoint()
|
|
if RIP == get_va(g_addr.load1) then
|
|
-- dump_message("1>>")
|
|
local new_addr = dump_addr(R8)
|
|
if new_addr then
|
|
R8 = new_addr
|
|
end
|
|
elseif RIP == get_va(g_addr.load2) then
|
|
-- dump_message("2>>")
|
|
local new_addr = dump_addr(R8)
|
|
if new_addr then
|
|
R8 = new_addr
|
|
end
|
|
elseif RIP == get_va(g_addr.load3) then
|
|
local new_addr = dump_addr(RSP + 28)
|
|
if new_addr then
|
|
writeQword(RSP + 28, new_addr)
|
|
end
|
|
elseif RIP == get_va(g_addr.load4) then
|
|
-- dump_message("4>>")
|
|
local new_addr = dump_addr(RCX)
|
|
if new_addr then
|
|
RCX = new_addr
|
|
end
|
|
elseif RIP == get_va(g_addr.load5) then
|
|
-- dump_message("5>>")
|
|
local new_addr = dump_addr(RCX)
|
|
if new_addr then
|
|
RCX = new_addr
|
|
end
|
|
elseif RIP == get_va(g_addr.kugane_workaround) then
|
|
if RAX == 0 then
|
|
-- jump and hope it doesn't cause a UB
|
|
print("Workaround hack triggered")
|
|
RIP = get_va(0xE61432)
|
|
end
|
|
else
|
|
-- user bp, update gui on ce, but who gives a shit?
|
|
-- continue execution anyway
|
|
debug_continueFromBreakpoint("co_run")
|
|
return 0
|
|
end
|
|
|
|
-- something is missing on the text file because it takes ageeeeeeeeees to write to file
|
|
-- let just wait it
|
|
|
|
|
|
-- continue execution
|
|
debug_continueFromBreakpoint("co_run")
|
|
--return 0 -- update gui
|
|
return 1
|
|
end
|
|
|
|
function dump_addr(addr)
|
|
local vfspath = readString(addr, 256)
|
|
|
|
if not g_loadseen[vfspath] then
|
|
g_loadseen[vfspath] = true -- set seen flag
|
|
|
|
local message = string.format("%X, %s", addr, vfspath)
|
|
dump_line(message)
|
|
end
|
|
|
|
if vfspath then
|
|
return replace_map_string(addr, vfspath)
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function dump_line(message)
|
|
dump_message(message .. "\n")
|
|
end
|
|
|
|
function dump_message(message)
|
|
g_dump:write(message)
|
|
g_dump:flush()
|
|
print(message)
|
|
end
|
|
|
|
-- mod_base + rva = va
|
|
function get_va(rva)
|
|
return getAddress(g_xiv.proc_name) + rva
|
|
end
|
|
|
|
-- rva = va - mod_base
|
|
function get_rva(va)
|
|
return va - getAddress(g_xiv.proc_name)
|
|
end
|
|
|
|
-- check mod_base <= va <= mod_base + mod_size
|
|
function is_mmod_addr(va)
|
|
local mod_base = getAddress(g_xiv.proc_name)
|
|
local mod_size = getModuleSize(g_xiv.proc_name)
|
|
|
|
if mod_base <= va and va <= mod_base + mod_size then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function replace_map_string(addr, source)
|
|
-- try to replace map strnig
|
|
for _, v in ipairs(g_override_path) do
|
|
local match_pat = v[1]
|
|
local replace_pat = v[2]
|
|
|
|
if string.find(source, match_pat) then
|
|
local path_override = string.gsub(source, match_pat, replace_pat)
|
|
dump_line(string.format("OVERRIDE>> %s => %s (w/ %s => %s)", source , path_override, match_pat, replace_pat))
|
|
|
|
-- check str len if
|
|
if string.len(source) < string.len(path_override) then
|
|
local new_addr = alloc(256) -- alloc 256 bytes
|
|
writeCString(new_addr, path_override)
|
|
print(string.format("Replaced string is longer than original, allocating it to 0x%X", new_addr))
|
|
return new_addr
|
|
else
|
|
-- original string len is longer or same
|
|
writeCString(addr, path_override)
|
|
return nil
|
|
end
|
|
end
|
|
end
|
|
|
|
-- can't find it
|
|
return nil
|
|
end
|
|
|
|
-- write a string with null
|
|
function writeCString(addr, content)
|
|
writeString(addr, content)
|
|
writeBytes(addr + string.len(content), 0) -- put null byte, this assume content is only ASCII char
|
|
end
|
|
|
|
-- no way to free, have fun
|
|
function alloc(size)
|
|
-- taken from http://forum.cheatengine.org/viewtopic.php?t=560739&sid=73dc5a565393a034ba16d1d468bb1eeb
|
|
autoAssemble([[
|
|
alloc(luatemp, ]]..size..[[)
|
|
registersymbol(luatemp)
|
|
]])
|
|
return getAddress("luatemp")
|
|
end
|
|
|
|
function split(str)
|
|
local result = {}
|
|
for token in string.gmatch(str..",", "([^,]+),%s*") do
|
|
table.insert(result, all_trim(token))
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
init()
|