# HG changeset patch # User Nicholas Hastings # Date 1344650043 14400 # Node ID d3fd010be8fef53be7f10b5583ed0368d2022bac # Parent 1b0eeb2823adff7954e8228cd2e873fb227466cc# Parent d3a2fb7b3a1eba96321225e0fa89932158c17707 Merge. diff -r 1b0eeb2823ad -r d3fd010be8fe configs/core.cfg --- a/configs/core.cfg Fri Aug 10 21:49:40 2012 -0400 +++ b/configs/core.cfg Fri Aug 10 21:54:03 2012 -0400 @@ -115,4 +115,14 @@ * In general, this option should be set to "yes" to increase the security of your server. */ "SteamAuthstringValidation" "yes" + + /** + * Enables or disables whether SourceMod blocks known or potentially malicious plugins from loading. + * It is STRONGLY advised that this is left enabled, there have been cases in the past with plugins that + * allow anyone to delete files on the server, gain full rcon control, etc. + * + * "yes" - Block malware or illegal plugins from loading (default) + * "no" - Warn about malware or illegal plugins loading + */ + "BlockBadPlugins" "yes" } diff -r 1b0eeb2823ad -r d3fd010be8fe core/PluginSys.cpp --- a/core/PluginSys.cpp Fri Aug 10 21:49:40 2012 -0400 +++ b/core/PluginSys.cpp Fri Aug 10 21:54:03 2012 -0400 @@ -789,6 +789,8 @@ m_AllPluginsLoaded = false; m_MyIdent = NULL; m_LoadingLocked = false; + + m_bBlockBadPlugins = true; } CPluginManager::~CPluginManager() @@ -1030,6 +1032,41 @@ } } } + + if (pPlugin->GetStatus() == Plugin_Created) + { + unsigned char *pCodeHash = pPlugin->m_pRuntime->GetCodeHash(); + + char codeHashBuf[40]; + UTIL_Format(codeHashBuf, 40, "plugin_"); + for (int i = 0; i < 16; i++) + UTIL_Format(codeHashBuf + 7 + (i * 2), 3, "%02x", pCodeHash[i]); + + const char *bulletinUrl = g_pGameConf->GetKeyValue(codeHashBuf); + if (bulletinUrl != NULL) + { + if (m_bBlockBadPlugins) + { + if (error) + { + if (bulletinUrl[0] != '\0') + { + UTIL_Format(error, maxlength, "Known malware detected and blocked. See %s for more info", bulletinUrl); + } else { + UTIL_Format(error, maxlength, "Possible malware or illegal plugin detected and blocked"); + } + } + pPlugin->m_status = Plugin_BadLoad; + } else { + if (bulletinUrl[0] != '\0') + { + g_Logger.LogMessage("%s: Known malware detected. See %s for more info, blocking disabled in core.cfg", pPlugin->GetFilename(), bulletinUrl); + } else { + g_Logger.LogMessage("%s: Possible malware or illegal plugin detected, blocking disabled in core.cfg", pPlugin->GetFilename()); + } + } + } + } LoadRes loadFailure = LoadRes_Failure; /* Get the status */ @@ -1129,7 +1166,7 @@ if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure) { - g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error); + g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s.", plugin, error); pl->SetErrorState( pl->GetStatus() <= Plugin_Created ? Plugin_BadLoad : pl->GetStatus(), "%s", @@ -1889,6 +1926,27 @@ g_ShareSys.DestroyIdentity(m_MyIdent); } +ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key, + const char *value, + ConfigSource source, + char *error, + size_t maxlength) +{ + if (strcmp(key, "BlockBadPlugins") == 0) { + if (strcasecmp(value, "yes") == 0) + { + m_bBlockBadPlugins = true; + } else if (strcasecmp(value, "no") == 0) { + m_bBlockBadPlugins = false; + } else { + UTIL_Format(error, maxlength, "Invalid value: must be \"yes\" or \"no\""); + return ConfigResult_Reject; + } + return ConfigResult_Accept; + } + return ConfigResult_Ignore; +} + void CPluginManager::OnHandleDestroy(HandleType_t type, void *object) { /* We don't care about the internal object, actually */ @@ -2269,6 +2327,15 @@ { g_RootMenu.ConsolePrint(" Timestamp: %s", pl->m_DateTime); } + + unsigned char *pCodeHash = pl->m_pRuntime->GetCodeHash(); + unsigned char *pDataHash = pl->m_pRuntime->GetDataHash(); + + char combinedHash[33]; + for (int i = 0; i < 16; i++) + UTIL_Format(combinedHash + (i * 2), 3, "%02x", pCodeHash[i] ^ pDataHash[i]); + + g_RootMenu.ConsolePrint(" Hash: %s", combinedHash); } else { diff -r 1b0eeb2823ad -r d3fd010be8fe core/PluginSys.h --- a/core/PluginSys.h Fri Aug 10 21:49:40 2012 -0400 +++ b/core/PluginSys.h Fri Aug 10 21:54:03 2012 -0400 @@ -57,6 +57,7 @@ #include "convar_sm.h" #endif #include "ITranslator.h" +#include "IGameConfigs.h" #include "NativeOwner.h" #include "ShareSys.h" @@ -326,6 +327,7 @@ public: //SMGlobalClass void OnSourceModAllInitialized(); void OnSourceModShutdown(); + ConfigResult OnSourceModConfigChanged(const char *key, const char *value, ConfigSource source, char *error, size_t maxlength); void OnSourceModMaxPlayersChanged(int newvalue); public: //IHandleTypeDispatch void OnHandleDestroy(HandleType_t type, void *object); @@ -470,6 +472,9 @@ List m_Natives; bool m_LoadingLocked; + + // Config + bool m_bBlockBadPlugins; }; extern CPluginManager g_PluginSys; diff -r 1b0eeb2823ad -r d3fd010be8fe gamedata/core.games/blacklist.plugins.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gamedata/core.games/blacklist.plugins.txt Fri Aug 10 21:54:03 2012 -0400 @@ -0,0 +1,16 @@ +/** + * Do not edit this file. Any changes will be overwritten by the gamedata + * updater or by upgrading your SourceMod install. + * + * For more information, see http://wiki.alliedmods.net/Gamedata_Updating_(SourceMod) + */ + +"Games" +{ + "#default" + { + "Keys" + { + } + } +} diff -r 1b0eeb2823ad -r d3fd010be8fe gamedata/core.games/master.games.txt --- a/gamedata/core.games/master.games.txt Fri Aug 10 21:49:40 2012 -0400 +++ b/gamedata/core.games/master.games.txt Fri Aug 10 21:54:03 2012 -0400 @@ -65,4 +65,7 @@ "engine" "csgo" } + "blacklist.plugins.txt" + { + } } diff -r 1b0eeb2823ad -r d3fd010be8fe plugins/include/tf2_stocks.inc --- a/plugins/include/tf2_stocks.inc Fri Aug 10 21:49:40 2012 -0400 +++ b/plugins/include/tf2_stocks.inc Fri Aug 10 21:54:03 2012 -0400 @@ -231,7 +231,7 @@ TF_WEAPON_GRENADE_CLEAVER, }; -// TF2 Weapon Slots for GetPlayerWeaponSlot +// TF2 Weapon Loadout Slots enum { TFWeaponSlot_Primary, diff -r 1b0eeb2823ad -r d3fd010be8fe public/sourcepawn/sp_vm_api.h --- a/public/sourcepawn/sp_vm_api.h Fri Aug 10 21:49:40 2012 -0400 +++ b/public/sourcepawn/sp_vm_api.h Fri Aug 10 21:54:03 2012 -0400 @@ -40,7 +40,7 @@ /** SourcePawn Engine API Version */ #define SOURCEPAWN_ENGINE_API_VERSION 4 -#define SOURCEPAWN_ENGINE2_API_VERSION 3 +#define SOURCEPAWN_ENGINE2_API_VERSION 4 #if !defined SOURCEMOD_BUILD #define SOURCEMOD_BUILD @@ -479,6 +479,20 @@ * @return Memory usage, in bytes. */ virtual size_t GetMemUsage() =0; + + /** + * @brief Returns the MD5 hash of the plugin's P-Code. + * + * @return 16-byte buffer with MD5 hash of the plugin's P-Code. + */ + virtual unsigned char *GetCodeHash() =0; + + /** + * @brief Returns the MD5 hash of the plugin's Data. + * + * @return 16-byte buffer with MD5 hash of the plugin's Data. + */ + virtual unsigned char *GetDataHash() =0; }; /** diff -r 1b0eeb2823ad -r d3fd010be8fe sourcepawn/jit/AMBuilder --- a/sourcepawn/jit/AMBuilder Fri Aug 10 21:49:40 2012 -0400 +++ b/sourcepawn/jit/AMBuilder Fri Aug 10 21:54:03 2012 -0400 @@ -36,6 +36,7 @@ 'zlib/trees.c', 'zlib/uncompr.c', 'zlib/zutil.c', + 'md5/md5.cpp', '../../knight/shared/KeCodeAllocator.cpp' ]) SM.AutoVersion('sourcepawn/jit', binary) diff -r 1b0eeb2823ad -r d3fd010be8fe sourcepawn/jit/BaseRuntime.cpp --- a/sourcepawn/jit/BaseRuntime.cpp Fri Aug 10 21:49:40 2012 -0400 +++ b/sourcepawn/jit/BaseRuntime.cpp Fri Aug 10 21:54:03 2012 -0400 @@ -8,6 +8,8 @@ #include "sp_vm_basecontext.h" #include "engine2.h" +#include "md5/md5.h" + using namespace SourcePawn; BaseRuntime::BaseRuntime() : m_Debug(&m_plugin), m_pPlugin(&m_plugin), m_pCtx(NULL), @@ -18,6 +20,9 @@ m_FuncCache = NULL; m_MaxFuncs = 0; m_NumFuncs = 0; + + memset(m_CodeHash, 0, sizeof(m_CodeHash)); + memset(m_DataHash, 0, sizeof(m_DataHash)); } BaseRuntime::~BaseRuntime() @@ -227,6 +232,16 @@ memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_pPlugin->num_publics); } + MD5 md5_pcode; + md5_pcode.update(plugin->pcode, plugin->pcode_size); + md5_pcode.finalize(); + md5_pcode.raw_digest(m_CodeHash); + + MD5 md5_data; + md5_data.update(plugin->data, plugin->data_size); + md5_data.finalize(); + md5_data.raw_digest(m_DataHash); + m_pPlugin->profiler = g_engine2.GetProfiler(); m_pCtx = new BaseContext(this); m_pCo = g_Jit.StartCompilation(this); @@ -483,6 +498,16 @@ return mem; } +unsigned char *BaseRuntime::GetCodeHash() +{ + return m_CodeHash; +} + +unsigned char *BaseRuntime::GetDataHash() +{ + return m_DataHash; +} + BaseContext *BaseRuntime::GetBaseContext() { return m_pCtx; diff -r 1b0eeb2823ad -r d3fd010be8fe sourcepawn/jit/BaseRuntime.h --- a/sourcepawn/jit/BaseRuntime.h Fri Aug 10 21:49:40 2012 -0400 +++ b/sourcepawn/jit/BaseRuntime.h Fri Aug 10 21:54:03 2012 -0400 @@ -48,6 +48,8 @@ virtual void SetPauseState(bool paused); virtual bool IsPaused(); virtual size_t GetMemUsage(); + virtual unsigned char *GetCodeHash(); + virtual unsigned char *GetDataHash(); JitFunction *GetJittedFunction(uint32_t idx); uint32_t AddJittedFunction(JitFunction *fn); public: @@ -65,6 +67,9 @@ JitFunction **m_PubJitFuncs; ICompilation *m_pCo; unsigned int m_CompSerial; + + unsigned char m_CodeHash[16]; + unsigned char m_DataHash[16]; }; #endif //_INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_ diff -r 1b0eeb2823ad -r d3fd010be8fe sourcepawn/jit/md5/md5.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sourcepawn/jit/md5/md5.cpp Fri Aug 10 21:54:03 2012 -0400 @@ -0,0 +1,476 @@ +// MD5.CC - source code for the C++/object oriented translation and +// modification of MD5. + +// Translation and modification (c) 1995 by Mordechai T. Abzug + +// This translation/ modification is provided "as is," without express or +// implied warranty of any kind. + +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially +// copied from the disclaimer below). + +/* based on: + + MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + MDDRIVER.C - test driver for MD2, MD4 and MD5 + + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + + */ + +#include "md5.h" + +#include +#include + +// MD5 simple initialization method + +MD5::MD5(){ + + init(); + +} + + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block, and updating the +// context. + +void MD5::update (uint1 *input, uint4 input_length) { + + uint4 input_index, buffer_index; + uint4 buffer_space; // how much space is left in buffer + + if (finalized){ // so we can't update! + /*cerr << "MD5::update: Can't update a finalized digest!" << endl;*/ + return; + } + + // Compute number of bytes mod 64 + buffer_index = (unsigned int)((count[0] >> 3) & 0x3F); + + // Update number of bits + if ( (count[0] += ((uint4) input_length << 3))<((uint4) input_length << 3) ) + count[1]++; + + count[1] += ((uint4)input_length >> 29); + + + buffer_space = 64 - buffer_index; // how much space is left in buffer + + // Transform as many times as possible. + if (input_length >= buffer_space) { // ie. we have enough to fill the buffer + // fill the rest of the buffer and transform + memcpy (buffer + buffer_index, input, buffer_space); + transform (buffer); + + // now, transform each 64-byte piece of the input, bypassing the buffer + for (input_index = buffer_space; input_index + 63 < input_length; + input_index += 64) + transform (input+input_index); + + buffer_index = 0; // so we can buffer remaining + } + else + input_index=0; // so we can buffer the whole input + + + // and here we do the buffering: + memcpy(buffer+buffer_index, input+input_index, input_length-input_index); +} + + + +// MD5 update for files. +// Like above, except that it works on files (and uses above as a primitive.) + +void MD5::update(FILE *file){ + + unsigned char buffer[1024]; + int len; + + while ((len=fread(buffer, 1, 1024, file))) + update(buffer, len); + + fclose (file); + +} + + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. + + +void MD5::finalize (){ + + unsigned char bits[8]; + unsigned int index, padLen; + static uint1 PADDING[64]={ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (finalized){ + /* cerr << "MD5::finalize: Already finalized this digest!" << endl;*/ + return; + } + + // Save number of bits + encode (bits, count, 8); + + // Pad out to 56 mod 64. + index = (uint4) ((count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + update (PADDING, padLen); + + // Append length (before padding) + update (bits, 8); + + // Store state in digest + encode (digest, state, 16); + + // Zeroize sensitive information + memset (buffer, 0, sizeof(*buffer)); + + finalized=1; + +} + + + + +MD5::MD5(FILE *file){ + + init(); // must be called be all constructors + update(file); + finalize (); +} + +unsigned char *MD5::raw_digest(){ + + uint1 *s = new uint1[16]; + + if (!finalized){ +/* cerr << "MD5::raw_digest: Can't get digest if you haven't "<< + "finalized the digest!" <> 8) & 0xff); + output[j+2] = (uint1) ((input[i] >> 16) & 0xff); + output[j+3] = (uint1) ((input[i] >> 24) & 0xff); + } +} + + + + +// Decodes input (unsigned char) into output (UINT4). Assumes len is +// a multiple of 4. +void MD5::decode (uint4 *output, uint1 *input, uint4 len){ + + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | + (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); +} + + + + + +// Note: Replace "for loop" with standard memcpy if possible. +void MD5::memcpy (uint1 *output, uint1 *input, uint4 len){ + + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + + + +// Note: Replace "for loop" with standard memset if possible. +void MD5::memset (uint1 *output, uint1 value, uint4 len){ + + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = value; +} + + + +// ROTATE_LEFT rotates x left n bits. + +inline unsigned int MD5::rotate_left (uint4 x, uint4 n){ + return (x << n) | (x >> (32-n)) ; +} + + + + +// F, G, H and I are basic MD5 functions. + +inline unsigned int MD5::F (uint4 x, uint4 y, uint4 z){ + return (x & y) | (~x & z); +} + +inline unsigned int MD5::G (uint4 x, uint4 y, uint4 z){ + return (x & z) | (y & ~z); +} + +inline unsigned int MD5::H (uint4 x, uint4 y, uint4 z){ + return x ^ y ^ z; +} + +inline unsigned int MD5::I (uint4 x, uint4 y, uint4 z){ + return y ^ (x | ~z); +} + + + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. + + +inline void MD5::FF(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += F(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void MD5::GG(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += G(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void MD5::HH(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += H(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void MD5::II(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += I(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} diff -r 1b0eeb2823ad -r d3fd010be8fe sourcepawn/jit/md5/md5.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sourcepawn/jit/md5/md5.h Fri Aug 10 21:54:03 2012 -0400 @@ -0,0 +1,106 @@ +// MD5.CC - source code for the C++/object oriented translation and +// modification of MD5. + +// Translation and modification (c) 1995 by Mordechai T. Abzug + +// This translation/ modification is provided "as is," without express or +// implied warranty of any kind. + +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially +// copied from the disclaimer below). + +/* based on: + + MD5.H - header file for MD5C.C + MDDRIVER.C - test driver for MD2, MD4 and MD5 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#include +//#include +//#include + +class MD5 { + +public: +// methods for controlled operation: + MD5 (); // simple initializer + void update (unsigned char *input, unsigned int input_length); + void update (FILE *file); + void finalize (); + +// constructors for special circumstances. All these constructors finalize +// the MD5 context. + MD5 (unsigned char *string); // digest string, finalize + MD5 (FILE *file); // digest file, close, finalize + +// methods to acquire finalized result + unsigned char *raw_digest (); // digest as a 16-byte binary array + unsigned char *raw_digest(unsigned char buffer[16]); + char * hex_digest (); // digest as a 33-byte ascii-hex string + char * hex_digest (char buffer[33]); //same as above, passing buffer + + + +private: + +// first, some types: + typedef unsigned int uint4; // assumes integer is 4 words long + typedef unsigned short int uint2; // assumes short integer is 2 words long + typedef unsigned char uint1; // assumes char is 1 word long + +// next, the private data: + uint4 state[4]; + uint4 count[2]; // number of *bits*, mod 2^64 + uint1 buffer[64]; // input buffer + uint1 digest[16]; + uint1 finalized; + +// last, the private methods, mostly static: + void init (); // called by all constructors + void transform (uint1 *buffer); // does the real update work. Note + // that length is implied to be 64. + + static void encode (uint1 *dest, uint4 *src, uint4 length); + static void decode (uint4 *dest, uint1 *src, uint4 length); + static void memcpy (uint1 *dest, uint1 *src, uint4 length); + static void memset (uint1 *start, uint1 val, uint4 length); + + static inline uint4 rotate_left (uint4 x, uint4 n); + static inline uint4 F (uint4 x, uint4 y, uint4 z); + static inline uint4 G (uint4 x, uint4 y, uint4 z); + static inline uint4 H (uint4 x, uint4 y, uint4 z); + static inline uint4 I (uint4 x, uint4 y, uint4 z); + static inline void FF (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void GG (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void HH (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void II (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + +}; diff -r 1b0eeb2823ad -r d3fd010be8fe tools/ida_scripts/vtable_dump.py --- a/tools/ida_scripts/vtable_dump.py Fri Aug 10 21:49:40 2012 -0400 +++ b/tools/ida_scripts/vtable_dump.py Fri Aug 10 21:54:03 2012 -0400 @@ -18,6 +18,79 @@ import re +""" +Output Format:" + VTable for <1>: (<2>, <3>) + Lin Win Function + <4><5> <6> <7> + <4><5> <6> <7> + ... + <4><5> <6> <7> + +1: Classname +2: VTable Offset +3: Linux This Pointer Offset +4: "T" if the function is a MI thunk +5: Linux VTable Index +6: Windows VTable Index +7: Function Signature +""" + +catchclass = False +innerclass = "" + +classname = None +offsetdata = {} + +def ExtractTypeInfo(ea, level = 0): + global catchclass + global innerclass + global classname + global offsetdata + + # Param needed to support old IDAPython versions + end = NextHead(ea, 4294967295) + + # Skip vtable + ea += 4 + + # Get type name + name = Demangle("_Z" + GetString(Dword(ea)), GetLongPrm(INF_LONG_DN)) + ea += 4 + + if classname is None and level == 0: + classname = name + + if catchclass: + innerclass = name + catchclass = False + + print "%*s%s" % (level, "", name) + + if not ea < end: # Base Type + pass + elif isData(GetFlags(Dword(ea))): # Single Inheritance + ExtractTypeInfo(Dword(ea), level + 1) + ea += 4 + else: # Multiple Inheritance + ea += 8 + while ea < end: + catchclass = True + ExtractTypeInfo(Dword(ea), level + 1) + ea += 4 + offset = Dword(ea) + ea += 4 + #print "%*s Offset: 0x%06X" % (level, "", offset >> 8) + if (offset >> 8) != 0: + offsetdata[offset >> 8] = innerclass + +# Source: http://stackoverflow.com/a/9147327 +def twos_comp(val, bits): + """compute the 2's compliment of int value val""" + if (val & (1 << (bits - 1))) != 0: + val = val - (1 << bits) + return val + def Analyze(): SetStatus(IDA_STATUS_WORK) @@ -29,7 +102,8 @@ ea = ScreenEA() if not isHead(GetFlags(ea)): - ea = PrevHead(ea) + # Param needed to support old IDAPython versions + ea = PrevHead(ea, 0) # Param needed to support old IDAPython versions end = NextHead(ea, 4294967295) @@ -43,18 +117,23 @@ linux_vtable = [] temp_windows_vtable = [] + other_linux_vtables = {} + other_thunk_linux_vtables = {} + temp_other_windows_vtables = {} + # Extract vtable while ea < end: - offset = Dword(ea) + # Read thisoffs + offset = -twos_comp(Dword(ea), 32) + ea += 4 - # A vtable starts with some metadata, if it's missing... - if isCode(GetFlags(offset)): - Warning("Something went wrong!") - SetStatus(IDA_STATUS_READY) - return + # Read typeinfo address + typeinfo = Dword(ea) + ea += 4 - # Skip thisoffs and typeinfo address - ea += 8 + if offset == 0: # We only need to read this once + print "Inheritance Tree:" + ExtractTypeInfo(typeinfo) while ea < end and isCode(GetFlags(Dword(ea))): name = Demangle(Name(Dword(ea)), GetLongPrm(INF_LONG_DN)) @@ -63,9 +142,22 @@ linux_vtable.append(name) temp_windows_vtable.append(name) else: + if offset not in other_linux_vtables: + other_linux_vtables[offset] = [] + temp_other_windows_vtables[offset] = [] + other_thunk_linux_vtables[offset] = [] + + if "`non-virtual thunk to'" in name: + other_linux_vtables[offset].append(name[22:]) + other_thunk_linux_vtables[offset].append(name[22:]) + temp_other_windows_vtables[offset].append(name[22:]) + else: + other_linux_vtables[offset].append(name) + temp_other_windows_vtables[offset].append(name) + # MI entry, strip "`non-virtual thunk to'" and remove from list # But not if it's a dtor... what the hell is this. - if (name.find("`non-virtual thunk to'") != -1) and name.find("::~") == -1: + if "`non-virtual thunk to'" in name and "::~" not in name: name = name[22:] #print "Stripping '%s' from windows vtable." % (name) temp_windows_vtable.remove(name) @@ -73,7 +165,7 @@ ea += 4 for i, v in enumerate(temp_windows_vtable): - if v.find("::~") != -1: + if "::~" in v: #print "Found destructor at index %d: %s" % (i, v) del temp_windows_vtable[i] break @@ -96,16 +188,19 @@ overload_stack.append(v) else: # If we've moved onto something new, dump the stack first - if len(overload_stack) > 0: - #print overload_stack - while len(overload_stack) > 0: - windows_vtable.append(overload_stack.pop()) + while len(overload_stack) > 0: + windows_vtable.append(overload_stack.pop()) windows_vtable.append(v) prev_function = function prev_symbol = v + # If there is anything left in the stack, dump it + while len(overload_stack) > 0: + windows_vtable.append(overload_stack.pop()) + + print "\nVTable for %s: (0, 0)" % (classname) print "Lin Win Function" for i, v in enumerate(linux_vtable): winindex = windows_vtable.index(v) if v in windows_vtable else None @@ -114,7 +209,45 @@ else: print "%3d %s" % (i, v) + for k in temp_other_windows_vtables: + for i, v in enumerate(temp_other_windows_vtables[k]): + if v.find("::~") != -1: + #print "Found destructor at index %d: %s" % (i, v) + del temp_other_windows_vtables[k][i] + break + + other_windows_vtables = {} + for k in temp_other_windows_vtables: + other_windows_vtables[k] = [] + overload_stack = [] + prev_function = "" + prev_symbol = "" + for v in temp_other_windows_vtables[k]: + function = v.split("(", 1)[0] + if function == prev_function: + if len(overload_stack) == 0: + other_windows_vtables[k].pop() + overload_stack.append(prev_symbol) + overload_stack.append(v) + else: + if len(overload_stack) > 0: + while len(overload_stack) > 0: + other_windows_vtables[k].append(overload_stack.pop()) + other_windows_vtables[k].append(v) + prev_function = function + prev_symbol = v + + for k in other_linux_vtables: + print "\nVTable for %s: (%d, %d)" % (offsetdata[k], offsetdata.keys().index(k) + 1, k) + print "Lin Win Function" + for i, v in enumerate(other_linux_vtables[k]): + winindex = other_windows_vtables[k].index(v) + if v not in other_thunk_linux_vtables[k]: + print "%3d %3d %s" % (i, winindex, v) + else: + print "T%2d %3d %s" % (i, winindex, v) + SetStatus(IDA_STATUS_READY) if __name__ == '__main__': - Analyze() \ No newline at end of file + Analyze()