Add "ctypes-based" fallback to the hashing by reading the non-writable sections of the running game#85
Conversation
pymhf/core/hashing.py
Outdated
|
|
||
|
|
||
| # ctypes/wintypes/kernel32/psapi struct definitions | ||
| class MODULEINFO(ctypes.Structure): |
There was a problem hiding this comment.
It would be good to move things like this into pymhf/utils/winapi.py.
Some of these functions and such may also be exposed in pymem, so can be used directly from there rather than defining them again ourselves (cf. pymhf/core/caching.py for example where I import MODULEINFO from pymem.
Edit: This includes things like _read_process_memory and _get_page_size. If they don't have implementations in pymem already, they can be moved to the winapi.py file as well with the rest of the stuff that would still need to be defined.
This way this has module can contain just the code relating to the hashing, and we can keep the messiness of the windows api in a separate file which will be easier to maintain.
There was a problem hiding this comment.
totally agree ... i admit i did not look into the whole stack. i'll make this changes right away
There was a problem hiding this comment.
this was a simple change, but i'll leave to sync the pr when i change the logic to work on top of pymem
hopefully i'm not blocking you
There was a problem hiding this comment.
all good, I have other things I am working on improving so no huge rush 😄
pymhf/core/hashing.py
Outdated
| return sys_info.dwPageSize | ||
|
|
||
|
|
||
| def hash_bytes_from_memory(binary_path: str, _bufsize: int = 2**18) -> str: |
There was a problem hiding this comment.
I think a good bit of this function could be simplified if the pymem.Pymem object was passed in instead of the path itself. By the time this function is called we have already found the process and some details about it. No point in going through this whole process again.
There was a problem hiding this comment.
you're right ... i'll take a more careful look to integrate it correctly with what already exists of pymem. i just can't right now as it is already past 1am here.
as soon as i get back from work i'll get into this
|
Just integrated the code to use |
|
My unit tests clearly don't cover the whole code bases since I can see that there are still some cases where python 3.10+ syntax exists. I'm also not sure why the CI doesn't fail for the |
pymhf/core/hashing.py
Outdated
| return True | ||
|
|
||
|
|
||
| def _read_bytes_into( |
There was a problem hiding this comment.
I suspect this function could be replaced with read_ctype in pymem...
pymhf/core/hashing.py
Outdated
| current = address | ||
| while current < region_end: | ||
| to_read = min(_bufsize, region_end - current) | ||
| res = _read_bytes_into( |
There was a problem hiding this comment.
Could use read_bytes from pymem also
| return sys_info.dwPageSize or 4096 | ||
|
|
||
|
|
||
| def _get_main_module(pm_binary: pymem.Pymem) -> MODULEINFO: |
There was a problem hiding this comment.
functions like this I think could also go in the winapi.py file.
I think for this file we can have probably just the two functions hash_bytes_from_file and hash_bytes_from_memory, and pretty much everything else can go under the other file.
pymhf/core/hashing.py
Outdated
| from ctypes import _CData, _Pointer, _SimpleCData | ||
|
|
||
| CDataLike: TypeAlias = ( | ||
| _CData | _SimpleCData | _Pointer[Any] | ctypes.Structure | ctypes.Union | ctypes.Array[Any] |
There was a problem hiding this comment.
this is the place where it's using 3.10+ syntax...
|
test was probably ignoring the |
|
Looks great to me thanks! |
I wrote this mainly to help GamePass players (like myself) who don' t want to go through the trouble of making a forced copy of the game files out of Windows default installation directoty ... but this fallback can also help with any permission errors that might happen while opening the game file.
Only downside would be the fact that the game (obviously) needs to be running and that this is windows-only.
One specific thing that I could add is that maybe instead of raising errors from this function you might want to just return
Noneand keeppyMHFrunning as it seems to be optional at this point.