Yo MPGH!
I guess that most of you won't know me, as I wasn't very active over here in the past due to some private reasons, but I said to myself: "Why not giving the guys over there a shot by releasing some stuff and see what they do with it.".
Beforehand: if you have no clue about how to use IDA and what the fuck I'm talking about: please don't tire me with silly questions.
I started divin' into CF reverse engineering (with varying activity) about a month ago and the first thing I realized was that, in many cases, it's very easy to find out what the game's code is good for due to the nice "commenting" with debug strings used by the CrossFire developers literally everywhere. There's one function I use to call
LogActionf (probably no perfect name as it actually does not log anything but pushes the given format string into CrossFire's debug console), that is used all over the code, probably prototyped somehow like this:
Code:
void LogActionf(const char *szFormat, ...);
Furthermore, there is the following format string used in 543 cases
Code:
LT ERROR: %s returned %s (%s)
where the first placeholder is filled with the calling function's name, the second one with some kind of error "descriptor" (e.g.
LT_NOTFOUND) and the third one with a string describing the error. To this format string, in turn, a pointer is addressed which I labeled
pLtErrorSReturn. Using this simple logic the script later in this post is able to rename a whole bunch of functions in the game to something better fitting than
sub_XXXXXX.
tl;dr:
- Locate format string (LT ERROR: %s returned %s (%s))
- Check X-Refs for a data reference (pointer to format-string)
- Take a look at the X-Refs to the pointer and see what function it is pushed to (will be LogActionf)
- Rename the format-string ptr to pLtErrorSReturn and the function it is pushed to to LogActionf
- Fire script and admire how it gives ~300 functions correct names
Code:
#include <idc.idc>
static main()
{
auto strAddr = LocByName("pLtErrorSReturn"),
logFunAddr = LocByName("LogActionf"),
curXref,
curAddr,
curTraceAddr,
firstParamFound,
funcNameStrAddr,
regId,
funAddr,
funName,
oldFunName;
for(curXref = DfirstB(strAddr)
; curXref != BADADDR
; curXref = DnextB(strAddr, curXref))
{
// locate function name
// ------------------------------------------------
// Check mnemonic
if (GetMnem(curXref) != "mov"
|| GetOpType(curXref, 0) != o_reg
|| GetOpType(curXref, 1) != o_mem)
{
Message("[0x%08X] referencing instruction is not \"mov eXx, [mem]\"!\n", curXref);
continue;
}
// Find LogActionf call (max 50 bytes away)
for (curAddr = curXref
; curAddr != BADADDR
; curAddr = NextAddr(curAddr))
{
// Is call to log fun? Break loop
if (GetMnem(curAddr) == "call"
&& GetOpnd(curAddr, 0) == "LogActionf")
{
break;
}
if (curAddr - curXref > 50)
{
Message("[0x%08X] too many steps till LogActionf call\n", curXref);
return;
}
}
// Find LogActionf's second parameter
for (firstParamFound = 0; curAddr != BADADDR; curAddr = PrevAddr(curAddr))
{
// Conditional?
if (RfirstB0(curAddr) != BADADDR)
{
Message("[0x%08X] conditional situation\n", curAddr);
break;
}
// Desired parameter?
if (GetMnem(curAddr) == "push")
{
if (!firstParamFound)
firstParamFound = 1;
else
{
funcNameStrAddr = 0;
// In case the operand is a register, we have to trace what
// is written into it
if (GetOpType(curAddr, 0) == o_reg)
{
regId = GetOperandValue(curAddr, 0);
for (curTraceAddr = curAddr
; curTraceAddr != BADADDR
; curTraceAddr = PrevAddr(curTraceAddr))
{
// Does instruction write into our register?
if (GetOpType(curTraceAddr, 0) == o_reg
&& GetOperandValue(curTraceAddr, 0) == regId
&& GetOpType(curTraceAddr, 1) == o_mem)
{
funcNameStrAddr = Dword(GetOperandValue(curTraceAddr, 1));
break;
}
// Max. trace distance = 50 non-conditional bytes
if (curAddr - curTraceAddr > 50 || RfirstB0(curTraceAddr) != BADADDR)
{
Message("[0x%08X] conditional situation or trace width exceeded\n",
curTraceAddr);
break;
}
}
}
else if (GetOpType(curAddr, 0) == o_imm)
funcNameStrAddr = GetOperandValue(curAddr, 0);
else
{
Message("[0x%08X] unsupported operand\n", curAddr);
return;
}
// check if string contains spaces (filter out names with comments)
funName = 0;
if (funcNameStrAddr)
{
if (strstr(GetString(funcNameStrAddr, -1, ASCSTR_C), " ") != -1)
{
Message("[0x%08X] no valid function name\n", curAddr);
funcNameStrAddr = 0;
}
else
funName = GetString(funcNameStrAddr, -1, ASCSTR_C);
}
break;
}
}
}
// rename function
// ------------------------------------------------
// only if function's name could be determined
if (funName)
{
oldFunName = GetFunctionName(curXref);
if (oldFunName != "" && !hasName(GetFlags(LocByName(oldFunName))))
{
// make name unique
while (LocByName(funName) != BADADDR)
funName = funName + "_";
Message("[0x%08X] %s => %s\n", LocByName(oldFunName), oldFunName, funName);
MakeNameEx(LocByName(oldFunName), funName, SN_CHECK | SN_NON_AUTO);
}
}
// ------------------------------------------------
}
Message("Done, leaving.\n");
} // ==> main
Hope you have some use for that!
Greets,
Ende! - **** team