commit
69ef04d5e6
@ -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 ~
|
@ -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;
|
||||
}
|
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@");
|
||||
}
|
Loading…
Reference in New Issue