├── .gitignore
├── LICENSE.txt
├── README.md
├── convar_sm_l4d.h
├── input.h
├── libdasm.c
├── libdasm.h
├── memutils.cpp
├── memutils.h
├── msvc10
├── recording_helpers.sln
├── recording_helpers.vcxproj
└── recording_helpers.vcxproj.filters
├── opcode_tables.h
├── recording_helpers.cpp
├── recording_helpers.h
├── recording_helpers.vdf
├── recording_helpers_l4d1.vdf
├── thirdperson_patch.cpp
└── thirdperson_patch.h
/.gitignore:
--------------------------------------------------------------------------------
1 | rls/*
2 | msvc10/Release - Left 4 Dead 2/*
3 | msvc10/Debug - Left 4 Dead 2/*
4 | msvc10/Release - Left 4 Dead/*
5 | msvc10/Debug - Left 4 Dead/*
6 | msvc10/ipch/*
7 | *.suo
8 | *.sdf
9 | *.opensdf
10 | *.vcxproj.user
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Recording Helpers is released under the GPL v3 with hl2sdk linking exceptions.
2 |
3 | This program is free software; you can redistribute it and/or modify it under
4 | the terms of the GNU General Public License, version 3.0, as published by the
5 | Free Software Foundation.
6 |
7 | This program is distributed in the hope that it will be useful, but WITHOUT
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10 | details.
11 |
12 | You should have received a copy of the GNU General Public License along with
13 | this program. If not, see .
14 |
15 | As a special exception, the authors give you permission to link the
16 | code of this program (as well as its derivative works) to "Half-Life 2," the
17 | "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
18 | by the Valve Corporation. You must obey the GNU General Public License in
19 | all respects for all other code used. Additionally, AlliedModders LLC grants
20 | this exception to all derivative works. AlliedModders LLC defines further
21 | exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
22 | or .
23 |
24 |
25 | libdasm is released under public domain.
26 | http://code.google.com/p/libdasm
27 |
28 | Code from the HL2SDK is copyright Valve Corporation.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | L4D2 Recording Helpers
2 | ======================
3 |
4 | This client plugin is simply a set of useful modifications and functions to help making recording from demos easier and more feature filled.
5 |
6 | Features
7 | ==
8 | - Re-enables many hidden CVars on L4D by removing DEVELOPMENTONLY flags from them.
9 | - Re-enables a native thirdperson(shoulder) mode in gamemodes with player-controlled zombies.
10 |
11 |
12 | Usage:
13 | ==
14 | Place the files in your l4d2 addons folder and launch the game with -insecure in order to load it.
15 |
16 | It may be useful to make a shortcut to l4d2 that launches with -insecure, so you don't have to edit your launch parameters all the time.
17 | e.g. make a shortcut to "C:\Program Files\Steam\steam.exe" -applaunch 550 -insecure
18 |
19 | Changelog:
20 | ==
21 | 0.6:
22 | - Fixed patches not working and after 2125 update
23 |
24 | 0.5:
25 | - Fixed thirdperson patches not working after recent L4D2 Updates (Stop fucking with your interfaces)
26 |
27 | 0.4:
28 | - Fixed thirdperson patches not working after recent L4D2 Updates
29 | - Initial release of L4D1 build (DEVONLY flag removal only)
30 |
31 | 0.3:
32 | - Fixed a crash that would occur upon entering thirdperson mode
33 |
34 | 0.2:
35 | - Added some patches to enable thirdpersonshoulder usage in gamemodes with player controlled zombies.
36 |
37 | 0.1:
38 | - Initial release
39 | - Unhides DEVONLY CVars
40 |
41 | License:
42 | ==
43 | Most of the code is GPL. All HL2SDK code is copyright Valve Corporation.
--------------------------------------------------------------------------------
/convar_sm_l4d.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProdigySim/recording_helpers/be147b7f9eeacc86f8a9c934df1503552330df27/convar_sm_l4d.h
--------------------------------------------------------------------------------
/input.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProdigySim/recording_helpers/be147b7f9eeacc86f8a9c934df1503552330df27/input.h
--------------------------------------------------------------------------------
/libdasm.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * libdasm -- simple x86 disassembly library
4 | * (c) 2004 - 2006 jt / nologin.org
5 | *
6 | * libdasm.c:
7 | * This file contains most code of libdasm. Check out
8 | * libdasm.h for function definitions.
9 | *
10 | */
11 |
12 | #include
13 | #include
14 | #include "libdasm.h"
15 | #include "opcode_tables.h"
16 |
17 |
18 | // Endianess conversion routines (thanks Ero)
19 |
20 | __inline__ BYTE FETCH8(BYTE *addr) {
21 | // So far byte cast seems to work on all tested platforms
22 | return *(BYTE *)addr;
23 | }
24 |
25 | __inline__ WORD FETCH16(BYTE *addr) {
26 | #if defined __X86__
27 | // Direct cast only for x86
28 | return *(WORD *)addr;
29 | #else
30 | // Revert to memcpy
31 | WORD val;
32 | memcpy(&val, addr, 2);
33 | #if defined __LITTLE_ENDIAN__
34 | return val;
35 | #else
36 | return ((val & 0xff00) >> 8) |
37 | ((val & 0x00ff) << 8);
38 |
39 | #endif // __LITTLE_ENDIAN__
40 | #endif // __X86__
41 | }
42 |
43 | __inline__ DWORD FETCH32(BYTE *addr) {
44 | #if defined __X86__
45 | return *(DWORD *)addr;
46 | #else
47 | DWORD val;
48 | memcpy(&val, addr, 4);
49 | #if defined __LITTLE_ENDIAN__
50 | return val;
51 | #else
52 | return ((val & (0xff000000)) >> 24) |
53 | ((val & (0x00ff0000)) >> 8) |
54 | ((val & (0x0000ff00)) << 8) |
55 | ((val & (0x000000ff)) << 24);
56 |
57 | #endif // __LITTLE_ENDIAN__
58 | #endif // __X86__
59 | }
60 |
61 | // Check for address/operand size override
62 |
63 | __inline__ enum Mode MODE_CHECK_ADDR(enum Mode mode, int flags) {
64 | if (((mode == MODE_32) && (MASK_PREFIX_ADDR(flags) == 0)) ||
65 | ((mode == MODE_16) && (MASK_PREFIX_ADDR(flags) == 1)))
66 | return MODE_32;
67 | else
68 | return MODE_16;
69 | }
70 | __inline__ enum Mode MODE_CHECK_OPERAND(enum Mode mode, int flags) {
71 | if (((mode == MODE_32) && (MASK_PREFIX_OPERAND(flags) == 0)) ||
72 | ((mode == MODE_16) && (MASK_PREFIX_OPERAND(flags) == 1)))
73 | return MODE_32;
74 | else
75 | return MODE_16;
76 | }
77 |
78 |
79 | // Parse 2 and 3-byte opcodes
80 |
81 | int get_real_instruction2(BYTE *addr, int *flags) {
82 | switch (*addr) {
83 |
84 | // opcode extensions for 2-byte opcodes
85 | case 0x00:
86 | // Clear extension
87 | *flags &= 0xffffff00;
88 | *flags |= EXT_G6;
89 | break;
90 | case 0x01:
91 | *flags &= 0xffffff00;
92 | *flags |= EXT_G7;
93 | break;
94 | case 0x71:
95 | *flags &= 0xffffff00;
96 | *flags |= EXT_GC;
97 | break;
98 | case 0x72:
99 | *flags &= 0xffffff00;
100 | *flags |= EXT_GD;
101 | break;
102 | case 0x73:
103 | *flags &= 0xffffff00;
104 | *flags |= EXT_GE;
105 | break;
106 | case 0xae:
107 | *flags &= 0xffffff00;
108 | *flags |= EXT_GF;
109 | break;
110 | case 0xba:
111 | *flags &= 0xffffff00;
112 | *flags |= EXT_G8;
113 | break;
114 | case 0xc7:
115 | *flags &= 0xffffff00;
116 | *flags |= EXT_G9;
117 | break;
118 | default:
119 | break;
120 | }
121 | return 0;
122 | }
123 |
124 | // Parse instruction flags, get opcode index
125 |
126 | int get_real_instruction(BYTE *addr, int *index, int *flags) {
127 | switch (*addr) {
128 |
129 | // 2-byte opcode
130 | case 0x0f:
131 | *index += 1;
132 | *flags |= EXT_T2;
133 | break;
134 |
135 | // Prefix group 2
136 | case 0x2e:
137 | *index += 1;
138 | // Clear previous flags from same group (undefined effect)
139 | *flags &= 0xff00ffff;
140 | *flags |= PREFIX_CS_OVERRIDE;
141 | get_real_instruction(addr + 1, index, flags);
142 | break;
143 | case 0x36:
144 | *index += 1;
145 | *flags &= 0xff00ffff;
146 | *flags |= PREFIX_SS_OVERRIDE;
147 | get_real_instruction(addr + 1, index, flags);
148 | break;
149 | case 0x3e:
150 | *index += 1;
151 | *flags &= 0xff00ffff;
152 | *flags |= PREFIX_DS_OVERRIDE;
153 | get_real_instruction(addr + 1, index, flags);
154 | break;
155 | case 0x26:
156 | *index += 1;
157 | *flags &= 0xff00ffff;
158 | *flags |= PREFIX_ES_OVERRIDE;
159 | get_real_instruction(addr + 1, index, flags);
160 | break;
161 | case 0x64:
162 | *index += 1;
163 | *flags &= 0xff00ffff;
164 | *flags |= PREFIX_FS_OVERRIDE;
165 | get_real_instruction(addr + 1, index, flags);
166 | break;
167 | case 0x65:
168 | *index += 1;
169 | *flags &= 0xff00ffff;
170 | *flags |= PREFIX_GS_OVERRIDE;
171 | get_real_instruction(addr + 1, index, flags);
172 | break;
173 | // Prefix group 3 or 3-byte opcode
174 | case 0x66:
175 | // Do not clear flags from the same group!!!!
176 | *index += 1;
177 | *flags |= PREFIX_OPERAND_SIZE_OVERRIDE;
178 | get_real_instruction(addr + 1, index, flags);
179 | break;
180 | // Prefix group 4
181 | case 0x67:
182 | // Do not clear flags from the same group!!!!
183 | *index += 1;
184 | *flags |= PREFIX_ADDR_SIZE_OVERRIDE;
185 | get_real_instruction(addr + 1, index, flags);
186 | break;
187 |
188 | // Extension group 1
189 | case 0x80:
190 | *flags |= EXT_G1_1;
191 | break;
192 | case 0x81:
193 | *flags |= EXT_G1_2;
194 | break;
195 | case 0x82:
196 | *flags |= EXT_G1_1;
197 | break;
198 | case 0x83:
199 | *flags |= EXT_G1_3;
200 | break;
201 |
202 | // Extension group 2
203 | case 0xc0:
204 | *flags |= EXT_G2_1;
205 | break;
206 | case 0xc1:
207 | *flags |= EXT_G2_2;
208 | break;
209 | case 0xd0:
210 | *flags |= EXT_G2_3;
211 | break;
212 | case 0xd1:
213 | *flags |= EXT_G2_4;
214 | break;
215 | case 0xd2:
216 | *flags |= EXT_G2_5;
217 | break;
218 | case 0xd3:
219 | *flags |= EXT_G2_6;
220 | break;
221 |
222 | // Escape to co-processor
223 | case 0xd8:
224 | case 0xd9:
225 | case 0xda:
226 | case 0xdb:
227 | case 0xdc:
228 | case 0xdd:
229 | case 0xde:
230 | case 0xdf:
231 | *index += 1;
232 | *flags |= EXT_CP;
233 | break;
234 |
235 | // Prefix group 1 or 3-byte opcode
236 | case 0xf0:
237 | *index += 1;
238 | *flags &= 0x00ffffff;
239 | *flags |= PREFIX_LOCK;
240 | get_real_instruction(addr + 1, index, flags);
241 | break;
242 | case 0xf2:
243 | *index += 1;
244 | *flags &= 0x00ffffff;
245 | *flags |= PREFIX_REPNE;
246 | get_real_instruction(addr + 1, index, flags);
247 | break;
248 | case 0xf3:
249 | *index += 1;
250 | *flags &= 0x00ffffff;
251 | *flags |= PREFIX_REP;
252 | get_real_instruction(addr + 1, index, flags);
253 | break;
254 |
255 | // Extension group 3
256 | case 0xf6:
257 | *flags |= EXT_G3_1;
258 | break;
259 | case 0xf7:
260 | *flags |= EXT_G3_2;
261 | break;
262 |
263 | // Extension group 4
264 | case 0xfe:
265 | *flags |= EXT_G4;
266 | break;
267 |
268 | // Extension group 5
269 | case 0xff:
270 | *flags |= EXT_G5;
271 | break;
272 | default:
273 | break;
274 | }
275 | return 0;
276 | }
277 |
278 | // Parse operand and fill OPERAND structure
279 |
280 | /*
281 | * This function is quite complex.. I'm not perfectly happy
282 | * with the logic yet. Anyway, the idea is to
283 | *
284 | * - check out modrm and sib
285 | * - based on modrm/sib and addressing method (AM_X),
286 | * figure out the operand members and fill the struct
287 | *
288 | */
289 | int get_operand(PINST inst, int oflags, PINSTRUCTION instruction,
290 | POPERAND op, BYTE *data, int offset, enum Mode mode, int iflags) {
291 | BYTE *addr = data + offset;
292 | int index = 0, sib = 0, scale = 0;
293 | int reg = REG_NOP;
294 | int basereg = REG_NOP;
295 | int indexreg = REG_NOP;
296 | int dispbytes = 0;
297 | enum Mode pmode;
298 |
299 | // Is this valid operand?
300 | if (oflags == FLAGS_NONE) {
301 | op->type = OPERAND_TYPE_NONE;
302 | return 1;
303 | }
304 | // Copy flags
305 | op->flags = oflags;
306 |
307 | // Set operand registers
308 | op->reg = REG_NOP;
309 | op->basereg = REG_NOP;
310 | op->indexreg = REG_NOP;
311 |
312 | // Offsets
313 | op->dispoffset = 0;
314 | op->immoffset = 0;
315 |
316 | // Parse modrm and sib
317 | if (inst->modrm) {
318 | pmode = MODE_CHECK_ADDR(mode, iflags);
319 |
320 | // Update length only once!
321 | if (!instruction->length) {
322 | instruction->modrm = *addr;
323 | instruction->length += 1;
324 | }
325 | // Register
326 | reg = MASK_MODRM_REG(*addr);
327 |
328 | // Displacement bytes
329 | // SIB can also specify additional displacement, see below
330 | if (MASK_MODRM_MOD(*addr) == 0) {
331 | if ((pmode == MODE_32) && (MASK_MODRM_RM(*addr) == REG_EBP))
332 | dispbytes = 4;
333 | if ((pmode == MODE_16) && (MASK_MODRM_RM(*addr) == REG_ESI))
334 | dispbytes = 2;
335 | } else if (MASK_MODRM_MOD(*addr) == 1) {
336 | dispbytes = 1;
337 |
338 | } else if (MASK_MODRM_MOD(*addr) == 2) {
339 | dispbytes = (pmode == MODE_32) ? 4 : 2;
340 | }
341 | // Base and index registers
342 |
343 | // 32-bit mode
344 | if (pmode == MODE_32) {
345 | if ((MASK_MODRM_RM(*addr) == REG_ESP) &&
346 | (MASK_MODRM_MOD(*addr) != 3)) {
347 | sib = 1;
348 | instruction->sib = *(addr + 1);
349 |
350 | // Update length only once!
351 | if (instruction->length == 1) {
352 | instruction->sib = *(addr + 1);
353 | instruction->length += 1;
354 | }
355 | basereg = MASK_SIB_BASE( *(addr + 1));
356 | indexreg = MASK_SIB_INDEX(*(addr + 1));
357 | scale = MASK_SIB_SCALE(*(addr + 1)) * 2;
358 | // Fix scale *8
359 | if (scale == 6)
360 | scale += 2;
361 |
362 | // Special case where base=ebp and MOD = 0
363 | if ((basereg == REG_EBP) && !MASK_MODRM_MOD(*addr)) {
364 | basereg = REG_NOP;
365 | dispbytes = 4;
366 | }
367 | if (indexreg == REG_ESP)
368 | indexreg = REG_NOP;
369 | } else {
370 | if (!MASK_MODRM_MOD(*addr) && (MASK_MODRM_RM(*addr) == REG_EBP))
371 | basereg = REG_NOP;
372 | else
373 | basereg = MASK_MODRM_RM(*addr);
374 | }
375 | // 16-bit
376 | } else {
377 | switch (MASK_MODRM_RM(*addr)) {
378 | case 0:
379 | basereg = REG_EBX;
380 | indexreg = REG_ESI;
381 | break;
382 | case 1:
383 | basereg = REG_EBX;
384 | indexreg = REG_EDI;
385 | break;
386 | case 2:
387 | basereg = REG_EBP;
388 | indexreg = REG_ESI;
389 | break;
390 | case 3:
391 | basereg = REG_EBP;
392 | indexreg = REG_EDI;
393 | break;
394 | case 4:
395 | basereg = REG_ESI;
396 | indexreg = REG_NOP;
397 | break;
398 | case 5:
399 | basereg = REG_EDI;
400 | indexreg = REG_NOP;
401 | break;
402 | case 6:
403 | if (!MASK_MODRM_MOD(*addr))
404 | basereg = REG_NOP;
405 | else
406 | basereg = REG_EBP;
407 | indexreg = REG_NOP;
408 | break;
409 | case 7:
410 | basereg = REG_EBX;
411 | indexreg = REG_NOP;
412 | break;
413 | }
414 | if (MASK_MODRM_MOD(*addr) == 3) {
415 | basereg = MASK_MODRM_RM(*addr);
416 | indexreg = REG_NOP;
417 | }
418 | }
419 | }
420 |
421 | // Operand addressing method -specific parsing
422 | switch (MASK_AM(oflags)) {
423 |
424 | // Register encoded in instruction
425 | case AM_REG:
426 | op->type = OPERAND_TYPE_REGISTER;
427 | op->reg = MASK_REG(oflags);
428 | break;
429 |
430 | // Register indirect encoded in instruction
431 | case AM_IND:
432 | op->type = OPERAND_TYPE_MEMORY;
433 | op->basereg = MASK_REG(oflags);
434 | break;
435 |
436 | // Register/memory encoded in MODRM
437 | case AM_M:
438 | if (MASK_MODRM_MOD(*addr) == 3)
439 | return 0;
440 | goto skip_rest;
441 | case AM_R:
442 | if (MASK_MODRM_MOD(*addr) != 3)
443 | return 0;
444 | skip_rest:
445 | case AM_Q:
446 | case AM_W:
447 | case AM_E:
448 | op->type = OPERAND_TYPE_MEMORY;
449 | op->dispbytes = dispbytes;
450 | instruction->dispbytes = dispbytes;
451 | op->basereg = basereg;
452 | op->indexreg = indexreg;
453 | op->scale = scale;
454 |
455 | index = (sib) ? 1 : 0;
456 | if (dispbytes)
457 | op->dispoffset = index + 1 + offset;
458 | switch (dispbytes) {
459 | case 0:
460 | break;
461 | case 1:
462 | op->displacement = FETCH8(addr + 1 + index);
463 | // Always sign-extend
464 | if (op->displacement >= 0x80)
465 | op->displacement |= 0xffffff00;
466 | break;
467 | case 2:
468 | op->displacement = FETCH16(addr + 1 + index);
469 | break;
470 | case 4:
471 | op->displacement = FETCH32(addr + 1 + index);
472 | break;
473 | }
474 |
475 | // MODRM defines register
476 | if ((basereg != REG_NOP) && (MASK_MODRM_MOD(*addr) == 3)) {
477 | op->type = OPERAND_TYPE_REGISTER;
478 | op->reg = basereg;
479 | }
480 | break;
481 |
482 | // Immediate byte 1 encoded in instruction
483 | case AM_I1:
484 | op->type = OPERAND_TYPE_IMMEDIATE;
485 | op->immbytes = 1;
486 | op->immediate = 1;
487 | break;
488 | // Immediate value
489 | case AM_J:
490 | op->type = OPERAND_TYPE_IMMEDIATE;
491 | // Always sign-extend
492 | oflags |= F_s;
493 | case AM_I:
494 | op->type = OPERAND_TYPE_IMMEDIATE;
495 | index = (inst->modrm) ? 1 : 0;
496 | index += (sib) ? 1 : 0;
497 | index += instruction->immbytes;
498 | index += instruction->dispbytes;
499 | op->immoffset = index + offset;
500 |
501 | // check mode
502 | mode = MODE_CHECK_OPERAND(mode, iflags);
503 |
504 | switch (MASK_OT(oflags)) {
505 | case OT_b:
506 | op->immbytes = 1;
507 | op->immediate = FETCH8(addr + index);
508 | if ((op->immediate >= 0x80) &&
509 | (MASK_FLAGS(oflags) == F_s))
510 | op->immediate |= 0xffffff00;
511 | break;
512 | case OT_v:
513 | op->immbytes = (mode == MODE_32) ?
514 | 4 : 2;
515 | op->immediate = (mode == MODE_32) ?
516 | FETCH32(addr + index) :
517 | FETCH16(addr + index);
518 | break;
519 | case OT_w:
520 | op->immbytes = 2;
521 | op->immediate = FETCH16(addr + index);
522 | break;
523 | }
524 | instruction->immbytes += op->immbytes;
525 | break;
526 |
527 | // 32-bit or 48-bit address
528 | case AM_A:
529 | op->type = OPERAND_TYPE_IMMEDIATE;
530 | // check mode
531 | mode = MODE_CHECK_OPERAND(mode, iflags);
532 |
533 | op->dispbytes = (mode == MODE_32) ? 6 : 4;
534 | op->displacement = (mode == MODE_32) ?
535 | FETCH32(addr) : FETCH16(addr);
536 | op->section = FETCH16(addr + op->dispbytes - 2);
537 |
538 | instruction->dispbytes = op->dispbytes;
539 | instruction->sectionbytes = 2;
540 | break;
541 |
542 | // Plain displacement without MODRM/SIB
543 | case AM_O:
544 | op->type = OPERAND_TYPE_MEMORY;
545 | switch (MASK_OT(oflags)) {
546 | case OT_b:
547 | op->dispbytes = 1;
548 | op->displacement = FETCH8(addr);
549 | break;
550 | case OT_v:
551 | op->dispbytes = (mode == MODE_32) ? 4 : 2;
552 | op->displacement = (mode == MODE_32) ?
553 | FETCH32(addr) : FETCH16(addr);
554 | break;
555 | }
556 | instruction->dispbytes = op->dispbytes;
557 | op->dispoffset = offset;
558 | break;
559 |
560 | // General-purpose register encoded in MODRM
561 | case AM_G:
562 | op->type = OPERAND_TYPE_REGISTER;
563 | op->reg = reg;
564 | break;
565 |
566 | // control register encoded in MODRM
567 | case AM_C:
568 | // debug register encoded in MODRM
569 | case AM_D:
570 | // Segment register encoded in MODRM
571 | case AM_S:
572 | // TEST register encoded in MODRM
573 | case AM_T:
574 | // MMX register encoded in MODRM
575 | case AM_P:
576 | // XMM register encoded in MODRM
577 | case AM_V:
578 | op->type = OPERAND_TYPE_REGISTER;
579 | op->reg = MASK_MODRM_REG(instruction->modrm);
580 | break;
581 | }
582 | return 1;
583 | }
584 |
585 |
586 | // Print operand string
587 |
588 | #if !defined NOSTR
589 | int get_operand_string(INSTRUCTION *inst, OPERAND *op,
590 | enum Format format, DWORD offset, char *string, int length) {
591 |
592 | enum Mode mode;
593 | int regtype = 0;
594 | DWORD tmp = 0;
595 |
596 | memset(string, 0, length);
597 |
598 | if (op->type == OPERAND_TYPE_REGISTER) {
599 | // check mode
600 | mode = MODE_CHECK_OPERAND(inst->mode, inst->flags);
601 |
602 | if (format == FORMAT_ATT)
603 | snprintf(string + strlen(string), length - strlen(string), "%%");
604 |
605 | // Determine register type
606 | switch (MASK_AM(op->flags)) {
607 | case AM_REG:
608 | if (MASK_FLAGS(op->flags) == F_r)
609 | regtype = REG_SEGMENT;
610 | else if (MASK_FLAGS(op->flags) == F_f)
611 | regtype = REG_FPU;
612 | else
613 | regtype = REG_GEN_DWORD;
614 | break;
615 | case AM_E:
616 | case AM_G:
617 | case AM_R:
618 | regtype = REG_GEN_DWORD;
619 | break;
620 | // control register encoded in MODRM
621 | case AM_C:
622 | regtype = REG_CONTROL;
623 | break;
624 | // debug register encoded in MODRM
625 | case AM_D:
626 | regtype = REG_DEBUG;
627 | break;
628 | // Segment register encoded in MODRM
629 | case AM_S:
630 | regtype = REG_SEGMENT;
631 | break;
632 | // TEST register encoded in MODRM
633 | case AM_T:
634 | regtype = REG_TEST;
635 | break;
636 | // MMX register encoded in MODRM
637 | case AM_P:
638 | case AM_Q:
639 | regtype = REG_MMX;
640 | break;
641 | // XMM register encoded in MODRM
642 | case AM_V:
643 | case AM_W:
644 | regtype = REG_XMM;
645 | break;
646 | }
647 | if (regtype == REG_GEN_DWORD) {
648 | switch (MASK_OT(op->flags)) {
649 | case OT_b:
650 | snprintf(string + strlen(string), length - strlen(string),
651 | "%s", reg_table[REG_GEN_BYTE][op->reg]);
652 | break;
653 | case OT_v:
654 | snprintf(string + strlen(string), length - strlen(string),
655 | "%s", (mode == MODE_32) ?
656 | reg_table[REG_GEN_DWORD][op->reg] :
657 | reg_table[REG_GEN_WORD][op->reg]);
658 | break;
659 | case OT_w:
660 | snprintf(string + strlen(string), length - strlen(string),
661 | "%s", reg_table[REG_GEN_WORD][op->reg]);
662 | break;
663 | case OT_d:
664 | snprintf(string + strlen(string), length - strlen(string),
665 | "%s", reg_table[REG_GEN_DWORD][op->reg]);
666 | break;
667 | }
668 | } else
669 | snprintf(string + strlen(string), length - strlen(string),
670 | "%s", reg_table[regtype][op->reg]);
671 |
672 | } else if (op->type == OPERAND_TYPE_MEMORY) {
673 | // check mode
674 | mode = MODE_CHECK_ADDR(inst->mode, inst->flags);
675 |
676 | // Operand-specific segment override
677 | if (MASK_PREFIX_G2(inst->flags))
678 | snprintf(string + strlen(string),
679 | length - strlen(string),
680 | "%s%s:", (format == FORMAT_ATT) ? "%" : "",
681 | reg_table[REG_SEGMENT][(MASK_PREFIX_G2(inst->flags)) - 1]);
682 | // Some ATT stuff we need to check at this point
683 | if (format == FORMAT_ATT) {
684 |
685 | // "executable" operand
686 | if (MASK_PERMS(op->flags) == P_x)
687 | snprintf(string + strlen(string),
688 | length - strlen(string), "*");
689 |
690 | // displacement in front of brackets
691 | if (op->dispbytes)
692 | snprintf(string + strlen(string),
693 | length - strlen(string),
694 | "0x%x", op->displacement);
695 |
696 | // no empty brackets - we're ready
697 | if ((op->basereg == REG_NOP) &&
698 | (op->indexreg == REG_NOP))
699 | return 1;
700 | }
701 | // Open memory addressing brackets
702 | snprintf(string + strlen(string), length - strlen(string),
703 | "%s", (format == FORMAT_ATT) ? "(" : "[");
704 |
705 | // Base register
706 | if (op->basereg != REG_NOP) {
707 | snprintf(string + strlen(string), length - strlen(string),
708 | "%s%s", (format == FORMAT_ATT) ? "%" : "",
709 | (mode == MODE_32) ?
710 | reg_table[REG_GEN_DWORD][op->basereg] :
711 | reg_table[REG_GEN_WORD][op->basereg]);
712 | }
713 | // Index register
714 | if (op->indexreg != REG_NOP) {
715 | if (op->basereg != REG_NOP)
716 | snprintf(string + strlen(string), length - strlen(string),
717 | "%s%s", (format == FORMAT_ATT) ? ",%" : "+",
718 | (mode == MODE_32) ?
719 | reg_table[REG_GEN_DWORD][op->indexreg] :
720 | reg_table[REG_GEN_WORD][op->indexreg]);
721 | else
722 | snprintf(string + strlen(string), length - strlen(string),
723 | "%s%s", (format == FORMAT_ATT) ? "%" : "",
724 | (mode == MODE_32) ?
725 | reg_table[REG_GEN_DWORD][op->indexreg] :
726 | reg_table[REG_GEN_WORD][op->indexreg]);
727 | switch (op->scale) {
728 | case 2:
729 | snprintf(string + strlen(string), length - strlen(string),
730 | "%s", (format == FORMAT_ATT) ?
731 | ",2" : "*2");
732 | break;
733 | case 4:
734 | snprintf(string + strlen(string), length - strlen(string),
735 | "%s", (format == FORMAT_ATT) ?
736 | ",4" : "*4");
737 | break;
738 | case 8:
739 | snprintf(string + strlen(string), length - strlen(string),
740 | "%s", (format == FORMAT_ATT) ?
741 | ",8" : "*8");
742 | break;
743 | }
744 | }
745 | // INTEL displacement
746 | if (inst->dispbytes && (format != FORMAT_ATT)) {
747 | if ((op->basereg != REG_NOP) || (op->indexreg != REG_NOP)) {
748 | // Negative displacement
749 | if (op->displacement & (1<<(op->dispbytes*8-1))) {
750 | tmp = op->displacement;
751 | switch (op->dispbytes) {
752 | case 1:
753 | tmp = ~tmp & 0xff;
754 | break;
755 | case 2:
756 | tmp = ~tmp & 0xffff;
757 | break;
758 | case 4:
759 | tmp = ~tmp;
760 | break;
761 | }
762 | snprintf(string + strlen(string),
763 | length - strlen(string),
764 | "-0x%x", tmp + 1);
765 | // Positive displacement
766 | } else
767 | snprintf(string + strlen(string),
768 | length - strlen(string),
769 | "+0x%x", op->displacement);
770 | // Plain displacement
771 | } else {
772 | snprintf(string + strlen(string),
773 | length - strlen(string),
774 | "0x%x", op->displacement);
775 | }
776 | }
777 | // Close memory addressing brackets
778 | snprintf(string + strlen(string), length - strlen(string),
779 | "%s", (format == FORMAT_ATT) ? ")" : "]");
780 |
781 | } else if (op->type == OPERAND_TYPE_IMMEDIATE) {
782 |
783 | switch (MASK_AM(op->flags)) {
784 | case AM_J:
785 | snprintf(string + strlen(string), length - strlen(string),
786 | "0x%x", op->immediate + inst->length + offset);
787 | break;
788 | case AM_I1:
789 | case AM_I:
790 | if (format == FORMAT_ATT)
791 | snprintf(string + strlen(string), length - strlen(string), "$");
792 | snprintf(string + strlen(string), length - strlen(string),
793 | "0x%x", op->immediate);
794 | break;
795 | // 32-bit or 48-bit address
796 | case AM_A:
797 | snprintf(string + strlen(string), length - strlen(string),
798 | "%s0x%x:%s0x%x",
799 | (format == FORMAT_ATT) ? "$" : "",
800 | op->section,
801 | (format == FORMAT_ATT) ? "$" : "",
802 | op->displacement);
803 | break;
804 | }
805 |
806 | } else
807 | return 0;
808 |
809 | return 1;
810 | }
811 |
812 | #endif
813 |
814 |
815 | // Fetch instruction
816 |
817 | /*
818 | * The operation is quite straightforward:
819 | *
820 | * - determine actual opcode (skip prefixes etc.)
821 | * - figure out which instruction table to use
822 | * - index the table with opcode
823 | * - parse operands
824 | * - fill instruction structure
825 | *
826 | * Only point where this gets hairy is those *brilliant*
827 | * opcode extensions....
828 | *
829 | */
830 | int get_instruction(PINSTRUCTION inst, BYTE *addr, enum Mode mode) {
831 | PINST ptr = NULL;
832 | int index = 0;
833 | int flags = 0;
834 |
835 | memset(inst, 0, sizeof(INSTRUCTION));
836 |
837 | // Parse flags, skip prefixes etc.
838 | get_real_instruction(addr, &index, &flags);
839 |
840 | // Select instruction table
841 |
842 | // No extensions - normal 1-byte opcode:
843 | if (MASK_EXT(flags) == 0) {
844 | inst->opcode = *(addr + index);
845 | ptr = &inst_table1[inst->opcode];
846 |
847 | // FPU opcodes
848 | } else if (MASK_EXT(flags) == EXT_CP) {
849 | if (*(addr + index) < 0xc0) {
850 | // MODRM byte adds the additional byte
851 | index--;
852 | inst->fpuindex = *(addr + index) - 0xd8;
853 | inst->opcode = *(addr + index + 1);
854 | ptr = &inst_table4[inst->fpuindex]
855 | [MASK_MODRM_REG(inst->opcode)];
856 | } else {
857 | inst->fpuindex = *(addr + index - 1) - 0xd8;
858 | inst->opcode = *(addr + index);
859 | ptr = &inst_table4[inst->fpuindex]
860 | [inst->opcode - 0xb8];
861 | }
862 | // 2 or 3-byte opcodes
863 | } else if (MASK_EXT(flags) == EXT_T2) {
864 | inst->opcode = *(addr + index);
865 |
866 | // Parse flags, skip prefixes etc. (again)
867 | get_real_instruction2(addr + index, &flags);
868 |
869 | // 2-byte opcode table
870 | ptr = &inst_table2[inst->opcode];
871 |
872 | // 3-byte opcode tables
873 | if (MASK_TYPE_FLAGS(ptr->type) == TYPE_3) {
874 | // prefix 0x66
875 | if (MASK_PREFIX_OPERAND(flags) == 1) {
876 | ptr = &inst_table3_66[inst->opcode];
877 |
878 | // prefix 0xf2
879 | } else if (MASK_PREFIX_G1(flags) == 2) {
880 | ptr = &inst_table3_f2[inst->opcode];
881 |
882 | // prefix 0xf3
883 | } else if (MASK_PREFIX_G1(flags) == 3) {
884 | ptr = &inst_table3_f3[inst->opcode];
885 |
886 | }
887 | }
888 | }
889 | // Opcode extension tables
890 | if (MASK_EXT(flags) && (MASK_EXT(flags) < EXT_T2)) {
891 | inst->opcode = *(addr + index);
892 | inst->extindex = MASK_MODRM_REG(*(addr + index + 1));
893 |
894 | switch (MASK_EXT(flags)) {
895 | case EXT_GC:
896 | // prefix 0x66
897 | if (MASK_PREFIX_OPERAND(flags) == 1)
898 | ptr = &inst_table_ext12_66[inst->extindex];
899 | else
900 | ptr = &inst_table_ext12[inst->extindex];
901 | break;
902 | case EXT_GD:
903 | // prefix 0x66
904 | if (MASK_PREFIX_OPERAND(flags) == 1)
905 | ptr = &inst_table_ext13_66[inst->extindex];
906 | else
907 | ptr = &inst_table_ext13[inst->extindex];
908 | break;
909 | case EXT_GE:
910 | // prefix 0x66
911 | if (MASK_PREFIX_OPERAND(flags) == 1)
912 | ptr = &inst_table_ext14_66[inst->extindex];
913 | else
914 | ptr = &inst_table_ext14[inst->extindex];
915 | break;
916 | // monitor/mwait
917 | // XXX: hack.....
918 | case EXT_G7:
919 | if (MASK_MODRM_MOD(*(addr + index + 1)) == 3) {
920 | if (inst->extindex != 1)
921 | return 0;
922 | if (MASK_MODRM_RM(*(addr + index + 1)) == 0) {
923 | ptr = &inst_monitor;
924 | // index is incremented to get
925 | // correct instruction len
926 | index++;
927 | } else if (MASK_MODRM_RM(*(addr + index + 1)) == 1) {
928 | ptr = &inst_mwait;
929 | index++;
930 | } else
931 | return 0;
932 |
933 | } else {
934 | ptr = &inst_table_ext7[inst->extindex];
935 | }
936 | break;
937 | default:
938 | ptr = &inst_table_ext[(MASK_EXT(flags)) - 1]
939 | [inst->extindex];
940 | break;
941 | }
942 | }
943 | // Index points now to first byte after prefixes/escapes
944 | index++;
945 |
946 | // MODRM byte offset
947 | if (ptr->modrm)
948 | inst->modrm_offset = index;
949 |
950 | // Illegal instruction
951 | if (!ptr)
952 | return 0;
953 | if (!ptr->mnemonic)
954 | return 0;
955 |
956 | // Copy instruction type
957 | inst->type = MASK_TYPE_VALUE(ptr->type);
958 |
959 | // Eflags affected by this instruction
960 | inst->eflags_affected = ptr->eflags_affected;
961 | inst->eflags_used = ptr->eflags_used;
962 |
963 | // Pointer to instruction table
964 | inst->ptr = ptr;
965 |
966 |
967 | // Parse operands
968 | if (!get_operand(ptr, ptr->flags1, inst, &inst->op1, addr, index,
969 | mode, flags))
970 | return 0;
971 | if (!get_operand(ptr, ptr->flags2, inst, &inst->op2, addr, index,
972 | mode, flags))
973 | return 0;
974 | if (!get_operand(ptr, ptr->flags3, inst, &inst->op3, addr, index,
975 | mode, flags))
976 | return 0;
977 |
978 | // Implied operands
979 | inst->iop_read = ptr->iop_read;
980 | inst->iop_written = ptr->iop_written;
981 |
982 | // Add modrm/sib, displacement and immediate bytes in size
983 | inst->length += index + inst->immbytes + inst->dispbytes;
984 |
985 | // Copy addressing mode
986 | inst->mode = mode;
987 |
988 | // Copy instruction flags
989 | inst->flags = flags;
990 |
991 | return inst->length;
992 | }
993 |
994 |
995 | // Print instruction mnemonic
996 |
997 | #if !defined NOSTR
998 | int get_mnemonic_string(INSTRUCTION *inst, enum Format format, char *string, int length) {
999 | int mode;
1000 |
1001 | memset(string, 0, length);
1002 |
1003 | // Segment override, branch hint
1004 | if (MASK_PREFIX_G2(inst->flags) &&
1005 | (inst->op1.type != OPERAND_TYPE_MEMORY) &&
1006 | (inst->op2.type != OPERAND_TYPE_MEMORY)) {
1007 | // Branch hint
1008 | if (inst->type == INSTRUCTION_TYPE_JMPC)
1009 | snprintf(string + strlen(string), length - strlen(string),
1010 | "%s ", reg_table[REG_BRANCH][(MASK_PREFIX_G2(inst->flags)) - 1]);
1011 | // Segment override for others
1012 | else
1013 | snprintf(string + strlen(string), length - strlen(string),
1014 | "%s ", reg_table[REG_SEGMENT][(MASK_PREFIX_G2(inst->flags)) - 1]);
1015 | }
1016 |
1017 | // Rep, lock etc.
1018 | if (MASK_PREFIX_G1(inst->flags) &&
1019 | (MASK_EXT(inst->flags) != EXT_T2))
1020 | snprintf(string + strlen(string), length - strlen(string),
1021 | "%s", rep_table[(MASK_PREFIX_G1(inst->flags)) - 1]);
1022 |
1023 | // Mnemonic
1024 | // XXX: quick hack for jcxz/jecxz.. check if there are more
1025 | // of these opcodes that have different mnemonic in same opcode
1026 | if (((inst->type == INSTRUCTION_TYPE_JMPC) &&
1027 | (inst->opcode == 0xe3)) &&
1028 | (MASK_PREFIX_ADDR(inst->flags) == 1))
1029 | snprintf(string + strlen(string), length - strlen(string),
1030 | "jcxz");
1031 | else
1032 | snprintf(string + strlen(string), length - strlen(string),
1033 | "%s", inst->ptr->mnemonic);
1034 |
1035 |
1036 | // memory operation size in push/pop:
1037 | if (inst->type == INSTRUCTION_TYPE_PUSH) {
1038 | if (inst->op1.type == OPERAND_TYPE_IMMEDIATE) {
1039 | switch (inst->op1.immbytes) {
1040 | case 1:
1041 | snprintf(string + strlen(string),
1042 | length - strlen(string),
1043 | "%s", (format == FORMAT_ATT) ?
1044 | "b" : " byte");
1045 | break;
1046 | case 2:
1047 | snprintf(string + strlen(string),
1048 | length - strlen(string),
1049 | "%s", (format == FORMAT_ATT) ?
1050 | "w" : " word");
1051 | break;
1052 | case 4:
1053 | snprintf(string + strlen(string),
1054 | length - strlen(string),
1055 | "%s", (format == FORMAT_ATT) ?
1056 | "l" : " dword");
1057 | break;
1058 | }
1059 |
1060 | } else if (inst->op1.type == OPERAND_TYPE_MEMORY) {
1061 | mode = MODE_CHECK_OPERAND(inst->mode, inst->flags);
1062 |
1063 | if (mode == MODE_16) {
1064 | snprintf(string + strlen(string),
1065 | length - strlen(string),
1066 | "%s", (format == FORMAT_ATT) ?
1067 | "w" : " word");
1068 | } else if (mode == MODE_32) {
1069 | snprintf(string + strlen(string),
1070 | length - strlen(string),
1071 | "%s", (format == FORMAT_ATT) ?
1072 | "l" : " dword");
1073 | }
1074 |
1075 | }
1076 | return 1;
1077 |
1078 | }
1079 | if (inst->type == INSTRUCTION_TYPE_POP) {
1080 | if (inst->op1.type == OPERAND_TYPE_MEMORY) {
1081 | mode = MODE_CHECK_OPERAND(inst->mode, inst->flags);
1082 |
1083 | if (mode == MODE_16) {
1084 | snprintf(string + strlen(string),
1085 | length - strlen(string),
1086 | "%s", (format == FORMAT_ATT) ?
1087 | "w" : " word");
1088 | } else if (mode == MODE_32) {
1089 | snprintf(string + strlen(string),
1090 | length - strlen(string),
1091 | "%s", (format == FORMAT_ATT) ?
1092 | "l" : " dword");
1093 | }
1094 | }
1095 | return 1;
1096 | }
1097 |
1098 | // memory operation size in immediate to memory operations
1099 | if (inst->ptr->modrm && (MASK_MODRM_MOD(inst->modrm) != 3) &&
1100 | (MASK_AM(inst->op2.flags) == AM_I)) {
1101 |
1102 | switch (MASK_OT(inst->op1.flags)) {
1103 | case OT_b:
1104 | snprintf(string + strlen(string), length - strlen(string),
1105 | "%s", (format == FORMAT_ATT) ?
1106 | "b" : " byte");
1107 | break;
1108 | case OT_w:
1109 | snprintf(string + strlen(string), length - strlen(string),
1110 | "%s", (format == FORMAT_ATT) ?
1111 | "w" : " word");
1112 | break;
1113 | case OT_d:
1114 | snprintf(string + strlen(string), length - strlen(string),
1115 | "%s", (format == FORMAT_ATT) ?
1116 | "l" : " dword");
1117 | break;
1118 | case OT_v:
1119 | if (((inst->mode == MODE_32) && (MASK_PREFIX_OPERAND(inst->flags) == 0)) ||
1120 | ((inst->mode == MODE_16) && (MASK_PREFIX_OPERAND(inst->flags) == 1)))
1121 | snprintf(string + strlen(string), length - strlen(string),
1122 | "%s", (format == FORMAT_ATT) ?
1123 | "l" : " dword");
1124 | else
1125 | snprintf(string + strlen(string), length - strlen(string),
1126 | "%s", (format == FORMAT_ATT) ?
1127 | "w" : " word");
1128 | break;
1129 | }
1130 | }
1131 |
1132 | // XXX: there might be some other cases where size is needed..
1133 |
1134 | return 1;
1135 | }
1136 |
1137 | // Print operands
1138 |
1139 | int get_operands_string(INSTRUCTION *inst, enum Format format, DWORD offset,
1140 | char *string, int length) {
1141 |
1142 | if (format == FORMAT_ATT) {
1143 | if (inst->op3.type != OPERAND_TYPE_NONE) {
1144 | get_operand_string(inst, &inst->op3, format, offset,
1145 | string + strlen(string), length - strlen(string));
1146 | snprintf(string + strlen(string), length - strlen(string), ",");
1147 | }
1148 | if (inst->op2.type != OPERAND_TYPE_NONE) {
1149 | get_operand_string(inst, &inst->op2, format, offset,
1150 | string + strlen(string), length - strlen(string));
1151 | snprintf(string + strlen(string), length - strlen(string), ",");
1152 | }
1153 | if (inst->op1.type != OPERAND_TYPE_NONE)
1154 | get_operand_string(inst, &inst->op1, format, offset,
1155 | string + strlen(string), length - strlen(string));
1156 | } else if (format == FORMAT_INTEL) {
1157 | if (inst->op1.type != OPERAND_TYPE_NONE)
1158 | get_operand_string(inst, &inst->op1, format, offset,
1159 | string + strlen(string), length - strlen(string));
1160 | if (inst->op2.type != OPERAND_TYPE_NONE) {
1161 | snprintf(string + strlen(string), length - strlen(string), ",");
1162 | get_operand_string(inst, &inst->op2, format, offset,
1163 | string + strlen(string), length - strlen(string));
1164 | }
1165 | if (inst->op3.type != OPERAND_TYPE_NONE) {
1166 | snprintf(string + strlen(string), length - strlen(string), ",");
1167 | get_operand_string(inst, &inst->op3, format, offset,
1168 | string + strlen(string), length - strlen(string));
1169 | }
1170 | } else
1171 | return 0;
1172 |
1173 | return 1;
1174 | }
1175 |
1176 | // Print instruction mnemonic, prefixes and operands
1177 |
1178 | int get_instruction_string(INSTRUCTION *inst, enum Format format, DWORD offset,
1179 | char *string, int length) {
1180 |
1181 | // Print the actual instruction string with possible prefixes etc.
1182 | get_mnemonic_string(inst, format, string, length);
1183 |
1184 | snprintf(string + strlen(string), length - strlen(string), " ");
1185 |
1186 | // Print operands
1187 | if (!get_operands_string(inst, format, offset,
1188 | string + strlen(string), length - strlen(string)))
1189 | return 0;
1190 |
1191 | return 1;
1192 | }
1193 |
1194 | #endif
1195 |
1196 | // Helper functions
1197 |
1198 | int get_register_type(POPERAND op) {
1199 |
1200 | if (op->type != OPERAND_TYPE_REGISTER)
1201 | return 0;
1202 | switch (MASK_AM(op->flags)) {
1203 | case AM_REG:
1204 | if (MASK_FLAGS(op->flags) == F_r)
1205 | return REGISTER_TYPE_SEGMENT;
1206 | else if (MASK_FLAGS(op->flags) == F_f)
1207 | return REGISTER_TYPE_FPU;
1208 | else
1209 | return REGISTER_TYPE_GEN;
1210 | case AM_E:
1211 | case AM_G:
1212 | case AM_R:
1213 | return REGISTER_TYPE_GEN;
1214 | case AM_C:
1215 | return REGISTER_TYPE_CONTROL;
1216 | case AM_D:
1217 | return REGISTER_TYPE_DEBUG;
1218 | case AM_S:
1219 | return REGISTER_TYPE_SEGMENT;
1220 | case AM_T:
1221 | return REGISTER_TYPE_TEST;
1222 | case AM_P:
1223 | case AM_Q:
1224 | return REGISTER_TYPE_MMX;
1225 | case AM_V:
1226 | case AM_W:
1227 | return REGISTER_TYPE_XMM;
1228 | default:
1229 | break;
1230 | }
1231 | return 0;
1232 | }
1233 |
1234 | int get_operand_type(POPERAND op) {
1235 | return op->type;
1236 | }
1237 |
1238 | int get_operand_register(POPERAND op) {
1239 | return op->reg;
1240 | }
1241 |
1242 | int get_operand_basereg(POPERAND op) {
1243 | return op->basereg;
1244 | }
1245 |
1246 | int get_operand_indexreg(POPERAND op) {
1247 | return op->indexreg;
1248 | }
1249 |
1250 | int get_operand_scale(POPERAND op) {
1251 | return op->scale;
1252 | }
1253 |
1254 | int get_operand_immediate(POPERAND op, DWORD *imm) {
1255 | if (op->immbytes) {
1256 | *imm = op->immediate;
1257 | return 1;
1258 | } else
1259 | return 0;
1260 | }
1261 |
1262 | int get_operand_displacement(POPERAND op, DWORD *disp) {
1263 | if (op->dispbytes) {
1264 | *disp = op->displacement;
1265 | return 1;
1266 | } else
1267 | return 0;
1268 | }
1269 |
1270 | // XXX: note that source and destination are not always literal
1271 |
1272 | POPERAND get_source_operand(PINSTRUCTION inst) {
1273 | if (inst->op2.type != OPERAND_TYPE_NONE)
1274 | return &inst->op2;
1275 | else
1276 | return NULL;
1277 | }
1278 | POPERAND get_destination_operand(PINSTRUCTION inst) {
1279 | if (inst->op1.type != OPERAND_TYPE_NONE)
1280 | return &inst->op1;
1281 | else
1282 | return NULL;
1283 | }
1284 |
1285 |
1286 |
--------------------------------------------------------------------------------
/libdasm.h:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * libdasm -- simple x86 disassembly library
4 | * (c) 2004 - 2006 jt / nologin.org
5 | *
6 | * libdasm.h:
7 | * Definitions for structures, functions and other weird stuff
8 | *
9 | */
10 |
11 |
12 | #ifndef _LIBDASM_H
13 | #define _LIBDASM_H
14 |
15 | #ifdef __cplusplus
16 | extern "C" {
17 | #endif
18 |
19 | #define __LIBDASM_VERSION__ 0x01050000
20 |
21 | #define GET_VERSION_MAJOR \
22 | (__LIBDASM_VERSION__ & 0xff000000) >> 24
23 | #define GET_VERSION_MINOR1 \
24 | (__LIBDASM_VERSION__ & 0x00ff0000) >> 16
25 | #define GET_VERSION_MINOR2 \
26 | (__LIBDASM_VERSION__ & 0x0000ff00) >> 8
27 | #define GET_VERSION_MINOR3 \
28 | (__LIBDASM_VERSION__ & 0x000000ff)
29 |
30 | // Data types
31 |
32 | #if _WIN32
33 | #include
34 | #define __inline__ __inline
35 | #define snprintf _snprintf
36 | typedef unsigned __int64 QWORD; // for MSVC
37 | typedef signed __int8 SBYTE;
38 | typedef signed __int16 SWORD;
39 | typedef signed __int32 SDWORD;
40 | typedef signed __int64 SQWORD;
41 | #else
42 | #if defined __sun
43 | #define BYTE_ORDER 1234
44 | #define BIG_ENDIAN 1234
45 | #define LITTLE_ENDIAN 4321
46 | #define u_int8_t uint8_t
47 | #define u_int16_t uint16_t
48 | #define u_int32_t uint32_t
49 | #define u_int64_t uint64_t
50 |
51 | #endif // other *nix
52 | #include
53 | typedef u_int8_t BYTE;
54 | typedef u_int16_t WORD;
55 | typedef u_int32_t DWORD;
56 | typedef u_int64_t QWORD;
57 | typedef int8_t SBYTE;
58 | typedef int16_t SWORD;
59 | typedef int32_t SDWORD;
60 | typedef int64_t SQWORD;
61 | #endif
62 |
63 | // Define endianess
64 |
65 | #ifndef __X86__
66 | // These should catch x86 with most compilers
67 | #if defined _X86_ || defined _i386_ || defined __i386__
68 | #define __X86__
69 | #endif
70 | #endif
71 |
72 | #ifndef __LITTLE_ENDIAN__
73 | // These should catch little-endian with most compilers
74 | #if (BYTE_ORDER == LITTLE_ENDIAN) || defined __X86__ || defined _ALPHA_
75 | #define __LITTLE_ENDIAN__
76 | #endif
77 | #endif
78 |
79 |
80 | // Registers
81 | #define REGISTER_EAX 0
82 | #define REGISTER_ECX 1
83 | #define REGISTER_EDX 2
84 | #define REGISTER_EBX 3
85 | #define REGISTER_ESP 4
86 | #define REGISTER_EBP 5
87 | #define REGISTER_ESI 6
88 | #define REGISTER_EDI 7
89 | #define REGISTER_NOP 8 // no register defined
90 |
91 | // Registers
92 | #define REG_EAX REGISTER_EAX
93 | #define REG_AX REG_EAX
94 | #define REG_AL REG_EAX
95 | #define REG_ES REG_EAX // Just for reg_table consistence
96 | #define REG_ST0 REG_EAX // Just for reg_table consistence
97 | #define REG_ECX REGISTER_ECX
98 | #define REG_CX REG_ECX
99 | #define REG_CL REG_ECX
100 | #define REG_CS REG_ECX
101 | #define REG_ST1 REG_ECX
102 | #define REG_EDX REGISTER_EDX
103 | #define REG_DX REG_EDX
104 | #define REG_DL REG_EDX
105 | #define REG_SS REG_EDX
106 | #define REG_ST2 REG_EDX
107 | #define REG_EBX REGISTER_EBX
108 | #define REG_BX REG_EBX
109 | #define REG_BL REG_EBX
110 | #define REG_DS REG_EBX
111 | #define REG_ST3 REG_EBX
112 | #define REG_ESP REGISTER_ESP
113 | #define REG_SP REG_ESP
114 | #define REG_AH REG_ESP // Just for reg_table consistence
115 | #define REG_FS REG_ESP
116 | #define REG_ST4 REG_ESP
117 | #define REG_EBP REGISTER_EBP
118 | #define REG_BP REG_EBP
119 | #define REG_CH REG_EBP
120 | #define REG_GS REG_EBP
121 | #define REG_ST5 REG_EBP
122 | #define REG_ESI REGISTER_ESI
123 | #define REG_SI REG_ESI
124 | #define REG_DH REG_ESI
125 | #define REG_ST6 REG_ESI
126 | #define REG_EDI REGISTER_EDI
127 | #define REG_DI REG_EDI
128 | #define REG_BH REG_EDI
129 | #define REG_ST7 REG_EDI
130 | #define REG_NOP REGISTER_NOP
131 |
132 | // Implied operands
133 | #define IOP_EAX 1
134 | #define IOP_ECX (1 << REG_ECX)
135 | #define IOP_EDX (1 << REG_EDX)
136 | #define IOP_EBX (1 << REG_EBX)
137 | #define IOP_ESP (1 << REG_ESP)
138 | #define IOP_EBP (1 << REG_EBP)
139 | #define IOP_ESI (1 << REG_ESI)
140 | #define IOP_EDI (1 << REG_EDI)
141 | #define IOP_ALL IOP_EAX|IOP_ECX|IOP_EDX|IOP_ESP|IOP_EBP|IOP_ESI|IOP_EDI
142 | #define IS_IOP_REG(x,y) (x >> y) & 1
143 | #define IS_IOP_EAX(x) (x) & 1
144 | #define IS_IOP_ECX(x) (x >> REG_ECX) & 1
145 | #define IS_IOP_EDX(x) (x >> REG_EDX) & 1
146 | #define IS_IOP_EBX(x) (x >> REG_EBX) & 1
147 | #define IS_IOP_EBP(x) (x >> REG_EBP) & 1
148 | #define IS_IOP_ESI(x) (x >> REG_ESI) & 1
149 | #define IS_IOP_EDI(x) (x >> REG_EDI) & 1
150 |
151 |
152 | // Register types
153 | #define REGISTER_TYPE_GEN 1
154 | #define REGISTER_TYPE_SEGMENT 2
155 | #define REGISTER_TYPE_DEBUG 3
156 | #define REGISTER_TYPE_CONTROL 4
157 | #define REGISTER_TYPE_TEST 5
158 | #define REGISTER_TYPE_XMM 6
159 | #define REGISTER_TYPE_MMX 7
160 | #define REGISTER_TYPE_FPU 8
161 |
162 | // Disassembling mode
163 | enum Mode {
164 | MODE_32, // 32-bit
165 | MODE_16 // 16-bit
166 | };
167 |
168 | // Disassembling format
169 | enum Format {
170 | FORMAT_ATT,
171 | FORMAT_INTEL,
172 | };
173 |
174 | // Process eflags
175 | #define EFL_CF (1 << 0)
176 | #define EFL_PF (1 << 2)
177 | #define EFL_AF (1 << 4)
178 | #define EFL_ZF (1 << 6)
179 | #define EFL_SF (1 << 7)
180 | #define EFL_TF (1 << 8)
181 | #define EFL_IF (1 << 9)
182 | #define EFL_DF (1 << 10)
183 | #define EFL_OF (1 << 11)
184 | #define EFL_MATH EFL_OF|EFL_SF|EFL_ZF|EFL_AF|EFL_PF|EFL_CF
185 | #define EFL_BITWISE EFL_OF|EFL_CF|EFL_SF|EFL_ZF|EFL_PF
186 | #define EFL_ALL_COMMON EFL_CF|EFL_OF|EFL_SF|EFL_ZF|EFL_AF|EFL_PF
187 |
188 | // Instruction types (just the most common ones atm)
189 | enum Instruction {
190 | // Integer instructions
191 | INSTRUCTION_TYPE_ASC, // aaa, aam, etc.
192 | INSTRUCTION_TYPE_DCL, // daa, das
193 | INSTRUCTION_TYPE_MOV,
194 | INSTRUCTION_TYPE_MOVSR, // segment register
195 | INSTRUCTION_TYPE_ADD,
196 | INSTRUCTION_TYPE_XADD,
197 | INSTRUCTION_TYPE_ADC,
198 | INSTRUCTION_TYPE_SUB,
199 | INSTRUCTION_TYPE_SBB,
200 | INSTRUCTION_TYPE_INC,
201 | INSTRUCTION_TYPE_DEC,
202 | INSTRUCTION_TYPE_DIV,
203 | INSTRUCTION_TYPE_IDIV,
204 | INSTRUCTION_TYPE_NOT,
205 | INSTRUCTION_TYPE_NEG,
206 | INSTRUCTION_TYPE_STOS,
207 | INSTRUCTION_TYPE_LODS,
208 | INSTRUCTION_TYPE_SCAS,
209 | INSTRUCTION_TYPE_MOVS,
210 | INSTRUCTION_TYPE_MOVSX,
211 | INSTRUCTION_TYPE_MOVZX,
212 | INSTRUCTION_TYPE_CMPS,
213 | INSTRUCTION_TYPE_SHX, // signed/unsigned shift left/right
214 | INSTRUCTION_TYPE_ROX, // signed/unsigned rot left/right
215 | INSTRUCTION_TYPE_MUL,
216 | INSTRUCTION_TYPE_IMUL,
217 | INSTRUCTION_TYPE_EIMUL, // "extended" imul with 2-3 operands
218 | INSTRUCTION_TYPE_XOR,
219 | INSTRUCTION_TYPE_LEA,
220 | INSTRUCTION_TYPE_XCHG,
221 | INSTRUCTION_TYPE_CMP,
222 | INSTRUCTION_TYPE_TEST,
223 | INSTRUCTION_TYPE_PUSH,
224 | INSTRUCTION_TYPE_AND,
225 | INSTRUCTION_TYPE_OR,
226 | INSTRUCTION_TYPE_POP,
227 | INSTRUCTION_TYPE_JMP,
228 | INSTRUCTION_TYPE_JMPC, // conditional jump
229 | INSTRUCTION_TYPE_JECXZ,
230 | INSTRUCTION_TYPE_SETC, // conditional byte set
231 | INSTRUCTION_TYPE_MOVC, // conditional mov
232 | INSTRUCTION_TYPE_LOOP,
233 | INSTRUCTION_TYPE_CALL,
234 | INSTRUCTION_TYPE_RET,
235 | INSTRUCTION_TYPE_ENTER,
236 | INSTRUCTION_TYPE_INT, // interrupt
237 | INSTRUCTION_TYPE_BT, // bit tests
238 | INSTRUCTION_TYPE_BTS,
239 | INSTRUCTION_TYPE_BTR,
240 | INSTRUCTION_TYPE_BTC,
241 | INSTRUCTION_TYPE_BSF,
242 | INSTRUCTION_TYPE_BSR,
243 | INSTRUCTION_TYPE_BSWAP,
244 | INSTRUCTION_TYPE_SGDT,
245 | INSTRUCTION_TYPE_SIDT,
246 | INSTRUCTION_TYPE_SLDT,
247 | INSTRUCTION_TYPE_LFP,
248 | INSTRUCTION_TYPE_CLD,
249 | INSTRUCTION_TYPE_STD,
250 | INSTRUCTION_TYPE_XLAT,
251 | // FPU instructions
252 | INSTRUCTION_TYPE_FCMOVC, // float conditional mov
253 | INSTRUCTION_TYPE_FADD,
254 | INSTRUCTION_TYPE_FADDP,
255 | INSTRUCTION_TYPE_FIADD,
256 | INSTRUCTION_TYPE_FSUB,
257 | INSTRUCTION_TYPE_FSUBP,
258 | INSTRUCTION_TYPE_FISUB,
259 | INSTRUCTION_TYPE_FSUBR,
260 | INSTRUCTION_TYPE_FSUBRP,
261 | INSTRUCTION_TYPE_FISUBR,
262 | INSTRUCTION_TYPE_FMUL,
263 | INSTRUCTION_TYPE_FMULP,
264 | INSTRUCTION_TYPE_FIMUL,
265 | INSTRUCTION_TYPE_FDIV,
266 | INSTRUCTION_TYPE_FDIVP,
267 | INSTRUCTION_TYPE_FDIVR,
268 | INSTRUCTION_TYPE_FDIVRP,
269 | INSTRUCTION_TYPE_FIDIV,
270 | INSTRUCTION_TYPE_FIDIVR,
271 | INSTRUCTION_TYPE_FCOM,
272 | INSTRUCTION_TYPE_FCOMP,
273 | INSTRUCTION_TYPE_FCOMPP,
274 | INSTRUCTION_TYPE_FCOMI,
275 | INSTRUCTION_TYPE_FCOMIP,
276 | INSTRUCTION_TYPE_FUCOM,
277 | INSTRUCTION_TYPE_FUCOMP,
278 | INSTRUCTION_TYPE_FUCOMPP,
279 | INSTRUCTION_TYPE_FUCOMI,
280 | INSTRUCTION_TYPE_FUCOMIP,
281 | INSTRUCTION_TYPE_FST,
282 | INSTRUCTION_TYPE_FSTP,
283 | INSTRUCTION_TYPE_FIST,
284 | INSTRUCTION_TYPE_FISTP,
285 | INSTRUCTION_TYPE_FISTTP,
286 | INSTRUCTION_TYPE_FLD,
287 | INSTRUCTION_TYPE_FILD,
288 | INSTRUCTION_TYPE_FICOM,
289 | INSTRUCTION_TYPE_FICOMP,
290 | INSTRUCTION_TYPE_FFREE,
291 | INSTRUCTION_TYPE_FFREEP,
292 | INSTRUCTION_TYPE_FXCH,
293 | INSTRUCTION_TYPE_SYSENTER,
294 | INSTRUCTION_TYPE_FPU_CTRL, // FPU control instruction
295 | INSTRUCTION_TYPE_FPU, // Other FPU instructions
296 |
297 | INSTRUCTION_TYPE_MMX, // Other MMX instructions
298 |
299 | INSTRUCTION_TYPE_SSE, // Other SSE instructions
300 |
301 | INSTRUCTION_TYPE_OTHER, // Other instructions :-)
302 | INSTRUCTION_TYPE_PRIV // Privileged instruction
303 | };
304 |
305 | // Operand types
306 | enum Operand {
307 | OPERAND_TYPE_NONE, // operand not present
308 | OPERAND_TYPE_MEMORY, // memory operand ([eax], [0], etc.)
309 | OPERAND_TYPE_REGISTER, // register operand (eax, mm0, etc.)
310 | OPERAND_TYPE_IMMEDIATE, // immediate operand (0x1234)
311 | };
312 |
313 | // Structure definitions
314 |
315 | // struct INST is used internally by the library
316 | typedef struct _INST {
317 | DWORD type; // Instruction type and flags
318 | const char *mnemonic; // Instruction mnemonic
319 | int flags1; // First operand flags (if any)
320 | int flags2; // Second operand flags (if any)
321 | int flags3; // Additional operand flags (if any)
322 | int modrm; // Is MODRM byte present?
323 | short eflags_affected; // Processor eflags affected
324 | short eflags_used; // Processor eflags used by this instruction
325 | int iop_written; // mask of affected implied registers (written)
326 | int iop_read; // mask of affected implied registers (read)
327 | } INST, *PINST;
328 |
329 | // Operands for the instruction
330 | typedef struct _OPERAND {
331 | enum Operand type; // Operand type (register, memory, etc)
332 | int reg; // Register (if any)
333 | int basereg; // Base register (if any)
334 | int indexreg; // Index register (if any)
335 | int scale; // Scale (if any)
336 | int dispbytes; // Displacement bytes (0 = no displacement)
337 | int dispoffset; // Displacement value offset
338 | int immbytes; // Immediate bytes (0 = no immediate)
339 | int immoffset; // Immediate value offset
340 | int sectionbytes; // Section prefix bytes (0 = no section prefix)
341 | WORD section; // Section prefix value
342 | DWORD displacement; // Displacement value
343 | DWORD immediate; // Immediate value
344 | int flags; // Operand flags
345 | } OPERAND, *POPERAND;
346 |
347 | // struct INSTRUCTION is used to interface the library
348 | typedef struct _INSTRUCTION {
349 | int length; // Instruction length
350 | enum Instruction type; // Instruction type
351 | enum Mode mode; // Addressing mode
352 | BYTE opcode; // Actual opcode
353 | BYTE modrm; // MODRM byte
354 | BYTE sib; // SIB byte
355 | int modrm_offset; // MODRM byte offset
356 | int extindex; // Extension table index
357 | int fpuindex; // FPU table index
358 | int dispbytes; // Displacement bytes (0 = no displacement)
359 | int immbytes; // Immediate bytes (0 = no immediate)
360 | int sectionbytes; // Section prefix bytes (0 = no section prefix)
361 | OPERAND op1; // First operand (if any)
362 | OPERAND op2; // Second operand (if any)
363 | OPERAND op3; // Additional operand (if any)
364 | PINST ptr; // Pointer to instruction table
365 | int flags; // Instruction flags
366 | short eflags_affected; // Process eflags affected
367 | short eflags_used; // Processor eflags used by this instruction
368 | int iop_written; // mask of affected implied registers (written)
369 | int iop_read; // mask of affected implied registers (read)
370 | } INSTRUCTION, *PINSTRUCTION;
371 |
372 |
373 | // Function definitions
374 |
375 | int get_instruction(
376 | INSTRUCTION *inst, // pointer to INSTRUCTION structure
377 | BYTE *addr, // code buffer
378 | enum Mode mode // mode: MODE_32 or MODE_16
379 | );
380 |
381 | // Get complete instruction string
382 | int get_instruction_string(
383 | INSTRUCTION *inst, // pointer to INSTRUCTION structure
384 | enum Format format, // instruction format: FORMAT_ATT or FORMAT_INTEL
385 | DWORD offset, // instruction absolute address
386 | char *string, // string buffer
387 | int length // string length
388 | );
389 |
390 | // Get mnemonic string
391 | int get_mnemonic_string(
392 | INSTRUCTION *inst, // pointer to INSTRUCTION structure
393 | enum Format format, // instruction format: FORMAT_ATT or FORMAT_INTEL
394 | char *string, // string buffer
395 | int length // string length
396 | );
397 |
398 | // Get individual operand string
399 | int get_operand_string(
400 | INSTRUCTION *inst, // pointer to INSTRUCTION structure
401 | POPERAND op, // pointer to OPERAND structure
402 | enum Format format, // instruction format: FORMAT_ATT or FORMAT_INTEL
403 | DWORD offset, // instruction absolute address
404 | char *string, // string buffer
405 | int length // string length
406 | );
407 |
408 | // Helper functions
409 |
410 | int get_register_type(
411 | POPERAND op
412 | );
413 | int get_operand_type(
414 | POPERAND op
415 | );
416 | int get_operand_register(
417 | POPERAND op
418 | );
419 | int get_operand_basereg(
420 | POPERAND op
421 | );
422 | int get_operand_indexreg(
423 | POPERAND op
424 | );
425 | int get_operand_scale(
426 | POPERAND op
427 | );
428 | int get_operand_immediate(
429 | POPERAND op,
430 | DWORD *imm // returned immediate value
431 | );
432 | int get_operand_displacement(
433 | POPERAND op,
434 | DWORD *disp // returned displacement value
435 | );
436 | POPERAND get_source_operand(
437 | PINSTRUCTION inst
438 | );
439 | POPERAND get_destination_operand(
440 | PINSTRUCTION inst
441 | );
442 |
443 |
444 | // Instruction flags (prefixes)
445 |
446 | // Group 1
447 | #define MASK_PREFIX_G1(x) ((x) & 0xff000000) >> 24
448 | #define PREFIX_LOCK 0x01000000 // 0xf0
449 | #define PREFIX_REPNE 0x02000000 // 0xf2
450 | #define PREFIX_REP 0x03000000 // 0xf3
451 | #define PREFIX_REPE 0x03000000 // 0xf3
452 | // Group 2
453 | #define MASK_PREFIX_G2(x) ((x) & 0x00ff0000) >> 16
454 | #define PREFIX_ES_OVERRIDE 0x00010000 // 0x26
455 | #define PREFIX_CS_OVERRIDE 0x00020000 // 0x2e
456 | #define PREFIX_SS_OVERRIDE 0x00030000 // 0x36
457 | #define PREFIX_DS_OVERRIDE 0x00040000 // 0x3e
458 | #define PREFIX_FS_OVERRIDE 0x00050000 // 0x64
459 | #define PREFIX_GS_OVERRIDE 0x00060000 // 0x65
460 | // Group 3 & 4
461 | #define MASK_PREFIX_G3(x) ((x) & 0x0000ff00) >> 8
462 | #define MASK_PREFIX_OPERAND(x) ((x) & 0x00000f00) >> 8
463 | #define MASK_PREFIX_ADDR(x) ((x) & 0x0000f000) >> 12
464 | #define PREFIX_OPERAND_SIZE_OVERRIDE 0x00000100 // 0x66
465 | #define PREFIX_ADDR_SIZE_OVERRIDE 0x00001000 // 0x67
466 |
467 | // Extensions
468 |
469 | #define MASK_EXT(x) ((x) & 0x000000ff)
470 | #define EXT_G1_1 0x00000001
471 | #define EXT_G1_2 0x00000002
472 | #define EXT_G1_3 0x00000003
473 | #define EXT_G2_1 0x00000004
474 | #define EXT_G2_2 0x00000005
475 | #define EXT_G2_3 0x00000006
476 | #define EXT_G2_4 0x00000007
477 | #define EXT_G2_5 0x00000008
478 | #define EXT_G2_6 0x00000009
479 | #define EXT_G3_1 0x0000000a
480 | #define EXT_G3_2 0x0000000b
481 | #define EXT_G4 0x0000000c
482 | #define EXT_G5 0x0000000d
483 | #define EXT_G6 0x0000000e
484 | #define EXT_G7 0x0000000f
485 | #define EXT_G8 0x00000010
486 | #define EXT_G9 0x00000011
487 | #define EXT_GA 0x00000012
488 | #define EXT_GB 0x00000013
489 | #define EXT_GC 0x00000014
490 | #define EXT_GD 0x00000015
491 | #define EXT_GE 0x00000016
492 | #define EXT_GF 0x00000017
493 | #define EXT_G0 0x00000018
494 |
495 | // Extra groups for 2 and 3-byte opcodes, and FPU stuff
496 | #define EXT_T2 0x00000020 // opcode table 2
497 | #define EXT_CP 0x00000030 // co-processor
498 |
499 | // Instruction type flags
500 |
501 | #define TYPE_3 0x80000000
502 | #define MASK_TYPE_FLAGS(x) ((x) & 0xff000000)
503 | #define MASK_TYPE_VALUE(x) ((x) & 0x00ffffff)
504 |
505 |
506 | // Operand flags
507 |
508 | #define FLAGS_NONE 0
509 |
510 | // Operand Addressing Methods, from the Intel manual
511 | #define MASK_AM(x) ((x) & 0x00ff0000)
512 | #define AM_A 0x00010000 // Direct address with segment prefix
513 | #define AM_C 0x00020000 // MODRM reg field defines control register
514 | #define AM_D 0x00030000 // MODRM reg field defines debug register
515 | #define AM_E 0x00040000 // MODRM byte defines reg/memory address
516 | #define AM_G 0x00050000 // MODRM byte defines general-purpose reg
517 | #define AM_I 0x00060000 // Immediate data follows
518 | #define AM_J 0x00070000 // Immediate value is relative to EIP
519 | #define AM_M 0x00080000 // MODRM mod field can refer only to memory
520 | #define AM_O 0x00090000 // Displacement follows (without modrm/sib)
521 | #define AM_P 0x000a0000 // MODRM reg field defines MMX register
522 | #define AM_Q 0x000b0000 // MODRM defines MMX register or memory
523 | #define AM_R 0x000c0000 // MODRM mod field can only refer to register
524 | #define AM_S 0x000d0000 // MODRM reg field defines segment register
525 | #define AM_T 0x000e0000 // MODRM reg field defines test register
526 | #define AM_V 0x000f0000 // MODRM reg field defines XMM register
527 | #define AM_W 0x00100000 // MODRM defines XMM register or memory
528 | // Extra addressing modes used in this implementation
529 | #define AM_I1 0x00200000 // Immediate byte 1 encoded in instruction
530 | #define AM_REG 0x00210000 // Register encoded in instruction
531 | #define AM_IND 0x00220000 // Register indirect encoded in instruction
532 |
533 | // Operand Types, from the intel manual
534 | #define MASK_OT(x) ((x) & 0xff000000)
535 | #define OT_a 0x01000000
536 | #define OT_b 0x02000000 // always 1 byte
537 | #define OT_c 0x03000000 // byte or word, depending on operand
538 | #define OT_d 0x04000000 // double-word
539 | #define OT_q 0x05000000 // quad-word
540 | #define OT_dq 0x06000000 // double quad-word
541 | #define OT_v 0x07000000 // word or double-word, depending on operand
542 | #define OT_w 0x08000000 // always word
543 | #define OT_p 0x09000000 // 32-bit or 48-bit pointer
544 | #define OT_pi 0x0a000000 // quadword MMX register
545 | #define OT_pd 0x0b000000 // 128-bit double-precision float
546 | #define OT_ps 0x0c000000 // 128-bit single-precision float
547 | #define OT_s 0x0d000000 // 6-byte pseudo descriptor
548 | #define OT_sd 0x0e000000 // Scalar of 128-bit double-precision float
549 | #define OT_ss 0x0f000000 // Scalar of 128-bit single-precision float
550 | #define OT_si 0x10000000 // Doubleword integer register
551 | #define OT_t 0x11000000 // 80-bit packed FP data
552 |
553 | // Operand permissions
554 | #define MASK_PERMS(x) ((x) & 0x0000f000)
555 | #define P_r 0x00004000 // Read
556 | #define P_w 0x00002000 // Write
557 | #define P_x 0x00001000 // Execute
558 |
559 | // Additional operand flags
560 | #define MASK_FLAGS(x) ((x) & 0x00000f00)
561 | #define F_s 0x00000100 // sign-extend 1-byte immediate
562 | #define F_r 0x00000200 // use segment register
563 | #define F_f 0x00000400 // use FPU register
564 |
565 | // Mask 0x000000f0 unused atm
566 |
567 | // Operand register mask
568 | #define MASK_REG(x) ((x) & 0x0000000f)
569 |
570 |
571 |
572 | // MODRM byte
573 | #define MASK_MODRM_MOD(x) (((x) & 0xc0) >> 6)
574 | #define MASK_MODRM_REG(x) (((x) & 0x38) >> 3)
575 | #define MASK_MODRM_RM(x) ((x) & 0x7)
576 |
577 | // SIB byte
578 | #define MASK_SIB_SCALE(x) MASK_MODRM_MOD(x)
579 | #define MASK_SIB_INDEX(x) MASK_MODRM_REG(x)
580 | #define MASK_SIB_BASE(x) MASK_MODRM_RM(x)
581 |
582 |
583 | #ifdef __cplusplus
584 | }
585 | #endif
586 |
587 | #endif
588 |
--------------------------------------------------------------------------------
/memutils.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 sw=4 tw=99 noet :
3 | * =============================================================================
4 | * MemoryUtils
5 | * Copyright (C) 2004-2011 AlliedModders LLC., 2012 Prodigysim
6 | * All rights reserved.
7 | * =============================================================================
8 | *
9 | * This program is free software; you can redistribute it and/or modify it under
10 | * the terms of the GNU General Public License, version 3.0, as published by the
11 | * Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful, but WITHOUT
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License along with
19 | * this program. If not, see .
20 | *
21 | * As a special exception, the authors give you permission to link the
22 | * code of this program (as well as its derivative works) to "Half-Life 2," the
23 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
24 | * by the Valve Corporation. You must obey the GNU General Public License in
25 | * all respects for all other code used. Additionally, AlliedModders LLC grants
26 | * this exception to all derivative works. AlliedModders LLC defines further
27 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
28 | * or .
29 | */
30 |
31 | #include "memutils.h"
32 | #include
33 |
34 | #if SH_SYS == SH_SYS_LINUX
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | #define PAGE_SIZE 4096
41 | #define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
42 | #define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
43 | #define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC
44 | #endif
45 |
46 | #if SH_SYS == SH_SYS_APPLE
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 |
53 | /* Define things from 10.6 SDK for older SDKs */
54 | #ifndef MAC_OS_X_VERSION_10_6
55 | struct task_dyld_info
56 | {
57 | mach_vm_address_t all_image_info_addr;
58 | mach_vm_size_t all_image_info_size;
59 | };
60 | typedef struct task_dyld_info task_dyld_info_data_t;
61 | #define TASK_DYLD_INFO 17
62 | #define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
63 | #endif // MAC_OS_X_VERSION_10_6
64 | #endif // SH_SYS_APPLE
65 |
66 | MemoryUtils g_MemUtils;
67 |
68 | MemoryUtils::MemoryUtils()
69 | {
70 | #if SH_SYS == SH_SYS_APPLE
71 |
72 | Gestalt(gestaltSystemVersionMajor, &m_OSXMajor);
73 | Gestalt(gestaltSystemVersionMinor, &m_OSXMinor);
74 |
75 | /* Get pointer to struct that describes all loaded mach-o images in process */
76 | if ((m_OSXMajor == 10 && m_OSXMinor >= 6) || m_OSXMajor > 10)
77 | {
78 | task_dyld_info_data_t dyld_info;
79 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
80 | task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
81 | m_ImageList = (struct dyld_all_image_infos *)dyld_info.all_image_info_addr;
82 | }
83 | else
84 | {
85 | struct nlist list[2];
86 | memset(list, 0, sizeof(list));
87 | list[0].n_un.n_name = (char *)"_dyld_all_image_infos";
88 | nlist("/usr/lib/dyld", list);
89 | m_ImageList = (struct dyld_all_image_infos *)list[0].n_value;
90 | }
91 |
92 | #endif
93 | }
94 |
95 | MemoryUtils::~MemoryUtils()
96 | {
97 | #if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_LINUX
98 | for (size_t i = 0; i < m_SymTables.size(); i++)
99 | {
100 | delete m_SymTables[i];
101 | }
102 | m_SymTables.clear();
103 | #endif
104 | }
105 |
106 |
107 | void *MemoryUtils::FindLibPattern(const void *libPtr, const char *pattern, size_t len)
108 | {
109 | DynLibInfo lib;
110 | char *ptr, *end;
111 |
112 | memset(&lib, 0, sizeof(DynLibInfo));
113 |
114 | if (!GetLibraryInfo(libPtr, lib))
115 | {
116 | return NULL;
117 | }
118 |
119 | ptr = reinterpret_cast(lib.baseAddress);
120 | end = ptr + lib.memorySize;
121 | return FindPattern((void*)ptr, (void*)end, pattern, len);
122 | }
123 |
124 | void *MemoryUtils::FindPattern(const void *start, const void *end, const char *pattern, size_t len)
125 | {
126 | bool found;
127 | char *ptr=(char*)start;
128 | while (ptr < (char*)end)
129 | {
130 | found = true;
131 | for (register size_t i = 0; i < len; i++)
132 | {
133 | if (pattern[i] != '\x2A' && pattern[i] != ptr[i])
134 | {
135 | found = false;
136 | break;
137 | }
138 | }
139 |
140 | if (found)
141 | return ptr;
142 |
143 | ptr++;
144 | }
145 |
146 | return NULL;
147 | }
148 |
149 | void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
150 | {
151 | #if SH_SYS == SH_SYS_WIN32
152 |
153 | return GetProcAddress((HMODULE)handle, symbol);
154 |
155 | #elif SH_SYS == SH_SYS_LINUX
156 |
157 | struct link_map *dlmap;
158 | struct stat dlstat;
159 | int dlfile;
160 | uintptr_t map_base;
161 | Elf32_Ehdr *file_hdr;
162 | Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr;
163 | Elf32_Sym *symtab;
164 | const char *shstrtab, *strtab;
165 | uint16_t section_count;
166 | uint32_t symbol_count;
167 | LibSymbolTable *libtable;
168 | SymbolTable *table;
169 | Symbol *symbol_entry;
170 |
171 | dlmap = (struct link_map *)handle;
172 | symtab_hdr = NULL;
173 | strtab_hdr = NULL;
174 | table = NULL;
175 |
176 | /* See if we already have a symbol table for this library */
177 | for (size_t i = 0; i < m_SymTables.size(); i++)
178 | {
179 | libtable = m_SymTables[i];
180 | if (libtable->lib_base == dlmap->l_addr)
181 | {
182 | table = &libtable->table;
183 | break;
184 | }
185 | }
186 |
187 | /* If we don't have a symbol table for this library, then create one */
188 | if (table == NULL)
189 | {
190 | libtable = new LibSymbolTable();
191 | libtable->table.Initialize();
192 | libtable->lib_base = dlmap->l_addr;
193 | libtable->last_pos = 0;
194 | table = &libtable->table;
195 | m_SymTables.push_back(libtable);
196 | }
197 |
198 | /* See if the symbol is already cached in our table */
199 | symbol_entry = table->FindSymbol(symbol, strlen(symbol));
200 | if (symbol_entry != NULL)
201 | {
202 | return symbol_entry->address;
203 | }
204 |
205 | /* If symbol isn't in our table, then we have open the actual library */
206 | dlfile = open(dlmap->l_name, O_RDONLY);
207 | if (dlfile == -1 || fstat(dlfile, &dlstat) == -1)
208 | {
209 | close(dlfile);
210 | return NULL;
211 | }
212 |
213 | /* Map library file into memory */
214 | file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0);
215 | map_base = (uintptr_t)file_hdr;
216 | if (file_hdr == MAP_FAILED)
217 | {
218 | close(dlfile);
219 | return NULL;
220 | }
221 | close(dlfile);
222 |
223 | if (file_hdr->e_shoff == 0 || file_hdr->e_shstrndx == SHN_UNDEF)
224 | {
225 | munmap(file_hdr, dlstat.st_size);
226 | return NULL;
227 | }
228 |
229 | sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff);
230 | section_count = file_hdr->e_shnum;
231 | /* Get ELF section header string table */
232 | shstrtab_hdr = §ions[file_hdr->e_shstrndx];
233 | shstrtab = (const char *)(map_base + shstrtab_hdr->sh_offset);
234 |
235 | /* Iterate sections while looking for ELF symbol table and string table */
236 | for (uint16_t i = 0; i < section_count; i++)
237 | {
238 | Elf32_Shdr &hdr = sections[i];
239 | const char *section_name = shstrtab + hdr.sh_name;
240 |
241 | if (strcmp(section_name, ".symtab") == 0)
242 | {
243 | symtab_hdr = &hdr;
244 | }
245 | else if (strcmp(section_name, ".strtab") == 0)
246 | {
247 | strtab_hdr = &hdr;
248 | }
249 | }
250 |
251 | /* Uh oh, we don't have a symbol table or a string table */
252 | if (symtab_hdr == NULL || strtab_hdr == NULL)
253 | {
254 | munmap(file_hdr, dlstat.st_size);
255 | return NULL;
256 | }
257 |
258 | symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset);
259 | strtab = (const char *)(map_base + strtab_hdr->sh_offset);
260 | symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
261 |
262 | /* Iterate symbol table starting from the position we were at last time */
263 | for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
264 | {
265 | Elf32_Sym &sym = symtab[i];
266 | unsigned char sym_type = ELF32_ST_TYPE(sym.st_info);
267 | const char *sym_name = strtab + sym.st_name;
268 | Symbol *cur_sym;
269 |
270 | /* Skip symbols that are undefined or do not refer to functions or objects */
271 | if (sym.st_shndx == SHN_UNDEF || (sym_type != STT_FUNC && sym_type != STT_OBJECT))
272 | {
273 | continue;
274 | }
275 |
276 | /* Caching symbols as we go along */
277 | cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value));
278 | if (strcmp(symbol, sym_name) == 0)
279 | {
280 | symbol_entry = cur_sym;
281 | libtable->last_pos = ++i;
282 | break;
283 | }
284 | }
285 |
286 | munmap(file_hdr, dlstat.st_size);
287 | return symbol_entry ? symbol_entry->address : NULL;
288 |
289 | #elif SH_SYS == SH_SYS_APPLE
290 |
291 | uintptr_t dlbase, linkedit_addr;
292 | uint32_t image_count;
293 | struct mach_header *file_hdr;
294 | struct load_command *loadcmds;
295 | struct segment_command *linkedit_hdr;
296 | struct symtab_command *symtab_hdr;
297 | struct nlist *symtab;
298 | const char *strtab;
299 | uint32_t loadcmd_count;
300 | uint32_t symbol_count;
301 | LibSymbolTable *libtable;
302 | SymbolTable *table;
303 | Symbol *symbol_entry;
304 |
305 | dlbase = 0;
306 | image_count = m_ImageList->infoArrayCount;
307 | linkedit_hdr = NULL;
308 | symtab_hdr = NULL;
309 | table = NULL;
310 |
311 | /* Loop through mach-o images in process.
312 | * We can skip index 0 since that is just the executable.
313 | */
314 | for (uint32_t i = 1; i < image_count; i++)
315 | {
316 | const struct dyld_image_info &info = m_ImageList->infoArray[i];
317 |
318 | /* "Load" each one until we get a matching handle */
319 | void *h = dlopen(info.imageFilePath, RTLD_NOLOAD);
320 | if (h == handle)
321 | {
322 | dlbase = (uintptr_t)info.imageLoadAddress;
323 | dlclose(h);
324 | break;
325 | }
326 |
327 | dlclose(h);
328 | }
329 |
330 | if (!dlbase)
331 | {
332 | /* Uh oh, we couldn't find a matching handle */
333 | return NULL;
334 | }
335 |
336 | /* See if we already have a symbol table for this library */
337 | for (size_t i = 0; i < m_SymTables.size(); i++)
338 | {
339 | libtable = m_SymTables[i];
340 | if (libtable->lib_base == dlbase)
341 | {
342 | table = &libtable->table;
343 | break;
344 | }
345 | }
346 |
347 | /* If we don't have a symbol table for this library, then create one */
348 | if (table == NULL)
349 | {
350 | libtable = new LibSymbolTable();
351 | libtable->table.Initialize();
352 | libtable->lib_base = dlbase;
353 | libtable->last_pos = 0;
354 | table = &libtable->table;
355 | m_SymTables.push_back(libtable);
356 | }
357 |
358 | /* See if the symbol is already cached in our table */
359 | symbol_entry = table->FindSymbol(symbol, strlen(symbol));
360 | if (symbol_entry != NULL)
361 | {
362 | return symbol_entry->address;
363 | }
364 |
365 | /* If symbol isn't in our table, then we have to locate it in memory */
366 |
367 | file_hdr = (struct mach_header *)dlbase;
368 | loadcmds = (struct load_command *)(dlbase + sizeof(struct mach_header));
369 | loadcmd_count = file_hdr->ncmds;
370 |
371 | /* Loop through load commands until we find the ones for the symbol table */
372 | for (uint32_t i = 0; i < loadcmd_count; i++)
373 | {
374 | if (loadcmds->cmd == LC_SEGMENT && !linkedit_hdr)
375 | {
376 | struct segment_command *seg = (struct segment_command *)loadcmds;
377 | if (strcmp(seg->segname, "__LINKEDIT") == 0)
378 | {
379 | linkedit_hdr = seg;
380 | if (symtab_hdr)
381 | {
382 | break;
383 | }
384 | }
385 | }
386 | else if (loadcmds->cmd == LC_SYMTAB)
387 | {
388 | symtab_hdr = (struct symtab_command *)loadcmds;
389 | if (linkedit_hdr)
390 | {
391 | break;
392 | }
393 | }
394 |
395 | /* Load commands are not of a fixed size which is why we add the size */
396 | loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize);
397 | }
398 |
399 | if (!linkedit_hdr || !symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff)
400 | {
401 | /* Uh oh, no symbol table */
402 | return NULL;
403 | }
404 |
405 | linkedit_addr = dlbase + linkedit_hdr->vmaddr;
406 | symtab = (struct nlist *)(linkedit_addr + symtab_hdr->symoff - linkedit_hdr->fileoff);
407 | strtab = (const char *)(linkedit_addr + symtab_hdr->stroff - linkedit_hdr->fileoff);
408 | symbol_count = symtab_hdr->nsyms;
409 |
410 | /* Iterate symbol table starting from the position we were at last time */
411 | for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
412 | {
413 | struct nlist &sym = symtab[i];
414 | /* Ignore the prepended underscore on all symbols, so +1 here */
415 | const char *sym_name = strtab + sym.n_un.n_strx + 1;
416 | Symbol *cur_sym;
417 |
418 | /* Skip symbols that are undefined */
419 | if (sym.n_sect == NO_SECT)
420 | {
421 | continue;
422 | }
423 |
424 | /* Caching symbols as we go along */
425 | cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlbase + sym.n_value));
426 | if (strcmp(symbol, sym_name) == 0)
427 | {
428 | symbol_entry = cur_sym;
429 | libtable->last_pos = ++i;
430 | break;
431 | }
432 | }
433 |
434 | return symbol_entry ? symbol_entry->address : NULL;
435 |
436 | #endif
437 | return NULL;
438 | }
439 |
440 | bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
441 | {
442 | uintptr_t baseAddr;
443 |
444 | if (libPtr == NULL)
445 | {
446 | return false;
447 | }
448 |
449 | #if SH_SYS == SH_SYS_WIN32
450 |
451 | MEMORY_BASIC_INFORMATION info;
452 | IMAGE_DOS_HEADER *dos;
453 | IMAGE_NT_HEADERS *pe;
454 | IMAGE_FILE_HEADER *file;
455 | IMAGE_OPTIONAL_HEADER *opt;
456 |
457 | if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
458 | {
459 | return false;
460 | }
461 |
462 | baseAddr = reinterpret_cast(info.AllocationBase);
463 |
464 | /* All this is for our insane sanity checks :o */
465 | dos = reinterpret_cast(baseAddr);
466 | pe = reinterpret_cast(baseAddr + dos->e_lfanew);
467 | file = &pe->FileHeader;
468 | opt = &pe->OptionalHeader;
469 |
470 | /* Check PE magic and signature */
471 | if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
472 | {
473 | return false;
474 | }
475 |
476 | /* Check architecture, which is 32-bit/x86 right now
477 | * Should change this for 64-bit if Valve gets their act together
478 | */
479 | if (file->Machine != IMAGE_FILE_MACHINE_I386)
480 | {
481 | return false;
482 | }
483 |
484 | /* For our purposes, this must be a dynamic library */
485 | if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
486 | {
487 | return false;
488 | }
489 |
490 | /* Finally, we can do this */
491 | lib.memorySize = opt->SizeOfImage;
492 |
493 | #elif SH_SYS == SH_SYS_LINUX
494 |
495 | Dl_info info;
496 | Elf32_Ehdr *file;
497 | Elf32_Phdr *phdr;
498 | uint16_t phdrCount;
499 |
500 | if (!dladdr(libPtr, &info))
501 | {
502 | return false;
503 | }
504 |
505 | if (!info.dli_fbase || !info.dli_fname)
506 | {
507 | return false;
508 | }
509 |
510 | /* This is for our insane sanity checks :o */
511 | baseAddr = reinterpret_cast(info.dli_fbase);
512 | file = reinterpret_cast(baseAddr);
513 |
514 | /* Check ELF magic */
515 | if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
516 | {
517 | return false;
518 | }
519 |
520 | /* Check ELF version */
521 | if (file->e_ident[EI_VERSION] != EV_CURRENT)
522 | {
523 | return false;
524 | }
525 |
526 | /* Check ELF architecture, which is 32-bit/x86 right now
527 | * Should change this for 64-bit if Valve gets their act together
528 | */
529 | if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
530 | {
531 | return false;
532 | }
533 |
534 | /* For our purposes, this must be a dynamic library/shared object */
535 | if (file->e_type != ET_DYN)
536 | {
537 | return false;
538 | }
539 |
540 | phdrCount = file->e_phnum;
541 | phdr = reinterpret_cast(baseAddr + file->e_phoff);
542 |
543 | for (uint16_t i = 0; i < phdrCount; i++)
544 | {
545 | Elf32_Phdr &hdr = phdr[i];
546 |
547 | /* We only really care about the segment with executable code */
548 | if (hdr.p_type == PT_LOAD && hdr.p_flags == (PF_X|PF_R))
549 | {
550 | /* From glibc, elf/dl-load.c:
551 | * c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
552 | * & ~(GLRO(dl_pagesize) - 1));
553 | *
554 | * In glibc, the segment file size is aligned up to the nearest page size and
555 | * added to the virtual address of the segment. We just want the size here.
556 | */
557 | lib.memorySize = PAGE_ALIGN_UP(hdr.p_filesz);
558 | break;
559 | }
560 | }
561 |
562 | #elif SH_SYS == SH_SYS_APPLE
563 |
564 | Dl_info info;
565 | struct mach_header *file;
566 | struct segment_command *seg;
567 | uint32_t cmd_count;
568 |
569 | if (!dladdr(libPtr, &info))
570 | {
571 | return false;
572 | }
573 |
574 | if (!info.dli_fbase || !info.dli_fname)
575 | {
576 | return false;
577 | }
578 |
579 | /* This is for our insane sanity checks :o */
580 | baseAddr = (uintptr_t)info.dli_fbase;
581 | file = (struct mach_header *)baseAddr;
582 |
583 | /* Check Mach-O magic */
584 | if (file->magic != MH_MAGIC)
585 | {
586 | return false;
587 | }
588 |
589 | /* Check architecture (32-bit/x86) */
590 | if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL)
591 | {
592 | return false;
593 | }
594 |
595 | /* For our purposes, this must be a dynamic library */
596 | if (file->filetype != MH_DYLIB)
597 | {
598 | return false;
599 | }
600 |
601 | cmd_count = file->ncmds;
602 | seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header));
603 |
604 | /* Add up memory sizes of mapped segments */
605 | for (uint32_t i = 0; i < cmd_count; i++)
606 | {
607 | if (seg->cmd == LC_SEGMENT)
608 | {
609 | lib.memorySize += seg->vmsize;
610 | }
611 |
612 | seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize);
613 | }
614 |
615 | #endif
616 |
617 | lib.baseAddress = reinterpret_cast(baseAddr);
618 |
619 | return true;
620 | }
621 |
622 | bool MemoryUtils::ProtectMemory(void *pAddr, int nLength, int nProt)
623 | {
624 | return SourceHook::SetMemAccess(pAddr, nLength, nProt);
625 | }
626 |
627 | bool MemoryUtils::SetMemPatchable(void *pAddr, int nSize)
628 | {
629 | return ProtectMemory(pAddr, (int)nSize, SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
630 | }
631 |
632 | BYTE * MemoryUtils::GetCallOrJumpAbsAddr(BYTE * pInstr)
633 | {
634 |
635 | BYTE * nextInstrAddress = pInstr +5;
636 | int jumpTargetOffset = *(int*)(pInstr+1);
637 | return nextInstrAddress + jumpTargetOffset;
638 | }
639 |
640 | int MemoryUtils::GetCallOrJumpRelOffset(BYTE * pInstrBase, BYTE *pAbsAddr)
641 | {
642 | return pAbsAddr - (pInstrBase + 5);
643 | }
644 |
645 |
646 | BYTE * MemoryUtils::CloneFunction(BYTE *pFunc)
647 | {
648 | static INSTRUCTION insBuf;
649 | // First pass, determine function length by seeking INT instruction
650 | int res;
651 | size_t length = 0;
652 | BYTE * pCurInstr = pFunc;
653 | do
654 | {
655 | res = get_instruction(&insBuf, pCurInstr, MODE_32);
656 | if(res == 0)
657 | {
658 | // Unknown instruction or not an instruction.
659 | return NULL;
660 | }
661 | length+=res;
662 | pCurInstr+=res;
663 | } while(insBuf.type != INSTRUCTION_TYPE_INT);
664 |
665 | // Note: Allocates an extra byte (INT instruction comes along with us)
666 | BYTE * pNewFunc = (BYTE*)malloc(length);
667 |
668 | // bad alloc
669 | if(pNewFunc == NULL) return NULL;
670 |
671 | // Pull the original function into our newly allocated memory
672 | memcpy(pNewFunc, pFunc, length);
673 |
674 |
675 | pCurInstr = pFunc;
676 | int curOffset = 0;
677 | do
678 | {
679 | res = get_instruction(&insBuf, pCurInstr, MODE_32);
680 | // assume res isn't 0 since we've done this before
681 |
682 | // TODO: There's probably other relative offset instructions that need
683 | // converting. This only fixes one of them--but it's all this project needs for now
684 | // I can't guarantee the correctness of this.
685 | switch (insBuf.type)
686 | {
687 | //case INSTRUCTION_TYPE_JMP:
688 | case INSTRUCTION_TYPE_CALL:
689 | if(insBuf.immbytes == 4)
690 | {
691 | *(int*)(pNewFunc+curOffset+1) = GetCallOrJumpRelOffset(pNewFunc+curOffset, GetCallOrJumpAbsAddr(pCurInstr));
692 | }
693 | break;
694 | default:
695 | break;
696 | }
697 |
698 | pCurInstr+=res;
699 | curOffset+=res;
700 | } while(insBuf.type != INSTRUCTION_TYPE_INT);
701 |
702 | // Sets it read/write/executable (executable being the important part here);
703 | SetMemPatchable(pNewFunc, length);
704 |
705 | return pNewFunc;
706 | }
707 |
--------------------------------------------------------------------------------
/memutils.h:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 sw=4 tw=99 noet :
3 | * =============================================================================
4 | * MemoryUtils
5 | * Copyright (C) 2004-2011 AlliedModders LLC., 2012 ProdigySim
6 | * All rights reserved.
7 | * =============================================================================
8 | *
9 | * This program is free software; you can redistribute it and/or modify it under
10 | * the terms of the GNU General Public License, version 3.0, as published by the
11 | * Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful, but WITHOUT
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License along with
19 | * this program. If not, see .
20 | *
21 | * As a special exception, the authors give you permission to link the
22 | * code of this program (as well as its derivative works) to "Half-Life 2," the
23 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
24 | * by the Valve Corporation. You must obey the GNU General Public License in
25 | * all respects for all other code used. Additionally, AlliedModders LLC grants
26 | * this exception to all derivative works. AlliedModders LLC defines further
27 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
28 | * or .
29 | */
30 |
31 | #ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
32 | #define _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
33 |
34 | #include
35 | #include
36 |
37 | #include "sourcehook.h"
38 | #include "sh_memory.h"
39 | #include "libdasm.h"
40 |
41 | #if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE
42 | #include
43 | #include "sm_symtable.h"
44 | using SourceHook::CVector;
45 | #endif
46 |
47 | #if SH_SYS == SH_SYS_APPLE
48 | #include
49 | #endif
50 |
51 | struct DynLibInfo
52 | {
53 | void *baseAddress;
54 | size_t memorySize;
55 | };
56 |
57 | #if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE
58 | struct LibSymbolTable
59 | {
60 | SymbolTable table;
61 | uintptr_t lib_base;
62 | uint32_t last_pos;
63 | };
64 | #endif
65 |
66 | class MemoryUtils
67 | {
68 | public:
69 | MemoryUtils();
70 | ~MemoryUtils();
71 | void *FindLibPattern(const void *libPtr, const char *pattern, size_t len);
72 | void *FindPattern(const void *start, const void *end, const char *pattern, size_t len);
73 | void *ResolveSymbol(void *handle, const char *symbol);
74 | // Sets protection on the memory
75 | static bool ProtectMemory(void *pAddr, int nLength, int nProt);
76 | // Allows the memory to be written to
77 | static bool SetMemPatchable(void *pAddr, int nSize);
78 |
79 | /* Assembly level functions */
80 | static BYTE * GetCallOrJumpAbsAddr(BYTE * pInstr);
81 | static int GetCallOrJumpRelOffset(BYTE * pInstrBase, BYTE *pAbsAddr);
82 | // Clones a function in memory, returning a newly allocated
83 | // memory chunk that can be CALLed or JUMP'd to as if it were the original function
84 | // Should replace all relative offsets/jumps properly
85 | static BYTE * CloneFunction(BYTE * pFunction);
86 | public:
87 | bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
88 | #if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE
89 | private:
90 | CVector m_SymTables;
91 | #if SH_SYS == SH_SYS_APPLE
92 | struct dyld_all_image_infos *m_ImageList;
93 | SInt32 m_OSXMajor;
94 | SInt32 m_OSXMinor;
95 | #endif
96 | #endif
97 | };
98 |
99 | extern MemoryUtils g_MemUtils;
100 |
101 | #endif // _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
102 |
--------------------------------------------------------------------------------
/msvc10/recording_helpers.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual C++ Express 2010
4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "recording_helpers", "recording_helpers.vcxproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug - Left 4 Dead 2|Win32 = Debug - Left 4 Dead 2|Win32
9 | Debug - Left 4 Dead|Win32 = Debug - Left 4 Dead|Win32
10 | Release - Left 4 Dead 2|Win32 = Release - Left 4 Dead 2|Win32
11 | Release - Left 4 Dead|Win32 = Release - Left 4 Dead|Win32
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Left 4 Dead 2|Win32.ActiveCfg = Debug - Left 4 Dead 2|Win32
15 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Left 4 Dead 2|Win32.Build.0 = Debug - Left 4 Dead 2|Win32
16 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Left 4 Dead|Win32.ActiveCfg = Debug - Left 4 Dead|Win32
17 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Left 4 Dead|Win32.Build.0 = Debug - Left 4 Dead|Win32
18 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Left 4 Dead 2|Win32.ActiveCfg = Release - Left 4 Dead 2|Win32
19 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Left 4 Dead 2|Win32.Build.0 = Release - Left 4 Dead 2|Win32
20 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Left 4 Dead|Win32.ActiveCfg = Release - Left 4 Dead|Win32
21 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Left 4 Dead|Win32.Build.0 = Release - Left 4 Dead|Win32
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------
/msvc10/recording_helpers.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug - Left 4 Dead 2
6 | Win32
7 |
8 |
9 | Debug - Left 4 Dead
10 | Win32
11 |
12 |
13 | Release - Left 4 Dead 2
14 | Win32
15 |
16 |
17 | Release - Left 4 Dead
18 | Win32
19 |
20 |
21 |
22 | {B3E797CF-4E77-4C9D-B8A8-7589B6902206}
23 | recording_helpers
24 | Win32Proj
25 |
26 |
27 |
28 | DynamicLibrary
29 | MultiByte
30 | true
31 |
32 |
33 | DynamicLibrary
34 | MultiByte
35 | true
36 |
37 |
38 | DynamicLibrary
39 | MultiByte
40 |
41 |
42 | DynamicLibrary
43 | MultiByte
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | <_ProjectFileVersion>10.0.30319.1
63 | $(SolutionDir)$(Configuration)\
64 | $(SolutionDir)$(Configuration)\
65 | $(Configuration)\
66 | $(Configuration)\
67 | $(Configuration)\
68 | false
69 | false
70 | $(SolutionDir)$(Configuration)\
71 | $(SolutionDir)$(Configuration)\
72 | $(Configuration)\
73 | $(Configuration)\
74 | false
75 | false
76 | recording_helpers
77 | recording_helpers
78 | recording_helpers
79 | recording_helpers
80 |
81 |
82 |
83 | %(AdditionalOptions)
84 | Disabled
85 | ..;$(HL2SDKL4D2)\game\shared;$(HL2SDKL4D2)\game\client;$(HL2SDKL4D2)\public;$(HL2SDKL4D2)\public\engine;$(HL2SDKL4D2)\public\tier0;$(HL2SDKL4D2)\public\tier1;$(MMSOURCE18)\core;$(MMSOURCE18)\core\sourcehook;%(AdditionalIncludeDirectories)
86 | L4D2;WIN32;COMPILER_MSVC;COMPILER_MSVC32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)
87 | true
88 | EnableFastChecks
89 | MultiThreadedDebug
90 | NotSet
91 | false
92 |
93 |
94 | Level3
95 | EditAndContinue
96 |
97 |
98 | $(HL2SDKL4D2)\lib\public\tier0.lib;$(HL2SDKL4D2)\lib\public\tier1.lib;$(HL2SDKL4D2)\lib\public\vstdlib.lib;%(AdditionalDependencies)
99 | $(OutDir)recording_helpers.dll
100 | LIBC;LIBCD;LIBCMT;%(IgnoreSpecificDefaultLibraries)
101 | true
102 | Windows
103 | false
104 |
105 |
106 | MachineX86
107 | true
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | %(AdditionalOptions)
117 | Disabled
118 | ..;$(HL2SDKL4D)\game\shared;$(HL2SDKL4D)\game\client;$(HL2SDKL4D)\public;$(HL2SDKL4D)\public\engine;$(HL2SDKL4D)\public\tier0;$(HL2SDKL4D)\public\tier1;$(MMSOURCE18)\core;$(MMSOURCE18)\core\sourcehook;%(AdditionalIncludeDirectories)
119 | L4D1;WIN32;COMPILER_MSVC;COMPILER_MSVC32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)
120 | true
121 | EnableFastChecks
122 | MultiThreadedDebug
123 | NotSet
124 | false
125 |
126 |
127 | Level3
128 | EditAndContinue
129 |
130 |
131 | $(HL2SDKL4D)\lib\public\tier0.lib;$(HL2SDKL4D)\lib\public\tier1.lib;$(HL2SDKL4D)\lib\public\vstdlib.lib;%(AdditionalDependencies)
132 | $(OutDir)recording_helpers.dll
133 | LIBC;LIBCD;LIBCMT;%(IgnoreSpecificDefaultLibraries)
134 | true
135 | Windows
136 | false
137 |
138 |
139 | MachineX86
140 | true
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | /MP%(AdditionalOptions)
150 | Speed
151 | ..;$(HL2SDKL4D2)\game\shared;$(HL2SDKL4D2)\game\client;$(HL2SDKL4D2)\public;$(HL2SDKL4D2)\public\engine;$(HL2SDKL4D2)\public\tier0;$(HL2SDKL4D2)\public\tier1;$(MMSOURCE18)\core;$(MMSOURCE18)\core\sourcehook;%(AdditionalIncludeDirectories)
152 | L4D2;WIN32;COMPILER_MSVC;COMPILER_MSVC32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)
153 | MultiThreaded
154 | NotSet
155 | false
156 |
157 |
158 | Level3
159 | ProgramDatabase
160 |
161 |
162 | $(HL2SDKL4D2)\lib\public\tier0.lib;$(HL2SDKL4D2)\lib\public\tier1.lib;$(HL2SDKL4D2)\lib\public\vstdlib.lib;%(AdditionalDependencies)
163 | $(OutDir)recording_helpers.dll
164 | LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries)
165 | true
166 | Windows
167 | true
168 | true
169 | false
170 |
171 |
172 | MachineX86
173 | true
174 |
175 |
176 |
177 |
178 | /MP%(AdditionalOptions)
179 | Speed
180 | ..;$(HL2SDKL4D)\game\shared;$(HL2SDKL4D)\game\client;$(HL2SDKL4D)\public;$(HL2SDKL4D)\public\engine;$(HL2SDKL4D)\public\tier0;$(HL2SDKL4D)\public\tier1;$(MMSOURCE18)\core;$(MMSOURCE18)\core\sourcehook;%(AdditionalIncludeDirectories)
181 | L4D1;WIN32;COMPILER_MSVC;COMPILER_MSVC32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)
182 | MultiThreaded
183 | NotSet
184 | false
185 |
186 |
187 | Level3
188 | ProgramDatabase
189 |
190 |
191 | $(HL2SDKL4D)\lib\public\tier0.lib;$(HL2SDKL4D)\lib\public\tier1.lib;$(HL2SDKL4D)\lib\public\vstdlib.lib;%(AdditionalDependencies)
192 | $(OutDir)recording_helpers.dll
193 | LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries)
194 | true
195 | Windows
196 | true
197 | true
198 | false
199 |
200 |
201 | MachineX86
202 | true
203 |
204 |
205 |
206 |
207 | ProgramDatabase
208 | ProgramDatabase
209 |
210 |
211 | ProgramDatabase
212 | ProgramDatabase
213 |
214 |
215 | ProgramDatabase
216 | ProgramDatabase
217 |
218 |
219 |
220 |
221 | ProgramDatabase
222 | ProgramDatabase
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
--------------------------------------------------------------------------------
/msvc10/recording_helpers.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | libdasm
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | libdasm
18 |
19 |
20 | libdasm
21 |
22 |
23 |
24 |
25 |
26 | {3fee3052-39bf-4b7e-9db1-ce8b76776490}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/recording_helpers.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 sw=4 tw=99 noet :
3 | * =============================================================================
4 | * Recording Helpers
5 | * Copyright (C) 2012 ProdigySim
6 | * All rights reserved.
7 | * =============================================================================
8 | *
9 | * This program is free software; you can redistribute it and/or modify it under
10 | * the terms of the GNU General Public License, version 3.0, as published by the
11 | * Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful, but WITHOUT
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License along with
19 | * this program. If not, see .
20 | *
21 | * As a special exception, the authors give you permission to link the
22 | * code of this program (as well as its derivative works) to "Half-Life 2," the
23 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
24 | * by the Valve Corporation. You must obey the GNU General Public License in
25 | * all respects for all other code used. Additionally, AlliedModders LLC grants
26 | * this exception to all derivative works. AlliedModders LLC defines further
27 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
28 | * or .
29 | */
30 | #include "recording_helpers.h"
31 | #ifdef L4D2
32 | #include "thirdperson_patch.h"
33 | #endif
34 |
35 | RecordingHelpers g_RecordingHelpersPlugin;
36 | EXPOSE_SINGLE_INTERFACE_GLOBALVAR(RecordingHelpers, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_RecordingHelpersPlugin );
37 |
38 | ICvar * g_pCvar = NULL;
39 |
40 | // Remove FCVAR_DEVELOPMENTONLY from all cvars
41 | void RemoveDevFlags();
42 |
43 | #ifdef L4D2
44 | IInput * g_pInput = NULL;
45 | // Find the global IInput instance (CInput actually)
46 | IInput * GetGlobalIInput();
47 | #endif
48 |
49 | //---------------------------------------------------------------------------------
50 | // Purpose: called once per server frame, do recurring work here (like checking for timeouts)
51 | //---------------------------------------------------------------------------------
52 | void RecordingHelpers::GameFrame( bool simulating )
53 | {
54 | }
55 |
56 | //---------------------------------------------------------------------------------
57 | // Purpose: called when the plugin is loaded, load the interface we need from the engine
58 | //---------------------------------------------------------------------------------
59 | bool RecordingHelpers::Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory )
60 | {
61 | g_pCvar = reinterpret_cast(interfaceFactory(CVAR_INTERFACE_VERSION,NULL));
62 |
63 | if(g_pCvar == NULL)
64 | {
65 | Warning("RecordingHelpers: Failed to get Cvar interface.\n");
66 | return false;
67 | }
68 |
69 | #ifdef L4D2
70 | DevMsg("Found g_pCVar at %08x\n", g_pCvar);
71 |
72 | g_pInput = GetGlobalIInput();
73 |
74 | DevMsg("Found g_pInput at %08x\n", g_pInput);
75 |
76 | PatchCInputPCZChecks(g_pInput);
77 |
78 | DevMsg("Patched Input Checks\n");
79 | #endif
80 | RemoveDevFlags();
81 |
82 | ConVar_Register(0, this);
83 |
84 | DevMsg("Registered CVars and Commands\n");
85 |
86 | Msg("RecordingHelpers Loaded Successfully.\n");
87 | return true;
88 | }
89 |
90 | //---------------------------------------------------------------------------------
91 | // Purpose: called when the plugin is unloaded (turned off)
92 | //---------------------------------------------------------------------------------
93 | void RecordingHelpers::Unload( void )
94 | {
95 | #ifdef L4D2
96 | UnpatchCInputPCZChecks(g_pInput);
97 | #endif
98 | ConVar_Unregister( );
99 | }
100 |
101 | //---------------------------------------------------------------------------------
102 | // Purpose: called when the plugin is paused (i.e should stop running but isn't unloaded)
103 | //---------------------------------------------------------------------------------
104 | void RecordingHelpers::Pause( void )
105 | {
106 | }
107 |
108 | //---------------------------------------------------------------------------------
109 | // Purpose: called when the plugin is unpaused (i.e should start executing again)
110 | //---------------------------------------------------------------------------------
111 | void RecordingHelpers::UnPause( void )
112 | {
113 | }
114 |
115 | //---------------------------------------------------------------------------------
116 | // Purpose: the name of this plugin, returned in "plugin_print" command
117 | //---------------------------------------------------------------------------------
118 | const char *RecordingHelpers::GetPluginDescription( void )
119 | {
120 | return "RecordingHelpers 0.6, ProdigySim";
121 | }
122 |
123 | //---------------------------------------------------------------------------------
124 | // Purpose: called on level start
125 | //---------------------------------------------------------------------------------
126 | void RecordingHelpers::LevelInit( char const *pMapName )
127 | {
128 | }
129 |
130 | //---------------------------------------------------------------------------------
131 | // Purpose: called on level start, when the server is ready to accept client connections
132 | // edictCount is the number of entities in the level, clientMax is the max client count
133 | //---------------------------------------------------------------------------------
134 | void RecordingHelpers::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
135 | {
136 | }
137 |
138 | //---------------------------------------------------------------------------------
139 | // Purpose: called on level end (as the server is shutting down or going to a new map)
140 | //---------------------------------------------------------------------------------
141 | void RecordingHelpers::LevelShutdown( void ) // !!!!this can get called multiple times per map change
142 | {
143 | }
144 |
145 | //---------------------------------------------------------------------------------
146 | // Purpose: called when a client spawns into a server (i.e as they begin to play)
147 | //---------------------------------------------------------------------------------
148 | void RecordingHelpers::ClientActive( edict_t *pEntity )
149 | {
150 | }
151 |
152 | void RecordingHelpers::ClientFullyConnect( edict_t *pEntity )
153 | {
154 | }
155 |
156 | //---------------------------------------------------------------------------------
157 | // Purpose: called when a client leaves a server (or is timed out)
158 | //---------------------------------------------------------------------------------
159 | void RecordingHelpers::ClientDisconnect( edict_t *pEntity )
160 | {
161 | }
162 |
163 | //---------------------------------------------------------------------------------
164 | // Purpose: called on
165 | //---------------------------------------------------------------------------------
166 | void RecordingHelpers::ClientPutInServer( edict_t *pEntity, char const *playername )
167 | {
168 | }
169 |
170 | //---------------------------------------------------------------------------------
171 | // Purpose: called on level start
172 | //---------------------------------------------------------------------------------
173 | void RecordingHelpers::SetCommandClient( int index )
174 | {
175 | }
176 |
177 | //---------------------------------------------------------------------------------
178 | // Purpose: called on level start
179 | //---------------------------------------------------------------------------------
180 | void RecordingHelpers::ClientSettingsChanged( edict_t *pEdict )
181 | {
182 | }
183 |
184 | //---------------------------------------------------------------------------------
185 | // Purpose: called when a client joins a server
186 | //---------------------------------------------------------------------------------
187 | PLUGIN_RESULT RecordingHelpers::ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
188 | {
189 | return PLUGIN_CONTINUE;
190 | }
191 |
192 | //---------------------------------------------------------------------------------
193 | // Purpose: called when a client types in a command (only a subset of commands however, not CON_COMMAND's)
194 | //---------------------------------------------------------------------------------
195 | PLUGIN_RESULT RecordingHelpers::ClientCommand( edict_t *pEntity, const CCommand &args )
196 | {
197 | return PLUGIN_CONTINUE;
198 | }
199 |
200 | //---------------------------------------------------------------------------------
201 | // Purpose: called when a client is authenticated
202 | //---------------------------------------------------------------------------------
203 | PLUGIN_RESULT RecordingHelpers::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
204 | {
205 | return PLUGIN_CONTINUE;
206 | }
207 |
208 | //---------------------------------------------------------------------------------
209 | // Purpose: called when a cvar value query is finished
210 | //---------------------------------------------------------------------------------
211 | void RecordingHelpers::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
212 | {
213 | }
214 | void RecordingHelpers::OnEdictAllocated( edict_t *edict )
215 | {
216 | }
217 | void RecordingHelpers::OnEdictFreed( const edict_t *edict )
218 | {
219 | }
220 |
221 | bool RecordingHelpers::RegisterConCommandBase( ConCommandBase *pVar )
222 | {
223 | pVar->SetNext(NULL);
224 | g_pCvar->RegisterConCommand(pVar);
225 | return true;
226 | }
227 |
228 | #ifdef L4D2
229 | IInput * GetGlobalIInput()
230 | {
231 | ConCommand * pCmdThirdPersonShoulder = g_pCvar->FindCommand("thirdpersonshoulder");
232 | if(pCmdThirdPersonShoulder == NULL)
233 | {
234 | Warning("Couldn't find thirdpersonshoulder command.\n");
235 | return NULL;
236 | }
237 | char * pAddr = (char*)pCmdThirdPersonShoulder->GetCallback();
238 | if(pAddr == NULL)
239 | {
240 | Warning("Couldn't read ThirdPersonShoulder callback\n");
241 | return NULL;
242 | }
243 |
244 | // First instruction of this command is mov ecx, offset input (g_pInput)
245 | // so to bytes into it is the IInput *
246 | return **(IInput***)(pAddr + 2);
247 | }
248 | #endif
249 |
250 | void RemoveDevFlags()
251 | {
252 | ICvar::Iterator iter(g_pCvar);
253 | for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() )
254 | {
255 | ConCommandBase *cmd = iter.Get();
256 | cmd->RemoveFlags(FCVAR_DEVELOPMENTONLY);
257 | }
258 | }
259 |
260 |
--------------------------------------------------------------------------------
/recording_helpers.h:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 sw=4 tw=99 noet :
3 | * =============================================================================
4 | * Recording Helpers
5 | * Copyright (C) 2012 ProdigySim
6 | * All rights reserved.
7 | * =============================================================================
8 | *
9 | * This program is free software; you can redistribute it and/or modify it under
10 | * the terms of the GNU General Public License, version 3.0, as published by the
11 | * Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful, but WITHOUT
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License along with
19 | * this program. If not, see .
20 | *
21 | * As a special exception, the authors give you permission to link the
22 | * code of this program (as well as its derivative works) to "Half-Life 2," the
23 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
24 | * by the Valve Corporation. You must obey the GNU General Public License in
25 | * all respects for all other code used. Additionally, AlliedModders LLC grants
26 | * this exception to all derivative works. AlliedModders LLC defines further
27 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
28 | * or .
29 | */
30 | #include
31 | #include "convar_sm_l4d.h"
32 | #include "eiface.h"
33 | #include "icvar.h"
34 | #include "tier1/iconvar.h"
35 | #include "input.h"
36 |
37 | class RecordingHelpers: public IServerPluginCallbacks, public IConCommandBaseAccessor
38 | {
39 | public:
40 | // IServerPluginCallbacks methods
41 | virtual bool Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory );
42 | virtual void Unload( void );
43 | virtual void Pause( void );
44 | virtual void UnPause( void );
45 | virtual const char *GetPluginDescription( void );
46 | virtual void LevelInit( char const *pMapName );
47 | virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax );
48 | virtual void GameFrame( bool simulating );
49 | virtual void LevelShutdown( void );
50 | virtual void ClientActive( edict_t *pEntity );
51 | virtual void ClientFullyConnect( edict_t *pEntity );
52 | virtual void ClientDisconnect( edict_t *pEntity );
53 | virtual void ClientPutInServer( edict_t *pEntity, char const *playername );
54 | virtual void SetCommandClient( int index );
55 | virtual void ClientSettingsChanged( edict_t *pEdict );
56 | virtual PLUGIN_RESULT ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen );
57 | virtual PLUGIN_RESULT ClientCommand( edict_t *pEntity, const CCommand &args );
58 | virtual PLUGIN_RESULT NetworkIDValidated( const char *pszUserName, const char *pszNetworkID );
59 | virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue );
60 |
61 | // added with version 3 of the interface.
62 | virtual void OnEdictAllocated( edict_t *edict );
63 | virtual void OnEdictFreed( const edict_t *edict );
64 |
65 | // IConCommandBaseAccessor
66 | virtual bool RegisterConCommandBase( ConCommandBase *pVar );
67 |
68 | };
69 |
70 |
71 | extern RecordingHelpers g_RecordingHelpersPlugin;
72 |
73 | extern ICvar * g_pCvar;
74 | #ifdef L4D2
75 | extern IInput * g_pInput;
76 | #endif
--------------------------------------------------------------------------------
/recording_helpers.vdf:
--------------------------------------------------------------------------------
1 | "Plugin"
2 | {
3 | "file" "addons/recording_helpers"
4 | }
5 |
--------------------------------------------------------------------------------
/recording_helpers_l4d1.vdf:
--------------------------------------------------------------------------------
1 | "Plugin"
2 | {
3 | "file" "../left4dead/addons/recording_helpers"
4 | }
5 |
--------------------------------------------------------------------------------
/thirdperson_patch.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 sw=4 tw=99 noet :
3 | * =============================================================================
4 | * Thirdperson Patch
5 | * Copyright (C) 2012 ProdigySim
6 | * All rights reserved.
7 | * =============================================================================
8 | *
9 | * This program is free software; you can redistribute it and/or modify it under
10 | * the terms of the GNU General Public License, version 3.0, as published by the
11 | * Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful, but WITHOUT
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License along with
19 | * this program. If not, see .
20 | *
21 | * As a special exception, the authors give you permission to link the
22 | * code of this program (as well as its derivative works) to "Half-Life 2," the
23 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
24 | * by the Valve Corporation. You must obey the GNU General Public License in
25 | * all respects for all other code used. Additionally, AlliedModders LLC grants
26 | * this exception to all derivative works. AlliedModders LLC defines further
27 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
28 | * or .
29 | */
30 | #include "recording_helpers.h"
31 | #include "thirdperson_patch.h"
32 | #include "memutils.h"
33 |
34 |
35 | typedef void (*FuncRvEv)(void);
36 | // Function returns bool expects void
37 | typedef bool (*Func_RbEv)(void);
38 | // IInput member func returns int expects int
39 | typedef union {
40 | int (IInput::*pFunc)(int);
41 | FuncRvEv pVoidFunc;
42 | void *pVoid;
43 | BYTE *pByte;
44 | } IInput_Func_RiEi;
45 | // IInput member func returns void expects void
46 | typedef union {
47 | void (IInput::*pFunc)(void);
48 | FuncRvEv pVoidFunc;
49 | void * pVoid;
50 | BYTE * pByte;
51 | } IInput_Func_RvEv;
52 |
53 |
54 |
55 | static IInput_Func_RvEv g_fpCInput_CAM_Think = {NULL};
56 | static IInput_Func_RiEi g_fpCInput_CAM_IsThirdPerson = {NULL};
57 | static IInput_Func_RvEv g_fpCInput_CAM_ToThirdPersonShoulder = {NULL};
58 |
59 | static IInput_Func_RiEi g_fpHaxed_CAM_IsThirdPerson = {NULL};
60 | static IInput_Func_RvEv g_fpHaxed_CAM_ToThirdPersonShoulder = {NULL};
61 | static IInput_Func_RvEv g_fpHaxed_CAM_Think = {NULL};
62 |
63 | static CInput_vtable g_HaxedVtable;
64 | static CInput_vtable * g_OriginalVtable = NULL;
65 |
66 |
67 | static IInput_Func_RiEi GenerateCAM_IsThirdPersonFunc(IInput_Func_RiEi origCAM_IsThirdPerson)
68 | {
69 | static const int PCZCheckOffset = 6;
70 | IInput_Func_RiEi newFunc;
71 |
72 | newFunc.pByte = g_MemUtils.CloneFunction(origCAM_IsThirdPerson.pByte);
73 | if(newFunc.pByte == NULL)
74 | {
75 | Warning("Couldn't clone CAM_IsThirdPerson function.\n");
76 | return newFunc;
77 | }
78 |
79 | if(newFunc.pByte[PCZCheckOffset] != 0xE8)
80 | {
81 | Warning("PCZ Check offset in CAM_IsThirdPerson incorrect.\n");
82 | goto fail;
83 | }
84 |
85 | // Patch out the PCZCheck in our new function.
86 | newFunc.pByte[PCZCheckOffset] = '\x33'; // XOR m32,m32
87 | newFunc.pByte[PCZCheckOffset+1] = '\xC0'; // eax, eax
88 | // 3 byte NOP
89 | newFunc.pByte[PCZCheckOffset+2] = '\x0F';
90 | newFunc.pByte[PCZCheckOffset+3] = '\x1F';
91 | newFunc.pByte[PCZCheckOffset+4] = '\x00';
92 |
93 | return newFunc;
94 |
95 | fail:
96 | free(newFunc.pByte);
97 | newFunc.pByte=NULL;
98 | return newFunc;
99 | }
100 |
101 | static ConVar * c_thirdpersonshoulder = NULL;
102 |
103 | static bool checkCThirdPersonShoulder()
104 | {
105 | return c_thirdpersonshoulder->GetBool();
106 | }
107 |
108 | static IInput_Func_RvEv GenerateCAM_ToThirdPersonShoulderFunc(IInput_Func_RvEv origCAM_ToThirdPersonShoulder)
109 | {
110 | static const int PCZCheckOffset = 10;
111 | IInput_Func_RvEv newFunc;
112 |
113 | newFunc.pByte = g_MemUtils.CloneFunction(origCAM_ToThirdPersonShoulder.pByte);
114 | if(newFunc.pByte == NULL)
115 | {
116 | Warning("Couldn't clone CAM_ToThirdPersonShoulder function.\n");
117 | return newFunc;
118 | }
119 |
120 | if(newFunc.pByte[PCZCheckOffset] != 0xE8)
121 | {
122 | Warning("PCZ Check offset in CAM_ToThirdPersonShoulder incorrect.\n");
123 | goto fail;
124 | }
125 | // Patch out the PCZCheck in our new function.
126 | newFunc.pByte[PCZCheckOffset] = '\x33'; // XOR m32,m32
127 | newFunc.pByte[PCZCheckOffset+1] = '\xC0'; // eax, eax
128 | // 3 byte NOP
129 | newFunc.pByte[PCZCheckOffset+2] = '\x0F';
130 | newFunc.pByte[PCZCheckOffset+3] = '\x1F';
131 | newFunc.pByte[PCZCheckOffset+4] = '\x00';
132 |
133 | return newFunc;
134 |
135 | fail:
136 | free(newFunc.pByte);
137 | newFunc.pByte=NULL;
138 | return newFunc;
139 | }
140 |
141 | static IInput_Func_RvEv GenerateCAM_ThinkFunc(IInput_Func_RvEv origCAM_Think)
142 | {
143 | static const int PCZCheckFuncOffset = 161;
144 | IInput_Func_RvEv newFunc = {NULL};
145 | c_thirdpersonshoulder = g_pCvar->FindVar("c_thirdpersonshoulder");
146 | if(c_thirdpersonshoulder == NULL)
147 | {
148 | Warning("Couldn't find c_thirdpersonshoulder cvar.\n");
149 | return newFunc;
150 | }
151 |
152 | newFunc.pByte = g_MemUtils.CloneFunction(origCAM_Think.pByte);
153 | if(newFunc.pByte == NULL)
154 | {
155 | Warning("Couldn't clone CAM_Think function.\n");
156 | return newFunc;
157 | }
158 |
159 | if(newFunc.pByte[PCZCheckFuncOffset] != 0xE8)
160 | {
161 | Warning("PCZ Check Func offset in CAM_Think incorrect.\n");
162 | goto fail;
163 | }
164 |
165 | // win32 has a subfunction that checks Hasplayercontrolledzombies + c_thirdpersonshoulder var.
166 | // We replace this with our function which just checks c_thirdpersonshoulder
167 |
168 | // Calculate call jump offset
169 | int jumpOffset = g_MemUtils.GetCallOrJumpRelOffset(&newFunc.pByte[PCZCheckFuncOffset], reinterpret_cast(checkCThirdPersonShoulder));
170 |
171 | // Replace it
172 | *reinterpret_cast(&newFunc.pByte[PCZCheckFuncOffset+1]) = jumpOffset;
173 |
174 | return newFunc;
175 | fail:
176 | free(newFunc.pByte);
177 | newFunc.pByte=NULL;
178 | return newFunc;
179 | }
180 |
181 | void PatchCInputPCZChecks(IInput *input)
182 | {
183 | CInput_vtable ** pInputInstance = reinterpret_cast(input);
184 | CInput_vtable * pInputVtable = *pInputInstance;
185 |
186 | // Copy the old vtable over to our vtable as a starting point
187 | memcpy(&g_HaxedVtable, pInputVtable, sizeof(CInput_vtable));
188 |
189 | // Copy old function pointers from vtable
190 | g_fpCInput_CAM_Think.pFunc = pInputVtable->IInput.CAM_Think;
191 | g_fpCInput_CAM_IsThirdPerson.pFunc = pInputVtable->IInput.CAM_IsThirdPerson;
192 | g_fpCInput_CAM_ToThirdPersonShoulder.pFunc = pInputVtable->IInput.CAM_ToThirdPersonShoulder;
193 |
194 | // Dev output checks
195 | DevMsg("CInput::CAM_Think(): %08x\n", g_fpCInput_CAM_Think);
196 | DevMsg("CInput::CAM_IsThirdPerson(): %08x\n", g_fpCInput_CAM_IsThirdPerson);
197 | DevMsg("CInput::CAM_ToThirdPersonShoulder(): %08x\n", g_fpCInput_CAM_ToThirdPersonShoulder);
198 |
199 | // Backup original vtable
200 | g_OriginalVtable = pInputVtable;
201 |
202 | // Start customizing our vtable
203 |
204 | g_fpHaxed_CAM_IsThirdPerson = GenerateCAM_IsThirdPersonFunc(g_fpCInput_CAM_IsThirdPerson);
205 | if(g_fpHaxed_CAM_IsThirdPerson.pVoid == NULL) goto fail;
206 | g_HaxedVtable.IInput.CAM_IsThirdPerson = g_fpHaxed_CAM_IsThirdPerson.pFunc;
207 |
208 | g_fpHaxed_CAM_ToThirdPersonShoulder = GenerateCAM_ToThirdPersonShoulderFunc(g_fpCInput_CAM_ToThirdPersonShoulder);
209 | if(g_fpHaxed_CAM_ToThirdPersonShoulder.pVoid == NULL) goto fail;
210 | g_HaxedVtable.IInput.CAM_ToThirdPersonShoulder = g_fpHaxed_CAM_ToThirdPersonShoulder.pFunc;
211 |
212 | g_fpHaxed_CAM_Think = GenerateCAM_ThinkFunc(g_fpCInput_CAM_Think);
213 | if(g_fpHaxed_CAM_Think.pVoid == NULL) goto fail;
214 | g_HaxedVtable.IInput.CAM_Think = g_fpHaxed_CAM_Think.pFunc;
215 |
216 | // Dev output checks
217 | DevMsg("Haxed CAM_IsThirdPerson(): %08x\n", g_fpHaxed_CAM_IsThirdPerson.pVoid);
218 | DevMsg("Haxed CAM_ToThirdPersonShoulder(): %08x\n", g_fpHaxed_CAM_ToThirdPersonShoulder.pVoid);
219 | DevMsg("Haxed CAM_Think(): %08x\n", g_fpHaxed_CAM_Think.pVoid);
220 |
221 | // Override the global CInput instance's vtable with our haxed one
222 | *pInputInstance = &g_HaxedVtable;
223 |
224 | return;
225 |
226 | fail:
227 | Warning("Failed to Patch Thirdperson function calls. Bailing out.\n");
228 | UnpatchCInputPCZChecks(input);
229 | }
230 |
231 | void UnpatchCInputPCZChecks(IInput *input)
232 | {
233 | CInput_vtable ** pInputInstance = reinterpret_cast(input);
234 | if(g_OriginalVtable != NULL)
235 | {
236 | *pInputInstance = g_OriginalVtable;
237 | }
238 |
239 | if(g_fpHaxed_CAM_IsThirdPerson.pVoid != NULL)
240 | {
241 | free(g_fpHaxed_CAM_IsThirdPerson.pVoid);
242 | }
243 |
244 | if(g_fpHaxed_CAM_ToThirdPersonShoulder.pVoid != NULL)
245 | {
246 | free(g_fpHaxed_CAM_ToThirdPersonShoulder.pVoid);
247 | }
248 |
249 | if(g_fpHaxed_CAM_Think.pVoid != NULL)
250 | {
251 | free(g_fpHaxed_CAM_Think.pVoid);
252 | }
253 | }
254 |
255 |
256 | // Can't do this because of CEG.
257 | // Hence curse words in rest of code.
258 | /*
259 | const char g_sCInput_CamToThirdPersonShoulder_pattern[] = "\x56\x6A\xFF\xE8\x2A\x2A\x2A\x2A\x8B\xF0\xE8\x2A\x2A\x2A\x2A\x84\xC0\x74\x07\xA0\x2A\x2A\x2A\x2A\xEB\x0C\xA1\x2A\x2A\x2A\x2A\x83\x78\x30\x00\x0F\x95\xC0\x84\xC0\xB9";
260 | const int g_iPatchPCZCheckOffset = 10;
261 | unsigned char g_sPCZBackup[5];
262 | const char g_sPCZPatch[] = "\x31\xC0\x90\x90\x90";
263 |
264 | bool PatchThirdpersonShoulderPCZCheck(char * client)
265 | {
266 | char *pAddr=NULL;
267 | pAddr = (char*)g_MemUtils.FindPattern(client-0x300000, client+0x300000, g_sCInput_CamToThirdPersonShoulder_pattern, sizeof(g_sCInput_CamToThirdPersonShoulder_pattern)-1);
268 | if(pAddr == NULL)
269 | {
270 | Warning("Couldn't find thirdpersonshoulder function!\n");
271 | return false;
272 | }
273 |
274 | pAddr+=g_iPatchPCZCheckOffset;
275 | memcpy(g_sPCZBackup, pAddr, 5);
276 | DevMsg("Old Patch: %02x%02x%02x%02x%02x\n", g_sPCZBackup[0], g_sPCZBackup[1], g_sPCZBackup[2], g_sPCZBackup[3], g_sPCZBackup[4]);
277 |
278 | if(!g_MemUtils.SetMemPatchable(pAddr,5))
279 | {
280 | Warning("Failed to set memory patchable in thirdpersonshoulder function!\n");
281 | return false;
282 | }
283 | memcpy(pAddr, g_sPCZPatch, 5);
284 |
285 | return true;
286 | };
287 | */
--------------------------------------------------------------------------------
/thirdperson_patch.h:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 sw=4 tw=99 noet :
3 | * =============================================================================
4 | * Thirdperson Patch
5 | * Copyright (C) 2012 ProdigySim
6 | * All rights reserved.
7 | * =============================================================================
8 | *
9 | * This program is free software; you can redistribute it and/or modify it under
10 | * the terms of the GNU General Public License, version 3.0, as published by the
11 | * Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful, but WITHOUT
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 | * details.
17 | *
18 | * You should have received a copy of the GNU General Public License along with
19 | * this program. If not, see .
20 | *
21 | * As a special exception, the authors give you permission to link the
22 | * code of this program (as well as its derivative works) to "Half-Life 2," the
23 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
24 | * by the Valve Corporation. You must obey the GNU General Public License in
25 | * all respects for all other code used. Additionally, AlliedModders LLC grants
26 | * this exception to all derivative works. AlliedModders LLC defines further
27 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
28 | * or .
29 | */
30 | #ifndef THIRDPERSON_PATCH_H
31 | #define THIRDPERSON_PATCH_H
32 |
33 | #include "input.h"
34 |
35 | // Patch out the PCZ checks in CInput Thirdperson code
36 | void PatchCInputPCZChecks(IInput *input);
37 | // Undo patching of CInput Thirdperson code
38 | void UnpatchCInputPCZChecks(IInput *input);
39 |
40 |
41 |
42 | // attempt to patch the PCZ check out of Thirdpersonshoulder command
43 | //bool PatchThirdpersonShoulderPCZCheck(char * client);
44 |
45 | #endif
--------------------------------------------------------------------------------