Closed mrfearless closed 5 years ago
This is very helpful information, thanks! The ultimate goal is indeed to use a loader similar to what TobEx has; the alpha uses a hardcoded loader simply because it was faster to implement.
IWDEE can most certainly be supported as it is running the same fork of the engine as BGEE and BG2EE, (finding equivalent code shouldn't be too hard). PSTEE's engine, on the other hard, is so different from the others that it might not be possible to translate modifications over.
I'll see what I can do.
I looked into additional unique pattern matches for the other functions, some are straight forward, a couple require finding the pattern and jumping back x bytes:
lua_pushnumber:
558BEC8B4D08DD450C8B5108DD1A8B42042500FFFF7F3D00A5F77F74088D42088941085DC3
lua_pushclosure:
558BEC83E4F856578B7D1085FF751C8B55088B450C8B4A088901C7410416A5F77F834208085F5E8BE55DC3
lua_tolstring:
558BEC83E4F8518B550C568B75088BCE
lua_setglobal:
558BEC53568B7508BA02000000578B460C8B4828
add 103 bytes to found location and check for 834608F0
(add dword ptr ds:[esi + 0x8], 0xFFFFFFF0
)
lua_tonumberx:
83C40485C07428DD4424088D442408DD5C240885F67406C70601000000DD005E8B4C241033CC
jump back 83 bytes to get start of function
luaL_loadstring:
8B550C8BC2568B7508578D78018A084084C9
_jump back 20 bytes to get start of function, or read the call luaL_loadstring
(at the jmp to codecave entry location) to get the offset for this function start_
_ftol2_sse:
742D558BEC83EC0883E4F8DD1C24F20F2C0424C9C3
jump back 7 bytes to get start of function
I searched for those patterns in BGEE, BG2EE, BGSOD and they seem to work.
I was looking into maybe creating a proof of concept loader to check how it would all work. Possibly the use of the codecave can be eliminated and the call luaL_loadstring
could be replaced and redirected to code in a dll, which is injected with the loader.
Other thoughts I had on this in relation to searching for the lua function addresses, they could be saved to an ini file. If the ini file exists read in the function addresses - which would speed up subsequent launches. Some checks still required to make sure the addresses correspond to what we would expect and if not then fall back to the full pattern search and match. For example if a new build is released and function checks fail, then re-search for functions and store in ini.
Example ini:
[EEex]
lua_pushnumber=0x1234ABCD
lua_pushclosure=0x234ABCD1
lua_tolstring=0x34ABCD12
lua_tonumberx=0x4ABCD123
lua_setglobal=0xABCD1234
luaL_loadstring=0xBCD1234A
_ftol2_sse=0xCD1234AB
A question related to the VirtualAlloc
- is that memory for storing lua variables / EE engine refs etc? Just wondering if moving that to a dll that is injected will work. Think it would but would have to test that I guess.
Anyhow, the loader doesnt have to inject a dll. I think it would still be possible to handle it all from the loader and write the byte code to the codecave. Couple of options or ways to handle it all to think about :D
Just a quick update. I've managed to code a loader and a dll that is injected into the EE game. The dll currently successfully locates the patch location and the lua functions and the g_lua variable (using bg2ee as the test). It patches to redirect to a function in the dll instead of a codecave. Working on those functions that were in the cave.
Ive uploaded my initial working version. Take a look when you get a chance. Fingers crossed it all works ok :D
https://github.com/mrfearless/EEexLoader
Let me know if you spot any issues or have any suggestions.
It works brilliantly! I've had a look over the asm code, and all I can come up with is wow. (In a good way!)
Since you know much more about these things than I do, I have a couple of questions, if you don't mind: 1) The pattern matching approach seems to be very powerful. Would it be possible for you to very briefly outline how you come up with the patterns? Is it just trial and error, or is there some systematic process you go through? I ask, because besides the loader, EEex itself uses many hardcoded memory locations defined in the following file, (and randomly sprinkled about at the moment):
https://github.com/Bubb13/EEex/blob/master/EEex/copy/EEex_Lab.lua
Every time I add new functionality to EEex it usually requires me to add one or more addresses to the list; if these were defined using the pattern matching capabilities you have shown, it would make updating the mod to new game versions much, MUCH easier.
2) One of the main features of the Enhanced Editions is that it is cross-platform. I really haven't looked into what is required to modify the Linux and Mac versions respectively, though I have to wonder if the loader you created could potentially be a stepping stone towards this goal...
I understand DLL injection is a Windows only thing, and that the internals of the other versions might look completely different, but this dynamic pattern matching has me thinking. Probably a dumb question, though I really only started getting into this lower level stuff ~6 months ago, so please forgive my ignorance on the subject.
I really appreciate all the work you've put into this; cheers! :D
Thanks :D
For the pattern matching process I look at the disassembly of the function using x64dbg and copy the selection to notepad++. Right click menu -> Copy -> Copy Selection
Looking at the asm instructions and the byte code, I look for unique combinations that might be a useful pattern.
Some instructions that have to be considered carefully are call
or jumps (je
/jne
/jmp
etc) that could change for different builds or different games due to the distance to the target location of those opcodes. Also other opcodes that rely on data locations that might be different in each build or game. Take the _ftol2_sse
function as an example:
BG2EE:
833D 5CA44D03 00 | cmp dword ptr ds:[<__isa_available>], 0x0
74 2D | je <baldur._ftol2>
55 | push ebp
8BEC | mov ebp, esp
83EC 08 | sub esp, 0x8
83E4 F8 | and esp, 0xFFFFFFF8
DD1C24 | fstp qword ptr ss:[esp], st(0)
F2:0F2C0424 | cvttsd2si eax, qword ptr ss:[esp]
C9 | leave
C3 | ret
BGSOD:
833D 84D5CC02 00 | cmp dword ptr ds:[0x2CCD584], 0x0
74 2D | je <siegeofdragonspear.sub_838246>
55 | push ebp
8BEC | mov ebp, esp
83EC 08 | sub esp, 0x8
83E4 F8 | and esp, 0xFFFFFFF8
DD1C24 | fstp qword ptr ss:[esp], st(0)
F2:0F2C0424 | cvttsd2si eax, qword ptr ss:[esp]
C9 | leave
C3 | ret
The bytecode is nearly the same except for the cmp instruction: 833D 5CA44D03 00
vs 833D 84D5CC02 00
I take the remainder of what i think is sufficiently unique bytecode to search for: 742D558BEC83EC0883E4F8DD1C24F20F2C0424C9C3
which starts at the je
(the 74
) instruction to the ret
(the C3
) instruction. In this situation the je
instruction is ok to include (742D
) as its always the same amount 2D
which is 45 bytes to the target location.
Searching with that pattern in x64dbg: Right click menu -> Search For -> Current Module - Pattern. Paste pattern.
Result of pattern search shows one location
Counting backwards from the start of the pattern to the start of the function is 7 bytes (833D 84D5CC02 00
) which is the address we would save after finding that pattern in our code and adjusting by -7.
To copy the byte code pattern out from x64bg:
To copy the byte code pattern out from x64bg for assembler:
Some patterns could return more than one result. So in those cases I have to look for differences between the results that uniquely identify the correct function we require. The pattern matching function I coded then would 'look' for the specific indicators (another byte sub-pattern to verify exists).
With regard to MacOS and Linux I don't have any coding experience with them or knowledge about loaders. I know the equivalent of the windows pdb
symbols for MacOS are DWARF
dSYM files. Which might be located in the BG2EE game folder as BaldursGateIIEnhancedEdition.app.dSYM\Contents\Resources\DWARF\BaldursGateIIEnhancedEdition
- searching through them for 'lua
' in a hex editor shows lots of results.
I'll take a look at the EEex_Lab.lua
file and see if I can pattern match for some of those. In some cases it might not be possible to uniquely identify some functions.
The global variables are also possibly problematic, the one i found: g_lua
was based on the assumption from locating the call LuaL_loadstring
used for the codecave jmp location patch and skipping back to read the offset used for the push instruction. But I'll see what I can find, fingers crossed.
A small status update:
Currently tested that nearly all the pattern matches are unique enough to be found, for all the functions and game variables listed here. Tested with the following Beamdog games: BG2EE, BGSOD, IWDEE, PSTEE.
Edit: See github.com/mrfearless/EEexLoader/projects/1 for the list of functions/globals tracked
Note: I have managed to compile the Lua 5.2.3 library (EE games indicate 5.2) into the EEex.dll
, which can store those function addresses instead of the ones found in the EE game by pattern matching. This reduces the amount of patterns to search for - if the Lua static library is included when compiling (using the EEEX_LUALIB
define). Only issue I came across was with lua_setglobal
in the static lua library - it crashed when using it - so after comparing the version in the EE games and the lua library (for some reason they are different) - I created a copy of the EE game version inside EEex.dll
and named it to lua_setglobalx
. If using the lua static library, EEex.dll
will redirect to this internal version instead. If not using the lua static library the EE game version will be used when found.
Once I add in the additional patterns to be processed I will push an update to github. If the EEex_LuaFunctions
function works then I envisage creating a similar one for the global variables, maybe called EEex_EEGlobals
or something. Once I push that update if you can look over the EEex_LuaFunctions
asm code to see if it makes sense and that it does work, that would be handy as I'm new to all the lua stuff :D
Additionally I was wondering if you can create a minimal EEex.lua just for my testing - just very bare bones required to initialize EEex and to see that the EEex lua functions work, even just EEexInit, and maybe a call to the other EEex lua functions for a single test. Useful for testing on the other EE games if possible.
Also I was wondering if maybe there is something you can also include to display a message or text or some visual indicator in game or on the main menu to show that EEex loaded up and everything was initialized ok. An 'EEex' beside the build/version number on main menu would be handy. No worries if you can't.
Cheers.
Very nice work! I apologize for my delayed response; time is a little thin right now.
The master branch now includes a boolean at the top of M__EEex.lua which forces EEex to only do its core initialization. I believe the assembly writing code in that function should be enough to tell if EEex is working or not. EEex now also adds "(EEex)" to the version string to make it easy to tell if it is loaded.
Regarding the use of an external Lua library, a few months ago I experimented with using one in order to enable the io module. For reasons I do not know, and as you have seen, the embedded Lua functions seem to be significantly different than any compiled Lua DLL I have investigated. The whole system is unstable when mixing internal Lua functions with the DLL's; it is essential that every single internal Lua function is redirected to the DLL's version. This was previously a very daunting task before I had found the symbol file, so I dropped the matter. Now, though, I believe it is feasible.
No worries. Thanks for adding that debugging option and version string.
Regarding the lua functions, I agree and I figured that there was a possibility that one or more (or all maybe) of the lua static library functions could be a problem compared to the game versions, so I built in that EEEX_LUALIB
define just in case. I've also added a ini file option to force enable/disable using the static lib functions vs the game functions (but only if the the EEex.dll
is compiled with the EEEX_LUALIB
define. Just for testing purposes). The only benefit currently in enabling the use of the static lua library functions is a tiny speed increase when initializing EEex.dll
, as there are less patterns to look for. Might be there are other benefits, so I'll keep that option in for now until we know for sure either way if it would be useful or not.
I'm currently working my way through that EEex_Lab.lua
file. Excluding the lua functions and g_
globals, I'm about half way through them, finding unique patterns for those functions and only a few have proved awkward. I'll update the tables above in my other post when I find them and implement them.
Cheers
I pushed an update that has the lua EEex_LuaFunctions function, hopefully it returns a table of lua function names and their addresses. From all your lua experience and looking through the game engine at the lua code, hopefully you will know if that function is correct and works.
I'm tracking the progress of the pattern matching of the functions and global variables (in the EEex_Lab.lua file) here: github.com/mrfearless/EEexLoader/projects/1
I uploaded another update to the exe loader and the dll. github.com/mrfearless/EEexLoader/releases
Able to search for all functions and globals listed in the EEex_Lab.lua
file.
Tested the loader on BGEE, BG2EE, BGSOD and IWDEE with a very minimal EEex - just the M__EEex.lua
file in overrides (no other files) with the following content:
function EEex_Main()
local initialMemory = EEex_Init()
end
EEex_Main()
PSTEE has a few issues. Some functions from other EE games don't exists in PSTEE. Or if they do exist some are different enough to require a specific pattern to search for and verify - compared to the other EE games.
After updating all my EE games, these are the versions that appear to work with the minimal EEex and the loader and injected dll.
Returning the addresses of the functions and globals to EEex is the next step. I'm unsure if using lua tables will work or if there is another way that this can be achieved, perhaps returning information to lua to be processed: number of functions or globals in asm array and a pointer to array.
Entry of array could be two DWORD
values, the first DWORD
a pointer to a zero terminated string representing the function/global name. The second DWORD
an address of the function/global.
If it was in asm I would create a function to return the no of entries in eax, and return the pointer to the array in a variable passed as a parameter, something like:
GetArray PROC lpArray:DWORD
LOCAL MyArrayOfFunctions:DWORD
Invoke GetArray, Addr MyArrayOfFunctions
.IF eax != 0 ; we have some entries in array
; do something
lea ebx, MyArrayOfFunctions
; start stepping through array entries, 2 x DWORDs at a time
.ENDIF
But unsure how something similar can be processed via lua.
Another idea is to perhaps dynamically create the EEex_Lab.lua
file, but that might take longer to implement.
Anyhow, hopefully you have a few ideas on returning the function/global addresses.
Transferring the label addresses to the Lua environment can be done in a multitude of ways, and if I'm reading your loader asm correctly, it looks like you defined a few new functions that return the addresses as Lua tables. That's a perfectly good way of doing it; the only other (decent) ways I can think of are:
a) Hardcoding a Lua global to be set to the table, or perhaps set as a pointer to some asm structure. b) Calling a function with each and every label definition.
In any case, I think the path you've gone down is best. It's pretty awesome that you have managed to get EEex running on almost every IE game... I really planned for that to happen much, much farther down the road.
Also, I was thinking: would it be possible for you to move the pattern definitions out of the DLL and into an external file? Most features I add usually require me to add one or more labels, and externalizing the pattern definitions would allow EEex to convey to the loader exactly what it needs. I believe this would decouple the loader from having to update every time I update EEex, as I could provide any new patterns to the loader via a file. Don't feel like you have to implement this even if it's possible; I'm just throwing out ideas.
I was already considering the option of externalizing the pattern database :D - just have to figure out the best way of handling this. An ini style format file which can edited easily in a text editor would probably be the most flexible. Something like:
[__ftol2_sse]
Pattern=742D558BEC83EC0883E4F8DD1C24F20F2C0424C9C3
PatAdj=-7
[_lua_setglobal]
Pattern=558BEC53568B7508BA02000000578B460C8B4828
PatAdj=0
Verify=834608F0
VerAdj=103
[_lua_getglobal]
Pattern=558BEC53568B7508BA02000000578B460C8B4828
PatAdj=0
Verify=83C0F8
VerAdj=87
[g_BaldurChitin]
Pattern=66 83 F8 FF 0F 84 B6 00 00 00 A1
PatAdj=34
PatType=1
For patterns that don't require a sub-pattern (Verify
and VerAdj
) to check that pattern is the correct one, those keys (Verify
and VerAdj
) can be left out. Could also make the patterns with the bytes separated with a space, for better readability, or handle both style formats. For patterns that read game global variables then PatType
would be 1
, otherwise if no PatType
key present then its by default 0
and a function address pattern.
Have to think on how to handle this for the lua functions (and ftol2_sse) as those are the only ones bound to a variable used internally to call some of those functions, the rest aren't used directly at all. If I build the loader with the static lua library, then that would eliminate the need for that - but I've left the door open for both options, if for example a problem is later found using the static lua functions vs the ingame found ones.
For returning the addresses, if the EEex_LuaFunctions
works by returning a table then I can refactor to return all the patterns addresses found. If that doesn't work then i can create an asm array in memory and return the pointer to the asm array in one function and the count of entries in another function.
Let me know if you get a chance to test that table of addresses returned by EEex_LuaFunctions
, if it works or if there are any problems with it. Once I know if it works then I can proceed with implementing it all.
Cheers :D
Here is the current pattern database in an ini format: https://github.com/mrfearless/EEexLoader/blob/master/EEexDLL/EEex.db
Just have to put together the functions to handle it :D
Have pushed an update v1.0.0.4 that uses the externalized pattern database: EEex.db
Renamed EEex_LuaFunctions
to EEex_AddressList
and added the EEex_AddressListAsm
lua registered function, which is the assembly version of EEex_AddressList
but it returns a pointer to an array. Each entry in array (ALENTRY
structure) is two DWORD
values. First DWORD
is pointer to zero terminated string for the name of the function or global. Second DWORD is the address.
There is also a EEex_AddressListCount
function for returning an integer of the array entries in EEex_AddressListAsm
. The last entry in the asm array is null for both DWORD
values in case you just want to process until both are null, then you have end of array. So either option is possible.
You can test out either function EEex_AddressList
or EEex_AddressListAsm
to see which, if any, works.
The log will now show (if successfully launched and patterns are verified/searched) an Address List
section that should correspond to the entries and values in the EEexLab.lua
file.
Entries listed in the log for Pattern Verify
or Pattern Searching
or the EEex.ini
file are addresses of the patterns - some might be a match for the address of the function, but others are not, and especially the the patterns for game global variables which are addresses to read the game global value from.
The EEex_Call
lua function is included and registered in this build, so you will probably have to comment it out in M__EEex.lua
. If you prefer having this in the lua file instead I can rebuild it without it in.
Let me know if there is anything else you think would be handy or if you want to move any of the other asm lua functions to the loader: WriteAssembly etc, or if your happy with them in the lua files that is totally fine.
Cheers.
Awesome! I'll verify that everything works when I get some time -
Also, a few seconds after I downloaded and extracted the latest release Bitdefender went haywire and quarantined EEex.dll complaining that it was "Gen:Variant.Razy.352370". I don't doubt your intentions, but I don't think users would be very happy if installing EEex caused their antivirus to freak out ;D
I can only assume its due to the injection of the dll. By its very nature it is loading another process and injecting into that process a dll - which is likely to flag up with some virus scanning engines.
Using pestudio 8.88 shows the following:
pestudio gives you a hint of what might flag up. I am not surprised that either or both of EEex.exe
or EEex.dll
would show up as something due to some of the functions used: 'CreateRemoteThread', VirtualProtectEx
, WriteProcessMemory
, GetModuleInformation
, GetCurrentProcess
etc etc.
Some virus scanners are more aggressive with marking as 'bad' certain functions or libraries used in an exe or dll. pestudio lists then in a blacklist category, which is similar and gives an idea as to how it might be seen by online or offline scanners.
Here is the results of both files from Jotti's malware scan and VirusTotal:
The source code is available for all to see and can be compiled by anyone to verify that there is nothing malicious inside either file. Only other thing that might help is code signing - but could still flag up regardless. I logged into VirusTotal and added a comment on both files to briefly indicate their purposes and included the github web links as well.
Update: I have submitted the files as false positives to various anti virus products: avg, avast, bitdefender, Qihoo-360. Emsisoft uses bitdefender engine based the (B) designation on the detection and reading on their false positive section of their forums. And also emailed to McAfee-GW about the false positive. Fingers crossed that after they look into the project and see the source and verify its not malicious and update signatures then some of the engines might not trigger anymore.
I didn't mean to make you look into it that much - just wanted to let you know; though, thanks for checking it out. A little interesting that previous versions didn't set off my antivirus, but the new one did - everyone knows that they can be very finicky about files they've never seen before. Hell, I've even had Bitdefender flag Near Infinity for some reason or another...
Anyways, it seems there is some quirkiness going on in EEex_AddressList. Instead of returning the table, it returns itself, and then sets itself to the table. Further, the table's addresses are floats:
To return values from a Lua "C" function, just push them to the Lua stack. You signal to Lua that there are return value(s) on the stack by setting eax to the number of return values present. Currently your eax value of 1 is telling Lua to return the top of the stack, which is the function itself.
Not sure about the floats though, that's weird.
EEex_AddressListAsm: As per the above, eax should be 1, and the asm structure address should be pushed to the Lua stack.
Cheers! :D
Its all good, and its worth trying to resolve as much of that as possible, just to eliminate any doubt in anyones mind regarding any anti virus triggering.
Ok, I'll take a look at EEex_AddressList, might be that I have to use some other lua function to push the number instead or do some conversion first maybe - not sure.
Ok so I take it I can return no of entries in table in eax. Say there are 108 patterns pushed onto stack. Do I return 109 (108 patterns + 1 for the function itself) or just 108? or do i have to count it as 108 x 2 (one for name, one for address pushed onto stack)
Ill check out modifying the EEex_AddressListAsm function by pushing the array address and returning 1 - hope that number thing is ok with that one as well - might have to check if pushnumber is correct.
Ok so I take it I can return no of entries in table in eax. Say there are 108 patterns pushed onto stack. Do I return 109 (108 patterns + 1 for the function itself) or just 108? or do i have to count it as 108 x 2 (one for name, one for address pushed onto stack)
The way you are constructing the table is correct, you just need to return it proper. Removing
Invoke F_Lua_setglobal, lua_State, Addr szEEex_AddressList
should be all that is needed to correct the return value, as the eax of 1 will then return the table instead of the function, (the table isn't getting popped off the stack by lua_setglobal once you remove the above line).
Just have to figure out the weird float values and it should be smooth sailing from there.
Yeh I think I just figured that out as well, thelua F_Lua_setglobal was just setting the function again as the last thing on the stack - rather than the table. Also I see that the pushnumber does require a lua_number (float) value - as can be seen when you push the VirtualAlloc memory onto stack in EEex_Init - It is using fpu commands to load the integer fild
, and store it as a qword (float/real4) using fstp
and then pushing that for pushnumber to use. Anyhow I have adjusted and will push a new release. Fingers crossed.
The return values are now correct, but the random floats are still there. _lua_pushnumber needs the float qword to be pushed directly, not its address. I use this in my assembly:
push eax ; The value you want to push onto the Lua stack
fild dword ptr ss:[esp]
sub esp, 0x4
fstp qword ptr ss:[esp]
; Push the Lua state here
call _lua_pushnumber
add esp, 0xC
However that works out in your flavor of assembly, I believe it should do the trick.
Ok cheers, I'll do a few minor adjustments and make a new release. Probably later on today at some point.
Ok, try this new release v1.0.0.6 to see if it works - it pushes the qword value (instead of the address of the value) as two dwords (from looking at the disassembly) - which should be the same as push qword, for the float value for lua_pushnumber to use.
If that doesn't work then I will strip the function down without the normal prologue and epilogue and add the asm code for the stack entry and exit and adjust the stack after calls like some of the other functions.
For readability I prefer being able to use Invoke (instead of pushes and call x) and have the assembler take care of the prologue and epilogue for me - the lazy option :D
Success! Everything is exported to Lua correctly. I've noted a few problems with the patterns:
1) Typos:
2) Seems a few of the patterns have wrong adjustment offsets:
3) Bad patterns:
I don't know how to fix this one; the function that is found and the one that we are targeting is only off by a single call at the end, and without knowing what bytes that call is supposed to be we can't precisely match these two...
4) Weirdness:
The db says these are hardcoded by the loader, so I can't fix the myself.
Everything else is working as expected. Also, once the initial db version is completely working EEex should be able to define the db for itself. Just gotta fix these things and EEex can run fully on every IE game :D
Nice :D
Yes we can adjust the typos in the database, so CAIActionDecode
to CAIAction::Decode
is just a quick edit of the EEex.db
.
For some patterns that end in =
I had to change them - as windows ini files wont read the key names that end in =
. The EEex.db
uses the ini section name for the pattern name - this can take the =
at the end - but as I use the EEex.ini
file to store the pattern address to speed up verification (instead of searching through the entire exe every load), the pattern in EEex.ini
is stored as a keyname.
So CAIObjectType::operator=
as the section / pattern name in EEex.db
would be [CAIObjectType::operator=]
but in the EEex.ini
file it would be stored as CAIObjectType::operator==0x1234ABCD
- which it cant read back. So I changed any names that end with =
to equ or notequ or equequ for =
!=
or ==
, and presumably for any other combinations +=
etc would also apply the same logic. So instead we get CAIObjectType::operator-equ=0x1234ABCD
I'll take a look at those that show the wrong pattern adjustments - might have mis calculated a few
The couple of patterns that are not unique are usually ok, as the verification bytes then decides which function it is - so couple others that get/set seem to also have same pattern (from what I recall), I will double check those remove spell ones to see what I wrote (have txt file notes on every function) and see if I was able to discern the difference.
Ill double check the GetProcAddress
and LoadLibrary
- Im getting the addresses of the functions using
lea eax, GetProcAddress
and lea eax, LoadLibrary
- which are the raw native addresses of the api's - which might not be directly callable outside the module? Might just resort to using GetProcAddress
on GetProcAddress
and LoadLibrary
to check if the return value is any different. But they are fetched dynamically similar to SDL_free
(which is an export)
Ok made a new release for tiny adjustment for getting the GetProcAddress
and LoadLibrary
addresses via GetProcAddress
using Kernel32.dll
module handle.
Updated the database with those typo and pattern adj changes that you found and hopefully found a unique pattern for the RemoveKnownSpells mage and priest functions.
https://github.com/mrfearless/EEexLoader/releases/tag/1.0.0.7
For PSTEE, you could create a specific pattern database for it - I think from looking at PSTEE a bit, some functions don't exist, but a good lot do, although some have slightly different pattern signatures for the functions, but you could still provide a lot of EEex support I would imagine. Maybe a M__EEex.lua specific to PSTEE is the way to handle it.
Woo! Everything seems correct now :D
Once I add patterns for my hooks EEex should work on BG:EE, BG2:EE, IWD:EE, and, for the most part, PST:EE!
Do you mind if I bundle the loader with EEex? I'll make sure to give you credits in the readme and all that good stuff.
:D
Totally fine with me to bundle the loader and any and all files you need. Let me know if there is anything else you need/want now or at any time - I'm happy to help
Do you want to move any of the lower level assembly functions that you use in M_EEex.lua to the loader? Ones like:
No major benefit cept it will clear up the M_EEex.lua file for you, and you wont need to do: disable code protection -> VirtualAlloc mem -> write asm bytecode -> enable code protection
Anyhow glad its all working. Im on discord btw, I joined the Gibberlings3 discord channel, if you ever want to chat directly about EEex etc.
Latest release available at https://github.com/mrfearless/EEexLoader
I've been looking at EEex and the hardcoded patches to the bg2ee baldur.exe.
Using this pattern:
FF35????????E8????????83C43085C075145050506AFF50FF35????????E8????????83C4186A00
And searching with that pattern in x64dbg: Right click menu->Search For->Current Module-Pattern. Paste pattern. Double click on the single reference found will bring you to the correct location for
luaL_loadstring
, which is replaced in the EEex patches to jmp to the codecave.I think it might be possible to support the other enhanced games and possibly other versions. I tried the pattern matching with some older versions of bg2ee and bgee, some have the pdb to verify its the luaL_loadstring location. Only one really old version didnt locate the pattern - it did have the pdb but there was no luaL_loadstring reference in that build.
Here are some attached screenshots to show the pattern matched location:
BG2EE:
BGEE:
IWDEE:
PSTEE:
The ones without the pdb (IWDEE and PSTEE) do show similar calls to the api call
SetPrivateProfileString
beforehand.I might be possible to build a loader - similar to the TobEx loader - that can apply the basic patches on the exe in memory instead of the exe file. Or the pattern might be useful to match with weidu (if it supports this) to support the other EE games if you prefer the hardcoded patches.
The codecave location and other asm code would need to adjusted for each EE game. A search for a codecave in memory with the reasonable size should be possible to code. The only other issue would be to locate the
lua_pushnumber
,lua_setglobal
,lua_pushclosure
,lua_tonumberx
,lua_tolstring
and_ftol2_sse
calls so that they can be properly coded in and the additional asm instructions adjusted according to their location and the codecave code location.Just thought it might be worth mentioning. Hope that info helps.