1 // -----------------------------------------------------------------------------
5 // Desc: Add references for strings, variables, and other data that seem mangled
6 // due to GCC's -fPIC option and the .got section of an x86 ELF binary.
12 // - Now works with GCC 4.x compiled binaries
14 // - Now works with GCC 4.3 compiled binaries
15 // - Fixed: Redefining alignment blocks as data caused IDA to pop up
16 // an annoying warning
17 // -----------------------------------------------------------------------------
30 #define OPFORMAT_STRING 1
31 #define OPFORMAT_DEREF 2
32 #define OPFORMAT_NORMAL 3
36 auto filetype, compiler, demang, strPrefix;
37 auto seg, codeStart, codeEnd, roStart, roEnd, gotStart, gotEnd, opformat;
38 auto addr, funcend, whichop, reg, tempstr;
39 auto operand1, operand2, opval, opstr, dataAddr, flags, count;
41 SetStatus(IDA_STATUS_WORK);
42 Message("Starting scan for -fPIC code...\n");
44 /* Check file type and compiler */
45 filetype = GetShortPrm(INF_FILETYPE);
46 if (filetype != FT_ELF)
48 Message("Scan aborted. Input file must be using ELF binary format!\n");
49 SetStatus(IDA_STATUS_READY);
52 compiler = GetCharPrm(INF_COMPILER);
53 if (compiler != COMP_GNU)
55 Message("Scan aborted. Input file must have been compiled with GNU GCC/G++!\n");
56 SetStatus(IDA_STATUS_READY);
61 * If the GCC v3.x names option is not set, then set it first.
63 * :TODO: Need to change this if GCC 2.95 support is to be added.
65 demang = GetCharPrm(INF_DEMNAMES);
66 if ((demang & DEMNAM_GCC3) == 0)
68 SetCharPrm(INF_DEMNAMES, demang | DEMNAM_GCC3);
71 /* Get string prefix */
72 strPrefix = GetCharPrm(INF_ASCIIPREF);
74 /* Get address of first section in binary */
77 /* Iterate through all sections and get address of .text, .rodata, and .got */
78 while (seg != BADADDR)
80 if (SegName(seg) == ".text")
83 codeEnd = NextSeg(seg);
84 Message("%08.8Xh - %08.8Xh: .text\n", codeStart, codeEnd);
86 else if (SegName(seg) == ".rodata")
90 Message("%08.8Xh - %08.8Xh: .rodata\n", roStart, roEnd);
92 else if (SegName(seg) == "abs")
94 addr = FindText(seg, SEARCH_DOWN|SEARCH_CASE|SEARCH_NOSHOW, 0, 0, "_GLOBAL_OFFSET_TABLE_");
95 gotStart = Dword(addr);
96 gotEnd = NextSeg(gotStart);
97 Message("%08.8Xh - %08.8Xh: .got\n", gotStart, gotEnd);
108 * Go through .text section while looking for anything like [e?x+blah] or [e?x-blah].
110 * The eax, ebx, ecx, or edx registers are used for storing the address of the .got
111 * section with -fPIC code. An offset, either negative or positive is added to e?x.
112 * This results in the address of a string, variable, or other type of data.
114 * In order to determine which register .got will be stored in, one can look at which
115 * __i686.get_pc_thunk.? function is called near the beginning of each function. The
116 * suffix on this function is either ax, bx, cx, or dx and corresponds to eax, ebx,
119 while (addr <= codeEnd)
121 operand1 = GetOpnd(addr, 0);
122 operand2 = GetOpnd(addr, 1);
125 /* Get function end */
126 if (FindFuncEnd(addr) != funcend)
129 funcend = FindFuncEnd(addr);
132 /* Get current PIC register */
133 reg = GetPICRegister(addr, reg, funcend);
137 /* Search first operand for substring containing PIC register and either a plus or minus sign */
138 if (strstr(operand1, GetPICSearchString(reg, OP_ADD)) != -1 || strstr(operand1, GetPICSearchString(reg, OP_SUB)) != -1)
143 /* Search second operand for substring containing PIC register and either a plus or minus sign */
144 if (strstr(operand2, GetPICSearchString(reg, OP_ADD)) != -1 || strstr(operand2, GetPICSearchString(reg, OP_SUB)) != -1)
152 /* Get .got offset */
153 opval = GetOperandValue(addr, whichop);
155 /* Get address inside .got */
156 dataAddr = gotStart + opval;
158 /* Get name at address if it exists */
159 opstr = Name(dataAddr);
161 /* If name doesn't exist then... */
165 * Check address to see if it falls in .rodata section.
166 * If it does, then try to make it a string which will automatically give it a name.
168 if (dataAddr >= roStart && dataAddr <= roEnd && MakeStr(dataAddr, BADADDR))
170 /* Get automatically created name */
171 opstr = Name(dataAddr);
172 opformat = OPFORMAT_STRING;
175 * Sometimes IDA creates a string successfully but not exactly in the right place.
176 * Uncertain as to why this is (perhaps an IDA bug?), but usually the string in
177 * question is a bunch of garbage.
181 /* Create a name based on the address */
182 opstr = form("unk_%X", dataAddr);
183 if (strstr(GetDisasm(dataAddr), "align") != -1)
185 MakeUnkn(dataAddr, DOUNK_SIMPLE);
187 MakeNameEx(dataAddr, opstr, SN_NOCHECK|SN_NOLIST|SN_NOWARN);
188 opformat = OPFORMAT_DEREF;
194 * If address didn't fall into .rodata and the string creation was unsuccessful,
195 * then try to read the address at 'addr' and get the name of that.
197 opstr = Name(Dword(dataAddr));
200 /* If name doesn't exist for that, then create name based on address */
201 opstr = form("unk_%X", dataAddr);
202 if (strstr(GetDisasm(dataAddr), "align") != -1)
204 MakeUnkn(dataAddr, DOUNK_SIMPLE);
206 MakeNameEx(dataAddr, opstr, SN_NOCHECK|SN_NOLIST|SN_NOWARN);
207 opformat = OPFORMAT_DEREF;
211 /* If the name did exist at this point, then use it */
212 opformat = OPFORMAT_NORMAL;
218 /* If the name at the original address does exist then ... */
220 flags = GetFlags(dataAddr);
223 * If this address falls into .rodata section and is considered an existing string
224 * then the replacement operand needs to shown as a string.
226 if (dataAddr >= roStart && dataAddr <= roEnd && strPrefix != "" && strstr(opstr, strPrefix) == 0)
228 opformat = OPFORMAT_STRING;
232 opformat = OPFORMAT_DEREF;
237 * Try to demangle the name that was found or created above and print it as sort of
238 * a status message to show the the script is still doing work as this usually
241 tempstr = Demangle(opstr, INF_LONG_DN);
244 Message("%8.8Xh: %s\n", addr, tempstr);
248 Message("%8.8Xh: %s\n", addr, opstr);
252 * The operand that was found to have the PIC register will now be replaced
253 * with more descriptive text. The format of this text depends upon the value
256 OpAlt(addr, whichop, DoOperandFormat(opformat, opstr));
264 Message("Scan for PIC code is complete! Found %d data items referenced via PIC register.\n", count);
265 Message("Please re-open the database so that newly found strings will appear in the Strings window\n");
266 SetStatus(IDA_STATUS_READY);
270 * Tries to determine the current PIC register given the current address being processed
271 * and the previous PIC register.
273 static GetPICRegister(addr, previous, funcend)
275 auto assemblyStr, idx, reg, ab;
276 assemblyStr = GetDisasm(addr);
278 if ((idx = strstr(assemblyStr, "call __i686_get_pc_thunk_")) != -1)
280 /* 28 is the length of the above string */
281 reg = substr(assemblyStr, idx + 28, 30);
283 else if (strstr(assemblyStr, "call $+5") != -1)
285 assemblyStr = GetDisasm(NextHead(addr, funcend));
286 reg = substr(assemblyStr, 9, 11);
293 else if (reg == "bx")
297 else if (reg == "cx")
301 else if (reg == "dx")
310 * Returns a string that is used as a substring search containing the specified
311 * PIC register and operator (+ or -).
313 static GetPICSearchString(reg, operator)
317 if (operator == OP_ADD)
321 else if (operator == OP_SUB)
326 else if (reg == REG_EBX)
328 if (operator == OP_ADD)
332 else if (operator == OP_SUB)
337 else if (reg == REG_ECX)
339 if (operator == OP_ADD)
343 else if (operator == OP_SUB)
348 else if (reg == REG_EDX)
350 if (operator == OP_ADD)
354 else if (operator == OP_SUB)
362 * Returns a formatted string depending upon the value of the format param.
363 * This will be the replacement for the operand containing the PIC register.
365 * OPFORMAT_STRING: The referenced data address is a string in the .rodata section.
366 * OPFORMAT_DEREF: The referenced data address has a name. The PIC register operand is
367 * deferenced so the dereference brackets are shown in the returned string.
368 * OPFORMAT_NORMAL: The referenced data address does not have a name. But upon reading
369 * the address at the referenced data address, it is discovered that that
370 * address does have a name. There are no dereference brackets because the
371 * referenced data address had to be read in order to discover a name.
373 static DoOperandFormat(format, str)
375 if (format == OPFORMAT_STRING)
377 return form("offset %s", str);
379 else if (format == OPFORMAT_DEREF)
381 return form("[ds:%s]", str);
383 else if (format == OPFORMAT_NORMAL)
385 return form("ds:%s", str);