Merge branch 'd19-update'

Conflicts:
	Compile.txt
	Readme.txt
develop
Petr Mrázek 2010-03-06 21:24:47 +01:00
commit 69ef04d5e6
15 changed files with 2937 additions and 1338 deletions

@ -1,138 +0,0 @@
Here's how you build dfhack!
----------------------------
First, there is one dependency, regardless of the OS you use:
cmake - it's the build system
Building on Linux:
--------------------
* To run in the output folder (without installing):
building the library is simple. Enter the build folder, run the tools. Like this:
cd build
cmake .. -DCMAKE_BUILD_TYPE:string=Release
make
This will build the library and its tools and place them in /output.
You can also use a cmake-friendly IDE like KDevelop 4 or the cmake GUI program.
* To be installed into the system or packaged
cd build
cmake -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=/usr -DMEMXML_DATA_PATH:path=/usr/share/dfhack ..
make
make install
With this dfhack installs:
library to $CMAKE_INSTALL_PREFIX/lib
executables to $CMAKE_INSTALL_PREFIX/bin
The Memory.xml file to /usr/share/dfhack
See the section on the shared memory hook library (SHM).
Building on Windows:
--------------------
You need cmake. Get the win32 installer version from the official site: http://www.cmake.org/cmake/resources/software.html
It has the usual installer wizard thing.
* Using mingw:
You also need a compiler. I build dfhack using mingw. You can get it from the mingw site:
Get the automated installer, it will download newest version of mingw and set things up nicely.
You'll have to add C:\MinGW\ to your PATH variable.
- Building:
open up cmd and navigate to the dfhack\build folder, run cmake and the mingw version of make:
cd build
cmake .. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE:string=Release
mingw32-make
* Using MSVC
open up cmd and navigate to the dfhack\build folder, run cmake:
cd build
cmake ..
This will generate MSVC solution and project files. Note that: you are working in the /build folder.
Files added to projects will end up there! (and that's wrong). Any changes to the build system should
be done by changing cmake configs and running cmake on them!
* Using some other compiler:
I'm afraid you are on your own. dfhack wasn't tested with any other compiler.
Try using a different cmake generator that's intended for your tools.
Building the shared memory hook library (SHM)
---------------------------------------------
Unlike the rest of DFHack, The SHM needs special treatment when it comes to compilation.
Because it shares the memory space with DF itself, it has to be built with the same tools as DF
and use the same C and C++/STL libraries.
For DF 40d15 - 40d17 on Windows, use MSVC 2008. You can get the Express edition for free from Microsoft.
Windows dependencies can be determined by a tool like depends.exe (google it). Both the fake SDL.dll
and DF have to use the same version of the C runtime (MSVCRT).
The SHM can't be debugged, because debug builds in MSVC use a different CRT!
Linux dependencies can be determined by setting the LD_DEBUG variable and running ./df:
export LD_DEBUG=versions
./df
Example of (a part of a) relevant output from a working SHM installation:
24472: checking for version `GLIBC_2.0' in file /opt/lib32/lib/libpthread.so.0 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GCC_3.0' in file ./libs/libgcc_s.so.1 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.0' in file ./libs/libgcc_s.so.1 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.1' in file /opt/lib32/lib/libm.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.0' in file /opt/lib32/lib/libm.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.1.3' in file /opt/lib32/lib/libc.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.3.4' in file /opt/lib32/lib/libc.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.4' in file /opt/lib32/lib/libc.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBC_2.0' in file /opt/lib32/lib/libc.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBCXX_3.4.9' in file ./libs/libstdc++.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `CXXABI_1.3' in file ./libs/libstdc++.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `GLIBCXX_3.4' in file ./libs/libstdc++.so.6 [0] required by file ./dwarfort.exe [0]
24472: checking for version `CXXABI_1.3' in file ./libs/libstdc++.so.6 [0] required by file ./libs/libdfconnect.so [0]
24472: checking for version `GLIBCXX_3.4' in file ./libs/libstdc++.so.6 [0] required by file ./libs/libdfconnect.so [0]
24472: checking for version `GLIBC_2.1.3' in file /opt/lib32/lib/libc.so.6 [0] required by file ./libs/libdfconnect.so [0]
24472: checking for version `GLIBC_2.2' in file /opt/lib32/lib/libc.so.6 [0] required by file ./libs/libdfconnect.so [0]
24472: checking for version `GLIBC_2.3.4' in file /opt/lib32/lib/libc.so.6 [0] required by file ./libs/libdfconnect.so [0]
24472: checking for version `GLIBC_2.0' in file /opt/lib32/lib/libc.so.6 [0] required by file ./libs/libdfconnect.so [0]
libdfconnect is the SHM. Both are compiled against the same C++ library and share the same CXXABI version.
Precompiled SHM libraries are provided in binary releases.
* Checking strings support
Strings are one of the important C++ types and a great indicator that the SHM works. Tools like Dwarf Therapist depend
on string support. Reading of strings can be checked by running any of the tools that deal with materials.
String writing is best tested with a fresh throw-away fort and dfrenamer. Embark, give one dwarf a very long name using dfrenamer
and save/exit. If DF crashes during the save sequence, your SHM is not compatible with DF and the throw-away fort is lost.
Build targets
-------------
dfhack has a few build targets. If you're only after the library run 'make dfhack'.
'make' will build everything.
'make expbench' will build the expbench throughput testing program and the library.
Build types
-----------
cmake allows you to pick a build type by changing this variable: CMAKE_BUILD_TYPE
cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
Without specifying a build type or 'None', cmake uses the CMAKE_CXX_FLAGS variable for building.
Valid build types include 'Release' and 'Debug'. There are others, but they aren't really that useful.
Have fun.

@ -1,174 +0,0 @@
Introduction
------------
DFHack is a Dwarf Fortress memory access library and a set of basic tools using
this library. The library is a work in progress, so things might change as more
tools are written for it.
It is an attempt to unite the various ways tools access DF memory and allow for
easier development of new tools.
Getting DFHack
----------------
The project is currently hosted on github:
http://github.com/peterix/dfhack
There's an SVN repository at sourceforge, but will only be updated for major releases:
https://sourceforge.net/projects/dfhack/
* subversion access:
svn co https://dfhack.svn.sourceforge.net/svnroot/dfhack/trunk dfhack
Compatibility
-------------
DFHack works on Windows XP, Vista, 7 or any modern Linux distribution.
Windows 2000 is currently *not supported* due to missing OS functionality.
If you know how to easily suspend processes, you can fix it :)
OSX is also not supported due to lack of developers with a Mac.
Currently supported Dwarf Fortress versions:
* Windows
40d
40d9 - 40d18
* Linux
40d9 - 40d18
Using the library
-----------------
The library is compilable under Linux with GCC and under Windows with MinGW32
and MSVC compilers. It is using the cmake build system. See COMPILE for details.
DFHack is using the zlib/libpng license. This makes it easy to link to it, use
it in-source or add your own extensions. Contributing back to the dfhack
repository is welcome and the right thing to do :)
At the time of writing there's no API reference or documentation. The code does
have a lot of comments though.
Using DFHack Tools
------------------
The project comes with a special extra library you should add to your DF
installation. It's used to boost the transfer speed between DF and DFHack, and
provide data consistency and synchronization. DFHack will work without the
library, but at suboptimal speeds and the consistency of data written back
to DF is questionable.
!!! on Windows this currently only works with DF 40d15 - 40d18 !!!
On Linux, it works with the whole range of supported DF versions.
!!! use the pre-compiled library intended for your OS and version of DF !!!
You can find them in the 'precompiled' folder.
** Installing on Windows
- Open your DF folder, locate SDL.dll and rename it to SDLreal.dll (making
a backup of it is a good idea)
- Copy the right DFHack SDL.dll into your DF folder.
- Restart DF if it is running
** Unistalling on Windows
- Open your DF folder, locate SDL.dll and delete it
- Rename SDLreal.dll to SDL.dll
- Restart DF if it is running
** Installing on Linux
- Open your DF folder and the libs folder within it
- copy DFHack libdfconnect.so to the libs folder
- copy the df startup script, name it dfhacked
- open the new dfhacked startup script and add this line:
export LD_PRELOAD="./libs/libdfconnect.so" # Hack DF!
just before the line that launches DF
Here's an example how the file can look after the change:
#!/bin/sh
DF_DIR=$(dirname "$0")
cd "${DF_DIR}"
export SDL_DISABLE_LOCK_KEYS=1 # Work around for bug in Debian/Ubuntu SDL patch.
#export SDL_VIDEO_CENTERED=1 # Centre the screen. Messes up resizing.
ldd dwarfort.exe | grep SDL_image | grep -qv "not found$"
if [ $? -eq 0 ]; then
mkdir unused_libs
mv libs/libSDL* unused_libs/
fi
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./libs" # Update library search path.
export LD_PRELOAD="./libs/libdfconnect.so" # Hack DF!
./dwarfort.exe $* # Go, go, go! :)
- Use this new startup script to start DF
** Uninstalling on Linux
- Open your DF and DF/libs folders
- Delete libdfconnect.so and the dfhacked startup script
- Go back to using the df startup script
Tools
-----
All the DFHack tools are terminal programs. This might seem strange to Windows
users, but these are meant mostly as examples for developers. Still, they can
be useful and are cross-platform just like the library itself.
If the tool writes back to DF's memory, make sure you are using the shared
memory interface mentioned in the previous section!
* reveal - plain old reveal tool. It reveals all the map blocks already
initialized by DF.
* prospector - scans the map for minerals. by default it only scans only visible
veins. You can make it show hidden things with '-a' and base rock
and soil layers with '-b'. These can be combined ('-ab')
* cleanmap - cleans mud, vomit, snow and all kinds of mess from the map.
It will clean your irrigated farms too, so consider yourself
warned.
* incremental - incremental search utility.
* bauxite - converts all mechanisms into bauxite mechanisms.
* itemdesignator - Allows mass-designating items by type and material - dump,
forbid, melt and set on fire ;)
* digger - allows designating tiles for digging/cutting/ramp removal
A list of accepted tile classes:
1 = WALL
2 = PILLAR
3 = FORTIFICATION
4 = STAIR_UP
5 = STAIR_DOWN
6 = STAIR_UPDOWN
7 = RAMP
8 = FLOOR
9 = TREE_DEAD
10 = TREE_OK
11 = SAPLING_DEAD
12 = SAPLING_OK
13 = SHRUB_DEAD
14 = SHRUB_OK
15 = BOULDER
16 = PEBBLES
Example : dfdigger -o 100,100,15 -t 9,10 -m 10
This will start looking for trees at coords 100,100,15 and designate ten of them for cutting.
Memory offset definitions
-------------------------
The file with memory offset definitions used by dfhack can be found in the
output folder.
~ EOF ~

@ -52,6 +52,10 @@ TARGET_LINK_LIBRARIES(dffindnameindexes dfhack)
ADD_EXECUTABLE(dfsettlementdump settlementdump.cpp)
TARGET_LINK_LIBRARIES(dfsettlementdump dfhack)
# veccheck - read vector values at address
ADD_EXECUTABLE(dfvecc veccheck.cpp)
TARGET_LINK_LIBRARIES(dfvecc dfhack)
IF(UNIX)
# veinlook - look at the map... sort of
ADD_EXECUTABLE(dfveinlook veinlook.cpp)

@ -0,0 +1,46 @@
// Just show some position data
#include <iostream>
#include <climits>
#include <integers.h>
#include <vector>
#include <sstream>
#include <ctime>
using namespace std;
#include <DFTypes.h>
#include <DFHackAPI.h>
#include <DFProcess.h>
int main (int numargs, const char ** args)
{
uint32_t addr;
if (numargs == 2)
{
istringstream input (args[1],istringstream::in);
input >> std::hex >> addr;
}
DFHack::API DF("Memory.xml");
if(!DF.Attach())
{
cerr << "DF not found" << endl;
}
else
{
DFHack::Process* p = DF.getProcess();
#ifdef LINUX_BUILD
cout << "start 0x" << hex << p->readDWord(addr+0x0) << endl;
cout << "end 0x" << hex << p->readDWord(addr+0x4) << endl;
cout << "cap 0x" << hex << p->readDWord(addr+0x8) << endl;
#else
cout << "start 0x" << hex << p->readDWord(addr+0x4) << endl;
cout << "end 0x" << hex << p->readDWord(addr+0x8) << endl;
cout << "cap 0x" << hex << p->readDWord(addr+0xC) << endl;
#endif
}
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();
#endif
return 0;
}

@ -184,12 +184,14 @@ namespace DFHack
class DFHACK_EXPORT MemoryXmlUnderspecifiedEntry : public std::exception
{
public:
MemoryXmlUnderspecifiedEntry() {}
MemoryXmlUnderspecifiedEntry(const char * _where) : where(_where) {}
virtual ~MemoryXmlUnderspecifiedEntry() throw(){};
std::string where;
virtual const char* what() const throw()
{
return "underspecified MemInfo entry, each entry needs to set both the name attribute and have a value";
std::stringstream s;
s << "underspecified MemInfo entry, each entry needs to set both the name attribute and have a value. parent: " << where;
return s.str().c_str();
}
};

@ -425,21 +425,16 @@ try_again:
// store it in the vector
ices.push_back (fv);
}
//#define ___FIND_THEM
#ifdef ___FIND_THEM
else if(g_pProcess->readClassName(type) == "block_square_event_frozen_liquid")
{
d->vein_ice_vptr = type;
cout << "block_square_event_frozen_liquid : 0x" << hex << type << endl;
goto try_again;
}
else if(g_pProcess->readClassName(type) == "block_square_event_mineral")
{
d->vein_mineral_vptr = type;
cout << "block_square_event_mineral : 0x" << hex << type << endl;
goto try_again;
}
#endif
}
return true;
}

@ -367,7 +367,8 @@ bool memory_info::resolveObjectToClassID(const uint32_t address, int32_t & class
}
}
string classname = g_pProcess->readClassName(vtable);
setClass(classname.c_str(),vtable);
t_class * c = setClass(classname.c_str(),vtable);
classid = c->assign;
return true;
}
@ -602,20 +603,3 @@ string memory_info::getLabor (const uint32_t laborIdx)
}
throw Error::MissingMemoryDefinition("labor", laborIdx);
}
// Reset everything
/*
0xDEADC0DE
void memory_info::flush()
{
d->base = 0;
d->addresses.clear();
d->offsets.clear();
d->strings.clear();
d->hexvals.clear();
d->classes.clear();
d->classindex = 0;
d->version = "";
d->OS = OS_BAD;
}
*/

@ -159,7 +159,7 @@ void MemInfoManager::ParseEntry (TiXmlElement* entry, memory_info* mem, map <str
}
if(!(cstr_name && cstr_value))
{
throw Error::MemoryXmlUnderspecifiedEntry();
throw Error::MemoryXmlUnderspecifiedEntry(cstr_version);
}
name = cstr_name;
value = cstr_value;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,802 @@
//Microsoft Visual C++ Win32 SEH/C++ EH info parser
//Version 3.0 2006.03.02 Igor Skochinsky <skochinsky@mail.ru>
#include <idc.idc>
#define __INCLUDED
#include <ms_rtti.idc>
static getEHRec()
{
auto id;
id = GetStrucIdByName("EHRegistrationNode");
if (id==-1)
{
id = AddStruc(-1,"EHRegistrationNode");
ForceDWMember(id, 0, "pNext");
ForceDWMember(id, 4, "frameHandler");
ForceDWMember(id, 8, "state");
}
return id;
}
static getEHRecCatch()
{
auto id;
id = GetStrucIdByName("EHRegistrationNodeCatch");
if (id==-1)
{
id = AddStruc(-1,"EHRegistrationNodeCatch");
ForceDWMember(id, 0, "SavedESP");
ForceDWMember(id, 4, "pNext");
ForceDWMember(id, 8, "frameHandler");
ForceDWMember(id, 12, "state");
}
return id;
}
static CommentStackEH(start, hasESP, EHCookie, GSCookie)
{
if (hasESP)
CommentStack(start,-16, "__$EHRec$", getEHRecCatch());
else
CommentStack(start,-12, "__$EHRec$", getEHRec());
if (GSCookie)
CommentStack(start,-GSCookie, "__$GSCookie$",-1);
if (EHCookie)
CommentStack(start,-EHCookie, "__$EHCookie$",-1);
}
/* from frame.obj
typedef struct _s_FuncInfo {
unsigned int magicNumber;
int maxState;
const struct _s_UnwindMapEntry * pUnwindMap;
unsigned int nTryBlocks;
const struct _s_TryBlockMapEntry * pTryBlockMap;
unsigned int nIPMapEntries;
void * pIPtoStateMap;
const struct _s_ESTypeList * pESTypeList;
} FuncInfo;
*/
//handler:
// mov eax, offset funcInfo
// jmp ___CxxFrameHandler
static ParseCxxHandler(func, handler, fixFunc)
{
auto x, start, y, z, end, i, count, t, u, i2, cnt2, a, hasESP;
auto EHCookieOffset, GSCookieOffset;
start = func;
x = handler;
y = x;
z = x;
EHCookieOffset=0; GSCookieOffset=0;
if (matchBytes(x,"8B5424088D420C"))
// 8B 54 24 08 mov edx, [esp+8]
// 8D 42 0C lea eax, [edx+0Ch]
{
//EH cookie check:
// 8B 4A xx mov ecx, [edx-XXh]
// OR
// 8B 8A xx xx xx xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
x = x+7;
if (matchBytes(x,"8B4A??33C8E8"))
{
//byte argument
EHCookieOffset = (~Byte(x+2)+1)&0xFF;
EHCookieOffset = 12 + EHCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
EHCookieOffset = (~Dword(x+2)+1);
EHCookieOffset = 12 + EHCookieOffset;
x = x+13;
}
if (matchBytes(x,"8B4A??33C8E8"))
{
// 8B 4A xx mov ecx, [edx-XXh]
// 33 C8 xor ecx, eax
// E8 xx xx xx xx call __security_check_cookie
GSCookieOffset = (~Byte(x+2)+1)&0xFF;
GSCookieOffset = 12 + GSCookieOffset;
x = x+10;
}
else if (matchBytes(x,"8B8A????????33C8E8"))
{
//dword argument
GSCookieOffset = (~Dword(x+9)+1);
GSCookieOffset = 12 + GSCookieOffset;
x = x+13;
}
//Message("EH3: EH Cookie=%02X, GSCookie=%02X\n",EHCookieOffset, GSCookieOffset);
}
if (Byte(x)==0xB8) {
x = Dword(x+1);
}
else {
Message("\"mov eax, offset FuncInfo\" not found at offset %08X!\n",x);
return;
}
if (Dword(x)-0x19930520>0xF) {
Message("Magic is not 1993052Xh!\n");
return;
}
Message(form("Detected function start at %08X\n",start));
u = x; //FuncInfo;
//parse unwind handlers
count = Dword(u+4); //maxState
i=0;
x = Dword(u+8); //pUnwindMap
while (i<count) {
t = Dword(x+4); //unwind action address
if (t<MAXADDR && t>y) y=t; //find lowest
if (t!=0 && t<z) z=t; //find highest
x = x+8;
i = i+1;
}
if (y==0) {
Message("All pointers are NULL!\n");
return;
}
if (z>y)
{
Message("Something's very wrong!\n");
return;
}
end = FindFuncEnd(y);
if (end==BADADDR) {
if (fixFunc) MakeUnkn(y, 1);
if (BADADDR == FindFuncEnd(y))
{
Message(form("Can't find function end at 0x%08X\n",y));
return;
}
}
Message(form("Handlers block: %08X-%08X\n", z, y));
if (GetFunctionFlags(start) == -1)
{
if (fixFunc)
{
MakeUnkn(start, 1);
MakeCode(start);
MakeFunction(start, BADADDR);
}
else
{
Message("There is no function defined at 0x%08X!\n", start);
return;
}
}
a = FindFuncEnd(start);
Message("Function end: %08X\n", a);
if (fixFunc) AnalyseArea(start,a);
if (1)//(z>a) && ((z-a)>0x20))
{
//the handlers block is too far from the function end, make it a separate chunk
if (fixFunc)
{
Message("Making separate handlers block\n");
Unknown(z, y-z);
MakeCode(z);
MakeFunction(z,y);
AnalyseArea(z,y);
MakeCode(y);
MakeFunction(y,BADADDR);
}
SetFunctionFlags(z, GetFunctionFlags(start) | FUNC_FRAME);
SetFunctionCmt(z, form("Unwind handlers of %08X", start), 0);
}
else if (fixFunc)
{
Message("Merging handlers block with main function.\n");
Unknown(start, y-start);
MakeCode(start);
MakeFunction(start,y);
AnalyseArea(start,y);
}
/*
typedef const struct _s_TryBlockMapEntry {
int tryLow; //00
int tryHigh; //04
int catchHigh; //08
int nCatches; //0C
const struct _s_HandlerType * pHandlerArray; //10
} TryBlockMapEntry;
typedef const struct _s_HandlerType {
unsigned int adjectives; //00
struct TypeDescriptor * pType; //04
int dispCatchObj; //08
void * addressOfHandler; //0C
}
*/
//parse catch blocks
y = 0;
z = 0x7FFFFFFF;
i=0;
count = Dword(u+12); //nTryBlocks
x = Dword(u+16); //pTryBlocksMap
Message("%d try blocks\n",count);
while (i<count) {
cnt2 = Dword(x+12); //nCatches
a = Dword(x+16); //pHandlerArray
i2 = 0;
Message(" %d catches\n",cnt2);
while (i2<cnt2)
{
t = Dword(a+12);
//Message(" t=0x%08.8X\n",t);
if (t!=BADADDR && t>y)
y=t; //find lowest
if (z>t)
z=t; //find highest
a = a+16;
i2 = i2+1;
}
x = x+20;
i = i+1;
}
hasESP = 0;
if (count>0)
{
hasESP = 1;
//Message("y=0x%08.8X, z=0x%08.8X\n",y,z);
end = FindFuncEnd(y);
if (end==BADADDR) {
if (fixFunc)
{
MakeUnkn(y, 1);
MakeCode(y);
}
if (BADADDR == FindFuncEnd(y))
{
Message(form("Can't find function end at 0x%08X\n",y));
return;
}
}
Message(form("Catch blocks: %08X-%08X\n", z, end));
y = FindFuncEnd(start);
if (y ==-1 || end > y)
{
if (fixFunc)
{
Message("Merging catch blocks with main function.\n");
Unknown(start, end-start);
MakeCode(start);
MakeFunction(start,end);
AnalyseArea(start,end);
}
else
Message("Catch blocks are not inside the function!\n");
}
}
//comment unwind handlers
i=0;
count = Dword(u+4); //maxState
x = Dword(u+8); //pUnwindMap
while (i<count) {
t = Dword(x+4); //unwind action address
if (t!=0)
MakeComm(t, form("state %d -> %d",i, Dword(x)));
x = x+8;
i = i+1;
}
Parse_FuncInfo(u, 0);
CommentStackEH(func, hasESP, EHCookieOffset, GSCookieOffset);
}
static fixCxx(s, doSEH, fixFunc) {
auto x, start;
start = s;
if ((Word(start) != 0xA164) || (Dword(start+2)!=0)) {
Message("Should start with \"move eax, large fs:0\"!\n");
return;
}
if ( !doSEH && (Byte(start-10) == 0x55) && (Dword(start-9) == 0xFF6AEC8B))
{
//(ebp frame)
//00: 55 push ebp
//01: 8B EC mov ebp, esp
//03: 6A FF push 0FFFFFFFFh
//05: 68 xx xx xx xx push loc_xxxxxxxx
//0A: 64 A1 00 00 00 00 mov eax, large fs:0
//10: 50 push eax
//11: 64 89 25 00 00 00 00 mov large fs:0, esp
start = start - 10;
x = Dword(start+6);
//Message("Match 1\n");
}
else if (!doSEH && (Word(start+9) == 0xFF6A) && (Byte(start+11)==0x68))
{
//00: 64 A1 00 00 00 00 mov eax, large fs:0
//06: xx xx xx
//09: 6A FF push 0FFFFFFFFh
//0B: 68 xx xx xx xx push loc_xxxxxxxx
//10: 50 push eax
//
x = Dword(start+12);
//Message("Match 2\n");
}
else if (!doSEH && (Word(start-7) == 0xFF6A) && (Byte(start-5)==0x68))
{
//-7: 6A FF push 0FFFFFFFFh
//-5: 68 xx xx xx xx push loc_xxxxxxxx
//00: 64 A1 00 00 00 00 mov eax, large fs:0
//06: 50 push eax
//07: 64 89 25 00 00 00 00 mov large fs:0, esp
//
x = Dword(start-4);
start = start-7;
//Message("Match 3\n");
}
else if (!doSEH && (Word(start+6) == 0xFF6A) && (Byte(start+8)==0x68))
{
//00: 64 A1 00 00 00 00 mov eax, large fs:0
//06: 6A FF push 0FFFFFFFFh
//08: 68 xx xx xx xx push loc_xxxxxxxx
//0D: 50 push eax
//0E: 64 89 25 00 00 00 00 mov large fs:0, esp
x = Dword(start+9);
//Message("Match 4\n");
}
else if (doSEH && (Byte(start-5)==0x68) && (Byte(start-10)==0x68) && (Dword(start-15)==0x6AEC8B55))
{
//-15: 55 push ebp
//-14: 8B EC mov ebp, esp
//-12: 6A F? push 0FFFFFFF?h
//-10: 68 xx xx xx xx push offset __sehtable$_func1
//-5 : 68 xx xx xx xx push offset _except_handlerx
//00 : 64 A1 00 00 00 00 mov eax, large fs:0
x = Dword(start-9);
//Message("Match 5\n");
if (Byte(start-11) == 0xFF) //-1 = SEH3
fixSEHFunc(start-15,x, 3, fixFunc);
else if (Byte(start-11) == 0xFE) //-2 = SEH4
fixSEHFunc(start-15,x, 4, fixFunc);
else
Message("Unknown SEH handler!\n");
return;
}
else {
//probably a custom handler
//Message("\"push 0FFFFFFFFh; push offset loc\" not found!\n");
return;
}
Message(form("Fixing function at 0x%08X\n",start));
ParseCxxHandler(start, x, fixFunc);
}
static doEHProlog(name,fixFunc)
{
auto i,s,a;
a=LocByName(name);
if (a==BADADDR)
return;
Message("%s = %08X\n",name,a);
i=RfirstB(a);
while(i!=BADADDR)
{
Message("- %08X - ",i);
// -5: mov eax, offset loc_XXXXXX
// 0: call __EH_prolog
if (Byte(i-5)==0xB8)
ParseCxxHandler(i-5, Dword(i-4),fixFunc);
else
{
Message(form("No mov eax, offset loc_XXXXXX at %08X!!!\n",i-5));
return;
}
if (SetFunctionFlags(i,GetFunctionFlags(i) | FUNC_FRAME))
{
MakeFrame(i,GetFrameLvarSize(i), 4, GetFrameArgsSize(i));
if (fixFunc) AnalyseArea(i, FindFuncEnd(i)+1);
Message("OK\n");
}
else
Message("Error\n");
i=RnextB(a,i);
}
}
static doEHPrologs(name, fixFunc)
{
doEHProlog("j"+name,fixFunc);
doEHProlog("j_"+name,fixFunc);
doEHProlog(name,fixFunc);
doEHProlog("_"+name,fixFunc);
}
static fixEHPrologs(fixFunc)
{
doEHPrologs("_EH_prolog",fixFunc);
doEHPrologs("_EH_prolog3",fixFunc);
doEHPrologs("_EH_prolog3_catch",fixFunc);
doEHPrologs("_EH_prolog3_GS",fixFunc);
doEHPrologs("_EH_prolog3_catch_GS",fixFunc);
}
static isInCodeSeg(a)
{
if (SegName(a)==".text")
return 1;
else
return 0;
}
//check a scopetable entry
static checkEntry(a,i,ver)
{
auto x;
x = Dword(a);
//EnclosingLevel should be negative or less than i
if (x&0x80000000)
{
if (ver==3 && x!=0xFFFFFFFF)
return 0;
if (ver==4 && x!=0xFFFFFFFE)
return 0;
}
else if (x>=i)
return 0;
x = Dword(a+4);
if ((x!=0) && !isInCodeSeg(x)) //filter should be zero or point to the code
return 0;
x = Dword(a+8);
if (!isInCodeSeg(x)) //handler should point to the code
return 0;
//check if there are xref to fields (i.e. after the end of the scopetable)
if (((ver!=3)||(i>0)) && isRef(GetFlags(a)))
return 0;
if (isRef(GetFlags(a+4)) || isRef(GetFlags(a+8)))
return 0;
return 1;
}
//check if there's a valid scopetable and calculate number of entries in it
static checkScopeTable(a, ver)
{
auto i,k;
if (ver==4)
{
k = Dword(a);
if ((k&0x80000000)==0) //first field should be negative
return 0;
if ((k!=0xFFFFFFFE) && (k&3)!=0) //GS cookie offset should be -2 or dword-aligned
return 0;
k = Dword(a+8);
if ((k&0x80000000)==0) //offset should be negative
return 0;
if ((k&3)!=0) //EH cookie offset should be dword-aligned
return 0;
a = a+16; //move to the scope entries list
}
i = 0;
while (checkEntry(a,i,ver))
{
i = i+1;
a = a+12;
}
return i;
}
/*
struct _EH4_EXCEPTION_REGISTRATION_RECORD {
void* SavedESP;
_EXCEPTION_POINTERS* ExceptionPointers;
_EXCEPTION_REGISTRATION_RECORD* Next;
enum _EXCEPTION_DISPOSITION (*Handler)(_EXCEPTION_RECORD*, void*, _CONTEXT*, void*);
DWORD EncodedScopeTable;
unsigned long TryLevel;
};
*/
static getSEHRec()
{
auto id;
id = GetStrucIdByName("SEHRegistrationNode");
if (id==-1)
{
id = AddStruc(-1,"SEHRegistrationNode");
ForceDWMember(id, 0, "SavedESP");
ForceDWMember(id, 4, "ExceptionPointers");
ForceDWMember(id, 8, "Next");
ForceDWMember(id, 12, "Handler");
ForceDWMember(id, 16, "EncodedScopeTable");
ForceDWMember(id, 20, "TryLevel");
}
return id;
}
static CommentStackSEH(start, scopetable)
{
auto x;
CommentStack(start,-24, "__$SEHRec$", getSEHRec());
if (scopetable)
{
x = Dword(scopetable);
if (x!=-2)
CommentStack(start,x, "__$GSCookie$", -1);
x = Dword(scopetable+8);
CommentStack(start,x, "__$EHCookie$", -1);
}
}
static fixSEHFunc(func, scopetable, ver, fixFunc)
{
auto k,i,t,u,x,y,z,hasESP,end;
k = checkScopeTable(scopetable, ver);
if (k==0)
{
Message("Bad scopetable\n");
return;
}
Message("function: %08X, scopetable: %08X (%d entries)\n", func, scopetable, k);
x = scopetable;
if (ver==4) x = x+16;
//parse the scopetable!
y = 0;
z = 0x7FFFFFFF;
i = 0;
hasESP = 0;
while (i<k) {
t = Dword(x+4);
if (t) {
hasESP=1;
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
//Message("t=0x%08.8X\n",t);
//check the code just before, it could be jump to the end of try
if (Byte(t-2)==0xEB)
t = getRelJmpTarget(t-2);
else if (Byte(t-5)==0xE9)
t = getRelJmpTarget(t-5);
//Message("t=0x%08.8X\n",t);
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
}
t = Dword(x+8);
//check the code just before, it could be jump to the end of try
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
//Message("t=0x%08.8X\n",t);
if (Byte(t-2)==0xEB)
t = getRelJmpTarget(t-2);
else if (Byte(t-5)==0xE9)
t = getRelJmpTarget(t-5);
//Message("t=0x%08.8X\n",t);
if (t>y) y=t; //find lowest
if (z>t) z=t; //find highest
x = x+12;
i = i+1;
}
//Message("y=0x%08.8X, z=0x%08.8X\n",y,z);
if (1)
{
end = FindFuncEnd(y);
if (end==BADADDR) {
if (fixFunc)
{
MakeUnkn(y, 1);
MakeCode(y);
}
if (BADADDR == FindFuncEnd(y))
{
Message(form("Can't find function end at 0x%08X\n",y));
return;
}
}
//Message(form("Except blocks: %08X-%08X\n", z, end));
z = FindFuncEnd(func);
if (z ==-1 || end > z && fixFunc)
{
//Message("Merging except blocks with main function.\n");
Unknown(func, end-func);
MakeCode(func);
MakeFunction(func,end);
AnalyseArea(func,end);
}
}
//walk once more and fix finally entries
x = scopetable;
if (ver==4) x = x+16;
i = 0;
while (fixFunc && i<k) {
if (Dword(x+4)==0 && Dword(x+8)==y)
{
//the last handler is a finally handler
//check that it ends with a ret, call or jmp
z = FindFuncEnd(y);
if (z!=BADADDR &&
!(Byte(z-1)==0xC3 || Byte(z-5)==0xE9 || Byte(z-5)==0xE8 ||
Byte(z-2)==0xEB || Byte(z-1)==0xCC || Word(z-6)==0x15FF) )
{
//we need to add the following funclet to our function
end = FindFuncEnd(z);
if (end!=BADADDR)
{
Unknown(z, end-z);
MakeCode(z);
SetFunctionEnd(func,end);
}
}
}
x = x+12;
i = i+1;
}
//comment the table and handlers
x = scopetable;
ExtLinA(x,0,form("; SEH scopetable for %08X",func));
if (ver==4)
{
OffCmt(x,"GSCookieOffset");
OffCmt(x+4,"GSCookieXOROffset");
OffCmt(x+8,"EHCookieOffset");
OffCmt(x+12,"EHCookieXOROffset");
x = x+16;
CommentStackSEH(func,scopetable);
}
else
CommentStackSEH(func,0);
i = 0;
while (i<k) {
ForceDword(x);
SoftOff(x+4);
SoftOff(x+8);
MakeComm(x, form("try block %d, enclosed by %d",i, Dword(x)));
t = Dword(x+4); //exception filter
if (t!=0)
ExtLinA(t,0,form("; __except() filter for try block %d",i));
u = Dword(x+8);
if (t!=0)
ExtLinA(u,0,form("; __except {} handler for try block %d",i));
else
ExtLinA(u,0,form("; __finally {} handler for try block %d",i));
x = x+12;
i = i+1;
}
}
static doSEHProlog(name, ver, fixFunc)
{
auto i,s,locals,scopetable,k,l,func,a;
a=LocByName(name);
if (a==BADADDR)
return;
Message("%s = %08X\n",name,a);
i=RfirstB(a);
while(i!=BADADDR)
{
Message("- %08X - ",i);
// -10 68 xx xx xx xx push xx
// or
// -7 6A xx push xx
// -5 68 xx xx xx xx push OFFSET __sehtable$_func
// 0 e8 00 00 00 00 call __SEH_prolog
//
//
locals = -1; scopetable=0;
if (Byte(i-5)==0x68)
{
scopetable = Dword(i-4);
if (Byte(i-7)==0x6A)
{
func = i-7;
locals = Byte(func+1);
}
else if (Byte(i-10)==0x68)
{
func = i-10;
locals = Dword(func+1);
}
if (GetFunctionFlags(func)==-1 && fixFunc)
{
MakeUnkn(func, 1);
MakeCode(func);
MakeFunction(func, BADADDR);
}
if (SetFunctionFlags(func,GetFunctionFlags(func)|FUNC_FRAME))
{
MakeFrame(func, GetFrameLvarSize(func), 4, GetFrameArgsSize(func));
fixSEHFunc(func, scopetable, ver, fixFunc);
Message("OK\n");
}
else
Message("Error\n");
}
i=RnextB(a,i);
}
}
static doSEHPrologs(name, ver, fixFunc)
{
doSEHProlog("j"+name, ver, fixFunc);
doSEHProlog("j_"+name, ver, fixFunc);
doSEHProlog(name, ver, fixFunc);
doSEHProlog("_"+name, ver, fixFunc);
}
static fixSEHPrologs(fixFunc)
{
doSEHPrologs("_SEH_prolog",3, fixFunc);
doSEHPrologs("__SEH_prolog",3, fixFunc);
doSEHPrologs("_SEH_prolog4",4, fixFunc);
doSEHPrologs("__SEH_prolog4",4, fixFunc);
doSEHPrologs("_SEH_prolog4_GS",4, fixFunc);
doSEHPrologs("__SEH_prolog4_GS",4, fixFunc);
}
static findFunc(name)
{
auto a;
a = LocByName("j_"+name);
if (a==BADADDR)
a = LocByName(name);
return a;
}
static doSEH(fixFunc)
{
auto start, a;
start = 0;
while (1) {
//mov eax, large fs:0
start = FindBinary(start+1, 3, "64 A1 00 00 00 00");
if (start==BADADDR)
break;
fixCxx(start,1,fixFunc);
}
fixSEHPrologs(fixFunc);
}
static doEH(fixFunc)
{
auto start, a;
start = 0;
while (1) {
//mov eax, large fs:0
start = FindBinary(start+1, 3, "64 A1 00 00 00 00");
if (start==BADADDR)
break;
fixCxx(start,0,fixFunc);
}
fixEHPrologs(fixFunc);
}
static main(void)
{
auto seh, fixseh, eh, fixeh;
seh = AskYN(1, "Do you wish to parse all Win32 SEH handlers?");
if (seh==-1) return;
if (seh) {
fixseh = AskYN(1, "Do you wish to fix function boundaries as needed?");
if (fixseh==-1) return;
}
eh = AskYN(1, "Do you wish to parse all C++ EH handlers?");
if (eh==-1) return;
if (eh) {
fixeh = AskYN(1, "Do you wish to fix function boundaries as needed?");
if (fixeh==-1) return;
}
if (seh) doSEH(fixseh);
if (eh) doEH(fixeh);
//fixCxx(ScreenEA());
}

@ -0,0 +1,995 @@
#include <idc.idc>
//Microsoft C++ RTTI support for IDA
//Version 3.0 2006.01.20 Igor Skochinsky <skochinsky@mail.ru>
//#define DEBUG
//////////////////////////////////////
// Unknown(long ea, long length)
//////////////////////////////////////
// Mark the ea as unknown for a length
// of length, but don't propagate.
static Unknown( ea, length )
{
auto i;
if (ea==BADADDR)
return;
// Message("Unknown(%x,%d)\n",ea, length);
for(i=0; i < length; i++)
{
MakeUnkn(ea+i,0);
}
}
static ForceQword( x ) { //Make dword, undefine as needed
if (x==BADADDR || x==0)
return;
if (!MakeQword( x ))
{
Unknown(x,8);
MakeQword(x);
}
}
static ForceDword( x ) { //Make dword, undefine as needed
if (x==BADADDR || x==0)
return;
if (!MakeDword( x ))
{
Unknown(x,4);
MakeDword(x);
}
}
static ForceWord( x ) { //Make word, undefine as needed
if (x==BADADDR || x==0)
return;
if (!MakeWord( x ))
{
Unknown(x,2);
MakeWord( x );
}
}
static ForceByte( x ) { //Make byte, undefine as needed
if (x==BADADDR || x==0)
return;
if (!MakeByte( x ))
{
MakeUnkn(x,0);
MakeByte( x );
}
}
static SoftOff ( x ) { //Make offset if !=0
if (x==BADADDR || x==0)
return;
ForceDword(x);
if (Dword(x)>0 && Dword(x)<=MaxEA()) OpOff(x,0,0);
}
static GetAsciizStr(x)
{
auto s,c;
if (x==BADADDR || x==0)
return "";
s = "";
while (c=Byte(x))
{
s = form("%s%c",s,c);
x = x+1;
}
return s;
}
//check if Dword(vtbl-4) points to typeinfo record and extract the type name from it
static GetTypeName(vtbl)
{
auto x, s, c;
if (vtbl==BADADDR)
return;
x = Dword(vtbl-4);
if ((!x) || (x==BADADDR)) return "";
// if (Dword(x)||Dword(x+4)||Dword(x+8)) return "";
x = Dword(x+12);
if ((!x) || (x==BADADDR)) return "";
s = "";
x = x+8;
while (c=Byte(x))
{
s = form("%s%c",s,c);
x = x+1;
}
return s;
}
static DwordCmt(x, cmt)
{
if (x==BADADDR || x==0)
return;
ForceDword(x);
MakeComm(x, cmt);
}
static OffCmt(x, cmt)
{
if (x==BADADDR || x==0)
return;
SoftOff(x);
MakeComm(x, cmt);
}
static StrCmt(x, cmt)
{
auto save_str;
if (x==BADADDR || x==0)
return;
MakeUnkn(x, 0);
save_str = GetLongPrm(INF_STRTYPE);
SetLongPrm(INF_STRTYPE,0);
MakeStr(x, BADADDR);
MakeName(x, "");
MakeComm(x, cmt);
SetLongPrm(INF_STRTYPE,save_str);
}
static DwordArrayCmt(x, n, cmt)
{
if (x==BADADDR || x==0)
return;
Unknown(x,4*n);
ForceDword(x);
MakeArray(x,n);
MakeComm(x, cmt);
}
//check if values match a pattern
static matchBytes(addr,match)
{
auto i,len,s;
len = strlen(match);
if (len%2)
{
Warning("Bad match string in matchBytes: %s",match);
return 0;
}
i=0;
while (i<len)
{
s = substr(match,i,i+2);
if (s!="??" && form("%02X",Byte(addr))!=s)
return 0;//mismatch
i = i+2;
addr++;
}
return 1;
}
static ForceDWMember(id, offset, name)
{
if (0!=AddStrucMember(id, name,offset, FF_DWRD, -1, 4))
SetMemberName(id, offset, name);
}
static ForceStrucMember(id, offset, sub_id, name)
{
auto a,i;
i = GetStrucSize(sub_id);
if (0!=AddStrucMember(id,name,offset,FF_DATA|FF_STRU,sub_id,i))
{
for (a=offset;a<offset+i;a++)
DelStrucMember(id,a);
AddStrucMember(id,name,offset,FF_DATA|FF_STRU,sub_id,i);
//SetMemberName(id, offset, name);
}
}
//add (or rename) a stack variable named name at frame offset offset (i.e. bp-based)
//struc_id = structure variable
//if struc_id == -1, then add a dword
static CommentStack(start, offset, name, struc_id)
{
auto id,l,bp;
id = GetFrame(start);
l = GetFrameLvarSize(start);
if ( (GetFunctionFlags(start) & FUNC_FRAME) == 0)
l = l + GetFrameRegsSize(start);
l = l+offset;
//Message("%a: ebp offset = %02Xh\n",start,l);
if (l<0)
{
//Message("growing the frame to locals=%d, regs=4, args=%d.\n",-offset, GetFrameArgsSize(start));
//we need to grow the locals
MakeFrame(start, -offset, GetFrameRegsSize(start), GetFrameArgsSize(start));
l = 0;
}
if (struc_id==-1)
ForceDWMember(id, l, name);
else
ForceStrucMember(id, l, struc_id, name);
}
static getRelJmpTarget(a)
{
auto b;
b = Byte(a);
if (b == 0xEB)
{
b = Byte(a+1);
if (b&0x80)
return a+2-((~b&0xFF)+1);
else
return a+2+b;
}
else if (b==0xE9)
{
b = Dword(a+1);
if (b&0x80000000)
return a+5-(~b+1);
else
return a+5+b;
}
else
return BADADDR;
}
static getRelCallTarget(a)
{
auto b;
b = Byte(a);
if (b==0xE8)
{
b = Dword(a+1);
if (b&0x80000000)
return a+5-(~b+1);
else
return a+5+b;
}
else
return BADADDR;
}
static MangleNumber(x)
{
//
// 0 = A@
// X = X-1 (1<=X<=10)
// -X = ?(X-1)
// 0x0..0xF = 'A'..'P'
auto s, sign;
s=""; sign=0;
if (x<0)
{
sign = 1;
x = -x;
}
if (x==0)
return "A@";
else if (x<=10)
return form("%s%d",sign?"?":"",x-1);
else
{
while (x>0)
{
s = form("%c%s",'A'+x%16,s);
x = x / 16;
}
return sign?"?":""+s+"@";
}
}
static Parse_BCD(x, indent)
{
auto indent_str,i,a,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
/*
struct _s_RTTIBaseClassDescriptor
{
struct TypeDescriptor* pTypeDescriptor; //type descriptor of the class
DWORD numContainedBases; //number of nested classes following in the array
struct PMD where; //some displacement info
DWORD attributes; //usually 0, sometimes 10h
};
struct PMD
{
int mdisp; //member displacement
int pdisp; //vbtable displacement
int vdisp; //displacement inside vbtable
};
*/
#ifdef DEBUG
Message(indent_str+"0x%08.8X: RTTIBaseClassDescriptor\n", x);
Message(indent_str+" pTypeDescriptor: %08.8Xh (%s)\n", Dword(x), GetAsciizStr(Dword(x)+8));
Message(indent_str+" numContainedBases: %08.8Xh\n", Dword(x+4));
Message(indent_str+" PMD where: (%d,%d,%d)\n", Dword(x+8), Dword(x+12), Dword(x+16));
Message(indent_str+" attributes: %08.8Xh\n", Dword(x+20));
#endif
OffCmt(x, "pTypeDescriptor");
DwordCmt(x+4, "numContainedBases");
DwordArrayCmt(x+8, 3, "PMD where");
DwordCmt(x+20, "attributes");
s = Parse_TD(Dword(x), indent+1);
//??_R1A@?0A@A@B@@8 = B::`RTTI Base Class Descriptor at (0,-1,0,0)'
MakeName(x,"??_R1"+MangleNumber(Dword(x+8))+MangleNumber(Dword(x+12))+
MangleNumber(Dword(x+16))+MangleNumber(Dword(x+20))+substr(s,4,-1)+'8');
return s;
}
static GetClassName(p)
{
/*auto s;
s = GetAsciizStr(Dword(p)+8);
Message("demangling %s\n",s);
return DemangleTIName(s);*/
auto s,s2;
s = "??_7"+GetAsciizStr(Dword(p)+12)+"6B@";
//Message("demangling %s\n",s);
s2 = Demangle(s,8);
if (s2!=0)
//CObject::`vftable'
return substr(s2,0,strlen(s2)-11);
else
return s;
}
static DumpNestedClass(x, indent, contained)
{
auto indent_str,i,a,n,p,s,off;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
i=0;
//indent=indent+1;
a = x;
while(i<contained)
{
p = Dword(a);
off = Dword(p+8);
s = form("%.4X: ",off);
Message("%s%s%s\n", s, indent_str, GetClassName(p));
//fprintf(f, form("%s%s%s\n",s,indent_str,GetClassName(p)));
n = Dword(p+4);
if (n>0) //check numContainedBases
DumpNestedClass(a+4, indent+1, n); //nested classes following
a=a+4*(n+1);
i=i+n+1;
}
}
static Parse_CHD(x, indent)
{
auto indent_str,i,a,n,p,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
//Message(indent_str+"0x%08.8X: RTTIClassHierarchyDescriptor\n", x);
/*
struct _s_RTTIClassHierarchyDescriptor
{
DWORD signature; //always zero?
DWORD attributes; //bit 0 = multiple inheritance, bit 1 = virtual inheritance
DWORD numBaseClasses; //number of classes in pBaseClassArray
struct _s_RTTIBaseClassArray* pBaseClassArray;
};
*/
a = Dword(x+4);
if ((a&3)==1)
p = "(MI)";
else if ((a&3)==2)
p = "(VI)";
else if ((a&3)==3)
p = "(MI VI)";
else
p="(SI)";
#ifdef DEBUG
Message(indent_str+" signature: %08.8Xh\n", Dword(x));
Message(indent_str+" attributes: %08.8Xh %s\n", a, p);
Message(indent_str+" numBaseClasses: %08.8Xh\n", n);
Message(indent_str+" pBaseClassArray: %08.8Xh\n", a);
#endif
DwordCmt(x, "signature");
DwordCmt(x+4, "attributes");
DwordCmt(x+8, "numBaseClasses");
OffCmt(x+12, "pBaseClassArray");
a=Dword(x+12);
n=Dword(x+8);
i=0;
DumpNestedClass(a, indent, n);
indent=indent+1;
while(i<n)
{
p = Dword(a);
//Message(indent_str+" BaseClass[%02d]: %08.8Xh\n", i, p);
OffCmt(a, form("BaseClass[%02d]", i));
if (i==0)
{
s = Parse_BCD(p,indent);
//??_R2A@@8 = A::`RTTI Base Class Array'
MakeName(a,"??_R2"+substr(s,4,-1)+'8');
//??_R3A@@8 = A::`RTTI Class Hierarchy Descriptor'
MakeName(x,"??_R3"+substr(s,4,-1)+'8');
}
else
Parse_BCD(p,indent);
i=i+1;
a=a+4;
}
return s;
}
static Parse_TD(x, indent)
{
auto indent_str,i,a;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
//Message(indent_str+"0x%08.8X: TypeDescriptor\n", x);
/*
struct TypeDescriptor
{
void* pVFTable; //always pointer to type_info::vftable ?
void* spare; //seems to be zero for most classes, and default constructor for exceptions
char name[0]; //mangled name, starting with .?A (.?AV=classes, .?AU=structs)
};
*/
a = GetAsciizStr(x+8);
#ifdef DEBUG
Message(indent_str+" pVFTable: %08.8Xh\n", Dword(x));
Message(indent_str+" spare: %08.8Xh\n", Dword(x+4));
Message(indent_str+" name: '%s'\n", a);
#endif
OffCmt(x, "pVFTable");
OffCmt(x+4, "spare");
StrCmt(x+8, "name");
//??_R0?AVA@@@8 = A `RTTI Type Descriptor'
MakeName(x,"??_R0"+substr(a,1,-1)+"@8");
return a;
}
static Parse_COL(x, indent)
{
/*
struct _s_RTTICompleteObjectLocator
{
DWORD signature; //always zero ?
DWORD offset; //offset of this vtable in the class ?
DWORD cdOffset; //no idea
struct TypeDescriptor* pTypeDescriptor; //TypeDescriptor of the class
struct _s_RTTIClassHierarchyDescriptor* pClassDescriptor; //inheritance hierarchy
};*/
auto indent_str,i,a,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
s = GetAsciizStr(Dword(x+12)+8);
//Message(indent_str+"0x%08.8X: RTTICompleteObjectLocator\n", x);
#ifdef DEBUG
Message(indent_str+" signature: %08.8Xh\n", Dword(x));
Message(indent_str+" offset: %08.8Xh\n", Dword(x+4));
Message(indent_str+" cdOffset: %08.8Xh\n", Dword(x+8));
Message(indent_str+" pTypeDescriptor: %08.8Xh (%s)\n", Dword(x+12), DemangleTIName(s));
Message(indent_str+" pClassDescriptor: %08.8Xh\n", Dword(x+16));
#endif
DwordCmt(x, "signature");
DwordCmt(x+4, "offset");
DwordCmt(x+8, "cdOffset");
OffCmt(x+12, "pTypeDescriptor");
OffCmt(x+16, "pClassDescriptor");
//
Parse_CHD(Dword(x+16),indent+1);
}
static Parse_CT(x, indent)
{
auto indent_str,i,a,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
/*
typedef const struct _s__CatchableType {
unsigned int properties;
_TypeDescriptor *pType;
_PMD thisDisplacement;
int sizeOrOffset;
_PMFN copyFunction;
} _CatchableType;
struct PMD
{
int mdisp; //members displacement ???
int pdisp; //
int vdisp; //vtable displacement ???
};
*/
s = GetAsciizStr(Dword(x+4)+8);
#ifdef DEBUG
Message(indent_str+"0x%08.8X: CatchableType\n", x);
Message(indent_str+" properties: %08.8Xh\n", Dword(x));
Message(indent_str+" pType: %08.8Xh (%s)\n", Dword(x+4), DemangleTIName(s));
Message(indent_str+" thisDisplacement: (%d,%d,%d)\n", Dword(x+8), Dword(x+12), Dword(x+16));
Message(indent_str+" sizeOrOffset: %08.8Xh\n", Dword(x+20));
Message(indent_str+" copyFunction: %08.8Xh\n", Dword(x+24));
#endif
a = "properties";
i = Dword(x);
if (i!=0) a = a+":";
if (i&1) a = a+" simple type";
if (i&2) a = a+" byref only";
if (i&4) a = a+" has vbases";
DwordCmt(x, a);
OffCmt(x+4, "pType");
DwordArrayCmt(x+8, 3, "thisDisplacement");
DwordCmt(x+20, "sizeOrOffset");
OffCmt(x+24, "copyFunction");
ForceDword(x+28);
//__CT??_R0 ?AVCTest@@ @81 = CTest::`catchable type'
MakeName(x,"__CT??_R0?"+substr(s,1,-1)+"@81");
if (Dword(x+24)) //we have a copy constructor
//.?AVexception@@ -> ??0exception@@QAE@ABV0@@Z = exception::exception(exception const &)
MakeName(Dword(x+24),"??0"+substr(s,4,-1)+"QAE@ABV0@@Z");
return s;
}
static Parse_CTA(x, indent)
{
/*
typedef const struct _s__CatchableTypeArray {
int nCatchableTypes;
_CatchableType *arrayOfCatchableTypes[];
} _CatchableTypeArray;
*/
auto indent_str,i,a,n,p,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
#ifdef DEBUG
Message(indent_str+" nCatchableTypes: %08.8Xh\n", Dword(x));
Message(indent_str+" arrayOfCatchableTypes: %08.8Xh\n", Dword(x+4));
#endif
DwordCmt(x, "nCatchableTypes");
//OffCmt(x+4, "arrayOfCatchableTypes");
a=x+4;
n=Dword(x);
i=0;
indent=indent+1;
while(i<n)
{
p = Dword(a);
//Message(indent_str+" BaseClass[%02d]: %08.8Xh\n", i, p);
OffCmt(a, form("CatchableType[%02d]", i));
if (i==0)
{
s = Parse_CT(p,indent);
//__CTA1 ?AVCTest@@ = CTest::`catchable type array'
MakeName(x,"__CTA1?"+substr(s,1,-1));
}
else
Parse_CT(p,indent);
i=i+1;
a=a+4;
}
return s;
}
//demangle names like .?AVxxx, .PAD, .H etc
static DemangleTIName(s)
{
auto i;
if (substr(s,0,1)!=".")
return "";
s = Demangle("??_R0"+substr(s,1,-1)+"@8",8);
i = strstr(s,"`RTTI Type Descriptor'");
if (i==-1)
return "";
else
{
s = substr(s,0,i-1);
//Message("throw %s;\n",s);
return s;
}
}
static Parse_ThrowInfo(x, indent)
{
/*
typedef const struct _s__ThrowInfo {
unsigned int attributes;
_PMFN pmfnUnwind;
int (__cdecl*pForwardCompat)(...);
_CatchableTypeArray *pCatchableTypeArray;
} _ThrowInfo;
*/
auto indent_str,i,a,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
#ifdef DEBUG
Message(indent_str+"0x%08.8X: ThrowInfo\n", x);
Message(indent_str+" attributes: %08.8Xh\n", Dword(x));
Message(indent_str+" pmfnUnwind: %08.8Xh\n", Dword(x+4));
Message(indent_str+" pForwardCompat: %08.8Xh\n", Dword(x+8));
Message(indent_str+" pCatchableTypeArray: %08.8Xh\n", Dword(x+12));
#endif
a = "attributes";
i = Dword(x);
if (i!=0) a = a+":";
if (i&1) a = a+" const";
if (i&2) a = a+" volatile";
DwordCmt(x, a);
OffCmt(x+4, "pmfnUnwind");
OffCmt(x+8, "pForwardCompat");
OffCmt(x+12, "pCatchableTypeArray");
s = Parse_CTA(Dword(x+12), indent+1);
if (s!="")
{
MakeName(x,"__TI1?"+substr(s,1,-1));
if (Dword(x+4)) //we have a destructor
//.?AVexception@@ -> ??1exception@@UAE@XZ = exception::~exception(void)
MakeName(Dword(x+4),"??1"+substr(s,4,-1)+"UAE@XZ");
i = Dword(x); //attributes
a = DemangleTIName(s);
if (i&1) a = "const "+a;
if (i&2) a = "volatile "+a;
a = "throw "+a;
Message("%s\n",a);
MakeRptCmt(x, a);
}
return s;
}
static Parse_TryBlock(x, indent)
{
/*
typedef const struct _s_TryBlockMapEntry {
int tryLow; //00
int tryHigh; //04
int catchHigh; //08
int nCatches; //0C
const struct _s_HandlerType * pHandlerArray; //10
} TryBlockMapEntry;
typedef const struct _s_HandlerType {
unsigned int adjectives; //00
struct TypeDescriptor * pType; //04
int dispCatchObj; //08
void * addressOfHandler; //0C
}
*/
auto indent_str,i,a,n,p,s;
if (x==BADADDR || x==0)
return;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
#ifdef DEBUG
Message(indent_str+" tryLow: %d\n", Dword(x));
Message(indent_str+" tryHigh: %d\n", Dword(x+4));
Message(indent_str+" catchHigh: %d\n", Dword(x+8));
Message(indent_str+" nCatches: %d\n", Dword(x+12));
Message(indent_str+" pHandlerArray: %08.8Xh\n", Dword(x+16));
#endif
DwordCmt(x, "tryLow");
DwordCmt(x+4, "tryHigh");
DwordCmt(x+8, "catchHigh");
DwordCmt(x+12, "nCatches");
OffCmt(x+16, "pHandlerArray");
a=Dword(x+16);
n=Dword(x+12);
if (a==BADADDR || a==0 || n==0)
return;
i=0;
indent=indent+1;
while(i<n)
{
#ifdef DEBUG
Message(indent_str+" adjectives: %08.8Xh\n", Dword(a));
Message(indent_str+" pType: %08.8Xh\n", Dword(a+4));
Message(indent_str+" dispCatchObj: %08.8Xh\n", Dword(a+8));
Message(indent_str+" addressOfHandler: %08.8Xh\n", Dword(a+12));
#endif
DwordCmt(a, "adjectives");
OffCmt(a+4,"pType");
DwordCmt(a+8, "dispCatchObj");
OffCmt(a+12,"addressOfHandler");
p = Dword(a+4);
if (p)
{
s = DemangleTIName(Parse_TD(p, indent+1));
if (Dword(a)&8) //reference
s = s+"&";
if (Dword(a)&2) //volatile
s = "volatile "+s;
if (Dword(a)&1) //const
s = "const "+s;
s = s+" e";
}
else
s = "...";
p = Dword(a+12);
if (p!=0 && p!=BADADDR)
{
ExtLinA(Dword(a+12),0,form("; catch (%s)",s));
ExtLinA(Dword(a+12),1,form("; states %d..%d",Dword(x),Dword(x+4)));
p = Dword(a+8);
if (p)
{
if (p&0x80000000)
s = form("; e = [epb-%Xh]",-p);
else
s = form("; e = [epb+%Xh]",p);
ExtLinA(Dword(a+12),2,s);
}
}
i=i+1;
a=a+16;
}
return s;
}
static Parse_FuncInfo(x, indent)
{
/*
typedef const struct _s_FuncInfo {
unsigned int magicNumber:29; //0
unsigned int bbtFlags:3;
int maxState; //4
const struct _s_UnwindMapEntry * pUnwindMap; //8
unsigned int nTryBlocks; //C
const struct _s_TryBlockMapEntry * pTryBlockMap; //10
unsigned int nIPMapEntries; //14
void * pIPtoStateMap; //18
const struct _s_ESTypeList * pESTypeList; //1C
int EHFlags; //present only in vc8? //20
} FuncInfo;
typedef const struct _s_UnwindMapEntry {
int toState; //0
function * action; //4
} UnwindMapEntry;
*/
auto indent_str,i,a,s,n;
if (x==BADADDR || x==0)
return;
if ((Dword(x)^0x19930520)>0xF) {
Message("Magic is not 1993052Xh!\n");
return;
}
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
#ifdef DEBUG
Message(indent_str+"0x%08.8X: FuncInfo\n", x);
Message(indent_str+" magicNumber: %08.8Xh\n", Dword(x));
Message(indent_str+" maxState: %d\n", Dword(x+4));
Message(indent_str+" pUnwindMap: %08.8Xh\n", Dword(x+8));
#endif
n = Dword(x+4);
i = 0; a = Dword(x+8);
while (i<n)
{
#ifdef DEBUG
Message(indent_str+" toState: %d\n", Dword(a));
Message(indent_str+" action: %08.8Xh\n", Dword(a+4));
#endif
DwordCmt(a, "toState");
OffCmt(a+4, "action");
a = a+8;
i = i+1;
}
#ifdef DEBUG
Message(indent_str+" nTryBlocks: %d\n", Dword(x+12));
Message(indent_str+" pTryBlockMap: %08.8Xh\n", Dword(x+16));
#endif
n = Dword(x+12);
i = 0; a = Dword(x+16);
while (i<n)
{
Parse_TryBlock(a, indent+1);
a = a+20;
i = i+1;
}
#ifdef DEBUG
Message(indent_str+" nIPMapEntries: %d\n", Dword(x+20));
Message(indent_str+" pIPtoStateMap: %08.8Xh\n", Dword(x+24));
#endif
DwordCmt(x, "magicNumber");
DwordCmt(x+4, "maxState");
OffCmt(x+8, "pUnwindMap");
//Parse_UnwindMap(Dword(x+8), indent+1);
DwordCmt(x+12, "nTryBlocks");
OffCmt(x+16, "pTryBlockMap");
DwordCmt(x+20, "nIPMapEntries");
OffCmt(x+24, "pIPtoStateMap");
if ((Dword(x+8)-x)>=32 || Dword(x)>0x19930520)
{
#ifdef DEBUG
Message(indent_str+" pESTypeList: %08.8Xh\n", Dword(x+28));
#endif
OffCmt(x+28, "pESTypeList");
}
if ((Dword(x+8)-x)>=36 || Dword(x)>0x19930521)
{
#ifdef DEBUG
Message(indent_str+" EHFlags: %08.8Xh\n", Dword(x+32));
#endif
OffCmt(x+32, "EHFlags");
}
return s;
}
//get class name for this vtable instance
static GetVtableClass(x)
{
auto offset, n, i, s, a, p;
offset = Dword(x+4);
x = Dword(x+16); //Class Hierarchy Descriptor
a=Dword(x+12); //pBaseClassArray
n=Dword(x+8); //numBaseClasses
i = 0;
s = "";
while(i<n)
{
p = Dword(a);
//Message(indent_str+" BaseClass[%02d]: %08.8Xh\n", i, p);
if (Dword(p+8)==offset)
{
//found it
s = GetAsciizStr(Dword(p)+8);
return s;
}
i=i+1;
a=a+4;
}
//didn't find matching one, let's get the first vbase
i=0;
a=Dword(x+12);
while(i<n)
{
p = Dword(a);
if (Dword(p+12)!=-1)
{
s = GetAsciizStr(Dword(p)+8);
return s;
}
i=i+1;
a=a+4;
}
return s;
}
static Parse_Vtable(a)
{
auto i,s,s2;
s=GetTypeName(a);
if (substr(s,0,3)==".?A")
{
#ifdef DEBUG
Message("RTTICompleteObjectLocator: %08.8Xh\n", Dword(a-4));
#endif
Parse_COL(Dword(a-4),0);
Unknown(a-4,4);
SoftOff(a-4);
i = Dword(a-4); //COL
s2 = Dword(i+4); //offset
i = Dword(i+16); //CHD
i = Dword(i+4); //Attributes
if ((i&3)==0 && s2==0)
{ //Single inheritance, so we don't need to worry about duplicate names (several vtables)
s=substr(s,4,-1);
MakeName(a,"??_7"+s+"6B@");
MakeName(Dword(a-4),"??_R4"+s+"6B@");
}
else// if ((i&3)==1)
{
//Message("Multiple inheritance\n");
s2 = GetVtableClass(Dword(a-4));
s2 = substr(s2,4,-1);
s = substr(s,4,-1);
s = s+"6B"+s2+"@";
MakeName(a,"??_7"+s);
MakeName(Dword(a-4),"??_R4"+s);
}
}
}
static ParseVtbl()
{
Parse_Vtable(ScreenEA());
}
static ParseExc()
{
Parse_ThrowInfo(ScreenEA(), 0);
}
static ParseFI()
{
Parse_FuncInfo(ScreenEA(), 0);
}
static AddHotkeys()
{
AddHotkey("Alt-F7","ParseFI");
AddHotkey("Alt-F8","ParseVtbl");
AddHotkey("Alt-F9","ParseExc");
Message("Use Alt-F7 to parse FuncInfo\n");
Message("Use Alt-F8 to parse vtable\n");
Message("Use Alt-F9 to parse throw info\n");
}
#ifndef __INCLUDED
static main(void)
{
AddHotkeys();
}
#endif

@ -0,0 +1,660 @@
#include <idc.idc>
#include "vtable.idc"
#include "ms_rtti.idc"
static GetAsciizStr(x)
{
auto s,c;
s = "";
while (c=Byte(x))
{
s = form("%s%c",s,c);
x = x+1;
}
return s;
}
// ??1?$CEventingNode@VCMsgrAppInfoImpl@@PAUIMsgrUser@@ABUtagVARIANT@@@@UAE@XZ
// ??_G CWin32Heap@ATL @@UAEPAXI@Z
// ATL::CWin32Heap::`scalar deleting destructor'(uint)
// .?AV?$CEventingNode@VCMsgrAppInfoImpl@@PAUIMsgrUser@@ABUtagVARIANT@@@@
// ??_7?$CEventingNode@VCMsgrAppInfoImpl@@PAUIMsgrUser@@ABUtagVARIANT@@@@6B@
// ??_G?$CEventingNode@VCMsgrAppInfoImpl@@PAUIMsgrUser@@ABUtagVARIANT@@@@@@UAEPAXI@Z
#define SN_constructor 1
#define SN_destructor 2
#define SN_vdestructor 3
#define SN_scalardtr 4
#define SN_vectordtr 5
static MakeSpecialName(name, type, adj)
{
auto basename;
//.?AUA@@ = typeid(struct A)
//basename = A@@
basename = substr(name,4,-1);
if (type==SN_constructor)
{
//??0A@@QAE@XZ = public: __thiscall A::A(void)
if (adj==0)
return "??0"+basename+"QAE@XZ";
else
return "??0"+basename+"W"+MangleNumber(adj)+"AE@XZ";
}
else if (type==SN_destructor)
{
//??1A@@QAE@XZ = "public: __thiscall A::~A(void)"
if (adj==0)
return "??1"+basename+"QAE@XZ";
else
return "??1"+basename+"W"+MangleNumber(adj)+"AE@XZ";
}
else if (type==SN_vdestructor)
{
//??1A@@UAE@XZ = public: virtual __thiscall A::~A(void)
if (adj==0)
return "??1"+basename+"UAE@XZ";
else
return "??1"+basename+"W"+MangleNumber(adj)+"AE@XZ";
}
else if (type==SN_scalardtr) //
{
//??_GA@@UAEPAXI@Z = public: virtual void * __thiscall A::`scalar deleting destructor'(unsigned int)
if (adj==0)
return "??_G"+basename+"UAEPAXI@Z";
else
return "??_G"+basename+"W"+MangleNumber(adj)+"AEPAXI@Z";
}
else if (type==SN_vectordtr)
{
//.?AUA@@ = typeid(struct A)
//??_EA@@UAEPAXI@Z = public: virtual void * __thiscall A::`vector deleting destructor'(unsigned int)
if (adj==0)
return "??_E"+basename+"QAEPAXI@Z";
else
return "??_E"+basename+"W"+MangleNumber(adj)+"AEPAXI@Z";
}
}
static DumpNestedClass2(x, indent, contained, f)
{
auto indent_str,i,a,n,p,s,off;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
i=0;
//indent=indent+1;
a = x;
while(i<contained)
{
p = Dword(a);
off = Dword(p+8);
s = form("%.4X: ",off);
//Message("%s%s%s\n", s, indent_str, GetClassName(p));
fprintf(f, form("%s%s%s\n",s,indent_str,GetClassName(p)));
n = Dword(p+4);
if (n>0) //check numContainedBases
DumpNestedClass2(a+4, indent+1, n, f); //nested classes following
a=a+4*(n+1);
i=i+n+1;
}
}
static Parse_CHD2(x, indent, f)
{
auto indent_str,i,a,n,p,s,off;
indent_str="";i=0;
while(i<indent)
{
indent_str=indent_str+" ";
i++;
}
a = Dword(x+4);
if ((a&3)==1)
p = "(MI)";
else if ((a&3)==2)
p = "(VI)";
else if ((a&3)==3)
p = "(MI VI)";
else
p="(SI)";
fprintf(f, form("%s%s\n",indent_str,p));
a=Dword(x+12);
n=Dword(x+8);
DumpNestedClass2(a, indent, n, f);
}
static GetTypeName2(col)
{
auto x, s, c;
//Message("GetTypeName2(%X)\n",col)
x = Dword(col+12);
if ((!x) || (x==BADADDR)) return "";
return GetAsciizStr(x+8);
}
static GetVtblName2(col)
{
auto i, s, s2;
s = GetTypeName2(col);
i = Dword(col+16); //CHD
i = Dword(i+4); //Attributes
if ((i&3)==0 && Dword(col+4)==0)
{
//Single inheritance, so we don't need to worry about duplicate names (several vtables)
s=substr(s,4,-1);
return "??_7"+s+"6B@";
}
else //if ((i&3)==1) //multiple inheritance
{
s2 = GetVtableClass(col);
s2 = substr(s2,4,-1);
s = substr(s,4,-1);
s = s+"6B"+s2+"@";
return "??_7"+s;
}
return "";
}
//check if Dword(vtbl-4) points to typeinfo record and extract the type name from it
static IsValidCOL(col)
{
auto x, s, c;
x = Dword(col+12);
if ((!x) || (x==BADADDR)) return "";
x = Dword(x+8);
if ((x&0xFFFFFF) == 0x413F2E) //.?A
return 1;
else
return 0;
}
static funcStart(ea)
{
if (GetFunctionFlags(ea) == -1)
return -1;
if ((GetFlags(ea)&FF_FUNC)!=0)
return ea;
else
return PrevFunction(ea);
}
// Add ea to "Sorted Address List"
static AddAddr(ea)
{
auto id, idx, val;
if ( (id = GetArrayId("AddrList")) == -1 )
{
id = CreateArray("AddrList");
SetArrayLong(id, 0, ea);
return;
}
for ( idx = GetFirstIndex(AR_LONG, id); idx != -1; idx = GetNextIndex(AR_LONG, id, idx) )
{
val = GetArrayElement(AR_LONG, id, idx);
if ( val == ea )
return;
if ( val > ea ) // InSort
{
for ( ; idx != -1; idx = GetNextIndex(AR_LONG, id, idx) )
{
val = GetArrayElement(AR_LONG, id, idx);
SetArrayLong(id, idx, ea);
ea = val;
}
}
}
SetArrayLong(id, GetLastIndex(AR_LONG, id) + 1, ea);
}
static getArraySize(id)
{
auto idx, count;
count = 0;
for ( idx = GetFirstIndex(AR_LONG, id); idx != -1; idx = GetNextIndex(AR_LONG, id, idx) )
{
count++;
}
return count;
}
static doAddrList(name,f)
{
auto idx, id, val, ctr, dtr;
id = GetArrayId("AddrList");
ctr = 0; dtr = 0;
if ( name!=0 && id != -1 )
{
Message("refcount:%d\n",getArraySize(id));
if (getArraySize(id)!=2)
return;
for ( idx = GetFirstIndex(AR_LONG, id); idx != -1; idx = GetNextIndex(AR_LONG, id, idx) )
{
val = GetArrayElement(AR_LONG, id, idx);
if (Byte(val)==0xE9)
val = getRelJmpTarget(val);
if ((substr(Name(val),0,3)=="??1"))
dtr = val;
else
ctr = val;
}
}
if (ctr!=0 && dtr!=0)
{
Message(" constructor at %a\n",ctr);
fprintf(f, " constructor: %08.8Xh\n",ctr);
MakeName(ctr, MakeSpecialName(name,SN_constructor,0));
}
DeleteArray(GetArrayId("AddrList"));
}
//check if there's a vtable at a and dump into to f
//returns position after the end of vtable
static DoVtable(a,f)
{
auto x,y,s,p,q,i,name;
//check if it looks like a vtable
y = GetVtableSize(a);
if (y==0)
return a+4;
s = form("%08.8Xh: possible vtable (%d methods)\n", a, y);
Message(s);
fprintf(f,s);
//check if it's named as a vtable
name = Name(a);
if (substr(name,0,4)!="??_7") name=0;
x = Dword(a-4);
//otherwise try to get it from RTTI
if (IsValidCOL(x))
{
Parse_Vtable(a);
if (name==0)
name = GetVtblName2(x);
//only output object tree for main vtable
if (Dword(x+4)==0)
Parse_CHD2(Dword(x+16),0,f);
MakeName(a, name);
}
if (name!=0)
{
s = Demangle(name, 0x00004006);
Message("%s\n",s);
fprintf(f, "%s\n", s);
//convert vtable name into typeinfo name
name = ".?AV"+substr(name, 4, strstr(name,"@@6B")+2);
}
{
DeleteArray(GetArrayId("AddrList"));
Message(" referencing functions: \n");
fprintf(f," referencing functions: \n");
q = 0; i = 1;
for ( x=DfirstB(a); x != BADADDR; x=DnextB(a,x) )
{
p = funcStart(x);
if (p!=-1)
{
if (q==p)
i++;
else
{
if (q) {
if (i>1) s = form(" %a (%d times)",q,i);
else s = form(" %a",q);
//if (strstr(Name(p),"sub_")!=0 && strstr(Name(p),"j_sub_")!=0)
if (hasName(GetFlags(q)))
s = s+" ("+Demangle(Name(q),8)+")";
s = s+"\n";
Message(s);fprintf(f,s);
AddAddr(q);
}
i = 1;
q = p;
}
}
}
if (q)
{
if (i>1) s = form(" %a (%d times)",q,i);
else s = form(" %a",q);
if (hasName(GetFlags(q)))
s = s+" ("+Demangle(Name(q),8)+")";
s = s+"\n";
Message(s);fprintf(f,s);
AddAddr(q);
}
x = a;
while (y>0)
{
p = Dword(x);
if (GetFunctionFlags(p) == -1)
{
MakeCode(p);
MakeFunction(p, BADADDR);
}
checkSDD(p,name,a,0,f);
y--;
x = x+4;
}
doAddrList(name,f);
Message("\n");
fprintf(f,"\n");
}
return x;
}
static scan_for_vtables(void)
{
auto rmin, rmax, cmin, cmax, s, a, x, y,f;
s = FirstSeg();
f = fopen("objtree.txt","w");
rmin = 0; rmax = 0;
while (s!=BADADDR)
{
if (SegName(s)==".rdata")
{
rmin = s;
rmax = NextSeg(s);
}
else if (SegName(s)==".text")
{
cmin = s;
cmax = NextSeg(s);
}
s = NextSeg(s);
}
if (rmin==0) {rmin=cmin; rmax=cmax;}
a = rmin;
Message(".rdata: %08.8Xh - %08.8Xh, .text %08.8Xh - %08.8Xh\n", rmin, rmax, cmin, cmax);
while (a<rmax)
{
x = Dword(a);
if (x>=cmin && x<cmax) //methods should reside in .text
{
a = DoVtable(a,f);
}
else
a = a + 4;
}
Message("Done\n");
fclose(f);
}
//check for `scalar deleting destructor'
static checkSDD(x,name,vtable,gate,f)
{
auto a,s,t;
//Message("checking function at %a\n",x);
t = 0; a = BADADDR;
if ((name!=0) && (substr(Name(x),0,3)=="??_") && (strstr(Name(x),substr(name,4,-1))==4))
name=0; //it's already named
if (Byte(x)==0xE9 || Byte(x)==0xEB) {
//E9 xx xx xx xx jmp xxxxxxx
return checkSDD(getRelJmpTarget(x),name,vtable,1,f);
}
else if (matchBytes(x,"83E9??E9")) {
//thunk
//83 E9 xx sub ecx, xx
//E9 xx xx xx xx jmp class::`scalar deleting destructor'(uint)
a = getRelJmpTarget(x+3);
Message(" %a: thunk to %a\n",x,a);
t = checkSDD(a,name,vtable,0,f);
if (t && name!=0)
{
//rename this function as a thunk
MakeName(x, MakeSpecialName(name,t,Byte(x+2)));
}
return t;
}
else if (matchBytes(x,"81E9????????E9")) {
//thunk
//81 E9 xx xx xx xx sub ecx, xxxxxxxx
//E9 xx xx xx xx jmp class::`scalar deleting destructor'(uint)
a = getRelJmpTarget(x+6);
Message(" %a: thunk to %a\n",x,a);
t = checkSDD(a,name,vtable,0,f);
if (t && name!=0)
{
//rename this function as a thunk
MakeName(x, MakeSpecialName(name,t,Dword(x+2)));
}
return t;
}
else if (matchBytes(x,"568BF1E8????????F64424080174") && matchBytes(x+15+Byte(x+14),"8BC65EC20400"))
{
//56 push esi
//8B F1 mov esi, ecx
//E8 xx xx xx xx call class::~class()
//F6 44 24 08 01 test [esp+arg_0], 1
//74 07 jz short @@no_free
//56 push esi
//
// call operator delete();
// @@no_free:
//8B C6 mov eax, esi
//5E pop esi
//C2 04 00 retn 4
t = SN_scalardtr;
a = getRelCallTarget(x+3);
if (gate && Byte(a)==0xE9)
{
//E9 xx xx xx xx jmp xxxxxxx
a = getRelJmpTarget(a);
}
}
else if (matchBytes(x,"568BF1FF15????????F64424080174") && matchBytes(x+16+Byte(x+15),"8BC65EC20400"))
{
//56 push esi
//8B F1 mov esi, ecx
//FF 15 xx xx xx xx call class::~class() //dllimport
//F6 44 24 08 01 test [esp+arg_0], 1
//74 07 jz short @@no_free
//56 push esi
//
// call operator delete();
// @@no_free:
//8B C6 mov eax, esi
//5E pop esi
//C2 04 00 retn 4
t = SN_scalardtr;
/*a = getRelCallTarget(x+3);
if (gate && Byte(a)==0xE9)
{
//E9 xx xx xx xx jmp xxxxxxx
a = getRelJmpTarget(a);
}*/
}
else if (matchBytes(x,"558BEC51894DFC8B4DFCE8????????8B450883E00185C0740C8B4DFC51E8????????83C4048B45FC8BE55DC20400") ||
matchBytes(x,"558BEC51894DFC8B4DFCE8????????8B450883E00185C074098B4DFC51E8????????8B45FC8BE55DC20400"))
{
//55 push ebp
//8B EC mov ebp, esp
//51 push ecx
//89 4D FC mov [ebp+var_4], ecx
//8B 4D FC mov ecx, [ebp+var_4]
//E8 xx xx xx xx call sub_10001099
//8B 45 08 mov eax, [ebp+arg_0]
//83 E0 01 and eax, 1
//85 C0 test eax, eax
//74 0C jz short skip
//8B 4D FC mov ecx, [ebp+var_4]
//51 push ecx
//E8 F0 56 05 00 call operator delete(void *)
//83 C4 04 add esp, 4
//
// skip:
//8B 45 FC mov eax, [ebp+var_4]
//8B E5 mov esp, ebp
//5D pop ebp
//C2 04 00 retn 4
t = SN_scalardtr;
a = getRelCallTarget(x+10);
if (gate && Byte(a)==0xE9)
{
//E9 xx xx xx xx jmp xxxxxxx
a = getRelJmpTarget(a);
}
}
else if (matchBytes(x,"568D71??578D7E??8BCFE8????????F644240C01"))
{
//56 push esi
//8D 71 xx lea esi, [ecx-XX]
//57 push edi
//8D 7E xx lea edi, [esi+XX]
//8B CF mov ecx, edi
//E8 xx xx xx xx call class::~class()
//F6 44 24 0C 01 test [esp+4+arg_0], 1
a = getRelCallTarget(x+10);
if (gate && Byte(a)==0xE9)
{
a = getRelJmpTarget(a);
}
t=SN_scalardtr;
}
else if (matchBytes(x,"568DB1????????578DBE????????8BCFE8????????F644240C01"))
{
//56 push esi
//8D B1 xx xx xx xx lea esi, [ecx-XX]
//57 push edi
//8D BE xx xx xx xx lea edi, [esi+XX]
//8B CF mov ecx, edi
//E8 xx xx xx xx call class::~class()
//F6 44 24 0C 01 test [esp+4+arg_0], 1
a = getRelCallTarget(x+16);
if (gate && Byte(a)==0xE9)
{
a = getRelJmpTarget(a);
}
t = SN_scalardtr;
}
else if ((matchBytes(x,"F644240401568BF1C706") /*&& Dword(x+10)==vtable*/) ||
(matchBytes(x,"8A442404568BF1A801C706") /*&& Dword(x+11)==vtable */) ||
(matchBytes(x,"568BF1C706????????E8????????F64424080174") && matchBytes(x+21+Byte(x+20),"8BC65EC20400"))
)
{
//F6 44 24 04 01 test [esp+arg_0], 1
//56 push esi
//8B F1 mov esi, ecx
// OR
//8A 44 24 04 mov al, [esp+arg_0]
//56 push esi
//8B F1 mov esi, ecx
//A8 01 test al, 1
//C7 06 xx xx xx xx mov dword ptr [esi], xxxxxxx //offset vtable
// <inlined destructor>
//74 07 jz short @@no_free
//56 push esi
//E8 CA 2D 0D 00 call operator delete(void *)
//59 pop ecx
// @@no_free:
//8B C6 mov eax, esi
//5E pop esi
//C2 04 00 retn 4
t = SN_scalardtr;
}
else if (matchBytes(x,"538A5C2408568BF1F6C302742B8B46FC578D7EFC68????????506A??56E8") ||
matchBytes(x,"538A5C2408F6C302568BF1742E8B46FC5768????????8D7EFC5068????????56E8"))
{
//53 push ebx
//8A 5C 24 08 mov bl, [esp+arg_0]
//56 push esi
//8B F1 mov esi, ecx
//F6 C3 02 test bl, 2
//74 2B jz short loc_100037F8
//8B 46 FC mov eax, [esi-4]
//57 push edi
//8D 7E FC lea edi, [esi-4]
//68 xx xx xx xx push offset class::~class(void)
//50 push eax
//6A xx push xxh
//56 push esi
//E8 xx xx xx xx call `eh vector destructor iterator'(void *,uint,int,void (*)(void *))
t = SN_vectordtr;
Message(" vector deleting destructor at %a\n",x);
if (name!=0)
a = Dword(x+21);
if (gate && Byte(a)==0xE9)
{
a = getRelJmpTarget(a);
}
}
if (t>0)
{
if (t==SN_vectordtr)
s = "vector";
else
s = "scalar";
Message(" %s deleting destructor at %a\n",s,x);
fprintf(f, " %s deleting destructor: %08.8Xh\n",s,x);
if (name!=0)
MakeName(x, MakeSpecialName(name,t,0));
if (a!=BADADDR)
{
Message(" virtual destructor at %a\n",a);
fprintf(f, " destructor: %08.8Xh\n",a);
if (name!=0)
MakeName(a, MakeSpecialName(name,SN_vdestructor,0));
}
CommentStack(x, 4, "__flags$",-1);
}
return t;
}
static ParseVtbl2()
{
auto a, n;
a = ScreenEA();
if (GetVtableSize(a)==0)
{
Warning("This location doesn't look like a vtable!");
return;
}
if (!hasName(GetFlags(a)) && !IsValidCOL(Dword(a-4)))
{
n = AskStr("","Enter class name");
if (n!=0)
MakeName(a,"??_7"+n+"@@6B@");
}
DoVtable(a,0);
}
static AddHotkeys()
{
AddHotkey("Alt-F7","ParseFI");
AddHotkey("Alt-F8","ParseVtbl2");
AddHotkey("Alt-F9","ParseExc");
Message("Use Alt-F7 to parse FuncInfo\n");
Message("Use Alt-F8 to parse vtable\n");
Message("Use Alt-F9 to parse throw info\n");
}
static main(void)
{
if(AskYN(1, "Do you wish to scan the executable for vtables/RTTI?"))
{
Message("Scanning...");
scan_for_vtables();
//Message("See objtree.txt for the class list/hierarchy.\n");
Exec("objtree.txt");
}
AddHotkeys();
}

@ -0,0 +1,8 @@
#!/usr/bin/perl
#local $/ = "\n\n";
while(<>){
if(/([0-9A-Z]+).*?([a-z]+_.+?)st\n/s){
print "<class vtable=\"0x$1\" name=\"$2\" />\n";
}
}

@ -0,0 +1,43 @@
#include <idc.idc>
static main(void)
{
auto SearchString;
auto searchStart;
auto searchTest;
auto occurances;
auto szFilePath,hFile;
auto strSize;
auto vTablePtr;
auto vTableLoc;
auto myString;
auto nextAddress;
auto byteVal;
occurances = 0;
searchStart = 0;
SearchString = AskStr("", "What vtable binary to search?");
szFilePath = AskFile(1, "*.txt", "Select output dump file:");
hFile = fopen(szFilePath, "wb");
Message("Scanning...");
searchStart = FindBinary(searchStart, SEARCH_DOWN, SearchString);
while(searchStart != BADADDR){
MakeStr(searchStart-2, BADADDR);
myString = GetString(searchStart-2,-1,GetStringType(searchStart-2));
strSize = strlen(myString);
nextAddress = searchStart-2+strSize+1;
byteVal = Byte(nextAddress);
while(byteVal == 0){
nextAddress++;
byteVal = Byte(nextAddress);
}
MakeDword(nextAddress);
vTableLoc = FindBinary(141301056,SEARCH_DOWN, form("%X", nextAddress));
MakeDword(nextAddress+4);
fprintf(hFile,"%a\t%s\n",vTableLoc,myString);
searchStart = FindBinary(searchStart+1, SEARCH_DOWN, SearchString);
occurances++;
}
fclose(hFile);
Message("Found %i Occurances",occurances);
}

@ -0,0 +1,114 @@
#include <idc.idc>
static GetVtableSize(a)
{
auto b,c,f;
b = BADADDR;
f = GetFlags(a);
//Message("checking vtable at: %a\n",a);
do {
f = GetFlags(a);
if (b == BADADDR) //first entry
{
b=a;
if (!(isRef(f) && (hasName(f) || (f&FF_LABL))))
{
//Message("Start of vtable should have a xref and a name (auto or manual)\n");
return 0;
}
}
else if (isRef(f)) //might mean start of next vtable
break;
//Message("hasValue(f):%d, isData(f):%d, isOff0(f):%d, (f & DT_TYPE) != FF_DWRD:%d\n",
// hasValue(f), isData(f), isOff0(f), (f & DT_TYPE) != FF_DWRD);
if (!hasValue(f) || !isData(f) /*|| !isOff0(f) || (f & DT_TYPE) != FF_DWRD*/)
break;
c = Dword(a);
if (c)
{
f = GetFlags(c);
if (!hasValue(f) || !isCode(f) || Dword(c)==0)
break;
}
a = a+4;
}
while (1);
if (b!=BADADDR)
{
c = (a-b)/4;
//Message("vtable: %08X-%08X, methods: %d\n",b,a,c);
return c;
}
else
{
//Message("no vtable at this EA\n");
return 0;
}
}
static main(void)
{
auto a, c, k, name, i, struct_id, bNameMethods,e, methName;
a = ScreenEA();
k = GetVtableSize(a);
if (k>100)
{
if (1!=AskYN(0,form("%08X: This vtable appears to have %d methods. Are you sure you want to continue?",a,k)))
return;
}
if (hasName(GetFlags(a)))
name = Name(a);
else
name = "";
if (substr(name,0,4)=="??_7")
name = substr(name,4,strlen(name)-5);
name = AskStr(name,"Please enter the class name");
if (name==0)
return;
struct_id = GetStrucIdByName(name+"_vtable");
if (struct_id != -1)
{
i = AskYN(0,form("A vtable structure for %s already exists. Are you sure you want to remake it?",name));
if (i==-1)
return;
if (i==1)
{
DelStruc(struct_id);
struct_id = AddStrucEx(-1,name+"_vtable",0);
}
}
else
struct_id = AddStrucEx(-1,name+"_vtable",0);
if (struct_id == -1)
Warning("Could not create the vtable structure!.\nPlease check the entered class name.");
bNameMethods = (1==AskYN(0,form("Would you like to assign auto names to the virtual methods (%s_virtXX)?",name)));
for (i=0;i<k;i++)
{
c = Dword(a+i*4);
if (bNameMethods && !hasName(GetFlags(c)))
MakeName(c,form("%s_virt%02X",name,i*4));
if (hasName(GetFlags(c)))
{
methName = Name(c);
if (substr(methName,0,1)=="?")
{
methName = substr(methName,1,strstr(methName,"@"));
}
}
else
methName = form("virt%02X",i*4);
e = AddStrucMember(struct_id,methName,i*4,FF_DWRD|FF_DATA,-1,4);
if (0!=e)
{
if (e!=-2 && e!=-1)
{
Warning(form("Error adding a vtable entry (%d)!",e));
return;
}
else if (substr(GetMemberName(struct_id, i*4),0,6)=="field_")
SetMemberName(struct_id, i*4, form("virt%02X",i*4));
}
SetMemberComment(struct_id,i*4,form("-> %08X, args: 0x%X",c,GetFrameArgsSize(c)),1);
}
MakeName(a,"??_7"+name+"@@6B@");
}