├── .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 --------------------------------------------------------------------------------