nosoop bf42d42fd4 Introduce memory indirection asserts | před 5 měsíci | |
---|---|---|
src | před 4 týdny | |
tests | před 4 týdny | |
.gitignore | před 8 měsíci | |
.justfile | před 8 měsíci | |
README.md | před 4 týdny | |
pyproject.toml | před 4 týdny |
SourceMod gamedata checker.
Rewrite of a previous internal project. Faster. Stronger. More maintainable.
smgdc <specfile> --add-binary <binary> --add-binary <binary> ...
specfile
is a specification file or directory (tree) containing specification files.--add-binary
specifies one or more binaries to add for checking.
os.pathsep
-separated pair of values that map a physical file to one present
in the specification file.A specification file is an INI configuration file used to check aspects of one or more binary (code) files.
Example:
; reports presence of a byte sequence in the Windows server binary, yielding signature
[CWeaponMedigun::SecondaryAttack()]
target = bin/tf/server.dll
type = bytesig
contents = 55 8B EC 56 8B 75 08 85 F6 0F 84 ?? ?? ?? ?? 53
; reports that a given immediate value in code is at a given location, yielding offsets, etc.
[CTFProjectile_Flare::Explode_Air()::SelfDamageRadius LINUX]
target = bin/tf/server_srv.so
type = value
symbol = _ZN19CTFProjectile_Flare11Explode_AirEP10CGameTraceib
offset = 0x468
struct = <f
assert = value == 100.0
; reports the presence of a symbol in a given vtable, yielding offset for Windows / Linux
[CBaseEntity::GetEnemy()]
target = bin/tf/server_srv.so
type = vfn
vtable = 11CBaseEntity
symbol = _ZN11CBaseEntity8GetEnemyEv
The entries specify a section name (must be unique within the file), a target binary's subpath to match against, a type of specification to check for, and additional information for a particular specification type.
This allows the validation process to be more flexible than a gameconf file is on its own, as that tells you nothing about the context (is the offset for a virtual file? a property?).
Types of specifications include:
value
: reads out a value from a binarybytesig
: confirms the presence of a byte sequencevfn
: takes a virtual method symbol and gets the vtable index for it on Linux and Windows
(guesstimate on the latter)Some entries accept an assert
option, which allows the evaluation of single Python
expressions. Returning False
causes validation to fail, indicating that an entry requires
review.
[!WARNING] Obviously, Python's
eval
is dangerous; this application was never designed to accomodate adversarial inputs. You should never blindly perform validations with user-submitted specification files.
Depending on the entry type, one or more of these variables will be made available:
addr
: An object representing a position in the binary. This is set to the configuration's
result address and provides the following methods:
read(offset: int = 0)
: Returns a new BinaryPosition
, acting as if the position + offset
was dereferenced as an absolute address.value(struct, offset = 0)
: Extracts the current position using a
struct.Struct-format string into a single result.string_value(encoding = "utf-8", errors = "strict", offset = 0)
: Extracts a string from
the current position.This is designed to mimic SourceMod's gamedata API of using reads via method chaining:
# process an indirection by dereferencing at position +3h
addr.read(0x3).value("<I") == 0xDEADBEEF
value
: In a value
entry, this returns the value that will be inserted into the output.
[!IMPORTANT] The constraint file specification is not finalized. Please ensure that you are reading the documentation for the version of
smgdc
that you are working with.
Linux binaries are sometimes compiled with flags that cause a many-to-one mapping of symbols to function addresses (in other words: link time optimization). While the application does attempt to uniquely identify symbols using other markers, there's sometimes insufficient information to do so.
A constraint file is used as a last resort for end users to manually identify which symbols map to which positions for a given virtual table and its subclasses. This is done by specifying relative ordering constraints for each symbol, allowing for reuse between binary revisions (with the assumption that vtables aren't reordered across them).
Example:
[[_ZTV17CBaseCombatWeapon]]
constraint = "soft"
symbols = [
"_ZN17CBaseCombatWeapon6DeleteEv",
"_ZN17CBaseCombatWeapon4KillEv",
]
[[_ZTV17CBaseCombatWeapon]]
constraint = "consecutive"
symbols = [
"_ZN17CBaseCombatWeapon27WeaponRangeAttack1ConditionEff",
"_ZN17CBaseCombatWeapon27WeaponRangeAttack2ConditionEff",
"_ZN17CBaseCombatWeapon27WeaponMeleeAttack1ConditionEff",
"_ZN17CBaseCombatWeapon27WeaponMeleeAttack2ConditionEff",
]
In this CBaseCombatWeapon
vtable, a soft order constraint is applied such that the offset
of CBaseCombatWeapon::Delete
is lower than CBaseCombatWeapon::Kill
— both symbols
point to the same address, and both are present at two offsets, so this ensures that once
solved, each symbol will be assigned the correct offset.
A consecutive order constraint is also applied such that
CBaseCombatWeapon::WeaponRangeAttack1Condition
and subsequent symbols have monotonically
increasing offsets.