diff options
| -rw-r--r-- | DevDocs/code-style.txt | 2 | ||||
| -rwxr-xr-x | compile | 2 | ||||
| -rw-r--r-- | compile.bat | 2 | ||||
| -rw-r--r-- | src/ac.c | 2 | ||||
| -rw-r--r-- | src/alias.c | 2 | ||||
| -rw-r--r-- | src/bind.c | 4 | ||||
| -rw-r--r-- | src/chatrate.c | 2 | ||||
| -rw-r--r-- | src/chunklets/README-x86 | 56 | ||||
| -rw-r--r-- | src/chunklets/x86.c (renamed from src/x86.c) | 28 | ||||
| -rw-r--r-- | src/chunklets/x86.h (renamed from src/x86.h) | 32 | ||||
| -rw-r--r-- | src/con_.c | 2 | ||||
| -rw-r--r-- | src/democustom.c | 2 | ||||
| -rw-r--r-- | src/demorec.c | 2 | ||||
| -rw-r--r-- | src/engineapi.c | 2 | ||||
| -rw-r--r-- | src/ent.c | 2 | ||||
| -rw-r--r-- | src/fastfwd.c | 5 | ||||
| -rw-r--r-- | src/fov.c | 2 | ||||
| -rw-r--r-- | src/gameserver.c | 2 | ||||
| -rw-r--r-- | src/hook.c | 2 | ||||
| -rw-r--r-- | src/hud.c | 2 | ||||
| -rw-r--r-- | src/inputhud.c | 2 | ||||
| -rw-r--r-- | src/kvsys.c | 2 | ||||
| -rw-r--r-- | src/l4d1democompat.c | 2 | ||||
| -rw-r--r-- | src/l4daddon.c | 2 | ||||
| -rw-r--r-- | src/l4dreset.c | 4 | ||||
| -rw-r--r-- | src/l4dwarp.c | 2 | ||||
| -rw-r--r-- | src/portalcolours.c | 1 | ||||
| -rw-r--r-- | src/portalisg.c | 2 | ||||
| -rw-r--r-- | src/sst.c | 1 | ||||
| -rw-r--r-- | src/x86util.h | 2 | ||||
| -rw-r--r-- | src/xhair.c | 1 | ||||
| -rw-r--r-- | test/hook.test.c | 2 | ||||
| -rw-r--r-- | test/x86.test.c | 5 |
33 files changed, 119 insertions, 64 deletions
diff --git a/DevDocs/code-style.txt b/DevDocs/code-style.txt index d008a5c..cdbc666 100644 --- a/DevDocs/code-style.txt +++ b/DevDocs/code-style.txt @@ -169,7 +169,7 @@ In no particular order: Bad example: call_function("parameter 1 takes up some space", param_2 + 5, another_var, - x); + x); Good example: call_function("parameter 1 takes up some space", param_2 + 5, @@ -58,6 +58,7 @@ src="\ chatrate.c chunklets/fastspin.c chunklets/msg.c + chunklets/x86.c clientcon.c con_.c crypto.c @@ -86,7 +87,6 @@ src="\ portalcolours.c sst.c trace.c - x86.c xhair.c" if [ "$dbg" = 1 ]; then src="$src \ dbg.c diff --git a/compile.bat b/compile.bat index e1e2a32..0affd05 100644 --- a/compile.bat +++ b/compile.bat @@ -68,6 +68,7 @@ setlocal DisableDelayedExpansion :+ chatrate.c
:+ chunklets/fastspin.c
:+ chunklets/msg.c
+:+ chunklets/x86.c
:+ crypto.c
:+ democustom.c
:+ demorec.c
@@ -98,7 +99,6 @@ setlocal DisableDelayedExpansion :+ rinput.c
:+ sst.c
:+ trace.c
-:+ x86.c
:+ xhair.c
:: just tack these on, whatever (repeated condition because of expansion memes)
if "%dbg%"=="1" set src=%src% src/dbg.c
@@ -27,6 +27,7 @@ #include "bind.h" #include "chunklets/fastspin.h" #include "chunklets/msg.h" +#include "chunklets/x86.h" #include "con_.h" #include "crypto.h" #include "democustom.h" @@ -45,7 +46,6 @@ #include "ppmagic.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE() diff --git a/src/alias.c b/src/alias.c index 697e97c..add8add 100644 --- a/src/alias.c +++ b/src/alias.c @@ -17,13 +17,13 @@ #include <string.h> #include "alias.h" +#include "chunklets/x86.h" #include "con_.h" #include "errmsg.h" #include "extmalloc.h" #include "feature.h" #include "gametype.h" #include "mem.h" -#include "x86.h" #include "x86util.h" FEATURE("alias management") @@ -14,15 +14,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#include "chunklets/x86.h" #include "con_.h" -#include "dbg.h" #include "errmsg.h" #include "feature.h" -#include "hook.h" #include "intdefs.h" #include "langext.h" #include "mem.h" -#include "x86.h" #include "x86util.h" FEATURE() diff --git a/src/chatrate.c b/src/chatrate.c index 06c16e9..daa275a 100644 --- a/src/chatrate.c +++ b/src/chatrate.c @@ -14,6 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#include "chunklets/x86.h" #include "con_.h" #include "errmsg.h" #include "feature.h" @@ -21,7 +22,6 @@ #include "intdefs.h" #include "langext.h" #include "os.h" -#include "x86.h" #include "x86util.h" FEATURE("chat rate limit removal") diff --git a/src/chunklets/README-x86 b/src/chunklets/README-x86 new file mode 100644 index 0000000..cbfcb5d --- /dev/null +++ b/src/chunklets/README-x86 @@ -0,0 +1,56 @@ +x86.{c,h}: opcode-based x86 instruction analysis (NOT a disassembler) + +Currently only handles opcodes found in basic 32-bit userspace functions; +there’s no kernel-mode instructions, no SSE 3+, no AVX, no REX (64-bit), no +EVEX, yadda yadda. + +Subject to extension later if there’s ever a use for it. + +== Compiling == + + gcc -c -O2 [-flto] x86.c + clang -c -O2 [-flto] x86.c + tcc -c x86.c + cl.exe /c /O2 x86.c + +In most cases you can just drop the .c file straight into your codebase/build +system. LTO is advised to avoid dead code and enable more efficient calls +including potential inlining. + +== Compiler compatibility == + +- Any reasonable GCC +- Any reasonable Clang +- Any reasonable MSVC +- TinyCC +- Probably almost all others; this is very portable code + +Note that GCC and Clang will generally give the best-performing output. + +Once the .c file is built, the public header can be consumed by virtually any C +or C++ compiler, as well as probably most half-decent FFIs. + +Note that the .c source file is probably C++-compatible at the moment, but this +is not guaranteed, so it's best to compile it as a C source. The header will +work fine from either language. + +== API usage == + +See documentation comments in x86.h for a basic idea. Some *pro tips*: + +== OS compatibility == + +- All. +- Seriously, this library doesn’t even use libc. + +== Architecture compatibility == + +- All, so long as char is 8 bits. + +== Copyright == + +The source file and header both fall under the ISC licence — read the notices in +both of the files for specifics. + +Thanks, and have fun! +- Michael Smith <mikesmiffy128@gmail.com> diff --git a/src/x86.c b/src/chunklets/x86.c index b017a70..012cbb0 100644 --- a/src/x86.c +++ b/src/chunklets/x86.c @@ -14,39 +14,35 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include "intdefs.h" +// _Static_assert needs MSVC >= 2019, and this check is irrelevant on Windows +#ifndef _MSC_VER +_Static_assert((unsigned char)-1 == 255, "this code requires 8-bit chars"); +#endif + #include "x86.h" -static int mrmsib(const uchar *p, int addrlen) { - // I won't lie: I thought I almost understood this, but after bill walked me - // through correcting a bunch of wrong cases I now realise that I don't - // really understand it at all. If it helps, I used this as a reference: - // https://github.com/Nomade040/length-disassembler/blob/e8b34546/ldisasm.cpp#L14 - // But it's confusingly-written enough that the code I wrote before didn't - // work, so with any luck nobody will need to refer to it again and this is - // actually correct now. Fingers crossed. +static int mrmsib(const unsigned char *p, int addrlen) { if (addrlen == 4 || *p & 0xC0) { int sib = addrlen == 4 && *p < 0xC0 && (*p & 7) == 4; switch (*p & 0xC0) { - // disp8 - case 0x40: return 2 + sib; - // disp16/32 - case 0: + case 0x40: // disp8 + return 2 + sib; + case 0: // disp16/32 if ((*p & 7) != 5) { // disp8/32 via SIB if (sib && (p[1] & 7) == 5) return *p & 0x40 ? 3 : 6; return 1 + sib; } - case 0x80: return 1 + addrlen + sib; + case 0x80: + return 1 + addrlen + sib; } } if (addrlen == 2 && (*p & 0xC7) == 0x06) return 3; return 1; // note: include the mrm itself in the byte count } -int x86_len(const void *insn_) { +int x86_len(const unsigned char *insn) { #define CASES(name, _) case name: - const uchar *insn = insn_; int pfxlen = 0, addrlen = 4, operandlen = 4; p: switch (*insn) { diff --git a/src/x86.h b/src/chunklets/x86.h index 92e4ccb..e47d4ed 100644 --- a/src/x86.h +++ b/src/chunklets/x86.h @@ -14,21 +14,23 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#ifndef INC_X86_H -#define INC_X86_H - -/* - * Opcode-based X86 instruction analysis. In other words, *NOT* a disassembler. - * Only cares about the instructions we expect to see in basic 32-bit userspace - * functions; there's no kernel-mode instructions, no SSE 3+, no AVX, no REX, - * EVEX, yadda yadda. - */ +#ifndef INC_CHUNKLETS_X86_H +#define INC_CHUNKLETS_X86_H // XXX: no BOUND (0x62): ambiguous with EVEX prefix - can't be arsed! // XXX: no LES (0xC4) or DES (0xC5) either for similar reasons. better to report // an unknown instruction than to potentially misinterpret an AVX thing. // these are all legacy instructions that won't really be used much anyway. +/* + * Below, we define groups of instruction opcode bytes based on their + * variable-length characteristics. This is used to drive the actual parsing of + * the instructions by x86_len(). The instructions are also put into a large + * enum defining their byte values. This allows pattern-matching on instruction + * bytes, useful for searching for certain instructions, or patterns thereof, + * for reverse-engineering or modding purposes. + */ + /* Instruction prefixes: segments */ #define X86_SEG_PREFIXES(X) \ X(X86_PFX_ES, 0x26) \ @@ -554,13 +556,21 @@ enum { }; #undef _X86_ENUM +#ifdef __cplusplus +extern "C" +#endif /* * Returns the length of an instruction, or -1 if it's a "known unknown" or * invalid instruction. Doesn't handle unknown unknowns: may explode or hang on * arbitrary untrusted data. Also doesn't handle, among other things, 3DNow!, - * SSE3+, MMX, AVX, and such. Aims to be small and fast, not comprehensive. + * SSE3+, MMX, AVX, and such. Doesn't cover 64-bit nor kernel-only stuff either + * Aims to be small and fast, not comprehensive. + * + * The main purpose of this function to assist in hooking functions or searching + * for certain instruction patterns in existing known and trusted binaries. It + * is once again not suitable for use with arbitrary unknown data. */ -int x86_len(const void *insn); +int x86_len(const unsigned char *insn); /* Constructs a ModRM byte, assuming the parameters are all in range. */ #define X86_MODRM(mod, reg, rm) (unsigned char)((mod) << 6 | (reg) << 3 | rm) @@ -22,6 +22,7 @@ #include <string.h> #include "abi.h" +#include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" // for factories and rgba - XXX: is this a bit circular? #include "errmsg.h" @@ -33,7 +34,6 @@ #include "os.h" #include "vcall.h" #include "version.h" -#include "x86.h" #include "x86util.h" /******************************************************************************\ diff --git a/src/democustom.c b/src/democustom.c index cae58a8..05c1d41 100644 --- a/src/democustom.c +++ b/src/democustom.c @@ -17,6 +17,7 @@ #include <string.h> #include "bitbuf.h" +#include "chunklets/x86.h" #include "demorec.h" #include "engineapi.h" #include "feature.h" @@ -25,7 +26,6 @@ #include "langext.h" #include "mem.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE() diff --git a/src/demorec.c b/src/demorec.c index a763176..0511d46 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -17,6 +17,7 @@ #include <string.h> +#include "chunklets/x86.h" #include "con_.h" #include "demorec.h" #include "engineapi.h" @@ -32,7 +33,6 @@ #include "os.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE("improved demo recording") diff --git a/src/engineapi.c b/src/engineapi.c index 1893e0c..980ee99 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -28,9 +28,7 @@ #include "intdefs.h" #include "langext.h" #include "mem.h" // " -#include "os.h" #include "vcall.h" -#include "x86.h" u32 _gametype_tag = 0; // declared in gametype.h but seems sensible enough here @@ -15,6 +15,7 @@ */ #include "accessor.h" +#include "chunklets/x86.h" #include "con_.h" #include "dictmaptree.h" #include "engineapi.h" @@ -25,7 +26,6 @@ #include "langext.h" #include "mem.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE() diff --git a/src/fastfwd.c b/src/fastfwd.c index 1b7588d..78f2d47 100644 --- a/src/fastfwd.c +++ b/src/fastfwd.c @@ -19,7 +19,7 @@ #include <stdlib.h> -#include "con_.h" +#include "chunklets/x86.h" #include "engineapi.h" #include "errmsg.h" #include "gamedata.h" @@ -29,10 +29,7 @@ #include "intdefs.h" #include "langext.h" #include "mem.h" -#include "os.h" -#include "ppmagic.h" #include "sst.h" -#include "x86.h" #include "x86util.h" FEATURE() @@ -18,6 +18,7 @@ // TODO(linux): theoretically, probably ifdef out the cvar-replacement stuff; we // expect any game that's been ported to linux to already have fov_desired +#include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" #include "errmsg.h" @@ -31,7 +32,6 @@ #include "mem.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE("extended FOV range") diff --git a/src/gameserver.c b/src/gameserver.c index 5cd8b0c..4315afe 100644 --- a/src/gameserver.c +++ b/src/gameserver.c @@ -14,6 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#include "chunklets/x86.h" #include "con_.h" #include "errmsg.h" #include "feature.h" @@ -21,7 +22,6 @@ #include "intdefs.h" #include "langext.h" #include "mem.h" -#include "x86.h" #include "vcall.h" #include "x86util.h" @@ -17,12 +17,12 @@ #include <string.h> +#include "chunklets/x86.h" #include "hook.h" #include "intdefs.h" #include "langext.h" #include "mem.h" #include "os.h" -#include "x86.h" // Warning: half-arsed hacky implementation (because that's all we really need) // Almost certainly breaks in some weird cases. Oh well! Most of the time, @@ -15,6 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#include "chunklets/x86.h" #include "engineapi.h" #include "errmsg.h" #include "event.h" @@ -28,7 +29,6 @@ #include "os.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE() diff --git a/src/inputhud.c b/src/inputhud.c index 6cff487..ef7f9ac 100644 --- a/src/inputhud.c +++ b/src/inputhud.c @@ -18,6 +18,7 @@ #include <math.h> +#include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" #include "event.h" @@ -32,7 +33,6 @@ #include "langext.h" #include "mem.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE("button input HUD") diff --git a/src/kvsys.c b/src/kvsys.c index 9c0f75c..25a6672 100644 --- a/src/kvsys.c +++ b/src/kvsys.c @@ -16,6 +16,7 @@ */ #include "abi.h" +#include "chunklets/x86.h" #include "extmalloc.h" #include "errmsg.h" #include "feature.h" @@ -25,7 +26,6 @@ #include "langext.h" #include "os.h" #include "vcall.h" -#include "x86.h" FEATURE() diff --git a/src/l4d1democompat.c b/src/l4d1democompat.c index 3c9b604..4280d53 100644 --- a/src/l4d1democompat.c +++ b/src/l4d1democompat.c @@ -17,6 +17,7 @@ */ #include "accessor.h" +#include "chunklets/x86.h" #include "con_.h" #include "errmsg.h" #include "feature.h" @@ -26,7 +27,6 @@ #include "mem.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE("Left 4 Dead 1 demo file backwards compatibility") diff --git a/src/l4daddon.c b/src/l4daddon.c index 0f5fbe7..5336199 100644 --- a/src/l4daddon.c +++ b/src/l4daddon.c @@ -18,6 +18,7 @@ #include <string.h> +#include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" #include "errmsg.h" @@ -31,7 +32,6 @@ #include "ppmagic.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE("Left 4 Dead 2 addon bugfixes") diff --git a/src/l4dreset.c b/src/l4dreset.c index 725c763..dd59c60 100644 --- a/src/l4dreset.c +++ b/src/l4dreset.c @@ -20,6 +20,7 @@ #include "abi.h" #include "accessor.h" +#include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" #include "ent.h" @@ -32,12 +33,11 @@ #include "gametype.h" #include "hook.h" #include "intdefs.h" -#include "langext.h" #include "l4dmm.h" +#include "langext.h" #include "mem.h" #include "sst.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" #ifdef _WIN32 diff --git a/src/l4dwarp.c b/src/l4dwarp.c index 8db87c9..7ddb4cc 100644 --- a/src/l4dwarp.c +++ b/src/l4dwarp.c @@ -19,6 +19,7 @@ #include <math.h> #include "accessor.h" +#include "chunklets/x86.h" #include "clientcon.h" #include "con_.h" #include "engineapi.h" @@ -31,7 +32,6 @@ #include "mem.h" #include "trace.h" #include "vcall.h" -#include "x86.h" #include "x86util.h" FEATURE("Left 4 Dead warp testing") diff --git a/src/portalcolours.c b/src/portalcolours.c index 24224c1..e6e0354 100644 --- a/src/portalcolours.c +++ b/src/portalcolours.c @@ -26,7 +26,6 @@ #include "intdefs.h" #include "langext.h" #include "mem.h" -#include "os.h" #include "ppmagic.h" #include "sst.h" #include "vcall.h" diff --git a/src/portalisg.c b/src/portalisg.c index bdc007f..d7851eb 100644 --- a/src/portalisg.c +++ b/src/portalisg.c @@ -14,6 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" #include "errmsg.h" @@ -22,7 +23,6 @@ #include "intdefs.h" #include "langext.h" #include "mem.h" -#include "x86.h" #include "x86util.h" FEATURE("Portal \"ISG\" state reset (experimental)") @@ -37,7 +37,6 @@ #include "hook.h" #include "intdefs.h" #include "langext.h" -#include "mem.h" // for shuntvars() in generated code #include "os.h" #include "sst.h" #include "vcall.h" diff --git a/src/x86util.h b/src/x86util.h index 33a3a08..433fbbf 100644 --- a/src/x86util.h +++ b/src/x86util.h @@ -17,9 +17,9 @@ #ifndef INC_X86UTIL_H #define INC_X86UTIL_H +#include "chunklets/x86.h" #include "errmsg.h" #include "langext.h" -#include "x86.h" // XXX: don't know where else to put this, or how else to design this, so this // is very much a plonk-it-here-for-now scenario (and has been for years!) diff --git a/src/xhair.c b/src/xhair.c index 9d1ee34..9754f1e 100644 --- a/src/xhair.c +++ b/src/xhair.c @@ -20,7 +20,6 @@ #include "gamedata.h" #include "hexcolour.h" #include "hud.h" -#include "intdefs.h" #include "vcall.h" FEATURE("custom crosshair drawing") diff --git a/test/hook.test.c b/test/hook.test.c index 625fdbf..6a9a7b4 100644 --- a/test/hook.test.c +++ b/test/hook.test.c @@ -4,7 +4,7 @@ #ifdef _WIN32 -#include "../src/x86.c" +#include "../src/chunklets/x86.c" #include "../src/hook.c" #include "../src/os.c" diff --git a/test/x86.test.c b/test/x86.test.c index bf6e6e8..370a697 100644 --- a/test/x86.test.c +++ b/test/x86.test.c @@ -2,7 +2,10 @@ {.desc = "x86 opcode parsing"}; -#include "../src/x86.c" +// TODO: should chunklets tests be moved or something? guess if/when that stuff +// gets its own repo it would just go in there, right? + +#include "../src/chunklets/x86.c" #include "../src/intdefs.h" #include "../src/ppmagic.h" |
