1313
1414import ida_idaapi
1515import ida_hexrays
16+ import ida_lines
17+ import ida_typeinf
1618
1719# --------------------------------------------------------------------------
1820class func_stroff_ah_t (ida_kernwin .action_handler_t ):
1921 def __init__ (self ):
2022 ida_kernwin .action_handler_t .__init__ (self )
2123
2224 def activate (self , ctx ):
23- # get current item
25+ # get the current item
2426 vu = ida_hexrays .get_widget_vdui (ctx .widget )
25- vu .get_current_item (idaapi .USE_KEYBOARD )
27+ vu .get_current_item (ida_hexrays .USE_KEYBOARD )
2628
27- # check the current item is union field
29+ # REGION1, will be referenced latter
30+ # check that the current item is a union field
2831 if not vu .item .is_citem ():
2932 return 0
3033 e = vu .item .e
@@ -41,8 +44,10 @@ def activate(self, ctx):
4144 break
4245 if not e .type .is_udt ():
4346 return 0
47+ # END REGION1
4448
45- # calculate member's offset
49+ # REGION2
50+ # calculate the member offset
4651 off = 0
4752 e = vu .item .e
4853 while True :
@@ -55,16 +60,18 @@ def activate(self, ctx):
5560 break
5661 if not e2 .type .is_udt ():
5762 break
63+ # END REGION2
5864
65+ # REGION3
5966 # go up and collect more member references (in order to calculate the final offset)
6067 p = vu .item .e
6168 while True :
6269 p2 = vu .cfunc .body .find_parent_of (p )
6370 if p2 .op == ida_hexrays .cot_memptr :
6471 break
6572 if p2 .op == ida_hexrays .cot_memref :
66- e2 = p2
67- tif = remove_pointer (e2 .x .type )
73+ e2 = p2 . cexpr
74+ tif = ida_typeinf . remove_pointer (e2 .x .type )
6875 if not tif .is_union ():
6976 off += e2 .m
7077 p = p2
@@ -81,45 +88,59 @@ def activate(self, ctx):
8188 objsize = add .type .get_ptrarr_objsize ()
8289 nbytes = delta * objsize
8390 off += nbytes
84- # we can use the calling helpers like WORD/BYTE/...
85- # to calculate the more precise offset
91+ # we could use helpers like WORD/BYTE/... to calculate a more precise offset
8692 # if ( p2->op == cot_call && (e2->exflags & EXFL_LVALUE) != 0 )
8793 break
94+ # END REGION3
8895
96+ # REGION4
8997 ea = vu .item .e .ea
9098 # the item itself may be unaddressable.
9199 # TODO: find its addressable parent
92- if ea == idaapi .BADADDR :
100+ if ea == ida_idaapi .BADADDR :
93101 return 0
102+ # END REGION4
94103
104+ # REGION5
95105 # prepare the text representation for the item,
96106 # use the neighborhoods of cursor
97- line = idaapi .tag_remove (ida_kernwin .get_custom_viewer_curline (vu .ct , False ))
107+ line = ida_lines .tag_remove (ida_kernwin .get_custom_viewer_curline (vu .ct , False ))
98108 line_len = len (line )
99109 x = max (0 , vu .cpos .x - 10 )
100110 l = min (10 , line_len - vu .cpos .x ) + 10
101111 line = line [x :x + l ]
112+ # END REGION5
102113
114+ # REGION6
103115 ops = ida_hexrays .ui_stroff_ops_t ()
104116 op = ops .push_back ()
105117 op .offset = off
106118 op .text = line
119+ # END REGION6
107120
121+ # REGION7
108122 class set_union_sel_t (ida_hexrays .ui_stroff_applicator_t ):
109- def __init__ (self , eas ):
123+ def __init__ (self , ea ):
110124 ida_hexrays .ui_stroff_applicator_t .__init__ (self )
111- self .eas = eas
125+ self .ea = ea
112126
113- def apply (self , opnum , path ):
114- vu .cfunc .set_user_union_selection (self .eas [opnum ], path )
127+ def apply (self , opnum , path , top_tif , spath ):
128+ typename = ida_typeinf .print_tinfo ('' , 0 , 0 , ida_typeinf .PRTYPE_1LINE , top_tif , '' , '' )
129+ idaapi .msg ("User selected %s of type %s\n " % (spath , typename ))
130+ if path .empty ():
131+ return False
132+ vu .cfunc .set_user_union_selection (self .ea , path )
115133 vu .cfunc .save_user_unions ()
116134 return True
135+ # END REGION7
117136
118- su = set_union_sel_t ([ea ])
137+ # REGION8
138+ su = set_union_sel_t (ea )
119139 res = ida_hexrays .select_udt_by_offset (None , ops , su )
120140 if res != 0 :
121141 # regenerate ctree
122142 vu .refresh_view (True )
143+ # END REGION8
123144
124145 return 1
125146
@@ -131,7 +152,7 @@ def update(self, ctx):
131152
132153# --------------------------------------------------------------------------
133154if ida_hexrays .init_hexrays_plugin ():
134- print ("Hex-rays version %s has been detected, Structure offsets ready to use" % idaapi .get_hexrays_version ())
155+ print ("Hex-rays version %s has been detected, Structure offsets ready to use" % ida_hexrays .get_hexrays_version ())
135156 ida_kernwin .register_action (
136157 ida_kernwin .action_desc_t (
137158 "vds17:strchoose" ,
@@ -140,3 +161,121 @@ def update(self, ctx):
140161 "Shift+T" ))
141162else :
142163 print ('vds17: Hex-rays is not available.' )
164+
165+ """
166+ # A few notes about the VDS17 sample
167+
168+ You can find two VDS17 samples in the IDA Pro install directory:
169+
170+ python/examples/hexrays/vds17.py
171+ plugins/hexrays_sdk/plugins/vds17
172+
173+ The former is an IDAPython plugin and the latter is a C++ IDA Pro plugin.
174+ Actually they have the same functionality.
175+ Just to be more concrete the vds17.py plugin will be used below.
176+
177+
178+ ## How the user interface works
179+
180+ Let us suppose that we have the following local types:
181+
182+ 1 C struct {int c0;int c1;}
183+ 2 U union {int u0;__int16 u1;}
184+ 3 D struct {int d0;C d1;U d2;}
185+ 4 E struct {int e0;D e1;}
186+ 5 res_t struct {int r0;int r1;__int16 r2;}
187+
188+ and the decompiler generates the following pseudocode:
189+
190+ void __cdecl f(res_t *r)
191+ {
192+ r->r0 = e->e1.d1.c0;
193+ r->r1 = e->e1.d2.u0;
194+ r->r2 = e->e1.d2.u1;
195+ }
196+
197+ As we see, it looks good, the decompiler did a really good job.
198+
199+ But let us imagine that we need reference to the ```e1.d2.u0``` union member on the last line.
200+
201+ At first we need to load the VDS17 plugin. For that,
202+ place the cursor at the ```u1``` union member on the last line
203+ and use ```Shift-T```.
204+
205+ The "Structure offsets" dialog appears on the screen.
206+ The left pane of the dialog contains the available local types
207+ and the right pane has the following view:
208+
209+ [checked] e->e1.d2.u1; | 10h | |
210+
211+ Use the left pane to select ```E```, expand it, and select (Double-click) on the ```u0``` member.
212+
213+ The right pane will change to:
214+
215+ [checked] e->e1.d2.u1; | 10h | [checke] E.e1.d2.u0 |
216+
217+ Since this is what we want, press the ```OK``` button and the pseudocode gets changed:
218+
219+ void __cdecl f(res_t *r)
220+ {
221+ r->r0 = e->e1.d1.c0;
222+ r->r1 = e->e1.d2.u0;
223+ r->r2 = e->e1.d2.u0;
224+ }
225+
226+
227+ ### What do we need this plugin for?
228+
229+ All union members have the same offset in the parent structure.
230+ The decompiler selects the first suitable union member.
231+ Sometimes we need to change this selection and use another union member.
232+
233+
234+ ## API details
235+
236+ Let us look into ```python/examples/hexrays/vds17.py```.
237+
238+ * REGION1: check that the cursor points to the union member
239+ * REGION2: calculate the member offset
240+ * REGION3: sometimes there is a need to adjust the offset
241+ * REGION4: the pointed item must be addressable
242+
243+ Then the magic begins:
244+
245+ * REGION5: we need something to show in the ```Operand``` column of the right pane.
246+ The text around the cursor looks like a good compromise.
247+ * REGION6: we are ready to fill the right pane: we have calculated the offset and
248+ have prepared the descriptive text for the operand.
249+ * REGION7: it is a callback that informs us about the user selection (will be described later)
250+ * REGION8: activate the "Structure offsets" dialog, let the user select,
251+ the callback specified above will update the union member, refresh pseudocode
252+
253+
254+ ### The devil is in the details
255+
256+ The main part of callback is the ```apply``` method (REGION7).
257+ It receives two arguments:
258+
259+ 1. ```opnum``` is the number of the selected operand (line) in the right pane of the dialog.
260+ We need something to map the line number to our operand.
261+ In the case of VDS17 ```ea``` performs this role.
262+ 2. ```path``` the path that describes the union selection.
263+ 3. ```top_tif``` typeinfo of the top-level UDT which user selected
264+ 4. ```spath``` the field names path to the selected member
265+
266+ The union selection path is denotes a concrete member inside a UDT.
267+
268+ For structure types there is no need in the union selection path because the member
269+ offset uniquely denotes the desired member.
270+
271+ For unions, on the other hand, the member offset is not enough because all union
272+ members start at the same offset zero. For them, we remember the ordinal number of the
273+ union member in the path. E.g., for the local types given above, the following holds:
274+
275+ * e1.d1.c0 is denoted by an empty path because it does not have any unions
276+ * e1.d2.u0 is denoted by path consisting of 0: we use the first member of U
277+ * e1.d2.u1 is denoted by path consisting of 1: we use the second member of U
278+
279+ You can retrieve the union selection path using API call ```get_user_union_selection```
280+ or apply it using ```set_user_union_selection```.
281+ """
0 commit comments