(num_inst));
292 |
293 | return true;
294 | }
295 |
296 |
297 | void graph_info_t::destroy(graph_info_t *gi)
298 | {
299 | // FIXME: never called
300 | for(auto it = instances.begin() ; it != instances.end() ; ++it)
301 | {
302 | if (*it == gi)
303 | {
304 | instances.erase(it);
305 | break;
306 | }
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | _ _ ______ _____ _ __ __ _
3 | | | | | | ___ \ / __ \ | | \ \ / / | |
4 | | |_| | _____ _| |_/ /__ _ _ _ ___| / \/ ___ __| | ___ \ V / _ __ | | ___ _ __ ___ _ __
5 | | _ |/ _ \ \/ / // _` | | | / __| | / _ \ / _` |/ _ \/ \| '_ \| |/ _ \| '__/ _ \ '__|
6 | | | | | __/> <| |\ \ (_| | |_| \__ \ \__/\ (_) | (_| | __/ /^\ \ |_) | | (_) | | | __/ |
7 | \_| |_/\___/_/\_\_| \_\__,_|\__, |___/\____/\___/ \__,_|\___\/ \/ .__/|_|\___/|_| \___|_|
8 | __/ | | |
9 | |___/ |_|
10 | ============================================================================
11 |
12 | [](http://www.gnu.org/licenses/gpl-3.0)
13 | [](https://codeclimate.com/github/REhints/HexRaysCodeXplorer)
14 | [](https://codeclimate.com/github/REhints/HexRaysCodeXplorer)
15 |
16 | The Hex-Rays Decompiler plugin for better code navigation in RE process. CodeXplorer automates code REconstruction of C++ applications or modern malware like Stuxnet, Flame, Equation, Animal Farm ... :octocat:
17 |
18 | The CodeXplorer plugin is one of the [first publicly available](https://www.hex-rays.com/products/decompiler/manual/third_party.shtml) Hex-Rays Decompiler plugins. We keep updated this project [since summer of 2013](https://www.hex-rays.com/contests/2013/) and continue contributing new features frequently. Also most interesting feutures of CodeXplorer have been presented on numerous security conferences like: REcon, ZeroNights, H2HC, NSEC and BHUS :space_invader:
19 |
20 | __Contributors__:
21 |
22 | Alex Matrosov ([@matrosov](https://github.com/matrosov))
23 |
24 | Eugene Rodionov ([@rodionov](https://github.com/rodionov))
25 |
26 | Rodrigo Branco ([@rrbranco](https://github.com/rrbranco))
27 |
28 | Gabriel Barbosa ([@gabrielnb](https://github.com/gabrielnb))
29 |
30 | __Supported versions of Hex-Rays products:__ everytime we focus on last versions of IDA and Decompiler because trying to use new interesting features in new SDK releases. It's also mean we tested just on last versions of Hex-Rays products and not guaranteed stable work on previous ones.
31 |
32 | __Why not IdaPython:__ all code developed on C/C++ because it's more stable way to support complex plugin for Hex-Rays Decompiler.
33 |
34 | __Supported Platforms:__ x86/x64 for Win, Linux and Mac.
35 |
36 | __HexRaysCodeXplorer__ - Hex-Rays Decompiler plugin for easier code navigation. Right-click context menu in the Pseudocode window shows CodeXplorer plugin commands:
37 |
38 | 
39 |
40 | :gem: __Here are the main features of the CodeXplorer plugin:__ :gem:
41 |
42 | * ***Automatic type REconstruction*** for C++ objects. To be able to reconstruct a type using HexRaysCodeXplorer one needs to select the variable holding pointer to the instance of position independed code or to an object and by right-button mouse click select from the context menu «REconstruct Type» option:
43 |
44 | 
45 |
46 | The reconstructed structure is displayed in “Output window”. Detailed information about type Reconstruction feature is provided in the blog post “[Type REconstruction in HexRaysCodeXplorer](http://rehints.com/2013-09-02-Type-REconstruction-in-HexRaysCodeXplorer.html)”.
47 |
48 | Also CodeXplorer plugin supports auto REconstruction type into IDA local types storage.
49 |
50 | 
51 |
52 | * ***Virtual function table identification*** - automatically identifies references to virtual function tables during type reconstruction. When a reference to a virtual function table is identified the plugin generates a corresponding C-structure. As shown below during reconstructing `struct_local_data_storage` two virtual function tables were identified and, as a result, two corresponding structures were generated: `struct_local_data_storage_VTABLE_0` and `struct_local_data_storage_VTABLE_4`.
53 |
54 | 
55 |
56 | * ***C-tree graph visualization*** – a special tree-like structure representing a decompiled routine in citem_t terms (hexrays.hpp). Useful feature for understanding how the decompiler works. The highlighted graph node corresponds to the current cursor position in the HexRays Pseudocode window:
57 |
58 | 
59 |
60 | * ***Ctree Item View*** – show ctree representation for highlighted element:
61 |
62 | 
63 |
64 | * ***Extract Ctrees to File*** – dump calculate SHA1 hash and dump all ctrees to file.
65 |
66 | 
67 |
68 | * ***Extract Types to File*** – dump all types information (include reconstructed types) into file.
69 |
70 | * ***Navigation through virtual function calls*** in HexRays Pseudocode window. After representing C++ objects by C-structures this feature make possible navigation by mouse clicking to the virtual function calls as structure fields:
71 |
72 | 
73 |
74 | * ***Jump to Disasm*** - small feature for navigate to assembly code into "IDA View window" from current Pseudocode line position. It is help to find a place in assembly code associated with decompiled line.
75 |
76 | 
77 |
78 | * ***Object Explorer*** – useful interface for navigation through virtual tables (VTBL) structures. Object Explorer outputs VTBL information into IDA custom view window. The output window is shown by choosing «Object Explorer» option in right-button mouse click context menu:
79 |
80 | 
81 |
82 | __Object Explorer supports following features:__
83 | * Auto structures generation for VTBL into IDA local types
84 |
85 | * Navigation in virtual table list and jump to VTBL address into "IDA View" window by click
86 |
87 | * Show hints for current position in virtual table list
88 |
89 | * Shows cross-references list by click into menu on "Show XREFS to VTBL"
90 |
91 | 
92 |
93 | * Support auto parsing RTTI objects:
94 |
95 | 
96 |
97 | __The Batch mode contains following features:__
98 |
99 | * Batch mode - useful feature to use CodeXplorer for processing multiple files without any interaction from user. We add this feature after Black Hat research in 2015 for processing 2 millions samples.
100 |
101 | ```
102 | Example (dump types and ctrees for functions with name prefix "crypto_"):
103 | idaq.exe -OHexRaysCodeXplorer:dump_types:dump_ctrees:CRYPTOcrypto_path_to_idb
104 | ```
105 |
106 | __Compiling__:
107 |
108 | ***Windows:***
109 | * Open the solution in Visual Studio
110 | * Open file `src/HexRaysCodeXplorer/PropertySheet.props` in notepad(++) and update values of `IDADIR` and `IDASDK` paths to point to IDA installation path and IDA7 SDK path accordingly. HexRays SDK should be in `$IDADIR\plugins\hexrays_sdk` (like by default)
111 | * Build `Release | x64` and `Release x64 | x64` configurations
112 |
113 | ***Linux***:
114 | * cd src/HexRaysCodeXplorer/
115 | * IDA_DIR= IDA_SDK= EA64=0 make -f makefile.lnx
116 | * IDA_DIR= IDA_SDK= EA64=0 make -f makefile.lnx install
117 |
118 | ***Mac***:
119 | * cd src/HexRaysCodeXplorer/
120 | * IDA_DIR= IDA_SDK= make -f makefile.mac
121 | * The Mac makefile might need some hand editing, pull requests welcome!
122 | * IDA 7.0 `.pmc` file extension should be `.dylib`
123 | * bash$ `export IDA_DIR="/Applications/IDA\ Pro\ 7.0/ida.app/Contents/MacOS" && export IDA_SDK="/Applications/IDA\ Pro\ 7.0/ida.app/Contents/MacOS/idasdk" && make -f makefile7.mac`
124 | * Or open project in Xcode `HexRaysCodeXplorer.xcodeproj`
125 |
126 | ============================================================================
127 |
128 | __Conference talks about CodeXplorer plugin:__
129 | * **2015**
130 | * "Distributing the REconstruction of High-Level IR for Large Scale Malware Analysis", BHUS [[slides]](https://github.com/REhints/Publications/blob/master/Conferences/BH'2015/BH_2015.pdf)
131 | * "Object Oriented Code RE with HexraysCodeXplorer", NSEC [[slides]](https://github.com/REhints/Publications/raw/master/Conferences/Nsec'2015/nsec_2015.pdf)
132 | * **2014**
133 | * "HexRaysCodeXplorer: object oriented RE for fun and profit", H2HC [[slides]](https://github.com/REhints/Publications/blob/master/Conferences/ZeroNights'2013/ZN_2013_pdf.pdf)
134 | * **2013**
135 | * "HexRaysCodeXplorer: make object-oriented RE easier", ZeroNights [[slides]](https://github.com/REhints/Publications/blob/master/Conferences/ZeroNights'2013/ZN_2013_pdf.pdf)
136 | * "Reconstructing Gapz: Position-Independent Code Analysis Problem", REcon [[slides]](https://github.com/REhints/Publications/blob/master/Conferences/RECON'2013/RECON_2013.pdf)
137 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/Utility.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2016
2 | REhints
3 | All rights reserved.
4 |
5 | ==============================================================================
6 |
7 | This file is part of HexRaysCodeXplorer
8 |
9 | HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
10 | under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program. If not, see
21 | .
22 |
23 | ==============================================================================
24 | */
25 |
26 | #include "Common.h"
27 | #include "Utility.h"
28 |
29 | #include "Debug.h"
30 |
31 | #if defined (__LINUX__) || defined (__MAC__)
32 | #include "Linux.h"
33 | #endif
34 |
35 | bool compilerIs(const char *name)
36 | {
37 | comp_t vc = default_compiler();
38 | //qstring comp = get_compiler_name(vc); //fullname
39 | qstring comp = get_compiler_abbr(vc);
40 |
41 | if (comp == name)
42 | return true;
43 | return false;
44 | }
45 |
46 | bool idaapi show_string_in_custom_view(void *ud, const qstring& title, const qstring& str)
47 | {
48 | TWidget *widget = create_empty_widget(title.c_str());
49 | string_view_form_info_t *si = new string_view_form_info_t(widget);
50 | si->sv.push_back(simpleline_t(str));
51 |
52 | simpleline_place_t s1;
53 | simpleline_place_t s2(static_cast(si->sv.size()));
54 | si->cv = create_custom_viewer((title + "_").c_str(), &s1, &s2, &s1, nullptr, &si->sv, nullptr, nullptr, widget);
55 | si->codeview = create_code_viewer(si->cv, CDVF_NOLINES, widget);
56 | set_custom_viewer_handlers(si->cv, nullptr, si);
57 | display_widget(widget, WOPN_RESTORE);
58 |
59 | return false;
60 | }
61 |
62 | void split_qstring(const qstring &options, const qstring &splitter, qvector &result) {
63 | size_t start_pos = 0;
64 |
65 | do {
66 | size_t npos = options.find(splitter, start_pos);
67 | if (npos != -1) {
68 | if (npos != start_pos) {
69 | result.push_back(options.substr(start_pos, npos));
70 | }
71 | start_pos = npos + splitter.length();
72 | }
73 | else {
74 | qstring token = options.substr(start_pos);
75 | if (token.length() != 0)
76 | result.push_back(token);
77 | break;
78 | }
79 | } while (start_pos < options.length());
80 | }
81 |
82 |
83 | // SHA1 implementation
84 | #define SHA1CircularShift(bits,word)(((word) << (bits)) | ((word) >> (32-(bits))))
85 |
86 | void SHA1PadMessage(SHA1Context *);
87 | void SHA1ProcessMessageBlock(SHA1Context *);
88 |
89 | int SHA1Reset(SHA1Context *context)
90 | {
91 | if (!context)
92 | return shaNull;
93 |
94 | context->Length_Low = 0;
95 | context->Length_High = 0;
96 | context->Message_Block_Index = 0;
97 | context->Intermediate_Hash[0] = 0x67452301;
98 | context->Intermediate_Hash[1] = 0xEFCDAB89;
99 | context->Intermediate_Hash[2] = 0x98BADCFE;
100 | context->Intermediate_Hash[3] = 0x10325476;
101 | context->Intermediate_Hash[4] = 0xC3D2E1F0;
102 | context->Computed = 0;
103 | context->Corrupted = 0;
104 | return shaSuccess;
105 | }
106 |
107 | int SHA1Result(SHA1Context *context, uint8_t Message_Digest[SHA1HashSize])
108 | {
109 | int i;
110 | if (!context || !Message_Digest)
111 | return shaNull;
112 |
113 | if (context->Corrupted)
114 | return context->Corrupted;
115 |
116 | if (!context->Computed)
117 | {
118 | SHA1PadMessage(context);
119 | for (i = 0; i<64; ++i)
120 | context->Message_Block[i] = 0;
121 |
122 | context->Length_Low = 0; /* and clear length */
123 | context->Length_High = 0;
124 | context->Computed = 1;
125 | }
126 | for (i = 0; i < SHA1HashSize; ++i)
127 | Message_Digest[i] = context->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x03));
128 |
129 | return shaSuccess;
130 | }
131 |
132 | int SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned int length)
133 | {
134 | if (!length)
135 | {
136 | return shaSuccess;
137 | }
138 | if (!context || !message_array)
139 | {
140 | return shaNull;
141 | }
142 | if (context->Computed)
143 | {
144 | context->Corrupted = shaStateError;
145 | return shaStateError;
146 | }
147 | if (context->Corrupted)
148 | {
149 | return context->Corrupted;
150 | }
151 | while (length-- && !context->Corrupted)
152 | {
153 | context->Message_Block[context->Message_Block_Index++] =
154 | (*message_array & 0xFF);
155 | context->Length_Low += 8;
156 | if (context->Length_Low == 0)
157 | {
158 | context->Length_High++;
159 | if (context->Length_High == 0)
160 | context->Corrupted = 1;
161 | }
162 | if (context->Message_Block_Index == 64)
163 | SHA1ProcessMessageBlock(context);
164 | message_array++;
165 | }
166 | return shaSuccess;
167 | }
168 |
169 | void SHA1ProcessMessageBlock(SHA1Context *context)
170 | {
171 | const uint32_t K[] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6};
172 | int t; // Loop counter
173 | uint32_t temp; // Temporary word value
174 | uint32_t W[80]; // Word sequence
175 | uint32_t A, B, C, D, E; // Word buffers
176 | // Initialize the first 16 words in the array W
177 | for (t = 0; t < 16; t++)
178 | {
179 | W[t] = context->Message_Block[t * 4] << 24;
180 | W[t] |= context->Message_Block[t * 4 + 1] << 16;
181 | W[t] |= context->Message_Block[t * 4 + 2] << 8;
182 | W[t] |= context->Message_Block[t * 4 + 3];
183 | }
184 |
185 | for (t = 16; t < 80; t++)
186 | W[t] = SHA1CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
187 |
188 | A = context->Intermediate_Hash[0];
189 | B = context->Intermediate_Hash[1];
190 | C = context->Intermediate_Hash[2];
191 | D = context->Intermediate_Hash[3];
192 | E = context->Intermediate_Hash[4];
193 |
194 | for (t = 0; t < 20; t++)
195 | {
196 | temp = SHA1CircularShift(5, A) +
197 | ((B & C) | ((~B) & D)) + E + W[t] + K[0];
198 | E = D;
199 | D = C;
200 | C = SHA1CircularShift(30, B);
201 | B = A;
202 | A = temp;
203 | }
204 |
205 | for (t = 20; t < 40; t++)
206 | {
207 | temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
208 | E = D;
209 | D = C;
210 | C = SHA1CircularShift(30, B);
211 | B = A;
212 | A = temp;
213 | }
214 |
215 | for (t = 40; t < 60; t++)
216 | {
217 | temp = SHA1CircularShift(5, A) +
218 | ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
219 | E = D;
220 | D = C;
221 | C = SHA1CircularShift(30, B);
222 | B = A;
223 | A = temp;
224 | }
225 |
226 | for (t = 60; t < 80; t++)
227 | {
228 | temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
229 | E = D;
230 | D = C;
231 | C = SHA1CircularShift(30, B);
232 | B = A;
233 | A = temp;
234 | }
235 |
236 | context->Intermediate_Hash[0] += A;
237 | context->Intermediate_Hash[1] += B;
238 | context->Intermediate_Hash[2] += C;
239 | context->Intermediate_Hash[3] += D;
240 | context->Intermediate_Hash[4] += E;
241 | context->Message_Block_Index = 0;
242 | }
243 |
244 | void SHA1PadMessage(SHA1Context *context)
245 | {
246 |
247 | if (context->Message_Block_Index > 55)
248 | {
249 | context->Message_Block[context->Message_Block_Index++] = 0x80;
250 | while (context->Message_Block_Index < 64)
251 | context->Message_Block[context->Message_Block_Index++] = 0;
252 |
253 | SHA1ProcessMessageBlock(context);
254 | while (context->Message_Block_Index < 56)
255 | context->Message_Block[context->Message_Block_Index++] = 0;
256 | }
257 | else
258 | {
259 | context->Message_Block[context->Message_Block_Index++] = 0x80;
260 | while (context->Message_Block_Index < 56)
261 | context->Message_Block[context->Message_Block_Index++] = 0;
262 | }
263 |
264 | context->Message_Block[56] = context->Length_High >> 24;
265 | context->Message_Block[57] = context->Length_High >> 16;
266 | context->Message_Block[58] = context->Length_High >> 8;
267 | context->Message_Block[59] = context->Length_High;
268 | context->Message_Block[60] = context->Length_Low >> 24;
269 | context->Message_Block[61] = context->Length_Low >> 16;
270 | context->Message_Block[62] = context->Length_Low >> 8;
271 | context->Message_Block[63] = context->Length_Low;
272 | SHA1ProcessMessageBlock(context);
273 | }
274 |
275 | char int_to_hex(uint8_t integ) {
276 | if (integ < 10)
277 | return '0' + integ;
278 | else
279 | return 'a' + (integ - 10);
280 | }
281 |
282 | void SHA1MessageDigestToString(uint8_t Message_Digest[SHA1HashSize], char outbuffer[SHA1HashSize * 2]) {
283 | for (int i = 0; i < SHA1HashSize; i++) {
284 | outbuffer[i * 2] = int_to_hex(Message_Digest[i] >> 4);
285 | outbuffer[i * 2 + 1] = int_to_hex(Message_Digest[i] & 0xF);
286 | }
287 | }
288 |
289 | void idaapi setUnknown(ea_t ea, asize_t size)
290 | {
291 | // TODO: Does the overrun problem still exist?
292 | //do_unknown_range(ea, (size_t)size, DOUNK_SIMPLE);
293 | while (size > 0)
294 | {
295 | asize_t isize = get_item_size(ea);
296 | if (isize > size)
297 | break;
298 |
299 | del_items(ea);
300 | ea += (ea_t)isize;
301 | size -= isize;
302 | };
303 | }
304 |
305 |
306 | void MakeName(ea_t ea, const qstring& name, const qstring& prefix, const qstring& postfix)
307 | {
308 | qstring g_name(prefix);
309 | g_name += name;
310 | g_name += postfix;
311 |
312 | g_name.replace(" ", "_");
313 | g_name.replace("*", "_");
314 | g_name.replace(",", "_");
315 | g_name.replace("<", "_lt");
316 | g_name.replace(">", "_ge");
317 | set_name(ea, g_name.c_str(), SN_NOWARN);
318 | }
319 |
320 | bool MakeArray(ea_t ea, size_t nitems)
321 | {
322 | asize_t itemsize = 0;
323 | tid_t tid = BADADDR;
324 | flags_t flags = get_flags(ea);
325 | if (is_code(flags) || is_tail(flags) || is_align(flags))
326 | return false;
327 |
328 | if (is_unknown(flags))
329 | flags = 0;
330 |
331 | if (is_struct(flags))
332 | {
333 | opinfo_t ti;
334 | if (!get_opinfo(&ti, ea, 0, flags))
335 | return false;
336 | itemsize = get_data_elsize(ea, flags, &ti);
337 | tid = ti.tid;
338 | }
339 | else
340 | {
341 | itemsize = get_item_size(ea);
342 | }
343 |
344 | return create_data(ea, flags, static_cast(itemsize * nitems), tid);
345 | }
346 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug x64
6 | x64
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release x64
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {F7E6B557-41F3-444A-BCA4-3527547DD665}
23 | Win32Proj
24 | HexRaysCodeXplorer
25 | HexRaysCodeXplorer
26 | 10.0
27 |
28 |
29 |
30 | DynamicLibrary
31 | true
32 | MultiByte
33 | v142
34 |
35 |
36 | DynamicLibrary
37 | true
38 | MultiByte
39 | v142
40 |
41 |
42 | DynamicLibrary
43 | false
44 | MultiByte
45 | v142
46 |
47 |
48 | DynamicLibrary
49 | false
50 | MultiByte
51 | v142
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | true
75 | build makefile
76 | .dll
77 | $(LibraryPath)
78 | $(IncludePath)
79 | $(IDADIR)\plugins\
80 |
81 |
82 | true
83 | build makefile
84 | .dll
85 | $(IncludePath)
86 | $(LibraryPath)
87 | $(ProjectName)64
88 | $(IDADIR)\plugins\
89 |
90 |
91 | false
92 | .dll
93 | $(IncludePath)
94 | $(LibraryPath)
95 | $(IDADIR)\plugins\
96 |
97 |
98 | false
99 | .dll
100 | $(ProjectName)64
101 | $(IDADIR)\plugins\
102 | $(IncludePath)
103 | $(LibraryPath)
104 |
105 |
106 |
107 |
108 |
109 | Level3
110 | Disabled
111 | __MAKEDLL__;__NT__;__IDP__;__X64__;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
112 | $(IDASDK)\include;$(IDADIR)\plugins\hexrays_sdk\include
113 | MultiThreadedDebug
114 |
115 |
116 | Console
117 | true
118 | ida.lib;user32.lib;%(AdditionalDependencies)
119 | /EXPORT:PLUGIN %(AdditionalOptions)
120 | $(IDASDK)\lib\x64_win_vc_32
121 |
122 |
123 |
124 |
125 |
126 |
127 | Level3
128 | Disabled
129 | __MAKEDLL__;__NT__;__IDP__;__EA64__;__X64__;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
130 | $(IDASDK)\include;$(IDADIR)\plugins\hexrays_sdk\include
131 | MultiThreadedDebugDLL
132 |
133 |
134 | Console
135 | true
136 | ida.lib;user32.lib;%(AdditionalDependencies)
137 | /EXPORT:PLUGIN %(AdditionalOptions)
138 | $(IDASDK)\lib\x64_win_vc_64
139 |
140 |
141 |
142 |
143 | Level3
144 |
145 |
146 | Disabled
147 | true
148 | true
149 | __MAKEDLL__;__NT__;__IDP__;__X64__;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
150 | $(IDASDK)\include;$(IDADIR)\plugins\hexrays_sdk\include
151 | MultiThreaded
152 | false
153 |
154 |
155 | Console
156 | true
157 | true
158 | true
159 | ida.lib;user32.lib;%(AdditionalDependencies)
160 | /EXPORT:PLUGIN %(AdditionalOptions)
161 | $(IDASDK)\lib\x64_win_vc_32
162 |
163 |
164 |
165 |
166 | Level3
167 |
168 |
169 | Disabled
170 | true
171 | true
172 | __MAKEDLL__;__NT__;__IDP__;__EA64__;__X64__;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
173 | $(IDASDK)\include;$(IDADIR)\plugins\hexrays_sdk\include
174 | MultiThreaded
175 | false
176 |
177 |
178 | Console
179 | true
180 | true
181 | true
182 | ida.lib;user32.lib;%(AdditionalDependencies)
183 | /EXPORT:PLUGIN %(AdditionalOptions)
184 | $(IDASDK)\lib\x64_win_vc_64
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/TypeExtractor.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2015
2 | REhints
3 | All rights reserved.
4 |
5 | ==============================================================================
6 |
7 | This file is part of HexRaysCodeXplorer
8 |
9 | HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
10 | under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program. If not, see
21 | .
22 |
23 | ==============================================================================
24 | */
25 |
26 | #include "Common.h"
27 | #include "TypeReconstructor.h"
28 | #include "TypeExtractor.h"
29 | #include "CtreeExtractor.h"
30 |
31 | #include "Debug.h"
32 | #include "Utility.h"
33 |
34 | #if defined (__LINUX__) || defined (__MAC__)
35 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
36 | #endif
37 |
38 | #define STRUCT_DUMP_MIN_MEMBER_COUNT 4
39 |
40 | extern qvector vtbl_t_list;
41 | extern std::unordered_map rtti_vftables;
42 |
43 | struct obj_fint_t : public ctree_parentee_t
44 | {
45 | qstring vtbl_name;
46 | qstring var_name;
47 | bool bFound;
48 |
49 | int idaapi visit_expr(cexpr_t *e);
50 |
51 | obj_fint_t()
52 | : bFound(false)
53 | {}
54 | };
55 |
56 |
57 | int idaapi obj_fint_t::visit_expr(cexpr_t *e)
58 | {
59 | // check if the expression being visited is variable
60 | if (e->op != cot_obj)
61 | return 0;
62 |
63 | // get the variable name
64 | qstring s;
65 | print1wrapper(e, &s, NULL);
66 | tag_remove(&s);
67 |
68 | // check for the target variable
69 | if (s != vtbl_name)
70 | return 0;
71 |
72 | size_t max_parents = 3;
73 | if (parents.size() < max_parents) {
74 | max_parents = parents.size();
75 | }
76 |
77 | for (size_t i = 1; i <= max_parents; i++) {
78 | citem_t *parent = parents.back();
79 | if (parent->is_expr() && parent->op == cot_asg) {
80 | cexpr_t * target_expr = (cexpr_t *)parent;
81 |
82 | while (target_expr->x != NULL && target_expr->op != cot_var && target_expr->op != cot_obj)
83 | target_expr = target_expr->x;
84 |
85 | if (target_expr->op == cot_var) {
86 | s.clear();
87 | print1wrapper(target_expr, &s, NULL);
88 | tag_remove(&s);
89 |
90 | var_name = s;
91 | bFound = true;
92 | break;
93 | }
94 | }
95 | }
96 |
97 | return 0;
98 | }
99 |
100 | void idaapi reset_pointer_type(cfuncptr_t cfunc, const qstring &var_name) {
101 | lvars_t * locals = cfunc->get_lvars();
102 | if (locals == NULL)
103 | return;
104 |
105 | qvector::iterator locals_iter;
106 |
107 | for (locals_iter = locals->begin(); locals_iter != locals->end(); locals_iter++) {
108 | if (var_name != locals_iter->name)
109 | continue;
110 |
111 | tinfo_t int_type = tinfo_t(BT_INT32);
112 | locals_iter->set_final_lvar_type(int_type);
113 | locals_iter->set_user_type();
114 | cfunc->build_c_tree();
115 | break;
116 | }
117 | }
118 |
119 | bool idaapi find_var(void *ud)
120 | {
121 | vdui_t &vu = *(vdui_t *)ud;
122 |
123 | // Determine the ctree item to highlight
124 | vu.get_current_item(USE_KEYBOARD);
125 | citem_t *highlight = vu.item.is_citem() ? vu.item.e : NULL;
126 |
127 | // highlight == NULL might happen if one chooses variable at local variables declaration statement
128 | if (!highlight)
129 | {
130 | logmsg(DEBUG, "Invalid item is choosen");
131 | return false;
132 | }
133 |
134 | // the chosen item must be an expression and of 'variable' type
135 | if (highlight->is_expr() && (highlight->op == cot_obj))
136 | {
137 | cexpr_t *highl_expr = (cexpr_t *)highlight;
138 |
139 | qstring s;
140 | print1wrapper(highlight, &s, NULL);
141 | tag_remove(&s);
142 |
143 | // initialize type rebuilder
144 | obj_fint_t obj_find;
145 | obj_find.vtbl_name = s;
146 |
147 | // traverse the ctree structure
148 | obj_find.apply_to(&vu.cfunc->body, NULL);
149 |
150 | if (obj_find.bFound) {
151 | logmsg(DEBUG, (obj_find.var_name + "\n").c_str());
152 | reset_pointer_type(vu.cfunc, obj_find.var_name);
153 |
154 | vu.refresh_ctext();
155 | } else {
156 | warning("Failed to find variable...\n");
157 | logmsg(DEBUG, "Failed to find variable...\n");
158 | }
159 | }
160 |
161 | return true;
162 | }
163 |
164 | bool idaapi find_var(cfuncptr_t cfunc, const qstring& vtbl_name, qstring &var_name)
165 | {
166 | var_name.clear();
167 |
168 | obj_fint_t obj_find;
169 | obj_find.vtbl_name = vtbl_name;
170 |
171 | if (obj_find.vtbl_name.find("const ") == 0)
172 | obj_find.vtbl_name.remove(0, 6);
173 |
174 | // traverse the ctree structure
175 | obj_find.apply_to(&cfunc->body, NULL);
176 |
177 | if (!obj_find.bFound) {
178 | logmsg(DEBUG, "Failed to find variable...\n");
179 | return false;
180 | }
181 |
182 | var_name = obj_find.var_name;
183 | reset_pointer_type(cfunc, var_name);
184 | return true;
185 | }
186 |
187 | tid_t idaapi merge_types(const qvector& types_to_merge, const qstring& type_name) {
188 | tid_t struct_type_id = BADADDR;
189 |
190 | if (types_to_merge.empty())
191 | return struct_type_id;
192 |
193 | std::set offsets;
194 |
195 | struct_type_id = add_struc(BADADDR, type_name.c_str());
196 | if (struct_type_id == BADADDR)
197 | return struct_type_id;
198 |
199 | struc_t * struc = get_struc(struct_type_id);
200 | if (!struc)
201 | return struct_type_id;
202 |
203 | for (auto types_iter = types_to_merge.begin(), end = types_to_merge.end(); types_iter != end; ++types_iter) {
204 | struc_t * struc_type = get_struc(get_struc_id(types_iter->c_str()));
205 | if (!struc_type)
206 | continue;
207 |
208 | // enumerate members
209 | for ( ea_t offset = get_struc_first_offset(struc_type) ; offset != BADADDR ; offset = get_struc_next_offset(struc_type, offset)) {
210 | member_t * member_info = get_member(struc_type, offset);
211 | if (!member_info)
212 | continue;
213 |
214 | if (offsets.count(member_info->soff) == 0) {
215 | qstring member_name = get_member_name(member_info->id);
216 | asize_t member_size = get_member_size(member_info);
217 |
218 | if (member_name.find("vftbl_", 0) != -1) {
219 | tinfo_t tif;
220 | if (get_member_tinfo(&tif, member_info)) {
221 | add_struc_member(struc, member_name.c_str(), member_info->soff, dword_flag(), NULL, member_size);
222 | if (member_t * membr = get_member(struc, member_info->soff)) {
223 | set_member_tinfo(struc, membr, 0, tif, SET_MEMTI_COMPATIBLE);
224 | }
225 | }
226 | }
227 | else {
228 | add_struc_member(struc, member_name.c_str(), member_info->soff, member_info->flag, NULL, member_size);
229 | }
230 |
231 | offsets.insert(member_info->soff);
232 | }
233 | }
234 | }
235 |
236 | return struct_type_id;
237 | }
238 |
239 | void get_struct_key(struc_t * struc_type, const VTBL_info_t& vtbl_info, qstring &file_entry_key, bool &filtered, const std::unordered_map& vtbl_map) {
240 | qstring sub_key;
241 | qstring vtables_sub_key;
242 | int vftbales_num = 0;
243 | int members_count = 0;
244 | for ( ea_t offset = get_struc_first_offset(struc_type) ; offset != BADADDR ; offset = get_struc_next_offset(struc_type, offset)) {
245 | member_t * member_info = get_member(struc_type, offset);
246 | if (member_info != NULL) {
247 | qstring member_name = get_member_name(member_info->id);
248 | asize_t member_size = get_member_size(member_info);
249 |
250 | if (member_name.find("vftbl_", 0) != -1) {
251 |
252 | ea_t vtable_addr = 0;
253 | int i;
254 |
255 | if (qsscanf(member_name.c_str(), "vftbl_%d_%" FMT_EA "x", &i, &vtable_addr) > 0) {
256 | if (vtbl_map.count(vtable_addr) != 0) {
257 | vtables_sub_key.cat_sprnt("_%d", vtbl_map.at(vtable_addr).methods);
258 | }
259 | }
260 |
261 | vftbales_num ++;
262 | }
263 |
264 | sub_key.cat_sprnt("_%d", member_size);
265 |
266 | members_count ++;
267 | }
268 | }
269 | file_entry_key.sprnt("t_%d_%d", vtbl_info.methods, vftbales_num);
270 | file_entry_key += vtables_sub_key;
271 | file_entry_key += sub_key;
272 |
273 | if (members_count < STRUCT_DUMP_MIN_MEMBER_COUNT)
274 | filtered = true;
275 | }
276 |
277 | void idaapi dump_type_info(int file_id, const VTBL_info_t& vtbl_info, const qstring& type_name, const std::unordered_map& vtbl_map) {
278 | struc_t * struc_type = get_struc(get_struc_id(type_name.c_str()));
279 | if (!struc_type)
280 | return;
281 |
282 | qstring file_entry_key;
283 | qstring key_hash;
284 | bool filtered = false;
285 |
286 | get_struct_key(struc_type, vtbl_info, file_entry_key, filtered, vtbl_map);
287 | get_hash_of_string(file_entry_key, key_hash);
288 |
289 | if (filtered)
290 | return;
291 |
292 | qstring file_entry_val;
293 | tinfo_t new_type = create_typedef(type_name.c_str());
294 |
295 | if (new_type.is_correct() && new_type.print(&file_entry_val, NULL, PRTYPE_DEF | PRTYPE_1LINE)) {
296 | qstring line;
297 |
298 | line = key_hash + ";" + file_entry_key + ";";
299 | line.cat_sprnt("%a;", vtbl_info.ea_begin);
300 | line += file_entry_val + ";";
301 |
302 | if (rtti_vftables.count(vtbl_info.ea_begin) != 0) {
303 | VTBL_info_t vi = rtti_vftables[vtbl_info.ea_begin];
304 | line += vi.vtbl_name;
305 | }
306 | line.rtrim();
307 | line += "\r\n";
308 | qwrite(file_id, line.c_str(), line.length());
309 | }
310 | }
311 |
312 | bool idaapi check_subtype(VTBL_info_t vtbl_info, qstring subtype_name) {
313 | qstring search_str;
314 | search_str.sprnt("_%a", vtbl_info.ea_begin);
315 |
316 | struc_t * struc_type = get_struc(get_struc_id(subtype_name.c_str()));
317 | if (!struc_type)
318 | return false;
319 |
320 | // enumerate members
321 | for ( ea_t offset = get_struc_first_offset(struc_type) ; offset != BADADDR ; offset = get_struc_next_offset(struc_type, offset)) {
322 | member_t * member_info = get_member(struc_type, offset);
323 | if (!member_info)
324 | continue;
325 |
326 | qstring member_name = get_member_name(member_info->id);
327 | if (member_name.find(search_str, 0) != member_name.npos)
328 | return true;
329 | }
330 |
331 | return false;
332 | }
333 |
334 | bool idaapi extract_all_types(void *ud)
335 | {
336 | logmsg(DEBUG, "extract_types()\n");
337 |
338 | // find vtables in the binary
339 | search_objects(false);
340 |
341 | qvector ::iterator vtbl_iter;
342 |
343 | std::unordered_map vtbl_map;
344 | for (vtbl_iter = vtbl_t_list.begin(); vtbl_iter != vtbl_t_list.end(); vtbl_iter++)
345 | vtbl_map[(*vtbl_iter).ea_begin] = (*vtbl_iter);
346 |
347 | int file_id = create_open_file("types.txt");
348 | if (file_id == -1)
349 | {
350 | logmsg(ERROR, "Failed to open file for dumping types.txt\r\n");
351 | return false;
352 | }
353 |
354 | int struct_no = 0;
355 |
356 | for (vtbl_iter = vtbl_t_list.begin(); vtbl_iter != vtbl_t_list.end(); vtbl_iter++) {
357 | qstring info_msg;
358 | info_msg.cat_sprnt("Processing vtable %s\n", (*vtbl_iter).vtbl_name.c_str());
359 | logmsg(DEBUG, info_msg.c_str());
360 |
361 | qstring type_name;
362 | type_name.sprnt("struc_2_%d", struct_no);
363 |
364 | ea_t cur_vt_ea = (*vtbl_iter).ea_begin;
365 | int struct_subno = 0;
366 |
367 | qvector types_to_merge;
368 | for (ea_t addr = get_first_dref_to(cur_vt_ea); addr != BADADDR; addr = get_next_dref_to(cur_vt_ea, addr)) {
369 | qstring name;
370 | if (get_func_name(&name, addr) <= 0)
371 | continue;
372 |
373 | qstring info_msg1;
374 | info_msg1.cat_sprnt("\t%s\n", name.c_str());
375 | logmsg(DEBUG, info_msg1.c_str());
376 |
377 | func_t *pfn = get_func(addr);
378 | if (!pfn)
379 | continue;
380 |
381 | hexrays_failure_t hf;
382 | cfuncptr_t cfunc = decompile(pfn, &hf);
383 | if (cfunc != NULL) {
384 | qstring var_name;
385 | info_msg.clear();
386 |
387 | if (find_var(cfunc, (*vtbl_iter).vtbl_name, var_name)) {
388 | info_msg.cat_sprnt(" : %s\n", var_name.c_str());
389 | logmsg(DEBUG, info_msg.c_str());
390 |
391 | qstring sub_type_name = type_name;
392 | sub_type_name.cat_sprnt("_%d", struct_subno);
393 | struct_subno++;
394 |
395 | if (reconstruct_type(cfunc, var_name, sub_type_name)) {
396 | if (check_subtype((*vtbl_iter), sub_type_name)) {
397 | types_to_merge.push_back(sub_type_name);
398 | }
399 | }
400 | }
401 | else {
402 | info_msg.cat_sprnt(" : none\n");
403 | logmsg(DEBUG, info_msg.c_str());
404 | }
405 | }
406 | }
407 |
408 | struct_no++;
409 |
410 | merge_types(types_to_merge, type_name);
411 | dump_type_info(file_id, (*vtbl_iter), type_name, vtbl_map);
412 | }
413 |
414 | qclose(file_id);
415 | return true;
416 | }
417 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/CtreeExtractor.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2020
2 | REhints
3 | All rights reserved.
4 |
5 | ==============================================================================
6 |
7 | This file is part of HexRaysCodeXplorer
8 |
9 | HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
10 | under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program. If not, see
21 | .
22 |
23 | ==============================================================================
24 | */
25 |
26 | #include "Common.h"
27 | #include "TypeReconstructor.h"
28 | #include "CtreeExtractor.h"
29 | #include "Utility.h"
30 | #include "Debug.h"
31 |
32 | #if defined (__LINUX__) || defined (__MAC__)
33 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
34 | #endif
35 |
36 | #define MIN_HEURISTIC_FUNC_SIZE_DUMP 0x160
37 | #define MIN_FUNC_SIZE_DUMP 0x60
38 |
39 | #define N_FUNCS_TO_DUMP 40
40 | #define N_HEUR_FUNCS_TO_DUMP 60
41 | #define N_CRYPTO_FUNCS_TO_DUMP 30
42 |
43 | #define MAX_FUNC_DEPTH 100
44 |
45 |
46 | bool idaapi ctree_dumper_t::filter_citem(citem_t *item) {
47 | if (item->is_expr()) {
48 | auto expr = static_cast(item);
49 |
50 | if (item->op == cot_cast)
51 | return true;
52 | else if (item->op == cot_helper)
53 | return true;
54 | else if ((item->op >= cot_postinc) && (item->op <= cot_predec))
55 | return true;
56 | else if ((item->op >= cot_idx) && ((item->op <= cot_last)))
57 | return true;
58 | } else {
59 | if (item->op == cit_expr)
60 | return true;
61 | }
62 |
63 | return false;
64 | }
65 |
66 | void ctree_dumper_t::process_for_hash(citem_t *item)
67 | {
68 | if (!filter_citem(item)) {
69 | const char* ctype_name = get_ctype_name(item->op);
70 | ctree_for_hash.cat_sprnt("%s:", ctype_name);
71 | }
72 | }
73 |
74 | // Process a ctree item
75 | int ctree_dumper_t::process(citem_t *item)
76 | {
77 | size_t parent_count = parents.size();
78 | if (parent_count > 1) {
79 | ctree_dump += "(";
80 | }
81 |
82 | qstring buf;
83 | parse_ctree_item(item, buf);
84 | ctree_dump += buf;
85 |
86 | process_for_hash(item);
87 | return 0;
88 | }
89 |
90 | int ctree_dumper_t::process_leave(citem_t *item)
91 | {
92 | size_t parent_count = parents.size();
93 | if (parent_count > 1) {
94 | ctree_dump += ")";
95 | }
96 | return 0;
97 | }
98 |
99 | void ctree_dumper_t::parse_ctree_item(citem_t *item, qstring& rv) const
100 | {
101 | rv.clear();
102 | // Each node will have the element type at the first line
103 | if (const auto v = get_ctype_name(item->op))
104 | rv = v;
105 |
106 | const auto e = static_cast(item);
107 | const auto i = static_cast(item);
108 |
109 | // For some item types, display additional information
110 | qstring func_name;
111 | qstring s;
112 | switch (item->op)
113 | {
114 | case cot_call:
115 | if (e->x->op == cot_obj) {
116 | if (get_func_name(&func_name, e->x->obj_ea) == 0)
117 | rv.cat_sprnt(" sub_%a", e->x->obj_ea);
118 | else
119 | rv.cat_sprnt(" %s", func_name.c_str());
120 | }
121 | break;
122 | case cot_ptr: // *x
123 | case cot_memptr: // x->m
124 | // Display access size for pointers
125 | rv.cat_sprnt(".%d", e->ptrsize);
126 | if (item->op == cot_ptr)
127 | break;
128 | case cot_memref: // x.m
129 | // Display member offset for structure fields
130 | rv.cat_sprnt(" (m=%d)", e->m);
131 | break;
132 | case cot_obj: // v
133 | case cot_var: // l
134 | // Display object size for local variables and global data
135 | rv.cat_sprnt(".%d", e->refwidth);
136 | case cot_num: // n
137 | case cot_helper: // arbitrary name
138 | case cot_str: // string constant
139 | // Display helper names and number values
140 | rv.append(' ');
141 | {
142 | qstring qbuf;
143 | print1wrapper(e, &qbuf, nullptr);
144 | tag_remove(&qbuf);
145 | rv += qbuf;
146 | }
147 | break;
148 | case cit_goto:
149 | // Display target label number for gotos
150 | rv.cat_sprnt(" LABEL_%d", i->cgoto->label_num);
151 | break;
152 | case cit_asm:
153 | // Display instruction block address and size for asm-statements
154 | rv.cat_sprnt(" %a.%" FMT_Z, *i->casm->begin(), i->casm->size());
155 | break;
156 | default:
157 | break;
158 | }
159 |
160 | // The second line of the node contains the item address
161 | rv.cat_sprnt(";ea->%a", item->ea);
162 |
163 | if ( item->is_expr() && !e->type.empty() )
164 | {
165 | // For typed expressions, the third line will have
166 | // the expression type in human readable form
167 | rv.append(';');
168 | qstring out;
169 | if (e->type.print(&out))
170 | {
171 | rv += out;
172 | }
173 | else
174 | { // could not print the type?
175 | rv.append('?');
176 | }
177 |
178 | if(e->type.is_ptr())
179 | {
180 | const auto ptr_rem = ::remove_pointer(e->type);
181 | if(ptr_rem.is_struct())
182 | {
183 | qstring typenm;
184 | ptr_rem.print(&typenm, "prefix ", 0, 0, PRTYPE_MULTI | PRTYPE_TYPE | PRTYPE_SEMI);
185 | }
186 | }
187 | }
188 |
189 | }
190 |
191 | struct ctree_dump_line {
192 | qvector referres;
193 | qstring ctree_for_hash;
194 | qstring ctree_dump;
195 | qstring func_name;
196 | int func_depth{};
197 | ea_t func_start{};
198 | ea_t func_end{};
199 | bool heuristic_flag{};
200 | };
201 |
202 | struct ctree_dump_line_impl : ctree_dump_line
203 | {
204 | };
205 |
206 |
207 | int create_open_file(const char* file_name) {
208 | auto file_id = qopen(file_name, O_BINARY | O_TRUNC | O_CREAT);
209 | if (file_id == BADADDR)
210 | file_id = qcreate(file_name, 511);
211 |
212 | return file_id;
213 | }
214 |
215 | int get_hash_of_string(const qstring &string_to_hash, qstring &hash) {
216 | SHA1Context sha;
217 | uint8_t message_digest[SHA1HashSize];
218 |
219 | auto err = SHA1Reset(&sha);
220 | if (err == shaSuccess) {
221 | err = SHA1Input(&sha, (uint8_t *)string_to_hash.c_str(), static_cast(string_to_hash.length()));
222 | if (err == shaSuccess) {
223 | err = SHA1Result(&sha, message_digest);
224 | if (err == shaSuccess) {
225 | char digest_hex[SHA1HashSize * 2 + 1];
226 | memset(digest_hex, 0x00, sizeof(digest_hex));
227 | SHA1MessageDigestToString(message_digest, digest_hex);
228 |
229 | hash = digest_hex;
230 | }
231 | }
232 | }
233 |
234 | return err;
235 | }
236 |
237 | void dump_ctrees_in_file(std::map &data_to_dump, const qstring &crypto_prefix) {
238 | const auto file_id = create_open_file("ctrees.txt");
239 | if (file_id == -1)
240 | {
241 | logmsg(ERROR, "Failed to open file for dumping ctress\r\n");
242 | return;
243 | }
244 |
245 | size_t crypt_prefix_len = crypto_prefix.length();
246 |
247 | for (auto ctrees_iter = data_to_dump.begin(); ctrees_iter != data_to_dump.end(); ++ctrees_iter) {
248 | const auto& cdl = ctrees_iter->second;
249 |
250 | qstring sha_hash;
251 | auto err = get_hash_of_string(cdl.ctree_for_hash, sha_hash);
252 | if (err != shaSuccess) {
253 | logmsg(ERROR, "Error in computing SHA1 hash\r\n");
254 | continue;
255 | }
256 |
257 | auto dump_line = sha_hash + ";";
258 | err = get_hash_of_string(cdl.ctree_dump, sha_hash);
259 | if (err != shaSuccess) {
260 | logmsg(ERROR, "Error in computing SHA1 hash\r\n");
261 | continue;
262 | }
263 | dump_line += sha_hash + ";";
264 | dump_line += cdl.ctree_dump;
265 | dump_line.cat_sprnt(";%d", cdl.func_depth);
266 | dump_line.cat_sprnt(";%08X", cdl.func_start);
267 | dump_line.cat_sprnt(";%08X", cdl.func_end);
268 | if ((cdl.func_name.length() > crypt_prefix_len) && (crypt_prefix_len > 0) && (cdl.func_name.find(crypto_prefix) == 0))
269 | dump_line.cat_sprnt(";E");
270 | else
271 | dump_line.cat_sprnt(";N");
272 |
273 | if ((cdl.heuristic_flag))
274 | dump_line.cat_sprnt(";H");
275 | else
276 | dump_line.cat_sprnt(";N");
277 |
278 | dump_line += "\n";
279 |
280 | qwrite(file_id, dump_line.c_str(), dump_line.length());
281 | }
282 |
283 | qclose(file_id);
284 | }
285 |
286 |
287 | inline bool func_name_has_prefix(const qstring &prefix, const ea_t start_ea) {
288 | if (prefix.length() <= 0)
289 | return false;
290 |
291 | qstring func_name;
292 | if (get_func_name(&func_name, start_ea) <= 0)
293 | return false;
294 |
295 | if (func_name.empty())
296 | return false;
297 |
298 | return func_name.find(prefix.c_str(), 0) == 0;
299 | }
300 |
301 | bool idaapi dump_funcs_ctree(void *ud, const qstring &crypto_prefix)
302 | {
303 | logmsg(DEBUG, "dump_funcs_ctree entered\n");
304 |
305 | std::map data_to_dump;
306 |
307 | size_t count = 0, heur_count = 0, crypto_count = 0;
308 | size_t total_func_qty = get_func_qty();
309 | for (size_t i = 0 ; i < total_func_qty ; i ++) {
310 | auto heuristic_flag = false;
311 |
312 | func_t *function = getn_func(i);
313 | if (function != nullptr) {
314 | bool crypto_flag = func_name_has_prefix(crypto_prefix, function->start_ea);
315 |
316 | // skip libs that are not marked as crypto
317 | if ( ((function->flags & FUNC_LIB) != 0) && !crypto_flag )
318 | continue;
319 |
320 | // From this point on, we have a function outside of lib or a crypto one
321 |
322 | // Ignore functions less than MIN_FUNC_SIZE_DUMP bytes
323 | if ( ((function->end_ea - function->start_ea) < MIN_FUNC_SIZE_DUMP) && !crypto_flag )
324 | continue;
325 |
326 | // If function is bigger than MIN_HEURISTIC_FUNC_SIZE_DUMP, mark as being triggered by the heuristic
327 | if (function->end_ea - function->start_ea > MIN_HEURISTIC_FUNC_SIZE_DUMP)
328 | heuristic_flag = true;
329 |
330 | // dump up to N_CRYPTO_FUNCS_TO_DUMP crypto functions
331 | // dump up to N_HEUR_FUNCS_TO_DUMP heuristic functions
332 | // at least N_FUNCS_TO_DUMP functions will be dumped
333 | if ((count < N_FUNCS_TO_DUMP) || (crypto_flag && (crypto_count < N_CRYPTO_FUNCS_TO_DUMP)) || (heuristic_flag && (heur_count < N_HEUR_FUNCS_TO_DUMP))) {
334 | hexrays_failure_t hf;
335 | cfuncptr_t cfunc = decompile(function, &hf);
336 |
337 | logmsg(DEBUG, "\nafter decompile()\n");
338 | if (cfunc != nullptr) {
339 | ctree_dumper_t ctree_dumper;
340 | ctree_dumper.apply_to(&cfunc->body, nullptr);
341 |
342 | ctree_dump_line func_dump;
343 | func_dump.ctree_dump = ctree_dumper.ctree_dump;
344 | func_dump.ctree_for_hash = ctree_dumper.ctree_for_hash;
345 |
346 | func_dump.func_depth = -1;
347 |
348 | func_dump.func_start = function->start_ea;
349 | func_dump.func_end = function->end_ea;
350 |
351 | qstring func_name;
352 | if (get_func_name(&func_name, function->start_ea) != 0) {
353 | if (func_name.length() > 0) {
354 | func_dump.func_name = func_name;
355 | }
356 | }
357 |
358 | func_parent_iterator_t fpi(function);
359 | for (ea_t addr = get_first_cref_to(function->start_ea); addr != BADADDR; addr = get_next_cref_to(function->start_ea, addr)) {
360 | func_t *referer = get_func(addr);
361 | if (referer != nullptr) {
362 | func_dump.referres.push_back(referer->start_ea);
363 | }
364 | }
365 |
366 | func_dump.heuristic_flag = heuristic_flag; // 0 or 1 depending on code above
367 | if (heuristic_flag)
368 | heur_count++;
369 |
370 | if (crypto_flag)
371 | crypto_count++;
372 |
373 | count++;
374 |
375 | data_to_dump[function->start_ea] = func_dump;
376 | }
377 | }
378 | }
379 | }
380 |
381 | dump_ctrees_in_file(data_to_dump, crypto_prefix);
382 |
383 | return true;
384 | }
385 |
386 | bool idaapi extract_all_ctrees(void *ud)
387 | {
388 | // default prefix to display in the dialog
389 | static const qstring kDefaultPrefix = "crypto_";
390 |
391 | va_list va;
392 | va_end(va);
393 |
394 | auto crypto_prefix = kDefaultPrefix;
395 | if (!ask_str(&crypto_prefix, 0, "Enter prefix of crypto function names", va))
396 | return false;
397 |
398 | if(!crypto_prefix.empty()) {
399 | dump_funcs_ctree(nullptr, crypto_prefix);
400 | } else {
401 | warning("Incorrect prefix!!");
402 | }
403 |
404 | return true;
405 | }
406 |
407 |
408 | // Ctree Item Form Init
409 | struct func_ctree_info_t
410 | {
411 | TWidget *widget;
412 | TWidget *cv;
413 | TWidget *codeview;
414 | strvec_t sv;
415 | explicit func_ctree_info_t(TWidget *f) : widget(f), cv(nullptr), codeview(nullptr){}
416 | };
417 |
418 |
419 | bool idaapi show_citem_custom_view(void *ud, const qstring& ctree_item, const qstring& item_name)
420 | {
421 | qstring form_name = "Ctree Item View: ";
422 | form_name.append(item_name);
423 | const auto widget = create_empty_widget(form_name.c_str());
424 | auto si = new func_ctree_info_t(widget);
425 |
426 | istringstream s_citem_str(ctree_item.c_str());
427 | string tmp_str;
428 | while (getline(s_citem_str, tmp_str, ';'))
429 | {
430 | qstring tmp_qstr = tmp_str.c_str();
431 | si->sv.push_back(simpleline_t(tmp_qstr));
432 | }
433 |
434 | simpleline_place_t s1;
435 | simpleline_place_t s2(static_cast(ctree_item.size()));
436 | si->cv = create_custom_viewer("", &s1, &s2, &s1, nullptr, &si->sv, nullptr, nullptr, widget);
437 | si->codeview = create_code_viewer(si->cv, CDVF_NOLINES, widget);
438 | set_custom_viewer_handlers(si->cv, nullptr, si);
439 | display_widget(widget, WOPN_RESTORE);
440 |
441 | return false;
442 | }
443 |
444 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/MicrocodeExtractor.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2020
2 | REhints
3 | All rights reserved.
4 |
5 | ==============================================================================
6 |
7 | This file is part of HexRaysCodeXplorer
8 |
9 | HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
10 | under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program. If not, see .
21 |
22 | ==============================================================================
23 | */
24 |
25 |
26 | // This code mostly adopted from https://github.com/RolfRolles/HexRaysDeob
27 | // All kudos going to Rolf https://www.hexblog.com/?p=1248
28 |
29 |
30 | #pragma once
31 |
32 |
33 | void show_microcode_explorer();
34 | mba_maturity_t ask_desired_maturity();
35 |
36 | // Produce a string for an operand type
37 | inline const char* mopt_t_to_string(const mopt_t t)
38 | {
39 | switch (t)
40 | {
41 | case mop_z: return "mop_z";
42 | case mop_r: return "mop_r";
43 | case mop_n: return "mop_n";
44 | case mop_str: return "mop_str";
45 | case mop_d: return "mop_d";
46 | case mop_S: return "mop_S";
47 | case mop_v: return "mop_v";
48 | case mop_b: return "mop_b";
49 | case mop_f: return "mop_f";
50 | case mop_l: return "mop_l";
51 | case mop_a: return "mop_a";
52 | case mop_h: return "mop_h";
53 | case mop_c: return "mop_c";
54 | case mop_fn: return "mop_fn";
55 | case mop_p: return "mop_p";
56 | case mop_sc: return "mop_sc";
57 | default: ;
58 | };
59 | return "???";
60 | }
61 |
62 | // Produce a brief representation of a microinstruction, including the types
63 | // of its operands.
64 | inline void mcode_t_to_string(minsn_t* o, char* out_buf, size_t n)
65 | {
66 | switch (o->opcode)
67 | {
68 | case m_nop: snprintf(out_buf, n, "m_nop"); break;
69 | case m_stx: snprintf(out_buf, n, "m_stx(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
70 | case m_ldx: snprintf(out_buf, n, "m_ldx(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
71 | case m_ldc: snprintf(out_buf, n, "m_ldc(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
72 | case m_mov: snprintf(out_buf, n, "m_mov(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
73 | case m_neg: snprintf(out_buf, n, "m_neg(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
74 | case m_lnot: snprintf(out_buf, n, "m_lnot(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
75 | case m_bnot: snprintf(out_buf, n, "m_bnot(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
76 | case m_xds: snprintf(out_buf, n, "m_xds(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
77 | case m_xdu: snprintf(out_buf, n, "m_xdu(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
78 | case m_low: snprintf(out_buf, n, "m_low(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
79 | case m_high: snprintf(out_buf, n, "m_high(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
80 | case m_add: snprintf(out_buf, n, "m_add(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
81 | case m_sub: snprintf(out_buf, n, "m_sub(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
82 | case m_mul: snprintf(out_buf, n, "m_mul(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
83 | case m_udiv: snprintf(out_buf, n, "m_udiv(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
84 | case m_sdiv: snprintf(out_buf, n, "m_sdiv(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
85 | case m_umod: snprintf(out_buf, n, "m_umod(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
86 | case m_smod: snprintf(out_buf, n, "m_smod(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
87 | case m_or: snprintf(out_buf, n, "m_or(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
88 | case m_and: snprintf(out_buf, n, "m_and(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
89 | case m_xor: snprintf(out_buf, n, "m_xor(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
90 | case m_shl: snprintf(out_buf, n, "m_shl(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
91 | case m_shr: snprintf(out_buf, n, "m_shr(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
92 | case m_sar: snprintf(out_buf, n, "m_sar(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
93 | case m_cfadd: snprintf(out_buf, n, "m_cfadd(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
94 | case m_ofadd: snprintf(out_buf, n, "m_ofadd(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
95 | case m_cfshl: snprintf(out_buf, n, "m_cfshl(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
96 | case m_cfshr: snprintf(out_buf, n, "m_cfshr(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
97 | case m_sets: snprintf(out_buf, n, "m_sets(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
98 | case m_seto: snprintf(out_buf, n, "m_seto(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
99 | case m_setp: snprintf(out_buf, n, "m_setp(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
100 | case m_setnz: snprintf(out_buf, n, "m_setnz(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
101 | case m_setz: snprintf(out_buf, n, "m_setz(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
102 | case m_setae: snprintf(out_buf, n, "m_setae(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
103 | case m_setb: snprintf(out_buf, n, "m_setb(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
104 | case m_seta: snprintf(out_buf, n, "m_seta(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
105 | case m_setbe: snprintf(out_buf, n, "m_setbe(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
106 | case m_setg: snprintf(out_buf, n, "m_setg(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
107 | case m_setge: snprintf(out_buf, n, "m_setge(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
108 | case m_setl: snprintf(out_buf, n, "m_setl(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
109 | case m_setle: snprintf(out_buf, n, "m_setle(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
110 | case m_jcnd: snprintf(out_buf, n, "m_jcnd(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
111 | case m_jnz: snprintf(out_buf, n, "m_jnz(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
112 | case m_jz: snprintf(out_buf, n, "m_jz(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
113 | case m_jae: snprintf(out_buf, n, "m_jae(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
114 | case m_jb: snprintf(out_buf, n, "m_jb(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
115 | case m_ja: snprintf(out_buf, n, "m_ja(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
116 | case m_jbe: snprintf(out_buf, n, "m_jbe(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
117 | case m_jg: snprintf(out_buf, n, "m_jg(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
118 | case m_jge: snprintf(out_buf, n, "m_jge(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
119 | case m_jl: snprintf(out_buf, n, "m_jl(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
120 | case m_jle: snprintf(out_buf, n, "m_jle(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
121 | case m_jtbl: snprintf(out_buf, n, "m_jtbl(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t)); break;
122 | case m_ijmp: snprintf(out_buf, n, "m_ijmp(%s,%s)", mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
123 | case m_goto: snprintf(out_buf, n, "m_goto(%s)", mopt_t_to_string(o->l.t)); break;
124 | case m_call: snprintf(out_buf, n, "m_call(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
125 | case m_icall: snprintf(out_buf, n, "m_icall(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
126 | case m_ret: snprintf(out_buf, n, "m_ret"); break;
127 | case m_push: snprintf(out_buf, n, "m_push(%s)", mopt_t_to_string(o->l.t)); break;
128 | case m_pop: snprintf(out_buf, n, "m_pop(%s)", mopt_t_to_string(o->d.t)); break;
129 | case m_und: snprintf(out_buf, n, "m_und(%s)", mopt_t_to_string(o->d.t)); break;
130 | case m_ext: snprintf(out_buf, n, "m_ext(???)"); break;
131 | case m_f2i: snprintf(out_buf, n, "m_f2i(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
132 | case m_f2u: snprintf(out_buf, n, "m_f2u(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
133 | case m_i2f: snprintf(out_buf, n, "m_i2f(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
134 | case m_u2f: snprintf(out_buf, n, "m_u2f(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
135 | case m_f2f: snprintf(out_buf, n, "m_f2f(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
136 | case m_fneg: snprintf(out_buf, n, "m_fneg(%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->d.t)); break;
137 | case m_fadd: snprintf(out_buf, n, "m_fadd(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
138 | case m_fsub: snprintf(out_buf, n, "m_fsub(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
139 | case m_fmul: snprintf(out_buf, n, "m_fmul(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
140 | case m_fdiv: snprintf(out_buf, n, "m_fdiv(%s,%s,%s)", mopt_t_to_string(o->l.t), mopt_t_to_string(o->r.t), mopt_t_to_string(o->d.t)); break;
141 | }
142 | }
143 |
144 | // Produce a string describing the microcode maturity level.
145 | inline const char* micro_maturity_to_string(const mba_maturity_t mmt)
146 | {
147 | switch (mmt)
148 | {
149 | case MMAT_ZERO: return "MMAT_ZERO";
150 | case MMAT_GENERATED: return "MMAT_GENERATED";
151 | case MMAT_PREOPTIMIZED: return "MMAT_PREOPTIMIZED";
152 | case MMAT_LOCOPT: return "MMAT_LOCOPT";
153 | case MMAT_CALLS: return "MMAT_CALLS";
154 | case MMAT_GLBOPT1: return "MMAT_GLBOPT1";
155 | case MMAT_GLBOPT2: return "MMAT_GLBOPT2";
156 | case MMAT_GLBOPT3: return "MMAT_GLBOPT3";
157 | case MMAT_LVARS: return "MMAT_LVARS";
158 | default: return "???";
159 | }
160 | }
161 |
162 | // Copied from http://www.hexblog.com/?p=1198
163 | // I did add code for the mop_d case; it used to return false
164 |
165 | //--------------------------------------------------------------------------
166 | // compare operands but ignore the sizes
167 | inline bool equal_mops_ignore_size(const mop_t& lo, const mop_t& ro)
168 | {
169 | if (lo.t != ro.t)
170 | return false;
171 |
172 | switch (lo.t)
173 | {
174 | case mop_z: // none
175 | return true;
176 | case mop_fn: // floating point
177 | return *ro.fpc == *lo.fpc;
178 | case mop_n: // immediate
179 | {
180 | const auto minsize = qmin(lo.size, ro.size);
181 | const auto v1 = extend_sign(ro.nnn->value, minsize, false);
182 | const auto v2 = extend_sign(lo.nnn->value, minsize, false);
183 | return v1 == v2;
184 | }
185 | case mop_S: // stack variable
186 | return *ro.s == *lo.s;
187 | case mop_v: // global variable
188 | return ro.g == lo.g;
189 | case mop_d: // result of another instruction
190 | // I added this
191 | return ro.d->equal_insns(*lo.d, EQ_IGNSIZE | EQ_IGNCODE);
192 | case mop_b: // micro basic block (mblock_t)
193 | return ro.b == lo.b;
194 | case mop_r: // register
195 | return ro.r == lo.r;
196 | case mop_f:
197 | break; // not implemented
198 | case mop_l:
199 | return *ro.l == *lo.l;
200 | case mop_a:
201 | return lo.a->insize == ro.a->insize
202 | && lo.a->outsize == ro.a->outsize
203 | && equal_mops_ignore_size(*lo.a, *ro.a);
204 | case mop_h:
205 | return streq(ro.helper, lo.helper);
206 | case mop_str:
207 | return streq(ro.cstr, lo.cstr);
208 | case mop_c:
209 | return *ro.c == *lo.c;
210 | case mop_p:
211 | return equal_mops_ignore_size(lo.pair->lop, ro.pair->lop)
212 | && equal_mops_ignore_size(lo.pair->hop, ro.pair->hop);
213 | case mop_sc: // not implemented
214 | break;
215 | }
216 | return false;
217 | }
218 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/MicrocodeExtractor.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2020
2 | REhints
3 | All rights reserved.
4 |
5 | ==============================================================================
6 |
7 | This file is part of HexRaysCodeXplorer
8 |
9 | HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
10 | under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program. If not, see .
21 |
22 | ==============================================================================
23 | */
24 |
25 |
26 | // This code mostly adopted from https://github.com/RolfRolles/HexRaysDeob
27 | // All kudos going to Rolf https://www.hexblog.com/?p=1248
28 |
29 | #include
30 | #include
31 | #include "Common.h"
32 | #include "MicrocodeExtractor.h"
33 |
34 |
35 |
36 | typedef std::shared_ptr shared_mbl_array_t;
37 |
38 | struct mblock_virtual_dumper_t : public vd_printer_t
39 | {
40 | virtual ~mblock_virtual_dumper_t();
41 | int nline;
42 | int serial;
43 | mblock_virtual_dumper_t();;
44 | virtual void add_line(qstring& qs);
45 | AS_PRINTF(3, 4) virtual int print(const int indent, const char* format, ...) override;
46 | };
47 |
48 | struct mblock_virtual_dumper_t_impl final : mblock_virtual_dumper_t
49 | {
50 | };
51 |
52 |
53 | mblock_virtual_dumper_t::mblock_virtual_dumper_t(): nline(0), serial(0)
54 | {
55 | }
56 |
57 | int mblock_virtual_dumper_t::print(const int indent, const char* format, ...)
58 | {
59 | qstring buf;
60 | if (indent > 0)
61 | buf.fill(0, ' ', indent);
62 | va_list va;
63 | va_start(va, format);
64 | buf.cat_vsprnt(format, va);
65 | va_end(va);
66 |
67 | static const char pfx_on[] = {COLOR_ON, COLOR_PREFIX};
68 | static const char pfx_off[] = {COLOR_OFF, COLOR_PREFIX};
69 | buf.replace(pfx_on, "");
70 | buf.replace(pfx_off, "");
71 |
72 | add_line(buf);
73 | return buf.length();
74 | }
75 |
76 | void mblock_virtual_dumper_t::add_line(qstring& qs)
77 | {
78 | }
79 |
80 |
81 | mblock_virtual_dumper_t::~mblock_virtual_dumper_t()
82 | = default;
83 |
84 | struct mblock_qstring_dumper_t final : public mblock_virtual_dumper_t
85 | {
86 | qstring q_str;
87 | mblock_qstring_dumper_t() : mblock_virtual_dumper_t() {};
88 |
89 | void add_line(qstring& qs) override
90 | {
91 | q_str.append(qs);
92 | }
93 | };
94 |
95 | struct mblock_dumper_t final : public mblock_virtual_dumper_t
96 | {
97 | strvec_t lines;
98 | mblock_dumper_t() : mblock_virtual_dumper_t() {};
99 |
100 | void add_line(qstring& qs) override
101 | {
102 | lines.push_back(simpleline_t(qs));
103 | }
104 | };
105 |
106 | struct sample_info_t
107 | {
108 | TWidget* cv;
109 | mblock_dumper_t md;
110 | shared_mbl_array_t mba;
111 | mba_maturity_t mat;
112 | sample_info_t() : cv(nullptr), mba(nullptr), mat(){}
113 | };
114 |
115 |
116 | class microcode_instruction_graph
117 | {
118 | public:
119 | qstring tmp; // temporary buffer for grcode_user_text
120 | qstrvec_t m_short_text;
121 | qstrvec_t m_block_text;
122 | intvec_t m_edge_colors;
123 | edgevec_t m_edges;
124 | int m_num_blocks{};
125 |
126 | void clear();
127 |
128 | void build(minsn_t* top);
129 |
130 | protected:
131 | void add_edge(int i_src, int i_dest, int i_pos);
132 |
133 | int get_incr_block_num();
134 |
135 | int insert(minsn_t* ins, int i_parent);
136 |
137 | int insert(mop_t& op, int i_parent, int i_pos);
138 | };
139 |
140 | class microcode_instruction_graph_impl : public microcode_instruction_graph
141 | {
142 | public:
143 | };
144 |
145 | class microcode_instruction_graph_container;
146 |
147 | static ssize_t idaapi migr_callback(void* ud, int code, va_list va);
148 |
149 | class microcode_instruction_graph_container
150 | {
151 | protected:
152 | TWidget* m_tw_;
153 | graph_viewer_t* m_gv_;
154 | qstring m_title_;
155 | qstring m_gv_name_;
156 |
157 | public:
158 | microcode_instruction_graph m_mg;
159 | microcode_instruction_graph_container() : m_tw_(nullptr), m_gv_(nullptr) {};
160 |
161 | bool display(minsn_t* top, sample_info_t* si, const int n_block, const int n_serial)
162 | {
163 | const auto mba = *si->mba;
164 | m_mg.build(top);
165 |
166 | m_title_.cat_sprnt("Microinstruction Graph - %a[%s]/%d:%d", mba->entry_ea, micro_maturity_to_string(si->mat), n_block, n_serial);
167 | m_tw_ = create_empty_widget(m_title_.c_str());
168 | netnode id;
169 | id.create();
170 |
171 | m_gv_name_.cat_sprnt("microins_%a_%s_%d_%d", mba->entry_ea, micro_maturity_to_string(si->mat), n_block, n_serial);
172 | m_gv_ = create_graph_viewer(m_gv_name_.c_str(), id, migr_callback, this, 0, m_tw_);
173 | activate_widget(m_tw_, true);
174 | viewer_fit_window(m_gv_);
175 | return true;
176 | }
177 | };
178 |
179 | void microcode_instruction_graph::clear()
180 | {
181 | m_short_text.clear();
182 | m_block_text.clear();
183 | m_edge_colors.clear();
184 | m_edges.clear();
185 | m_num_blocks = 0;
186 | }
187 |
188 | void microcode_instruction_graph::build(minsn_t* top)
189 | {
190 | clear();
191 | insert(top, -1);
192 | }
193 |
194 | void microcode_instruction_graph::add_edge(const int i_src, const int i_dest, const int i_pos)
195 | {
196 | if (i_src < 0 || i_dest < 0)
197 | return;
198 |
199 | m_edges.push_back(edge_t(i_src, i_dest));
200 | m_edge_colors.push_back(i_pos);
201 | }
202 |
203 | int microcode_instruction_graph::get_incr_block_num()
204 | {
205 | return m_num_blocks++;
206 | }
207 |
208 | int microcode_instruction_graph::insert(minsn_t* ins, int i_parent)
209 | {
210 | char l_buf[MAXSTR];
211 | mcode_t_to_string(ins, l_buf, sizeof(l_buf));
212 | m_short_text.push_back(l_buf);
213 |
214 | qstring q_str;
215 | ins->print(&q_str);
216 | m_block_text.push_back(q_str);
217 |
218 | const auto i_this_block = get_incr_block_num();
219 |
220 | insert(ins->l, i_this_block, 0);
221 | insert(ins->r, i_this_block, 1);
222 | insert(ins->d, i_this_block, 2);
223 |
224 | return i_this_block;
225 | }
226 |
227 | int microcode_instruction_graph::insert(mop_t& op, const int i_parent, const int i_pos)
228 | {
229 | if (op.t == mop_z)
230 | return -1;
231 |
232 | m_short_text.push_back(mopt_t_to_string(op.t));
233 |
234 | qstring q_str;
235 | op.print(&q_str);
236 | m_block_text.push_back(q_str);
237 |
238 | const auto i_this_block = get_incr_block_num();
239 | add_edge(i_parent, i_this_block, i_pos);
240 |
241 | switch (op.t)
242 | {
243 | case mop_d: // result of another instruction
244 | {
245 | const auto i_dest_block = insert(op.d, i_this_block);
246 | add_edge(i_this_block, i_dest_block, 0);
247 | break;
248 | }
249 | case mop_f: // list of arguments
250 | for (auto i = 0; i < op.f->args.size(); ++i)
251 | insert(op.f->args[i], i_this_block, i);
252 | break;
253 | case mop_p: // operand pair
254 | {
255 | insert(op.pair->lop, i_this_block, 0);
256 | insert(op.pair->hop, i_this_block, 1);
257 | break;
258 | }
259 | case mop_a: // result of another instruction
260 | {
261 | insert(*op.a, i_this_block, 0);
262 | break;
263 | }
264 | default: ;
265 | }
266 | return i_this_block;
267 | }
268 |
269 | static ssize_t idaapi migr_callback(void* ud, const int code, va_list va)
270 | {
271 | auto gcont = static_cast(ud);
272 | auto microg = &gcont->m_mg;
273 | auto result = false;
274 |
275 | switch (code)
276 | {
277 | #if IDA_SDK_VERSION < 760
278 | case grcode_user_gentext:
279 | result = true;
280 | break;
281 | #endif
282 |
283 | // refresh user-defined graph nodes and edges
284 | case grcode_user_refresh:
285 | // in: mutable_graph_t *g
286 | // out: success
287 | {
288 | auto mg = va_arg(va, mutable_graph_t*);
289 |
290 | // we have to resize
291 | mg->resize(microg->m_num_blocks);
292 |
293 | for (auto& it : microg->m_edges)
294 | mg->add_edge(it.src, it.dst, nullptr);
295 |
296 | result = true;
297 | }
298 | break;
299 |
300 | // retrieve text for user-defined graph node
301 | case grcode_user_text:
302 | //mutable_graph_t *g
303 | // int node
304 | // const char **result
305 | // bgcolor_t *bg_color (maybe NULL)
306 | // out: must return 0, result must be filled
307 | // NB: do not use anything calling GDI!
308 | {
309 | va_arg(va, mutable_graph_t*);
310 | const auto node = va_arg(va, int);
311 | const auto text = va_arg(va, const char**);
312 |
313 | microg->tmp = microg->m_short_text[node];
314 | microg->tmp.append('\n');
315 | microg->tmp.append(microg->m_block_text[node]);
316 | *text = microg->tmp.begin();
317 | result = true;
318 | }
319 | break;
320 | default: ;
321 | }
322 | return static_cast(result);
323 | }
324 |
325 | static ssize_t idaapi mgr_callback(void* ud, int code, va_list va);
326 |
327 | class microcode_graph_container
328 | {
329 | public:
330 | shared_mbl_array_t m_mba;
331 | mblock_qstring_dumper_t m_mqd;
332 | qstring m_title;
333 | qstring m_gv_name;
334 | qstring tmp;
335 | shared_mbl_array_t mba;
336 | explicit microcode_graph_container(shared_mbl_array_t mba) : m_mba(std::move(mba)), mba(std::move(mba)) {};
337 | bool display(sample_info_t* si)
338 | {
339 | const auto mba = *si->mba;
340 | m_title.cat_sprnt("Microcode Graph - %a[%s]", mba->entry_ea, micro_maturity_to_string(si->mat));
341 |
342 | const auto tw = create_empty_widget(m_title.c_str());
343 | netnode id;
344 | id.create();
345 |
346 | m_gv_name.cat_sprnt("microblkgraph_%a_%s", mba->entry_ea, micro_maturity_to_string(si->mat));
347 | const auto gv = create_graph_viewer(m_gv_name.c_str(), id, mgr_callback, this, 0, tw);
348 | activate_widget(tw, true);
349 | viewer_fit_window(gv);
350 | return true;
351 | }
352 |
353 | };
354 |
355 | static ssize_t idaapi mgr_callback(void* ud, const int code, va_list va)
356 | {
357 | auto gcont = static_cast(ud);
358 | auto mba = *gcont->m_mba;
359 | auto result = false;
360 |
361 | switch (code)
362 | {
363 | #if IDA_SDK_VERSION < 760
364 | case grcode_user_gentext:
365 | result = true;
366 | break;
367 | #endif
368 |
369 | // refresh user-defined graph nodes and edges
370 | case grcode_user_refresh:
371 | // in: mutable_graph_t *g
372 | // out: success
373 | {
374 | mutable_graph_t* mg = va_arg(va, mutable_graph_t*);
375 |
376 | // we have to resize
377 | mg->resize(mba->qty);
378 |
379 | for (auto i = 0; i < mba->qty; ++i)
380 | for (auto dst : mba->get_mblock(i)->succset)
381 | mg->add_edge(i, dst, nullptr);
382 |
383 | result = true;
384 | }
385 | break;
386 |
387 | // retrieve text for user-defined graph node
388 | case grcode_user_text:
389 | //mutable_graph_t *g
390 | // int node
391 | // const char **result
392 | // bgcolor_t *bg_color (maybe NULL)
393 | // out: must return 0, result must be filled
394 | // NB: do not use anything calling GDI!
395 | {
396 | va_arg(va, mutable_graph_t*);
397 | const auto node = va_arg(va, int);
398 | const auto text = va_arg(va, const char**);
399 |
400 | gcont->m_mqd.q_str.clear();
401 | mba->get_mblock(node)->print(gcont->m_mqd);
402 | *text = gcont->m_mqd.q_str.begin();
403 | result = true;
404 | }
405 | break;
406 | default: ;
407 | }
408 | return static_cast(result);
409 | }
410 |
411 | static bool idaapi ct_keyboard(TWidget* /*v*/, const int key, const int shift, void* ud)
412 | {
413 | if (shift == 0)
414 | {
415 | auto* si = static_cast(ud);
416 | switch (key)
417 | {
418 | case 'G':
419 | {
420 | auto mgc = new microcode_graph_container(si->mba);
421 | return mgc->display(si);
422 | }
423 |
424 |
425 | // User wants to show a graph of the current instruction
426 | case 'I':
427 | {
428 | qstring buf;
429 | tag_remove(&buf, get_custom_viewer_curline(si->cv, false));
430 | const auto p_line = buf.c_str();
431 | const auto p_dot = strchr(p_line, '.');
432 | if (p_dot == nullptr)
433 | {
434 | warning(
435 | "Couldn't find the block number on the current line; was the block empty?\n"
436 | "If it was not empty, and you don't see [int].[int] at the beginning of the lines\n"
437 | "please run the plugin again to generate a new microcode listing.\n"
438 | "That should fix it.");
439 | return true; // reacted to the keypress
440 | }
441 | const auto n_block = atoi(p_line);
442 | const auto n_serial = atoi(p_dot + 1);
443 | auto mba = *si->mba;
444 |
445 | if (n_block > mba->qty)
446 | {
447 | warning("Plugin error: line prefix was %d:%d, but block only has %d basic blocks.", n_block, n_serial, mba->qty);
448 | return true;
449 | }
450 |
451 | const auto blk = mba->get_mblock(n_block);
452 | auto minsn = blk->head;
453 | int i;
454 | for (i = 0; i < n_serial; ++i)
455 | {
456 | minsn = minsn->next;
457 | if (minsn == nullptr)
458 | break;
459 | }
460 |
461 | if (minsn == nullptr)
462 | {
463 | if (i == 0)
464 | warning(
465 | "Couldn't get first minsn_t from %d:%d; was the block empty?\n"
466 | "If it was not empty, and you don't see [int].[int] at the beginning of the lines\n"
467 | "please run the plugin again to generate a new microcode listing.\n"
468 | "That should fix it.", n_block, n_serial);
469 | else
470 | warning("Couldn't get first minsn_t from %d:%d; last valid instruction was %d", n_block, n_serial, i - 1);
471 | return true;
472 | }
473 |
474 | char repr[MAXSTR];
475 | mcode_t_to_string(minsn, repr, sizeof(repr));
476 | auto mcg = new microcode_instruction_graph_container;
477 | return mcg->display(minsn, si, n_block, n_serial);
478 | }
479 | case IK_ESCAPE:
480 | close_widget(si->cv, WCLS_SAVE | WCLS_CLOSE_LATER);
481 | return true;
482 | default: ;
483 | }
484 | }
485 | return false;
486 | }
487 |
488 | static const custom_viewer_handlers_t handlers(
489 | ct_keyboard,
490 | nullptr, // popup
491 | nullptr, // mouse_moved
492 | nullptr, // click
493 | nullptr, // dblclick
494 | nullptr,
495 | nullptr, // close
496 | nullptr, // help
497 | nullptr);// adjust_place
498 |
499 | ssize_t idaapi ui_callback(void* ud, const int code, va_list va)
500 | {
501 | const auto si = static_cast(ud);
502 | switch (code)
503 | {
504 | case ui_widget_invisible:
505 | {
506 | const auto f = va_arg(va, TWidget*);
507 | if (f == si->cv)
508 | {
509 | delete si;
510 | unhook_from_notification_point(HT_UI, ui_callback);
511 | }
512 | }
513 | break;
514 | default: ;
515 | }
516 | return 0;
517 | }
518 |
519 | const char* mat_levels[] =
520 | {
521 | "MMAT_GENERATED",
522 | "MMAT_PREOPTIMIZED",
523 | "MMAT_LOCOPT",
524 | "MMAT_CALLS",
525 | "MMAT_GLBOPT1",
526 | "MMAT_GLBOPT2",
527 | "MMAT_GLBOPT3",
528 | "MMAT_LVARS"
529 | };
530 |
531 | mba_maturity_t ask_desired_maturity()
532 | {
533 | const char dlg_text[] =
534 | "Select maturity level\n"
535 | "\n";
536 |
537 | qstrvec_t opts;
538 | for (auto& mat_level : mat_levels)
539 | opts.push_back(mat_level);
540 |
541 | auto sel = 0;
542 | const auto ret = ask_form(dlg_text, &opts, &sel);
543 |
544 | if (ret > 0)
545 | return static_cast(static_cast(MMAT_GENERATED) + sel);
546 | return MMAT_ZERO;
547 | }
548 |
549 | void show_microcode_explorer()
550 | {
551 | const auto pfn = get_func(get_screen_ea());
552 | if (pfn == nullptr)
553 | {
554 | warning("Please position the cursor within a function");
555 | return;
556 | }
557 |
558 | const auto mmat = ask_desired_maturity();
559 | if (mmat == MMAT_ZERO)
560 | return;
561 |
562 | hexrays_failure_t hf;
563 | auto mba = gen_microcode(pfn, &hf, nullptr, 0, mmat);
564 | if (mba == nullptr)
565 | {
566 | warning("#error \"%a: %s", hf.errea, hf.desc().c_str());
567 | return;
568 | }
569 |
570 | auto si = new sample_info_t;
571 | si->mba = std::make_shared(mba);
572 | si->mat = mmat;
573 | // Dump the microcode to the output window
574 | mba->print(si->md);
575 |
576 | simpleline_place_t s1;
577 | simpleline_place_t s2(si->md.lines.size() - 1);
578 |
579 | qstring title;
580 | title.cat_sprnt("Microcode Explorer - %a - %s", pfn->start_ea, micro_maturity_to_string(mmat));
581 |
582 | si->cv = create_custom_viewer(
583 | title.c_str(), // title
584 | &s1, // minplace
585 | &s2, // maxplace
586 | &s1, // curplace
587 | nullptr, // renderer_info_t *rinfo
588 | &si->md.lines, // ud
589 | &handlers, // cvhandlers
590 | si, // cvhandlers_ud
591 | nullptr); // parent
592 |
593 | hook_to_notification_point(HT_UI, ui_callback, si);
594 | display_widget(si->cv, WOPN_DP_TAB | WOPN_RESTORE);
595 | }
596 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/GCCObjectFormatParser.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2020
2 | REhints
3 | All rights reserved.
4 |
5 | ==============================================================================
6 |
7 | This file is part of HexRaysCodeXplorer
8 |
9 | HexRaysCodeXplorer is free software: you can redistribute it and/or modify it
10 | under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program. If not, see .
21 |
22 | ==============================================================================
23 | */
24 |
25 |
26 |
27 |
28 | #include "GCCObjectFormatParser.h"
29 | #include "Common.h"
30 | #include "entry.hpp"
31 | #include "Debug.h"
32 | #include "demangle.hpp"
33 | #include "name.hpp"
34 | #include "offset.hpp"
35 | #include "nalt.hpp"
36 | #include "bytes.hpp"
37 | #include "Utility.h"
38 | #include "stddef.h"
39 | #include "GCCVtableInfo.h"
40 | #include "GCCTypeInfo.h"
41 | #include "struct.hpp"
42 | #include "Debug.h"
43 | #include "ReconstructableType.h"
44 | #include
45 |
46 | #define vmi_class_type_info_name "_ZTVN10__cxxabiv121__vmi_class_type_infoE"
47 | #define class_type_info_name "_ZTVN10__cxxabiv117__class_type_infoE"
48 | #define si_class_type_info_name "_ZTVN10__cxxabiv120__si_class_type_infoE"
49 |
50 | std::unordered_mapg_KnownVtables;
51 | std::unordered_mapg_KnownTypes;
52 | std::unordered_mapg_KnownVtableNames;
53 | std::unordered_mapg_KnownTypeNames;
54 |
55 | ea_t class_type_info_vtbl = -1;
56 | ea_t si_class_type_info_vtbl = -1;
57 | ea_t vmi_class_type_info_vtbl = -1;
58 |
59 | static void buildReconstructableTypes();
60 |
61 | GCCObjectFormatParser::GCCObjectFormatParser()
62 | {
63 | }
64 |
65 | GCCObjectFormatParser::~GCCObjectFormatParser()
66 | {
67 | }
68 |
69 |
70 | static int import_enum_cb(ea_t ea, const char *name, uval_t ord, void *param) {
71 | if (name == 0)
72 | return 1;
73 | ea += sizeof(GCC_RTTI::__vtable_info); // BUG From IDA. Hello funny imports.
74 | if (class_type_info_vtbl == BADADDR && !memcmp(class_type_info_name, name, sizeof(class_type_info_name) - 1))
75 | {
76 | class_type_info_vtbl = ea;
77 | set_name(class_type_info_vtbl, "__cxxabiv1::__class_type_info::vtable", SN_NOWARN);
78 | }
79 |
80 | if (si_class_type_info_vtbl == BADADDR && !memcmp(si_class_type_info_name, name, sizeof(si_class_type_info_name) - 1))
81 | {
82 | si_class_type_info_vtbl = ea;
83 | set_name(si_class_type_info_vtbl, "__cxxabiv1::__si_class_type_info::vtable", SN_NOWARN);
84 | }
85 |
86 | if (vmi_class_type_info_vtbl == BADADDR && !memcmp(vmi_class_type_info_name, name, sizeof(vmi_class_type_info_name) - 1))
87 | {
88 | vmi_class_type_info_vtbl = ea;
89 | set_name(vmi_class_type_info_vtbl, "__cxxabiv1::__vmi_class_type_info::vtable", SN_NOWARN);
90 | }
91 |
92 | return 1;
93 | }
94 |
95 | static int __get_ea_of_name(size_t index, ea_t *value) {
96 | ea_t ea = get_nlist_ea(index);
97 | if (ea == BADADDR)
98 | return -1;
99 | *value = ea;
100 | return 0;
101 | }
102 |
103 | static int find_vtbls_by_names(bool force) {
104 | size_t cnt = get_nlist_size();
105 | unsigned int found_vtbls = 0;
106 | ea_t ea;
107 |
108 | if (force)
109 | class_type_info_vtbl = si_class_type_info_vtbl = vmi_class_type_info_vtbl = BADADDR;
110 | else {
111 | if (class_type_info_vtbl != BADADDR)
112 | ++found_vtbls;
113 | if (si_class_type_info_vtbl != BADADDR)
114 | ++found_vtbls;
115 | if (vmi_class_type_info_vtbl != BADADDR)
116 | ++found_vtbls;
117 | }
118 | for (size_t i = 0; i < cnt && found_vtbls < 3; ++i) {
119 | const char *name = get_nlist_name(i);
120 | if (name && memcmp(name, "_ZTVN10__cxxabiv", sizeof("_ZTVN10__cxxabiv")-1) == 0) {
121 | if (class_type_info_vtbl == BADADDR)
122 | if (memcmp(name, class_type_info_name, sizeof(class_type_info_name)-1) == 0)
123 | if (!__get_ea_of_name(i, &ea))
124 | {
125 | ea += sizeof(GCC_RTTI::__vtable_info);
126 | class_type_info_vtbl = ea;
127 | ++found_vtbls;
128 | continue;
129 | }
130 | if (si_class_type_info_vtbl == BADADDR)
131 | if (memcmp(name, si_class_type_info_name, sizeof(si_class_type_info_name)-1) == 0)
132 | if (!__get_ea_of_name(i, &ea))
133 | {
134 | ea += sizeof(GCC_RTTI::__vtable_info);
135 | si_class_type_info_vtbl = ea;
136 | ++found_vtbls;
137 | continue;
138 | }
139 | if (vmi_class_type_info_vtbl == BADADDR)
140 | if (memcmp(name, vmi_class_type_info_name, sizeof(vmi_class_type_info_name)-1) == 0)
141 | if (!__get_ea_of_name(i, &ea))
142 | {
143 | ea += sizeof(GCC_RTTI::__vtable_info);
144 | vmi_class_type_info_vtbl = ea;
145 | ++found_vtbls;
146 | continue;
147 | }
148 | }
149 | }
150 | return 0;
151 | }
152 |
153 | int GCCObjectFormatParser::collect_info_vtbls(bool force) {
154 | size_t count = get_entry_qty();
155 | qstring buffer;
156 |
157 | /* We already know some values, so lets omit the search. */
158 | if (!force && (class_type_info_vtbl != -1 ||
159 | si_class_type_info_vtbl != -1 ||
160 | vmi_class_type_info_vtbl != -1))
161 | return 0;
162 |
163 | if (force) {
164 | class_type_info_vtbl = si_class_type_info_vtbl = vmi_class_type_info_vtbl = BADADDR;
165 | }
166 | for (int i = 0; i < count; ++i) {
167 | uval_t ordinal = get_entry_ordinal(i);
168 | get_entry_name(&buffer, ordinal);
169 | ea_t ea = get_entry(ordinal);
170 | ea += sizeof(GCC_RTTI::__vtable_info);
171 |
172 | if (class_type_info_vtbl == BADADDR && !memcmp(class_type_info_name, buffer.c_str(), sizeof(class_type_info_name) - 1))
173 | {
174 | class_type_info_vtbl = ea;
175 | set_name(ea, "__cxxabiv1::__class_type_info::vtable", SN_NOWARN);
176 | }
177 |
178 | if (si_class_type_info_vtbl == BADADDR && !memcmp(si_class_type_info_name, buffer.c_str(), sizeof(si_class_type_info_name) - 1))
179 | {
180 | si_class_type_info_vtbl = ea;
181 | set_name(ea, "__cxxabiv1::__si_class_type_info::vtable", SN_NOWARN);
182 | }
183 |
184 | if (vmi_class_type_info_vtbl == BADADDR && !memcmp(vmi_class_type_info_name, buffer.c_str(), sizeof(vmi_class_type_info_name) - 1))
185 | {
186 | vmi_class_type_info_vtbl = ea;
187 | set_name(ea, "__cxxabiv1::__vmi_class_type_info::vtable", SN_NOWARN);
188 | }
189 | }
190 |
191 | count = get_import_module_qty();
192 | for (uint index = 0; index < count; ++index)
193 | enum_import_names(index, &import_enum_cb, this);
194 |
195 | find_vtbls_by_names(false);
196 |
197 |
198 | if (class_type_info_vtbl == -1 &&
199 | si_class_type_info_vtbl == -1 &&
200 | vmi_class_type_info_vtbl == -1)
201 | return -1;
202 | return 0;
203 | }
204 |
205 | void GCCObjectFormatParser::get_rtti_info()
206 | {
207 | collect_info_vtbls();
208 | if (class_type_info_vtbl == -1 &&
209 | si_class_type_info_vtbl == -1 &&
210 | vmi_class_type_info_vtbl == -1)
211 | return;
212 | // if no any rtti vtables, we cant read it.
213 | // now we can scan segments for vtables.
214 | int segCount = get_segm_qty();
215 | for (int i = 0; i < segCount; i++)
216 | {
217 | if (segment_t *seg = getnseg(i))
218 | {
219 | if (seg->type == SEG_DATA)
220 | {
221 | scanSeg4Vftables(seg);
222 | }
223 | }
224 | }
225 |
226 | buildReconstructableTypes();
227 | }
228 |
229 | void GCCObjectFormatParser::scanSeg4Vftables(segment_t *seg)
230 | {
231 | size_t size = (std::max)(sizeof(GCC_RTTI::__vtable_info), sizeof(GCC_RTTI::type_info));
232 | unsigned char buffer[(std::max)(sizeof(GCC_RTTI::__vtable_info), sizeof(GCC_RTTI::type_info))];
233 |
234 | ea_t startEA = ((seg->start_ea + sizeof(ea_t)) & ~((ea_t)(sizeof(ea_t) - 1)));
235 | ea_t endEA = (seg->end_ea - sizeof(ea_t));
236 | ea_t ea = startEA;
237 | while (ea < endEA)
238 | {
239 | if (g_KnownTypes.count(ea)) {
240 | ea += g_KnownTypes[ea]->size;
241 | continue;
242 | }
243 |
244 | if (g_KnownVtables.count(ea)) {
245 | ea = g_KnownVtables[ea]->ea_end;
246 | continue;
247 | }
248 | if (!get_bytes(buffer, size, ea)) {
249 | ea += sizeof(ea_t);
250 | continue;
251 | }
252 |
253 | GCC_RTTI::type_info *ti = (GCC_RTTI::type_info *)buffer;
254 | GCC_RTTI::__vtable_info *vt = (GCC_RTTI::__vtable_info *)buffer;
255 | // do some sanity checks if it looks like a Virtual Table
256 | if (vt->ptrdiff == 0) {
257 | GCCTypeInfo *type = GCCTypeInfo::parseTypeInfo(vt->type_info);
258 | if (type != 0) {
259 | GCCVtableInfo * info = GCCVtableInfo::parseVtableInfo(ea);
260 | if (info)
261 | {
262 | VTBL_info_t vtbl_info;
263 | vtbl_info.ea_begin = info->ea_start + sizeof(GCC_RTTI::__vtable_info);
264 | vtbl_info.ea_end = info->ea_end;
265 | vtbl_info.vtbl_name = info->typeName.c_str();
266 | vtbl_info.methods = info->vtables[0].methodsCount;
267 | rtti_vftables[ea + sizeof(GCC_RTTI::__vtable_info)] = vtbl_info;
268 | ea = info->ea_end;
269 | continue;
270 | }
271 |
272 | }
273 | }
274 |
275 | GCCTypeInfo *typeInfo = GCCTypeInfo::parseTypeInfo(ea);
276 | if (typeInfo)
277 | ea += typeInfo->size;
278 | else
279 | ea += sizeof(ea_t);
280 | }
281 | return;
282 | }
283 |
284 | void GCCObjectFormatParser::clear_info()
285 | {
286 | g_KnownVtables.clear();
287 | g_KnownVtableNames.clear();
288 | g_KnownTypes.clear();
289 | g_KnownTypeNames.clear();
290 | assert(false); // reasonable question what to do with ReconstructableTypes.
291 | }
292 |
293 | void buildReconstructableTypesRecursive(GCCTypeInfo *type, std::set &visitedTypes) {
294 | if (visitedTypes.count(type))
295 | return;
296 | // Handle parents first
297 | if (type->parentsCount)
298 | {
299 | for (unsigned long i = 0; i < type->parentsCount; ++i) {
300 | GCCParentType * parent = type->parentsTypes[i];
301 | buildReconstructableTypesRecursive(parent->info, visitedTypes);
302 | }
303 | }
304 |
305 | ReconstructableType *reType;
306 | if (g_ReconstractedTypes.count(type->typeName)) {
307 | reType = g_ReconstractedTypes[type->typeName];
308 | return;
309 | }
310 | else {
311 | reType = ReconstructableType::getReconstructableType(type->typeName);
312 | reType->SyncTypeInfo();
313 | }
314 |
315 | if (type->vtable) // type has vtable;
316 | {
317 | GCCVtableInfo *vtblInfo = type->vtable;
318 | std::string vtbl_class_name = type->typeName + VTBL_CLSNAME_POSTFIX;
319 | if (g_ReconstractedTypes.count(vtbl_class_name)) {
320 | assert(false); // one more assert for the future
321 | }
322 | char buffer[256];
323 |
324 | for (unsigned int i = 0; i < vtblInfo->vtablesCount; ++i) {
325 | unsigned int offset = (unsigned int)(-(signed int)vtblInfo->vtables[i].ptrDiff);
326 | std::string parentName = vtblInfo->vtables[i].name.c_str();
327 | std::string vtblName = vtbl_class_name;
328 | if (i != 0) {
329 | snprintf(buffer, sizeof(buffer), "%s_%x_of_%s", vtbl_class_name.c_str(), offset, parentName.c_str());
330 | vtblName = buffer;
331 | }
332 | ReconstructableType * reVtbl = ReconstructableTypeVtable::get_reconstructable_type_vtable(vtblName, vtblInfo->ea_start);
333 | if (i != 0) {
334 | ReconstructableType *parent;
335 | if (g_ReconstractedTypes.count(type->parentsTypes[i]->info->typeName))
336 | {
337 | parent = g_ReconstractedTypes[type->parentsTypes[i]->info->typeName];
338 | std::map pmembers = parent->getOwnMembers();
339 | if (parent->getSize() < sizeof(uval_t)) {
340 | ReconstructableType *parentVtbl = ReconstructableType::getReconstructableType(parent->name + VTBL_CLSNAME_POSTFIX);
341 | if (parentVtbl->getSize() < vtblInfo->vtables[i].methodsCount) {
342 | for (unsigned int methodIndx = parentVtbl->getSize(); methodIndx < vtblInfo->vtables[i].methodsCount; ++methodIndx)
343 | {
344 | ReconstructableMember *pmethod = new ReconstructableMember();
345 | pmethod->name = "purecall";
346 | pmethod->name += std::to_string(methodIndx);
347 | pmethod->offset = methodIndx * sizeof(uval_t);
348 | tinfo_t info = dummy_ptrtype(sizeof(uval_t), 0);
349 | pmethod->memberType = new MemberTypeIDATypeInfoGate(info);
350 | parentVtbl->AddMember(pmethod);
351 | }
352 | }
353 | ReconstructableMember *pmember = new ReconstructableMember();
354 | pmember->name = "vtable";
355 | pmember->offset = 0;
356 | pmember->memberType = new MemberTypePointer(parentVtbl->name);
357 | parent->AddMember(pmember);
358 | }
359 | }
360 | }
361 |
362 |
363 | for (unsigned int j = 0; j < vtblInfo->vtables[i].methodsCount; ++j) {
364 | ReconstructableMember *member = new ReconstructableMember();
365 | member->offset = sizeof(uval_t)*j;
366 | ea_t funcPtr = getEa(vtblInfo->vtables[i].ea + sizeof(uval_t)*j + sizeof(GCC_RTTI::__vtable_info));
367 | if (funcPtr == 0) {
368 | member->name = "purecall";
369 | member->name += std::to_string(j);
370 | }
371 |
372 | else {
373 | if (ph.id == PLFM_ARM)
374 | funcPtr &= (ea_t)-2;
375 | qstring method_name;
376 | get_ea_name(&method_name, funcPtr);
377 | if (method_name.find("sub_", 0) == 0 || method_name.length() == 0) {
378 | // we can rename it.
379 | qstring newName;
380 | newName.sprnt("%s::refunc_%x", type->typeName.c_str(), funcPtr);
381 | if (set_name(funcPtr, newName.c_str(), SN_NOWARN)) {
382 | method_name = newName;
383 | }
384 | }
385 | if (method_name.length() == 0)
386 | method_name.sprnt("___refunc_%x", funcPtr);
387 | member->name = method_name.c_str();
388 | }
389 |
390 | tinfo_t info = dummy_ptrtype(sizeof(uval_t), 0);
391 | member->memberType = new MemberTypeIDATypeInfoGate(info);
392 | reVtbl->AddMember(member);
393 | }
394 | if (i == 0) {
395 | // add vtable info
396 | ReconstructableMember *member = new ReconstructableMember();
397 | member->name = "vtable";
398 | member->offset = 0;
399 | member->memberType = new MemberTypePointer(vtbl_class_name);
400 | reType->AddMember(member);
401 | }
402 | if (i < type->parentsCount && g_ReconstractedTypes.count(type->parentsTypes[i]->info->typeName + VTBL_CLSNAME_POSTFIX)) {
403 |
404 | ReconstructableType *parentVtbl = g_ReconstractedTypes[type->parentsTypes[i]->info->typeName + VTBL_CLSNAME_POSTFIX];
405 |
406 | ReconstructableMember *dmember = new ReconstructableMember();
407 | dmember->name = parentVtbl->name;
408 | dmember->offset = 0;
409 | dmember->memberType = new ReconstructedMemberReType(parentVtbl);
410 | reVtbl->AddDerivedMember(dmember);
411 | }
412 | reVtbl->SyncTypeInfo();
413 | // we have vtable, we have it as structure, lets apply its name and type to IDB
414 | if (i == 0) {
415 | std::string idb_name = type->typeName + "::_vftable";
416 | ea_t ea = vtblInfo->vtables[0].ea + 2 * sizeof(uval_t);
417 | setUnknown(ea, vtblInfo->vtables[0].methodsCount * sizeof(uval_t));
418 | MakeName(ea, idb_name.c_str());
419 | tinfo_t tinfo;
420 | if (tinfo.get_named_type(get_idati(), reVtbl->name.c_str())) {
421 | apply_tinfo(ea, tinfo, TINFO_DEFINITE);
422 | }
423 | }
424 | }
425 | }
426 | for (unsigned int i = 0; i < type->parentsCount; ++i) {
427 | assert(g_ReconstractedTypes.count(type->parentsTypes[i]->info->typeName));
428 | ReconstructableType *parent = g_ReconstractedTypes[type->parentsTypes[i]->info->typeName];
429 | type->parentsTypes[i];
430 | ReconstructableMember* member = new ReconstructableMember();
431 | member->offset = type->parentsTypes[i]->offset;
432 | member->name = type->parentsTypes[i]->info->typeName;
433 | member->memberType = new ReconstructedMemberReType(parent);
434 | reType->AddDerivedMember(member);
435 | }
436 | visitedTypes.emplace(type);
437 | }
438 |
439 |
440 | void fixupRecounstructableTypesId() {
441 | unsigned long id = 0;
442 | for (auto iterator = g_ReconstractedTypes.begin(); iterator != g_ReconstractedTypes.end(); iterator++, id++)
443 | {
444 | iterator->second->id = id;
445 | }
446 | }
447 |
448 | static void buildReconstructableTypes() {
449 | std::set visitedTypes;
450 | SyncTypeInfoMethod curMethod = syncTypeInfoMethod;
451 | syncTypeInfoMethod = SyncTypeInfo_Names;
452 | std::unordered_map::iterator typesIterator;
453 | for (typesIterator = g_KnownTypes.begin(); typesIterator != g_KnownTypes.end(); ++typesIterator) {
454 | GCCTypeInfo *curType = typesIterator->second;
455 | if (visitedTypes.count(curType))
456 | continue; // already parsed
457 | buildReconstructableTypesRecursive(curType, visitedTypes);
458 | }
459 | fixupRecounstructableTypesId();
460 | syncTypeInfoMethod = curMethod;
461 | for (auto typeIt = g_ReconstractedTypes.begin(); typeIt != g_ReconstractedTypes.end(); ++typeIt) {
462 | typeIt->second->SyncTypeInfo();
463 | }
464 |
465 | return;
466 | }
467 |
--------------------------------------------------------------------------------
/src/HexRaysCodeXplorer/reconstructed_place_t.cpp:
--------------------------------------------------------------------------------
1 | #include "reconstructed_place_t.h"
2 | #include "ReconstructableType.h"
3 | #include "GCCVtableInfo.h"
4 | #include "Utility.h"
5 |
6 | extern std::unordered_mapg_KnownVtableNames;
7 |
8 | reconstructed_place_t g_replace;
9 |
10 |
11 | reconstructed_place_t::reconstructed_place_t() : place_t(0), typeName(), index(0), position(REPLACE_SPLIT) {
12 | }
13 |
14 | reconstructed_place_t::reconstructed_place_t(std::string n) : typeName(n), index(0), position(REPLACE_SPLIT),
15 | own_offset(0), atOwnMembers(false)
16 | {
17 | if (g_ReconstractedTypes.count(n))
18 | lnnum = g_ReconstractedTypes[n]->id;
19 |
20 | else
21 | lnnum = 0;
22 | }
23 |
24 |
25 | reconstructed_place_t::reconstructed_place_t(const reconstructed_place_t &other) : place_t(other.lnnum), typeName(other.typeName), index(other.index),
26 | position(other.position), own_offset(other.own_offset), atOwnMembers(other.atOwnMembers) {
27 | if (g_ReconstractedTypes.count(typeName))
28 | lnnum = g_ReconstractedTypes[typeName]->id;
29 |
30 | }
31 |
32 |
33 |
34 | void idaapi reconstructed_place_t::print(qstring * out_buf, void * ud) const
35 | {
36 | if (g_ReconstractedTypes.count(typeName) == 0)
37 | return;
38 |
39 | ReconstructableType *reType = g_ReconstractedTypes[typeName];
40 | //assert(!reType);
41 | out_buf->sprnt("lnnum %d index 0x%x type %s", lnnum, index, typeName.c_str());
42 | }
43 |
44 | uval_t idaapi reconstructed_place_t::touval(void * ud) const
45 | {
46 | return index;
47 | }
48 |
49 | place_t *idaapi reconstructed_place_t::clone(void) const
50 | {
51 | reconstructed_place_t *ptr = new reconstructed_place_t(*this);
52 | return ptr;
53 | }
54 |
55 | void idaapi reconstructed_place_t::copyfrom(const place_t * from)
56 | {
57 | reconstructed_place_t* other = (reconstructed_place_t*)from;
58 | typeName = other->typeName;
59 | lnnum = other->lnnum;
60 | if (g_ReconstractedTypes.count(typeName))
61 | lnnum = g_ReconstractedTypes[typeName]->id;
62 | index = other->index;
63 | position = other->position;
64 | own_offset = other->own_offset;
65 | atOwnMembers = other->atOwnMembers;
66 | }
67 |
68 | place_t *idaapi reconstructed_place_t::makeplace(void * ud, uval_t x, int lnnum) const
69 | {
70 | int offset = (long)ud;
71 | std::map::iterator it = g_ReconstractedTypes.begin();
72 | while (it != g_ReconstractedTypes.end()) {
73 | if (it->second->typeId == x)
74 | break;
75 | it++;
76 | }
77 |
78 |
79 | if (it == g_ReconstractedTypes.end()) {
80 | g_replace.index = 0;
81 | g_replace.typeName = g_ReconstractedTypes.rbegin()->first;
82 | g_replace.lnnum = 0;
83 | g_replace.own_offset = 0;
84 | g_replace.position = REPLACE_SPLIT;
85 | g_replace.atOwnMembers = false;
86 |
87 | }
88 | else {
89 | g_replace.index = x;
90 | g_replace.typeName = it->second->name;
91 | g_replace.own_offset = offset;
92 | g_replace.lnnum = lnnum;
93 | g_replace.position = REPLACE_SPLIT;
94 | g_replace.atOwnMembers = false;
95 | if (offset) {
96 | g_replace.position = REPLACE_MEMBERS;
97 | g_replace.atOwnMembers = true;
98 | }
99 |
100 | }
101 | return &g_replace;
102 | }
103 |
104 | int idaapi reconstructed_place_t::compare(const place_t * t2) const
105 | {
106 | reconstructed_place_t * other = ((reconstructed_place_t*)t2);
107 | if (lnnum != other->lnnum)
108 | return lnnum - other->lnnum;
109 | if (position != other->position)
110 | return (int)position - (int)other->position;
111 | if (own_offset != other->own_offset)
112 | return own_offset - other->own_offset;
113 | return atOwnMembers - other->atOwnMembers;
114 | }
115 |
116 | void idaapi reconstructed_place_t::adjust(void * ud)
117 | {
118 | if (g_ReconstractedTypes.count(typeName) == 0) {
119 | if (g_ReconstractedTypes.size())
120 | typeName = g_ReconstractedTypes.begin()->first;
121 | else
122 | typeName = "";
123 | lnnum = 0;
124 | }
125 | else
126 | lnnum = g_ReconstractedTypes[typeName]->id;
127 | }
128 |
129 | reconstructed_place_pos_t& operator--(reconstructed_place_pos_t&pos) {
130 | switch (pos) {
131 | case REPLACE_BLANK: return pos = REPLACE_SPLIT;
132 | case REPLACE_CLASSNAME_TOP: return pos = REPLACE_BLANK;
133 | case REPLACE_PARENTS: return pos = REPLACE_CLASSNAME_TOP;
134 | case REPLACE_CHILDREN: return pos = REPLACE_PARENTS;
135 | case REPLACE_MEMBERS: return pos = REPLACE_CHILDREN;
136 | case REPLACE_BLANK_BOT: return pos = REPLACE_MEMBERS;
137 | case REPLACE_CLASSNAME_BOT: return pos = REPLACE_BLANK_BOT;
138 | default:
139 | assert(false);
140 | }
141 |
142 | }
143 |
144 | reconstructed_place_pos_t& operator++(reconstructed_place_pos_t&pos) {
145 | switch (pos) {
146 | case REPLACE_SPLIT: return pos = REPLACE_BLANK;
147 | case REPLACE_BLANK: return pos = REPLACE_CLASSNAME_TOP;
148 | case REPLACE_CLASSNAME_TOP: return pos = REPLACE_PARENTS;
149 | case REPLACE_PARENTS: return pos = REPLACE_CHILDREN;
150 | case REPLACE_CHILDREN: return pos = REPLACE_MEMBERS;
151 | case REPLACE_MEMBERS: return pos = REPLACE_BLANK_BOT;
152 | case REPLACE_BLANK_BOT: return pos = REPLACE_CLASSNAME_BOT;
153 | default:
154 | assert(false);
155 | }
156 | }
157 |
158 |
159 |
160 | bool idaapi reconstructed_place_t::prev(void * ud)
161 | {
162 | ReconstructableType* t;
163 | std::map::iterator it = g_ReconstractedTypes.find(typeName);
164 | if (it == g_ReconstractedTypes.end()) {
165 | index = 0;
166 | lnnum = 0;
167 | position = REPLACE_SPLIT;
168 | typeName = g_ReconstractedTypes.begin()->second->name;
169 | atOwnMembers = false;
170 | return false;
171 | }
172 |
173 | t = it->second;
174 | std::map members = t->getOwnMembers();
175 | std::map::iterator members_it;
176 | std::map::reverse_iterator members_rit;
177 | std::map derivedMembers = t->getDerivedMembers();
178 | std::map::iterator derived_it;
179 | std::map::reverse_iterator derived_rit;
180 | std::set parents = t->getParents();
181 | std::set children = t->getChildren();
182 | unsigned int new_offset;
183 |
184 | while (1) {
185 |
186 | switch (position) {
187 | case REPLACE_SPLIT:
188 | if (it == g_ReconstractedTypes.begin())
189 | return false;
190 |
191 | t = (--it)->second;
192 | position = REPLACE_CLASSNAME_BOT;
193 | own_offset = t->getSize();
194 | typeName = t->name;
195 | index = t->typeId;
196 | lnnum = t->id;
197 | return true;
198 |
199 | case REPLACE_CLASSNAME_BOT:
200 | case REPLACE_PARENTS:
201 | case REPLACE_CLASSNAME_TOP:
202 | case REPLACE_BLANK:
203 | --position;
204 | return true;
205 |
206 | case REPLACE_CHILDREN:
207 | --position;
208 | if (parents.size())
209 | return true;
210 | break;
211 |
212 | case REPLACE_BLANK_BOT:
213 | --position;
214 | if (members.size() || derivedMembers.size())
215 | {
216 | own_offset = 0;
217 | members_rit = members.rbegin();
218 | derived_rit = derivedMembers.rbegin();
219 |
220 | if (derived_rit != derivedMembers.rend())
221 | {
222 | own_offset = derived_rit->first;
223 | atOwnMembers = false;
224 | }
225 |
226 | if (members_rit != members.rend() && members_rit->first >= own_offset) {
227 | atOwnMembers = true;
228 | own_offset = members_rit->first;
229 | }
230 | return true;
231 | }
232 | // no members at all, go back to children
233 | --position;
234 | break;
235 |
236 | case REPLACE_MEMBERS:
237 | {
238 | if (atOwnMembers && derivedMembers.count(own_offset)) {
239 | atOwnMembers = false;
240 | return true;
241 | }
242 | atOwnMembers = false;
243 | if (own_offset == 0) {
244 | --position;
245 | if (children.size())
246 | return true;
247 | break;
248 | }
249 | new_offset = 0;
250 | if (derivedMembers.size())
251 | {
252 | derived_it = derivedMembers.lower_bound(own_offset);
253 | if (derived_it == derivedMembers.end())
254 | new_offset = derivedMembers.rbegin()->first;
255 | else
256 | {
257 | --derived_it;
258 | if (derived_it != derivedMembers.end())
259 | new_offset = derived_it->first;
260 | else
261 | new_offset = 0;
262 | }
263 | }
264 | if (members.size())
265 | {
266 | members_it = members.lower_bound(own_offset);
267 | if (members_it == members.end()) {
268 | if (members.rbegin()->first >= new_offset) {
269 | new_offset = members.rbegin()->first;
270 | atOwnMembers = true;
271 | }
272 | }
273 | else
274 | {
275 | --members_it;
276 | if (members_it != members.end()) {
277 | if (new_offset <= members_it->first) {
278 | new_offset = members_it->first;
279 | atOwnMembers = true;
280 | }
281 | }
282 | else
283 | {
284 | if (new_offset == 0) {
285 | if (members.begin()->first == 0) {
286 | new_offset = members_it->first;
287 | atOwnMembers = true;
288 | }
289 | }
290 | }
291 |
292 | }
293 | }
294 | own_offset = new_offset;
295 | if (atOwnMembers || derivedMembers.count(own_offset))
296 | return true;
297 | //assert(false);
298 | return true;
299 | }
300 | default:
301 | assert(false);
302 | }
303 |
304 | }
305 | }
306 |
307 | bool idaapi reconstructed_place_t::next(void * ud)
308 | {
309 | ReconstructableType* t;
310 | std::map::iterator it = g_ReconstractedTypes.find(typeName);
311 | if (it == g_ReconstractedTypes.end())
312 | return false;
313 | t = it->second;
314 | size_t size = t->getSize();
315 | std::map members = t->getOwnMembers();
316 | std::map::iterator members_it;
317 | std::map derivedMembers = t->getDerivedMembers();
318 | std::map::iterator derived_it;
319 | std::set parents = t->getParents();
320 | std::set children = t->getChildren();
321 |
322 |
323 | while (1) {
324 |
325 | switch (position) {
326 | case REPLACE_SPLIT:
327 | case REPLACE_BLANK:
328 | case REPLACE_BLANK_BOT:
329 | ++position;
330 | return true;
331 | case REPLACE_CLASSNAME_TOP:
332 | ++position;
333 | if (parents.size())
334 | return true;
335 | break;
336 | case REPLACE_PARENTS:
337 | ++position;
338 | if (children.size())
339 | return true;
340 | break;
341 | case REPLACE_CHILDREN:
342 | ++position;
343 | atOwnMembers = false;
344 | own_offset = t->getSize();
345 | if (derivedMembers.size() || members.size()) {
346 | if (derivedMembers.size()) {
347 | own_offset = derivedMembers.begin()->first;
348 | }
349 | if (members.size()) {
350 | if (members.begin()->first < own_offset) {
351 | own_offset = members.begin()->first;
352 | atOwnMembers = true;
353 | }
354 |
355 | }
356 | return true;
357 | }
358 | // no members, continue search position
359 | ++position;
360 | break;
361 | case REPLACE_MEMBERS:
362 |
363 | //we showed derived member
364 | if (atOwnMembers == false && members.count(own_offset)) {
365 | atOwnMembers = true;
366 | return true;
367 | }
368 |
369 | members_it = members.upper_bound(own_offset);
370 | derived_it = derivedMembers.upper_bound(own_offset);
371 |
372 | if (members_it != members.end() || derived_it != derivedMembers.end()) {
373 | own_offset = size;
374 | if (members_it != members.end()) {
375 | own_offset = members_it->first;
376 | atOwnMembers = true;
377 | }
378 | if (derived_it != derivedMembers.end()) {
379 | if (derived_it->first <= own_offset) {
380 | own_offset = derived_it->first;
381 | atOwnMembers = false;
382 | }
383 | }
384 | if (own_offset == size) {
385 | ++position;
386 | return true;
387 | }
388 | return true;
389 | }
390 | else {
391 | own_offset = size;
392 | ++position;
393 | return true;
394 | }
395 | break;
396 | case REPLACE_CLASSNAME_BOT:
397 | ++it;
398 | if (it == g_ReconstractedTypes.end())
399 | return false;
400 | t = it->second;
401 | position = REPLACE_SPLIT;
402 | own_offset = 0;
403 | typeName = t->name;
404 | index = t->typeId;
405 | lnnum = t->id;
406 | return true;
407 | default:
408 | assert(false);
409 | }
410 |
411 | }
412 | }
413 |
414 | bool idaapi reconstructed_place_t::beginning(void * ud) const
415 | {
416 | if (g_ReconstractedTypes.count(typeName) == 0)
417 | return true;
418 | if (g_ReconstractedTypes.empty())
419 | return true;
420 | bool is_first = g_ReconstractedTypes.begin()->first.compare(typeName) == 0 &&
421 | position == REPLACE_SPLIT;
422 | return is_first;
423 | }
424 |
425 | bool idaapi reconstructed_place_t::ending(void * ud) const
426 | {
427 |
428 | if (g_ReconstractedTypes.empty())
429 | return true;
430 | if (g_ReconstractedTypes.count(typeName) == 0)
431 | return true;
432 | if (g_ReconstractedTypes.rbegin()->first.compare(typeName) == 0)
433 | if (position == REPLACE_CLASSNAME_BOT)
434 | return true;
435 | return false;
436 | }
437 |
438 |
439 |
440 | int idaapi reconstructed_place_t::generate(qstrvec_t * out, int * out_deflnnum,
441 | color_t * out_pfx_color, bgcolor_t * out_bgcolor, void * ud, int maxsize) const
442 | {
443 | size_t curSize = out->size();
444 | if (g_ReconstractedTypes.count(typeName) == 0)
445 | return 0;
446 | ReconstructableType *reType = g_ReconstractedTypes[typeName];
447 | std::set children = reType->getChildren();
448 | std::set::iterator children_it = children.begin();
449 | std::set parents = reType->getParents();
450 | std::set::iterator parents_it = parents.begin();
451 | ReconstructableMember *member;
452 | qstring line;
453 | line.sprnt(COLSTR(" %08x ", SCOLOR_DREF), own_offset);
454 |
455 | switch (position) {
456 |
457 | case REPLACE_SPLIT:
458 | line += "; -------------------------------------------------------------------- ";
459 | break;
460 | case REPLACE_BLANK:
461 | break;
462 |
463 | case REPLACE_PARENTS:
464 | if (parents.size() == 0)
465 | {
466 | //parents_pos = 2;
467 | line += COLSTR(" error XREFS TO: ", SCOLOR_ERROR);
468 | break;
469 | }
470 | line += COLSTR(" XREFS TO: ", SCOLOR_PREFIX);
471 | for (parents_it = parents.begin(); parents_it != parents.end(); parents_it++) {
472 | line += (*parents_it)->name.c_str();
473 | line += " ";
474 | }
475 | break;
476 | case REPLACE_CHILDREN:
477 |
478 | if (children.size() == 0) {
479 | line += COLSTR(" error XREFS FROM: ", SCOLOR_ERROR);
480 | break;
481 | }
482 |
483 | line += COLSTR(" XREFS FROM: ", SCOLOR_PREFIX);
484 | for (children_it = children.begin(); children_it != children.end(); children_it++)
485 | {
486 | line += (*children_it)->name.c_str();
487 | line += " ";
488 | }
489 | break;
490 | case REPLACE_MEMBERS:
491 | if (atOwnMembers) {
492 | if (reType->getOwnMembers().count(own_offset)) {
493 | char * format = COLSTR("\t %s", SCOLOR_DCHAR) " %s";
494 | std::map derived = reType->getDerivedMembers();
495 | for (std::map::iterator derIt = derived.begin(); derIt != derived.end(); derIt++) {
496 | if (own_offset == derIt->first || (own_offset > derIt->first && own_offset <= derIt->second->getSize() + derIt->first)) {
497 | format = COLSTR(" | ", SCOLOR_DNUM) COLSTR("\t %s", SCOLOR_DCHAR) " %s";
498 | break;
499 | }
500 | }
501 | member = reType->getOwnMembers().at(own_offset);
502 | line.cat_sprnt(format,
503 | member->memberType->getTypeString().c_str(),
504 | member->name.c_str()
505 | );
506 | }
507 | else
508 | line += COLSTR(" errormember ", SCOLOR_ERROR);
509 | }
510 | else {
511 | if (reType->getDerivedMembers().count(own_offset)) {
512 | member = reType->getDerivedMembers().at(own_offset);
513 | line.cat_sprnt(COLSTR(" %s", SCOLOR_DREF) " %s\t",
514 | member->memberType->getTypeString().c_str(),
515 | member->name.c_str()
516 | );
517 | }
518 | else
519 | line += COLSTR(" errormember ", SCOLOR_ERROR);
520 | }
521 | break;
522 | case REPLACE_BLANK_BOT:
523 | break;
524 | case REPLACE_CLASSNAME_TOP:
525 | case REPLACE_CLASSNAME_BOT:
526 | line += typeName.c_str();
527 | break;
528 | }
529 | out->push_back(line);
530 | return 1;
531 | }
532 |
533 | void idaapi reconstructed_place_t::serialize(bytevec_t * out) const
534 | {
535 | }
536 |
537 | bool idaapi reconstructed_place_t::deserialize(const uchar ** pptr, const uchar * end)
538 | {
539 | return false;
540 | }
541 |
542 | int idaapi reconstructed_place_t::id() const
543 | {
544 | return g_replace_id;
545 | }
546 |
547 | const char *idaapi reconstructed_place_t::name() const
548 | {
549 | return "reconstructed_place_t";
550 | }
551 |
552 | ea_t idaapi reconstructed_place_t::toea() const
553 | {
554 | if (g_ReconstractedTypes.count(typeName) == 0)
555 | return BADADDR;
556 | ReconstructableTypeVtable *reVtable = 0;
557 | ReconstructableType *reType = g_ReconstractedTypes[typeName];
558 | std::map members = reType->getOwnMembers();
559 | std::map derivedMembers = reType->getDerivedMembers();
560 | switch (position) {
561 |
562 | case REPLACE_SPLIT:
563 | case REPLACE_BLANK:
564 | case REPLACE_CLASSNAME_TOP:
565 | case REPLACE_BLANK_BOT:
566 | case REPLACE_CLASSNAME_BOT:
567 | if (reVtable = dynamic_cast(reType))
568 | return reVtable->vtable_address;
569 | break;
570 | case REPLACE_PARENTS:
571 | case REPLACE_CHILDREN:
572 | break; // TODO
573 | case REPLACE_MEMBERS:
574 | if (atOwnMembers) {
575 | if (members.count(own_offset)) {
576 | if (reVtable = dynamic_cast(reType))
577 | return reVtable->to_ea(own_offset);
578 | }
579 | }
580 | }
581 | return BADADDR;
582 | }
583 |
584 | ReconstructableType * reconstructed_place_t::getReType()
585 | {
586 | if (g_ReconstractedTypes.count(typeName))
587 | return g_ReconstractedTypes[typeName];
588 | return nullptr;
589 | }
590 |
591 | bool reconstructed_place_t::isDerived()
592 | {
593 | return position == REPLACE_MEMBERS && !atOwnMembers;
594 | }
595 |
596 | bool reconstructed_place_t::isOwnMember()
597 | {
598 | return position == REPLACE_MEMBERS && !atOwnMembers;
599 | }
600 |
--------------------------------------------------------------------------------