More work on dfusion. Embark anywhere script separated.

develop
Warmist 2012-10-21 13:42:55 +03:00
parent da92fb9a1c
commit 86ec66c0fb
6 changed files with 196 additions and 130 deletions

@ -58,8 +58,28 @@ static int loadObjectFile(lua_State* L)
lua_setfield(L,table_pos,"symbols");
return 1;
}
static int markAsExecutable(lua_State* L)
{
unsigned addr=luaL_checkunsigned(L,1);
std::vector<DFHack::t_memrange> ranges;
DFHack::Core::getInstance().p->getMemRanges(ranges);
for(size_t i=0;i<ranges.size();i++)
{
if(ranges[i].isInRange((void*)addr))
{
DFHack::t_memrange newperm=ranges[i];
newperm.execute=true;
DFHack::Core::getInstance().p->setPermisions(ranges[i],newperm);
return 0;
}
}
lua_pushlstring(L,"Memory range not found",23);
lua_error(L);
return 0;
}
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(loadObjectFile),
DFHACK_LUA_COMMAND(markAsExecutable),
DFHACK_LUA_END
};
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)

@ -1,108 +1,69 @@
local dfu=require("dfusion")
local dfu=require("plugins.dfusion")
local ms=require("memscan")
local MAX_RACES=100
CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin)
CustomEmbark.ATTRS{filename="dfusion/embark/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL}
function CustomEmbark:install()
local stoff=dfhack.internal.getAddress('start_dwarf_count')
if #self.race_caste_data<7 then
error("caste and race count must be bigger than 6")
end
if #self.race_caste_data>MAX_RACES then
error("caste and race count must be less then "..MAX_RACES)
end
if stoff==nil then
error("address for start_dwarf_count not found!")
end
local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id"))
local needle={0x0f,0xb7,0x0d} --movzx,...
add_dword(needle,race_id_offset) -- ...word ptr[]
print(string.format("start=%08x",stoff))
local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id]
local tmp_table=dfu.dwordToTable(race_id_offset)
for k,v in ipairs(tmp_table) do
table.insert(needle,v)
end
local mem=ms.get_code_segment()
local trg_offset=mem.uint8_t.find(needle,stoff)--maybe endoff=stoff+bignumber
print(mem.uint8_t:addr2idx(stoff))
print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff)))
local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber
if trg_offset==nil then
error("address for race_load not found")
end
local call_data={0x90,0x90}
local _,data_offset=df.sizeof(self.data)
dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset))
self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"}
needle={0x83,0xc8,0xff} -- or eax, 0xFF
local caste_offset=mem.uint8_t.find(needle,trg_offset)
local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil)
if caste_offset==nil or caste_offset-stoff>1000 then
error("Caste change code not found or found too far!")
end
self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"}
self.disable_castes:apply()
self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"}
self.dwarfcount:apply()
local caste_array=self:allocate("caste_array","uint16_t",#self.race_caste_data)
local race_array=self:allocate("race_array","uint16_t",#self.race_caste_data)
for k,v in ipairs(self.race_caste_data) do
caste_array[k-1]=v[2]
race_array[k-1]=v[1]
end
local race_array_off,caste_array_off
local _
_,race_array_off=df.sizeof(race_array)
_,caste_array_off=df.sizeof(caste_array)
self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess...
self:set_marker_dword("caste",race_array_off)
self:move_to_df()
self.call_patch:apply()
self.installed=true
end
function MakeTable(modpos,modsize,names)
count=0
castes={}
--print("Making table")
for _,line in pairs(names) do
--print("Line:"..line)
tpos=string.find(line,":")
if tpos~=nil then
--print("Was line:"..line)
table.insert(castes,tonumber(string.sub(line,tpos+1)))
line=string.sub(line,1,tpos-1)
--print("IS line:"..line)
else
table.insert(castes,-1)
end
if RaceTable[line] == nil then
error("Failure, "..line.." not found!")
end
--print("adding:"..line.." id:"..RaceTable[line])
engine.pokew(modpos+modsize+count*2,RaceTable[line]) -- add race
count = count + 1
end
i=0
for _,caste in pairs(castes) do
engine.pokew(modpos+modsize+count*2+i*2,caste) -- add caste
i= i+1
end
engine.poked(stoff,count)
return count
end
function embark(names)
RaceTable=RaceTable or BuildNameTable()
mypos=engine.getmod('Embark')
stoff=VersionInfo.getAddress('start_dwarf_count')
if mypos then --if mod already loaded
print("Mod already loaded @:"..mypos.." just updating")
modpos=mypos
_,modsize=engine.loadobj('dfusion/embark/embark.o')
count=MakeTable(modpos,modsize,names) --just remake tables
else
_,tofind=df.sizeof(df.global.ui:_field("race_id"))
loc=offsets.find(stoff,0x0f,0xb7,0x0d,DWORD_,tofind) --MOVZX ECX,WORD PTR[]
print(string.format("found:%x",loc))
if((loc~=0)and(loc-stoff<1000)) then
loc2=offsets.find(loc,0x83,0xc8,0xff) -- or eax, ffffff (for caste)
if loc2== 0 then
error ("Location for caste nulling not found!")
end
engine.pokeb(loc2,0x90)
engine.pokeb(loc2+1,0x90)
engine.pokeb(loc2+2,0x90)
ModData=engine.installMod("dfusion/embark/embark.o","Embark",256)
modpos=ModData.pos
modsize=ModData.size
local castepos=modpos+engine.FindMarker(ModData,"caste")
local racepos=modpos+engine.FindMarker(ModData,"race")
count=MakeTable(modpos,modsize,names)
engine.poked(castepos,modpos+modsize) --fix array start for race
engine.poked(racepos,modpos+modsize+count*2) --fix array start for caste
print("sucess loading mod @:"..modpos)
-- build race vector after module.
--finaly poke in the call!
engine.pokeb(loc,0x90)
engine.pokeb(loc+1,0x90)
engine.pokeb(loc+2,0xe8)
engine.poked(loc+3,modpos-loc-7)
--engine.pokeb(loc+5,0x90)
SetExecute(modpos)
else
error("did not find patch location, failing...")
end
end
function CustomEmbark:uninstall()
if self.installed then
self.call_patch:remove()
self.disable_castes:remove()
self.dwarfcount:remove()
end
end

@ -18,7 +18,6 @@ DFHACK_PLUGIN(stripcaged stripcaged.cpp)
DFHACK_PLUGIN(rprobe rprobe.cpp)
DFHACK_PLUGIN(nestboxes nestboxes.cpp)
DFHACK_PLUGIN(vshook vshook.cpp)
DFHACK_PLUGIN(steam-engine steam-engine.cpp)
IF(UNIX)
DFHACK_PLUGIN(ref-index ref-index.cpp)
ENDIF()

@ -4,8 +4,23 @@ local _ENV = mkmodule('plugins.dfusion')
local ms=require("memscan")
local marker={0xDE,0xAD,0xBE,0xEF}
patches={}
--utility functions
function dwordToTable(dword)
local b={bit32.extract(dword,0,8),bit32.extract(dword,8,8),bit32.extract(dword,16,8),bit32.extract(dword,24,8)}
return b
end
function concatTables(t1,t2)
for k,v in pairs(t2) do
table.insert(t1,v)
end
end
function makeCall(from,to)
local ret={0xe8}
concatTables(ret,dwordToTable(to-from-5))
return ret
end
-- A reversable binary patch
patches={}
BinaryPatch=defclass(BinaryPatch)
BinaryPatch.ATTRS {pre_data=DEFAULT_NIL,data=DEFAULT_NIL,address=DEFAULT_NIL,name=DEFAULT_NIL}
function BinaryPatch:init(args)
@ -16,19 +31,8 @@ function BinaryPatch:init(args)
if patches[self.name]~=nil then
error("Patch already exist")
end
self.max_val=0
for k,v in pairs(args.pre_data) do
if type(k)~="number" then
error("non number key in pre_data")
end
if self.max_val<k then
self.max_val=k
end
end
for k,v in pairs(args.data) do
if type(k)~="number" then
error("non number key in data")
end
for k,v in ipairs(args.data) do
if args.pre_data[k]==nil then
error("can't edit without revert data")
end
@ -37,32 +41,39 @@ end
function BinaryPatch:postinit(args)
patches[args.name]=self
end
function BinaryPatch:apply()
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+self.max_val)
for k,v in pairs(self.pre_data) do
function BinaryPatch:test()
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+#self.pre_data)
for k,v in ipairs(self.pre_data) do
if arr[k-1]~=v then
error(string.format("pre-data does not match expected:%x got:%x",v,arr[k-1]))
return false
end
end
return true
end
function BinaryPatch:apply()
if not self:test() then
error(string.format("pre-data for binary patch does not match expected")
end
local post_buf=df.new('uint8_t',self.max_val)
for k,v in pairs(self.pre_data) do
local post_buf=df.new('uint8_t',#self.pre_data)
for k,v in ipairs(self.pre_data) do
if self.data[k]==nil then
post_buf[k-1]=v
else
post_buf[k-1]=self.data[k]
end
end
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,self.max_val)
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,#self.pre_data)
if not ret then
error("Patch application failed!")
end
self.is_applied=true
--[[
for k,v in pairs(self.data) do
arr[k]=v
end
]]--
end
function BinaryPatch:repatch(newdata)
if newdata==nil then newdata=self.data end
self:remove()
self.data=newdata
self:apply()
end
function BinaryPatch:remove(delete)
if delete==nil then
@ -71,13 +82,13 @@ function BinaryPatch:remove(delete)
if not self.is_applied then
error("can't remove BinaryPatch, not applied.")
end
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+self.max_val)
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+#self.pre_data)
local post_buf=df.new('uint8_t',self.max_val)
local post_buf=df.new('uint8_t',#self.pre_data)
for k,v in pairs(self.pre_data) do
post_buf[k-1]=v
end
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,self.max_val)
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,#self.pre_data)
if not ret then
error("Patch remove failed!")
end
@ -85,11 +96,7 @@ function BinaryPatch:remove(delete)
if delete then
patches[self.name]=nil
end
--[[
for k,v in pairs(self.data) do
arr[k]=self.pre_data[k]
end --]]
end
function BinaryPatch:__gc()
@ -104,16 +111,16 @@ plugins=plugins or {}
BinaryPlugin=defclass(BinaryPlugin)
BinaryPlugin.ATTRS {filename=DEFAULT_NIL,reloc_table={},name=DEFAULT_NIL}
function BinaryPlugin:init(args)
if args.name==nil then error("Not a valid plugin name!") end
if plugins[args.name]==nil then
plugins[args.name]=true
else
error("Trying to create a same plugin")
end
self.allocated_object={}
end
function BinaryPlugin:postinit(args)
if self.name==nil then error("Not a valid plugin name!") end
--if plugins[args.name]==nil then
plugins[self.name]=self
--else
-- error("Trying to create a same plugin")
--end
self.allocated_object={}
self:load()
end
function BinaryPlugin:allocate(name,typename,arrsize)
@ -149,6 +156,21 @@ function BinaryPlugin:find_marker(start_pos)
end
end
end
function BinaryPlugin:set_marker_dword(marker,dword) -- i hope Toady does not make a 64bit version...
if self.reloc_table[marker]==nil then
error("marker ".. marker.. " not found")
end
local b=dwordToTable(dword)
local off=self.reloc_table[marker]
for k,v in ipairs(b) do
self.data[off+k]=b[k]
end
end
function BinaryPlugin:move_to_df()
local _,addr=df.sizeof(self.data)
markAsExecutable(addr)
end
function BinaryPlugin:print_data()
local out=""
for i=0,self.size do
@ -160,4 +182,13 @@ function BinaryPlugin:print_data()
end
print(out)
end
function BinaryPlugin:__gc()
for k,v in pairs(self.allocated_object) do
df.delete(v)
end
if self.unload then
self:unload()
end
self.data:delete()
end
return _ENV

@ -0,0 +1,2 @@
-- a binary hack/plugin collection for df

@ -0,0 +1,53 @@
-- lets you embark anywhere.
helpstring=[[ embark [-t|-d|-e|-h]
-t test if the patch can be applied
-d disable the patch if enabled
-e enable the patch if disabled
-h shows help
]]
args={...}
if args[1]=="-h" then
print(helpstring)
return
end
local ms=require("memscan")
local dfu=require("plugins.dfusion")
local patch
function embark() --windows only?
local seg=ms.get_code_segment()
local idx,off
local patch=dfu.patches.embark_anywhere
if patch~=nil then
return patch
else
idx,off=seg.uint8_t:find_one{0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04}
if idx then
patch=dfu.BinaryPatch{name="embark_anywhere",pre_data={0x74,0x04},data={0x90,0x90},address=off+5}
return patch
else
qerror("Offset for embark patch not found!")
end
end
end
patch=embark()
if args[1]=="-t" then
if patch:test() and not patch.is_applied then
print("all okay, patch can be applied")
elseif patch.is_applied then
print("patch is currently applied, can be removed")
else
qerror("patch can't be applied")
end
elseif args[1]=="-d" then
if patch.is_applied then
patch:remove()
print("patch removed")
end
elseif args[1]=="-e" then
if not patch.is_applied then
patch:apply()
print("patch applied")
end
else
print(helpstring)
end