ds@2392: // ----------------------------------------------------------------------------- ds@2392: // - IDA Pro Script - ds@2392: // Name: gcc_fpic.idc ds@2392: // By: Damaged Soul ds@2392: // Desc: Add references for strings, variables, and other data that seem mangled ds@2392: // due to GCC's -fPIC option and the .got section of an x86 ELF binary. ds@2392: // ds@2392: // Version History ds@2392: // 1.0 [2007-11-22] ds@2392: // - Initial Version ds@2392: // 1.1 [2008-05-02] ds@2392: // - Now works with GCC 4.x compiled binaries ds@2392: // 1.2 [2008-11-06] ds@2392: // - Now works with GCC 4.3 compiled binaries ds@2392: // - Fixed: Redefining alignment blocks as data caused IDA to pop up ds@2392: // an annoying warning ds@2392: // ----------------------------------------------------------------------------- ds@2392: ds@2392: #include ds@2392: ds@2392: #define REG_NONE 0 ds@2392: #define REG_EAX 1 ds@2392: #define REG_EBX 2 ds@2392: #define REG_ECX 3 ds@2392: #define REG_EDX 4 ds@2392: ds@2392: #define OP_ADD 1 ds@2392: #define OP_SUB 2 ds@2392: ds@2392: #define OPFORMAT_STRING 1 ds@2392: #define OPFORMAT_DEREF 2 ds@2392: #define OPFORMAT_NORMAL 3 ds@2392: ds@2392: static main() ds@2392: { ds@2392: auto filetype, compiler, demang, strPrefix; ds@2392: auto seg, codeStart, codeEnd, roStart, roEnd, gotStart, gotEnd, opformat; ds@2392: auto addr, funcend, whichop, reg, tempstr; ds@2392: auto operand1, operand2, opval, opstr, dataAddr, flags, count; ds@2392: ds@2392: SetStatus(IDA_STATUS_WORK); ds@2392: Message("Starting scan for -fPIC code...\n"); ds@2392: ds@2392: /* Check file type and compiler */ ds@2392: filetype = GetShortPrm(INF_FILETYPE); ds@2392: if (filetype != FT_ELF) ds@2392: { ds@2392: Message("Scan aborted. Input file must be using ELF binary format!\n"); ds@2392: SetStatus(IDA_STATUS_READY); ds@2392: return; ds@2392: } ds@2392: compiler = GetCharPrm(INF_COMPILER); ds@2392: if (compiler != COMP_GNU) ds@2392: { ds@2392: Message("Scan aborted. Input file must have been compiled with GNU GCC/G++!\n"); ds@2392: SetStatus(IDA_STATUS_READY); ds@2392: return; ds@2392: } ds@2392: ds@2392: /* ds@2392: * If the GCC v3.x names option is not set, then set it first. ds@2392: * ds@2392: * :TODO: Need to change this if GCC 2.95 support is to be added. ds@2392: */ ds@2392: demang = GetCharPrm(INF_DEMNAMES); ds@2392: if ((demang & DEMNAM_GCC3) == 0) ds@2392: { ds@2392: SetCharPrm(INF_DEMNAMES, demang | DEMNAM_GCC3); ds@2392: } ds@2392: ds@2392: /* Get string prefix */ ds@2392: strPrefix = GetCharPrm(INF_ASCIIPREF); ds@2392: ds@2392: /* Get address of first section in binary */ ds@2392: seg = FirstSeg(); ds@2392: ds@2392: /* Iterate through all sections and get address of .text, .rodata, and .got */ ds@2392: while (seg != BADADDR) ds@2392: { ds@2392: if (SegName(seg) == ".text") ds@2392: { ds@2392: codeStart = seg; ds@2392: codeEnd = NextSeg(seg); ds@2392: Message("%08.8Xh - %08.8Xh: .text\n", codeStart, codeEnd); ds@2392: } ds@2392: else if (SegName(seg) == ".rodata") ds@2392: { ds@2392: roStart = seg; ds@2392: roEnd = NextSeg(seg); ds@2392: Message("%08.8Xh - %08.8Xh: .rodata\n", roStart, roEnd); ds@2392: } ds@2392: else if (SegName(seg) == "abs") ds@2392: { ds@2392: addr = FindText(seg, SEARCH_DOWN|SEARCH_CASE|SEARCH_NOSHOW, 0, 0, "_GLOBAL_OFFSET_TABLE_"); ds@2392: gotStart = Dword(addr); ds@2392: gotEnd = NextSeg(gotStart); ds@2392: Message("%08.8Xh - %08.8Xh: .got\n", gotStart, gotEnd); ds@2392: } ds@2392: ds@2392: seg = NextSeg(seg); ds@2392: } ds@2392: ds@2392: addr = codeStart; ds@2392: funcend = -1; ds@2392: count = 0; ds@2392: ds@2392: /** ds@2392: * Go through .text section while looking for anything like [e?x+blah] or [e?x-blah]. ds@2392: * ds@2392: * The eax, ebx, ecx, or edx registers are used for storing the address of the .got ds@2392: * section with -fPIC code. An offset, either negative or positive is added to e?x. ds@2392: * This results in the address of a string, variable, or other type of data. ds@2392: * ds@2392: * In order to determine which register .got will be stored in, one can look at which ds@2392: * __i686.get_pc_thunk.? function is called near the beginning of each function. The ds@2392: * suffix on this function is either ax, bx, cx, or dx and corresponds to eax, ebx, ds@2392: * ecx, or edx. ds@2392: */ ds@2392: while (addr <= codeEnd) ds@2392: { ds@2392: operand1 = GetOpnd(addr, 0); ds@2392: operand2 = GetOpnd(addr, 1); ds@2392: whichop = -1; ds@2392: ds@2392: /* Get function end */ ds@2392: if (FindFuncEnd(addr) != funcend) ds@2392: { ds@2392: reg = REG_NONE; ds@2392: funcend = FindFuncEnd(addr); ds@2392: } ds@2392: ds@2392: /* Get current PIC register */ ds@2392: reg = GetPICRegister(addr, reg, funcend); ds@2392: ds@2392: if (reg != REG_NONE) ds@2392: { ds@2392: /* Search first operand for substring containing PIC register and either a plus or minus sign */ ds@2392: if (strstr(operand1, GetPICSearchString(reg, OP_ADD)) != -1 || strstr(operand1, GetPICSearchString(reg, OP_SUB)) != -1) ds@2392: { ds@2392: whichop = 0; ds@2392: } ds@2392: ds@2392: /* Search second operand for substring containing PIC register and either a plus or minus sign */ ds@2392: if (strstr(operand2, GetPICSearchString(reg, OP_ADD)) != -1 || strstr(operand2, GetPICSearchString(reg, OP_SUB)) != -1) ds@2392: { ds@2392: whichop = 1; ds@2392: } ds@2392: } ds@2392: ds@2392: if (whichop != -1) ds@2392: { ds@2392: /* Get .got offset */ ds@2392: opval = GetOperandValue(addr, whichop); ds@2392: ds@2392: /* Get address inside .got */ ds@2392: dataAddr = gotStart + opval; ds@2392: ds@2392: /* Get name at address if it exists */ ds@2392: opstr = Name(dataAddr); ds@2392: ds@2392: /* If name doesn't exist then... */ ds@2392: if (opstr == "") ds@2392: { ds@2392: /* ds@2392: * Check address to see if it falls in .rodata section. ds@2392: * If it does, then try to make it a string which will automatically give it a name. ds@2392: */ ds@2392: if (dataAddr >= roStart && dataAddr <= roEnd && MakeStr(dataAddr, BADADDR)) ds@2392: { ds@2392: /* Get automatically created name */ ds@2392: opstr = Name(dataAddr); ds@2392: opformat = OPFORMAT_STRING; ds@2392: ds@2392: /* ds@2392: * Sometimes IDA creates a string successfully but not exactly in the right place. ds@2392: * Uncertain as to why this is (perhaps an IDA bug?), but usually the string in ds@2392: * question is a bunch of garbage. ds@2392: */ ds@2392: if (opstr == "") ds@2392: { ds@2392: /* Create a name based on the address */ ds@2392: opstr = form("unk_%X", dataAddr); ds@2392: if (strstr(GetDisasm(dataAddr), "align") != -1) ds@2392: { ds@2392: MakeUnkn(dataAddr, DOUNK_SIMPLE); ds@2392: } ds@2392: MakeNameEx(dataAddr, opstr, SN_NOCHECK|SN_NOLIST|SN_NOWARN); ds@2392: opformat = OPFORMAT_DEREF; ds@2392: } ds@2392: } ds@2392: else ds@2392: { ds@2392: /* ds@2392: * If address didn't fall into .rodata and the string creation was unsuccessful, ds@2392: * then try to read the address at 'addr' and get the name of that. ds@2392: */ ds@2392: opstr = Name(Dword(dataAddr)); ds@2392: if (opstr == "") ds@2392: { ds@2392: /* If name doesn't exist for that, then create name based on address */ ds@2392: opstr = form("unk_%X", dataAddr); ds@2392: if (strstr(GetDisasm(dataAddr), "align") != -1) ds@2392: { ds@2392: MakeUnkn(dataAddr, DOUNK_SIMPLE); ds@2392: } ds@2392: MakeNameEx(dataAddr, opstr, SN_NOCHECK|SN_NOLIST|SN_NOWARN); ds@2392: opformat = OPFORMAT_DEREF; ds@2392: } ds@2392: else ds@2392: { ds@2392: /* If the name did exist at this point, then use it */ ds@2392: opformat = OPFORMAT_NORMAL; ds@2392: } ds@2392: } ds@2392: } ds@2392: else ds@2392: { ds@2392: /* If the name at the original address does exist then ... */ ds@2392: ds@2392: flags = GetFlags(dataAddr); ds@2392: ds@2392: /* ds@2392: * If this address falls into .rodata section and is considered an existing string ds@2392: * then the replacement operand needs to shown as a string. ds@2392: */ ds@2392: if (dataAddr >= roStart && dataAddr <= roEnd && strPrefix != "" && strstr(opstr, strPrefix) == 0) ds@2392: { ds@2392: opformat = OPFORMAT_STRING; ds@2392: } ds@2392: else ds@2392: { ds@2392: opformat = OPFORMAT_DEREF; ds@2392: } ds@2392: } ds@2392: ds@2392: /* ds@2392: * Try to demangle the name that was found or created above and print it as sort of ds@2392: * a status message to show the the script is still doing work as this usually ds@2392: * can take awhile. ds@2392: */ ds@2392: tempstr = Demangle(opstr, INF_LONG_DN); ds@2392: if (tempstr != "") ds@2392: { ds@2392: Message("%8.8Xh: %s\n", addr, tempstr); ds@2392: } ds@2392: else ds@2392: { ds@2392: Message("%8.8Xh: %s\n", addr, opstr); ds@2392: } ds@2392: ds@2392: /* ds@2392: * The operand that was found to have the PIC register will now be replaced ds@2392: * with more descriptive text. The format of this text depends upon the value ds@2392: * of opformat. ds@2392: */ ds@2392: OpAlt(addr, whichop, DoOperandFormat(opformat, opstr)); ds@2392: ds@2392: count++; ds@2392: } ds@2392: ds@2392: addr++; ds@2392: } ds@2392: ds@2392: Message("Scan for PIC code is complete! Found %d data items referenced via PIC register.\n", count); ds@2392: Message("Please re-open the database so that newly found strings will appear in the Strings window\n"); ds@2392: SetStatus(IDA_STATUS_READY); ds@2392: } ds@2392: ds@2392: /* ds@2392: * Tries to determine the current PIC register given the current address being processed ds@2392: * and the previous PIC register. ds@2392: */ ds@2392: static GetPICRegister(addr, previous, funcend) ds@2392: { ds@2392: auto assemblyStr, idx, reg, ab; ds@2392: assemblyStr = GetDisasm(addr); ds@2392: ds@2392: if ((idx = strstr(assemblyStr, "call __i686_get_pc_thunk_")) != -1) ds@2392: { ds@2392: /* 28 is the length of the above string */ ds@2392: reg = substr(assemblyStr, idx + 28, 30); ds@2392: } ds@2392: else if (strstr(assemblyStr, "call $+5") != -1) ds@2392: { ds@2392: assemblyStr = GetDisasm(NextHead(addr, funcend)); ds@2392: reg = substr(assemblyStr, 9, 11); ds@2392: } ds@2392: ds@2392: if (reg == "ax") ds@2392: { ds@2392: return REG_EAX; ds@2392: } ds@2392: else if (reg == "bx") ds@2392: { ds@2392: return REG_EBX; ds@2392: } ds@2392: else if (reg == "cx") ds@2392: { ds@2392: return REG_ECX; ds@2392: } ds@2392: else if (reg == "dx") ds@2392: { ds@2392: return REG_EDX; ds@2392: } ds@2392: ds@2392: return previous; ds@2392: } ds@2392: ds@2392: /* ds@2392: * Returns a string that is used as a substring search containing the specified ds@2392: * PIC register and operator (+ or -). ds@2392: */ ds@2392: static GetPICSearchString(reg, operator) ds@2392: { ds@2392: if (reg == REG_EAX) ds@2392: { ds@2392: if (operator == OP_ADD) ds@2392: { ds@2392: return "[eax+"; ds@2392: } ds@2392: else if (operator == OP_SUB) ds@2392: { ds@2392: return "[eax-"; ds@2392: } ds@2392: } ds@2392: else if (reg == REG_EBX) ds@2392: { ds@2392: if (operator == OP_ADD) ds@2392: { ds@2392: return "[ebx+"; ds@2392: } ds@2392: else if (operator == OP_SUB) ds@2392: { ds@2392: return "[ebx-"; ds@2392: } ds@2392: } ds@2392: else if (reg == REG_ECX) ds@2392: { ds@2392: if (operator == OP_ADD) ds@2392: { ds@2392: return "[ecx+"; ds@2392: } ds@2392: else if (operator == OP_SUB) ds@2392: { ds@2392: return "[ecx-"; ds@2392: } ds@2392: } ds@2392: else if (reg == REG_EDX) ds@2392: { ds@2392: if (operator == OP_ADD) ds@2392: { ds@2392: return "[edx+"; ds@2392: } ds@2392: else if (operator == OP_SUB) ds@2392: { ds@2392: return "[edx-"; ds@2392: } ds@2392: } ds@2392: } ds@2392: ds@2392: /* ds@2392: * Returns a formatted string depending upon the value of the format param. ds@2392: * This will be the replacement for the operand containing the PIC register. ds@2392: * ds@2392: * OPFORMAT_STRING: The referenced data address is a string in the .rodata section. ds@2392: * OPFORMAT_DEREF: The referenced data address has a name. The PIC register operand is ds@2392: * deferenced so the dereference brackets are shown in the returned string. ds@2392: * OPFORMAT_NORMAL: The referenced data address does not have a name. But upon reading ds@2392: * the address at the referenced data address, it is discovered that that ds@2392: * address does have a name. There are no dereference brackets because the ds@2392: * referenced data address had to be read in order to discover a name. ds@2392: */ ds@2392: static DoOperandFormat(format, str) ds@2392: { ds@2392: if (format == OPFORMAT_STRING) ds@2392: { ds@2392: return form("offset %s", str); ds@2392: } ds@2392: else if (format == OPFORMAT_DEREF) ds@2392: { ds@2392: return form("[ds:%s]", str); ds@2392: } ds@2392: else if (format == OPFORMAT_NORMAL) ds@2392: { ds@2392: return form("ds:%s", str); ds@2392: } ds@2392: else ds@2392: { ds@2392: return str; ds@2392: } ds@2392: }