From 454ae02a36da3e2980e65650bbfebb7d218b18f3 Mon Sep 17 00:00:00 2001 From: ecx86 Date: Sun, 5 Aug 2018 01:08:36 -0400 Subject: [PATCH] Replace HexRaysCodeXplorer and HexraysInvertIf with HexRaysPyTools --- plugins/HexRaysCodeXplorer.dll | Bin 358912 -> 0 bytes plugins/HexRaysCodeXplorer64.dll | Bin 357888 -> 0 bytes plugins/HexRaysPyTools.py | 279 ++++ plugins/HexRaysPyTools/Actions.py | 1440 +++++++++++++++++ plugins/HexRaysPyTools/Api.py | 583 +++++++ plugins/HexRaysPyTools/Core/Cache.py | 62 + plugins/HexRaysPyTools/Core/Classes.py | 536 ++++++ plugins/HexRaysPyTools/Core/Const.py | 58 + plugins/HexRaysPyTools/Core/Helper.py | 312 ++++ .../HexRaysPyTools/Core/NegativeOffsets.py | 255 +++ plugins/HexRaysPyTools/Core/SpaghettiCode.py | 120 ++ plugins/HexRaysPyTools/Core/StructXrefs.py | 181 +++ plugins/HexRaysPyTools/Core/StructureGraph.py | 184 +++ .../HexRaysPyTools/Core/TemporaryStructure.py | 864 ++++++++++ .../HexRaysPyTools/Core/VariableScanner.py | 339 ++++ plugins/HexRaysPyTools/Core/__init__.py | 0 plugins/HexRaysPyTools/Cute.py | 116 ++ plugins/HexRaysPyTools/Forms.py | 246 +++ plugins/HexRaysPyTools/Settings.py | 57 + plugins/HexRaysPyTools/__init__.py | 7 + plugins/HexraysInvertIf32.dll | Bin 16384 -> 0 bytes plugins/HexraysInvertIf64.dll | Bin 16384 -> 0 bytes 22 files changed, 5639 insertions(+) delete mode 100644 plugins/HexRaysCodeXplorer.dll delete mode 100644 plugins/HexRaysCodeXplorer64.dll create mode 100644 plugins/HexRaysPyTools.py create mode 100644 plugins/HexRaysPyTools/Actions.py create mode 100644 plugins/HexRaysPyTools/Api.py create mode 100644 plugins/HexRaysPyTools/Core/Cache.py create mode 100644 plugins/HexRaysPyTools/Core/Classes.py create mode 100644 plugins/HexRaysPyTools/Core/Const.py create mode 100644 plugins/HexRaysPyTools/Core/Helper.py create mode 100644 plugins/HexRaysPyTools/Core/NegativeOffsets.py create mode 100644 plugins/HexRaysPyTools/Core/SpaghettiCode.py create mode 100644 plugins/HexRaysPyTools/Core/StructXrefs.py create mode 100644 plugins/HexRaysPyTools/Core/StructureGraph.py create mode 100644 plugins/HexRaysPyTools/Core/TemporaryStructure.py create mode 100644 plugins/HexRaysPyTools/Core/VariableScanner.py create mode 100644 plugins/HexRaysPyTools/Core/__init__.py create mode 100644 plugins/HexRaysPyTools/Cute.py create mode 100644 plugins/HexRaysPyTools/Forms.py create mode 100644 plugins/HexRaysPyTools/Settings.py create mode 100644 plugins/HexRaysPyTools/__init__.py delete mode 100644 plugins/HexraysInvertIf32.dll delete mode 100644 plugins/HexraysInvertIf64.dll diff --git a/plugins/HexRaysCodeXplorer.dll b/plugins/HexRaysCodeXplorer.dll deleted file mode 100644 index 264810e1e8096aaa7c8d1f9b5537f1f25b675ebb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358912 zcmd?S3!I(P^*4SdnURyoWQH0hN}Vv}kkAu}7($&8JgEr>QG;UCZ4iuPCI)FGoe4=N zhagq07ty*z-O^G`LUlqaCmQWgH0l=jJ--tg}3nBv}kw`iI@9T?1mg1BDlJd9zKhd&CWY{jtheaM9{LF}@ zWev}an9_XS^jOQZi_e{Q#)Yvn&$#HKi(6ygKRY(9?V{Lu7sVzXbA0T=i_bcHpUTQ1 zbr$v0hwU`}nq8+gyZ^5~`HkkQ@cg|aE;%nPpO>C@i+s*FZ;pIUKkvu*{35BJ!)Hzx zxNCNu-h3v?&pzVP^E%{n+IiQ?XS!KGTjcq;^UjpM@;OT#9*LZF)mD+zqj#R{>NZ4n zjcq+(*wDx~Af2nthZ_W_Z}R#FsvM!G9hs3;#8X#%J=avz1yTC$|P zB9bDmii${vpNlFY>)27MBGP;e3+%ro6_IZAyQHf;GR2jJKC9E^kvN{K=Yw`7U27BN zk(NOrz`U>fw4UAGin2L7p&dyk@AxnLFBXY3?KADHGg{AxM7I3|HNY9!h|gVf0h2JJ zeH1LRU^~J=CPVPKBR)g_l95RFJ}n9;Jc3sdLR|q|a-V6_r=5wmA`{4lvX8VDgFAcb z#Q>y?B9N8vFrxtOP}Kha`k&XaKJKL}+xD!8nAoo)5quv$ygXv&zA!N2b*zj(xhD6Y zh2Jj(KbY{1{lgzD@aL5g|7W#;kM|EhneZE$1-_T?8=vo&oLH6>K{HGf?r1X=KkR?48h++`1;cD?Ou9fUBa6;u`cGNYqD7|-PKSRGao*I zZzzp>T~qKUUD-{f<1-?ZRw8}tuOgK8kdmOETzn%pI()b1X?v2j41N^f-(<5(@|zXN`{l(0tp0vI0N=v;>mjz6r=v7o`7p8V zrr7$`-vw}DY7b}Rbq=A|KzP-GT2Y#kZ39 zZrBWbT`L`Z&tmS$(QkzK{(Xt?yO8(}D~b=}spgGi#lCJOMYi( zu2|*zKQ_PrZuWnL_CJN#p8U(E<#S5L;VUbOZ=~X@BfjZH@zFp0H&xv;5Zz(^AgHIH z0V8%Ac_I?YnNYlWQ|hYcHPl5LFj8t8@GSpN!FCCg?E$ew8a)YsQX2bmI4cTKEI z(ov#57HJ*KkFwTb*;-n(XLi9tC;N5%J{{9~wn|;6RQ>{$-YU3L&i9zw>cwk%>|2P) zCBPhS`}IO92A-rj8F)y7j|((`<{W$->%HZJ*-g6gp%IYAx~B$88`9g?RHa*-)1-doE#1kypkH-PJvG$l_+=TnI~4 zA8zSYJp%Z-=wxEmBMo(<8(@IVUV2Jh6H%rNMy#Mr1%z7y0xak;`sA7bzZjWtb|Nf1 zMnv@{{U8x#%-MJG6V!G^=9MY29oF)g

p%cTh%Rw^l58dqcm4I6IC<+P3Pe`7?GCghmnC zcC1^+`ue3&it&Q(K9PkE)p^?q`}I0>n^+Sw`xCdCuy}|jilD_-3B*|{u%?A4j3j|+Y$BLMF)fS~#)M8GX zug&x>j|Gl7s$u)^jxXp*CIv6R6# zdh;yQ5pB8%4oc1n!DOd#xHrw|ZrzW)HWNz#IYipp9=6pUK4}f`Wygjad5}73C=b(R zTWz?Pv^v}z4Y1G{TjNhMTJ?Ccc20NIoCi5MxI$L24In_mglN^A)uKGxMyr$J5J5MH zS1fJgeGD*u8B)#ei*&|@ZdmvQLQnP_zbSyKDcM-t&Ow>dJVnhtWwsVkSaLSiMat@9 zT-Sg|FO~xNe9D;pPw{bfK(iW1e{9_GV~*r4WTR?&S)}(Mj91lp;#XpeDg+wAA#;f| zWA47kxB56i8%@ngG$1-!b!%?=g8zoD^ZncG^&GO^s?ItFlEgn?;MSXq&mItY3d01b z=GS$j%Dnm-;O2Ciz93p7s5q>dGF3srAo7Y)4D~#yOdKG|xYfZ*Hu#d>#F!p^I zWN?;`y;b_{l$p;LOce{!f39GtX-<$ZhQr5n7Z}6eKMTpGb1;nI+JQuqNAyD%E71#t z=sbO%^C@}lth*`9(RKT?&(1p6LMt=orM-nIti&ePO{9Z7M8w~zCIX4odufh_H%`|c z7YWkNI{gd;{2@Y>>=f2eNN%|$cZpP5mGq5Mid+k=%$OsUqQgni;iL$YEKrgXcuaWU z;h0N~^2NOPPS)m&xdD!#zL!1w?*(Mcer!?D_vz z;VdmCocAtN$##c@^X7gk9M?iCGiI+*qOKz-oFgcl3yQ1j35*k}BSWMZRP6$0CBGj+ z-5gYq8tcso=gOcul1Pt)VP9BaZ--eLdxXX*RE5R$*~iM-TB)&xb(VSshwVxw)>|Wm zdf5QLkvi2{V+*ua4I-|8lTO)w$(9%pY)>lPJ|mV7Bx3B89xcy6hvB-#S}KpXv*D4YW%w z8mY9xT{T8c!?n=LjH%j7g!>)#_Z=~LUHDLqW)a;@&6qX;umnty^F1Po*Slb zeM2tGLzU&DNWxKM`Tb&CeR95XwK2@qXwnE)H%OuT4E)P=|J3?Oxc|rYRZ%FWg>_n) zG39%TC>lsd14I#c>Zfm4G*OF2rm(YFSYTxI)C4QxsS_WEMD%w!>E1ckQ|C;sFMX&T zR!fI@ZhSps_F%f!Qqzr3^WJD>%C*qSjQLhvn3_zcCR4bDo;rV;m~ZjNi9by@z9RxG z>@g-iy3at&ds%W5VTw+$6bTyip_LhP%N|0}(WK~TQq;ev{_?hBVlL^a|MJg}fG-=6 z`*|%8Z$XC<2E6 zF%-@*6wW!)s$gQR#h4*dkJa!g3&O~|CQ)SKsabFCJwtp-ibzxHsl(2A>OYjgl;Fr? z?aFhNwZ?D>ri_gT@!;iyt#H*}JZ+WqB8%!ITr(^SrJw{x8-Oe`nn+02R}p?j#V-l)iWoB`fGsB2YswC@kGdys;!_iAlD#V+c>vX>=oT`pj86OjW(lqqppc=-F=UMoM8lcJ+{rl` zw!w$qFMzoBldv}Kg0rG%hoNg1HTKfnn9lr8nMf0ywN>;YU?C$uV{waw=3(h18y>1^V{16cI>ze2 zQ+9|F{?K8JG38m^K4+>!StKZ?=q6a0c>BMA2O?Z-kxO9}7Jqa#3ck=*X3U8r!~(~W z$#Is+{R1X5KP$;(pun}^j&5A9d@eyD`P`qB^TAi(+nnVrpV>pQ2N@RfT-mOyzYzp|ehi#BxgL;WDVgh6A`hFrsV`aiT0E4hd8xUof$LOAw7(%`t z_)U$?Ol9sz>I__?7b= zl;SL@o20gQ>G?9i&98UT{+gU#thsn*2Sg%#N|IE{cjR%?I8U`WK~Tml`HAWe{PmD9 z&s_6kBDNup*(<4~U?@opFhBH%bPjqX>XBnylr{?wrsKzOZEn;{Nm-J}5s2a{x~_2m zM1a|*4QGhqcG-6Tr9z)yC+&vIVUiqOte~5bzCivN;6+eWbzT**Aux&omQN!TK43LD56x@o%55vkFyWCwBCLz-7OfkOZkRd)Am z9j2Gj0yWwf2dm7(Avg>UKc%8*JhfweBTriDbtMEEmbDYdq8eoe1Xz+jxIs%|WTM@? zZXYoqdHU1CM||TUA-pAAK|@zr)g#sXnlQh3i$0##?Zzqpy}62J)4>DD zlJq)(oMLGREMbx@NfKAPInYuCgI~>(q?|-SqV#;fe_M$HixhN6w{V8T6-J%AGXBC# z22zu`s2AXXZ1#B|B&Gl)jR9Z6JaauoAgDA~EQdci&x)y(Y5unXdVw7bNmKTEnI2|Q zLV6m%2Ir-@M^CfYQ@zwyJMrKXu3U^U<|4N`HaPBg8A|*x%w|(p>l<$xc11zY7iVUk z=3!#m`hZt2ki&ck)5RSl-kHgA0t;GodR>{mnzz2tgDH`6vajZ$FXc#o6qp|cEyuHm zZW_mJ7KcaSd3`nKDvksNNF3(NgM^&1v~8qSG}MhX;}5oswAKKut|Lrq!0F9e!AM_9mlNc5#n__C1B`8ILQhhb=;yHU-_C#M;7oLY6(4n|<&IRUAN1z1I zI5}Q%X_b;RfFxmb?n08|X72eU85U1F!8qchNnEAsfblR~p5jue{^xqhVkS#|R)Q-K0Hf1_&$n5XHcSqO<&$Z(xVLG=)Gn5m(H)f6b_(6z$_DT|xGh^kAcd(i?ORu5+ z8Cr;mp_{u=z;EC^G8e0U%I*eRhDa;RoCheBADpUP%wd$0@RuZ_&3PzKR}`Z2Pk=?M zsz>CUGGpF(RUKK_)ria;Jp38G7^b~{rAaIwRwb*UCI10d>>h%SbNB}mF(p#@wIaYm zUnoIEiEN-e#6C#w2-0vPCux!8_r9)uI>Ph&qZlX^cva+IZGyhs{DLHVy6+Y`VJxM* zNb5vm^33o!aErxkj%M6w z!)J_&hlm;@t>0qHB=Q>W8-x+a^a4Is$Xr41^v6#j0t!X_w1^PW=&n9|%xXCb=n=K{ z)!acqZH=X?OgR*Cy5=ZBtXVJ+y4LNj9f%qRduL9<1O-nrZ{m>8(N_w6307pNR8Q1Z zu4jG;nr#PeY*8SET>A5K(){7`zCN@;H|BXTZAHw--|kivTX?)tP;RPEn$`f-JfJE< ziR!3O^d22u!ePRsbcPcZ)|{31VeM$;o`PIkX?Gr0)sROMDNmX6F=Etu8K1cC`)bS> zntXibn*gQ8b!`OSf+cT{HoP4Tg-#%L>2e`?VN#q}S4~iuiYUGs9wG`cLr|=NmNj9E zWty;69hw=GU*~UR#~vd#_)9rnry+$k<={hB2`0oZu}|rgKFX&cyq+`DbY;yz-^@6C!B@uoI)te^$&%YbC3XcHf74hvt;~c%+c~G1J1NVn0+;G ze*pA8HG#br@E6Eq9_IFb<-wtqu6#oFrWk1y=TEkG;_R!L^banb^5hBmaI9Sj++lM3 z`NYc4I*#vR@1tN&zWMZiEDN4u^Re1X0B>IUP<#bXcA!4>7G|rTg2*hw>7;%Y4(=?) zh5ebI6>I<0jiL5e_uKxYV(qUAw?DG~_6sntN}w-9{}JK#ClAMO^PfWswqwnL%2|$D)O;Ai@{JE6^vVOtG1#qz$ zvz=dnhrb1Zhyr{`LceQQj0v|kc|i&|0h`$2J*A?rYj%)%%3xfU4(LV zX?p4-tyK)JiSvw_y%*-X17APD&;EZOU{Z-W`hxKk)?cyl=dXv+XZ4e=y~Tg+4F&!C z@K}(MPLBsj$<`~_k7r)l7vhrb0ycQD$c&Q-lC$YOc@nLMup1BoAguJ2G`}9`Z-5c? zvU&b*-U|YN0&23Mhw}mUl$=wFFBmzI=m;Rp<;;JGXmBImwzb51pelKTiCxdJWTG4P zA0ilOhIs_vrHSYoL90?G$GvLAtAZw(|2P9+M*xq z_jqtgQA;S|(5~`X^UANwt}G;BM#>~`>Z*QjyS#cVn6o4E%0Tvv9c&rM#9I&2-Mk6W z>Q+OYy(dd!Q~`>+O}S|e>93)Mi~ zT7xe=<`;p-lEq=uJge;hg*&1txpj;<#pQ|28V$GQ6Pj*f+Q9>3x zN@HO>tI-e4I@6^+HZjVXFq2_&3;5h(pE*1<=E|t`KBxwEKUySh21`z7_|U}>kI%gL zup?I_1*?$_3mwK;^i@EOGCbO?I68)kGUh!5(HM{y2+?XIBHU3#X06Y93IHnG{`yO? zKkdMLjP;$55*0a}#@OjJA7_Z2BJn{OB%J|Gt!pq+5(cj-XP!qSI|yI!iO_O+9f+$H zord%+nVeUcxO)d+#8wc<$A*J9AEPGheTi(;Tf|43vTpM-?6R?t0a+wtzEtWXeh*O7U=Zi zT7^cA_L7I6Huy7;8lbIO-Y&RU^W?P-n~$eR`(-8DcWhSslVHz5|GVqga-9DRRnZ@S zcz(8O`4XS3cx?e$z6guZ_tGZtzl3cl@HhJSGdpY%{$-oMf0^Qs`}mu;4F6@Dz&|`6 ze{{?6A270v;U;TVh4LqVkWb8~fARLE<>SUfUX<2MIi5M97`V$hR;k#yS->+SH+hQ> zxV2S}`0>!AFf)0;2Z2a)}v(L~Pk}`2qi^`Baju00l3@YBAz{`XQ0U;NqAS zgjR9aYP4<#a^T#a4)Va(o(Y;V(_Q=$#m||h(M`}f^joKkj_R?BtVUYW@1yA3cUEA! za6T4oB@EI5<#-CxwBe}h_~KHyEoJ8HS*C%c+1W0~L{v%hjcMpk_(a5C)*2<|Lk~;` zZ&f!@AdNGI^u7sE^}Z?tf>SYHt!|>QWBxnURX@7lR&xaG!Pl{4mk#N@hYjz@sgY(- z2v&cPHdO=duiJ$i>hkQ&EK+Wz#K;O8?1<`|!Lo{~omQSbHhT;!)rzhDIyOe9muL6S z%HdmH%^ow#vpe%v1-3!gDGF5NlQKV=hkOCN-~GXB@EEcZ&C#C~YW*>GY!QbCHqkm` zu5Y1idqlkEomXfHHvAeh?>-@_Ld2{XzGg0>C zqCq!O^fMnn38bWvRtjQc{O-v&&3SG+O^dry)g(M=98GzzcU3kjQ0R}1+c_xY| z3Q?hqE^uImE|3DnzN|UugG(F{TNutJS;hQ8gjAwhyqgm=gN&-?%<{P)wRkPO)on$C z9zj|>WTvIMLb=;N_0>G{61xJaeKpVVD@d$meo9OZrvtb-0!JyLe|x+7{x2}n1?WsbaRfkO!kK>ip5PenO|?^7vo$QGNIm)g(dG3u-yiY{QGki zJ&2=6`JuFA@qU>V@(F82$gSW>g#JxFlj&@=Eg9)b?DJ84X7AQDQ_Hq0AypZ(v5kb% z-a1x@*@E0BxFtUwyA4e=;2bO+KK8ePDC+i~-^R1bH)pJn?k}ZDo|YYd9xGGk2NFWy zx3?H^bi~EY$54c-Zwk?aWZ_dpn=+4B;DoKqn1#p@v)(^p_D7V;)?lq+v!wm4**r-_ zBF@k$7awY|#nVkE-+kdtA|e*I*nr|Xx-fdBy&^2IgJEPHHp&>II|5!zIlw47 zNSZefm6uXteymv1Dji+4HU7)B4>DeF>M0NmjB2F&*{paGRl*FTA>&;yw<&?C8{ z=D_ztasvoD0}?1zBEE=8m6y$xkgwJ``(w!W=ZZBv35WRO`36FeHb<1+?GJ|yz#$c6 zxck{Wtz4;7$RHkQ>wftj_PO|Zcl-OcmP|Hs*Ng1DCdmq^#H|8n{VkngX4 zaCr%T<@;X|5Z040I-NOiw^+sZ)}a{*%}R_u$%zZ}vri?T1U@h1aA>$9N$ob6_{nU&5Xs_-#M22h1 ze2eM%^or~Vz0qCZ8*1gwsk!VAg(=Xgv)`IE=TKhzc3bO*zr^E`GXK$+r>HWqjA`b5 zHJ?83`z|MMTi+@5e=DpHpAVD8?arhH^12f~U4>Vj@x-r>em`uQDU+3FR6+cMC3%NX zKXFhZos?-qOiK{pIe@y@K_wLTl@1r41E|ZkYN~q{=JL{;^2tnu!F-u2$*~^MKCJwK6vs_**d|vE_uhxj6n{ykcmD;XiB3 zqdY{9Kc8|c<=99~gf=~n=cG)>xJYm}+)`&R5~QkQ{&=QS#8^aF}-1CF_5LdTr^T~d5h(F*;Wux-9{&IPwgB{Yj9oibco$_mXJEhd@V`~Cb zg`$a1ka;b*RSpv2D&skvZRMDj5K*)}7oiiD>MGRkw(8CGo7ukJ9lKGO5E^{;vhM<6 zUrn+HZp&p@9=~LOO}gX@G$Ng()p;PD@O%Pn0KoVjOG$#MNLR9{FB3FI0c-`Qo*F!n z^J;)a%}w8gWbGVPgkRDLIDceaFg;|+VWSJ{7uQ`yU>+B)bmn0VHe{u<*UKCCc*}n- zfMqI7SC;=78;wDD0NtZN=%~K7?U5Ds#WMpVx*WOUSABiGW4PLeP1a&rWmfZ58i1#` z^@XkLo9X6y9z)2>)}DFsO-`2XRY40ddk~TIFtqELu`&DpVO8fd15hr77uVQhTM;6P z8qN25WF`<(#i2BMY~+N0HWmc{ii6^Kq;&)+zQU(?^*wANtzTOU;w8T_UGwD{h%s$n zIjZ;zU>eePFam2r8ypP6(bhuUhURvd;Mm*s-HY%}(Dx|xy^G)XWY;&|pFv|w#)%~3 zO>T|THMdzZc${T^_-9Lo9Bx5i>!?N{{h>Dij&52=KnuT#BWP!B!h91i4+@=ZU_Sku zeG;U-cnYXifhWvIR0&WawLRGcI*t@Nh-)wu=W5PE%_V-#KXCHnYMN1Vx?l5xt3hN8 zCXg`?^Ej1vh0ZugBWg2bSilOqJ&7GHv{%Gr%y~REL15`?z*ysB?BFTWHYnQ4FKTg} z;OKoSLR@UH{!i2*$v)0Cc-1dDgbmU)JyKwAhWa`d`;ZiFth1s`7z5FY@K|J)zsN?! zxB*7xUj`v17s(|;&KDzy*MlKF${c(#R|ozqW_}E~Tj^>shQ`XJdkS!}%uKp39>190i_jM=q?thVEXsyNYUD=%=t5ghy`UYI|qs^bW88uR5AAoJd{5D-VR-U(#i zjchdkz=;l&Q}ft%X7{vl~zdRr8_?NJ55H9GCfa)?rg+<((|Y zBV8=<&myMNaJ#ii7RqpiWfwx*kp3Qi$^MAzp=3sl^ooY8*+>LClawwIh<&5=3g=Mc zn28PqD%eLNyB%N>s`aSZ%a%gaiN4mox;Qp2pQ+x zS+^5-uv%-i(;l{|=m;;|iNR>8$G5mp3J1d3L?2R?T)seE#Zw;$QoJLa%po`ufj%uK z*Ez2zqWb7LTPHs7Cps*xR{s_Z(oxS_Fq(?_MlJ3ceGWs`>snYxOXr|&F{?HrkTbDv zS9uk&#Vq|RV?KDsc;=AJ9nW^XL>`j#gqL$K*0Hhd(ji=U06g?ohX}<@{j#iZ>-$=^ zhqr9UZQYw0ALr8S;BVQ@vD|vI$=-6}9fk3L2glfW!1gDEH(iJa$TJKfCmzsJUMwCE zS`}lg`>U86kmm@_ZCJoxQic3s?)p7l9DXSWPe=hd?Nx|Xk7GQ+$5cH&c>Icfg$XK( zw3o(AE!tFDGEwThm#c*sF{R99j7{n^++^{zp*TkAevtWR}7Sss6x%fcgdR0bm6UVW1hxNp8J#G{%z`vIsby{g0#qIzM z&c)#Mu=md6FlZnE>dkC?T=|t2ar4<7d?cw=meflDZ3B~VS|s%yTn<5mVN%7$a`cON zkb}mvH!lFc4dBGg9j|Mj;D#V~a`h!6+OE?D8UDEOD)}Mu7UEJI2S&QOyDFEhDv#h! z;qAWJhyt%`=+j;0(E}=Wd!jt@!wCra?}#tv!j^%Nj)RBcYhN2zj%PoKQIW2EcLnM$ zS&2T=l|M(`u-Ox{GCW=7JFHmCr39Qw9C!g;vlNBYY`qzVCJVTMjW?t#MgSMWVN&r& zM1CY_*0#OQ^-}s5tcAn{We-uXZ{v7F!4O-6bsz|J3RZ*Jq3C+r3ea)t-m+L2A)c__ zTw@W8fJNvrSD}186Nf}JVkZfndSFgz!o8Iz+O}F!mi?aobO8${O2JXtaSmd?ncKLp z^;Z)3TiG35!GSa7IxV(4{K|6FeT^5vD`u~a1q1f&4m;RFdzB$qZ*D-UsNHFa*(D6N zHH99ku)Q^+C&46FRH;jmVNH`cl)0xcY*-a%GnDy9zhY$>li>NFm_FuEY+Epg3OLlK zhk4mSE=k0Ao_s zgM?XyWrf;73+*6b#$lnP8Y9>Uv95$!WWh%8{!x0E(-CHM%ON&#$_pRXaGrqT1Cr96O*&`A6-qxqb9mwF~E5&HAR2tYN(nZJ8z1Tt!!`+Cv2a8rUy!m=9E3R+YkGp`` zE6HE(E6F+MWfTnbt?#P_D;qG!_p8P5lUP-L{(aGdo_9Kdli7s1Q>%hifb*w1b<42s zZ~5cHw+ZSRkFyTBmNZ!3mGxs?BHM~LJRB~-ARxRXopYr$tF<`X+ID;ewe1bj=e-NH`d9| z(!B3=p~$6b3xX2&Ent`+YQ%VE(hH>U^(HF??VEo+SskEY(~c&0rpz3QJk zK7B#_!Ls~(k^AGi7{o|Xrx zGIaP5UqxCsnNA*1W;b!zuEMEjcQcow%DVl$EeE)b5eT1DZ2gmgdCp$Hm|}us=MGIW zVUWtyI@Ih5Y`q!GW!bT2XM9Kdl<%}4BAkDrRHvtHz4Ux>aq|01z1MRF3}cJ}V;kKr z`{=9rGTKyXXQOL zp4jH){5hZz0P5Wj)2u@)qY()K&E80aB7IsMpDw!)(g^AhQJN(TIjnDCdqG7c^PuMD zKs{3o3MNTha&Meozvt4<9rA0~Y;%P$0l-<8r(%AESAv)YN=%_H-dmDK z2H%%~xan?RKAHm6KA9h9O`*peN*f5#&a6fSW6E3Fx}7yhVZyR@PgmDh^G|ff@wulz zJ{cwgX)*!j`}5iBH=>F`U%DKupV6m1AGuxjoSm{r>+;IYu?#-Lkg=zgyM-j=Ch}qs z(Y*ObrI%|uf%~rAj&g2 zMADW2zKp)Nvuxo!IPK2at@!W;uIO9GX6Q0mjWt`cO@2#99+~;<`aZD{dcZ^>mM*n8 zd_Z;0(HxMD?|H9b$64n$`WMDARhq7;3qzd=sNT4Y26PNOm~3l?g&`&Y!pQ(cjluoI zB5M)~cpM(pyeos$%6l7Pe39C)5X9IWhWH0a`Ay6xw41@yG_%j1`YnWNgDmh3w5#2I zgWH>B_uXtxF*cw%VMqio8np$ZEx~TNuMQ@r7y*T0Jyey@OQS?ZD-3a;@sb52UCt5ciJL zmtK>#6K~a=!%+7dTr+GLdU!2mVZ+qFdzjo&iqa61Lp{VQF^dGhpdfBPWBjz2U8NBs z#}hmHCMGkT;dkKFuf?{j12Pat)o%{*`>to-es5^~-baqiPW9f75)G*}m>E&VGoiW$ zlXbAI*k9Q}{V9@*<$$G-Y`!iJ@?-cQ*$A^dP3Hm{m!d>bHe;Rwc8lzMr2N~y4p|{z zt(1<{3S97ymgX?`bQ7QU!xP-bMtpyA!SzTe8;<{-ADynN80fl^@qRBRQjhI8`)JX#Dc%-VRb49lv{`M_n%-ewtWOePP&$TPqb zSf0!{*UZ7rAGH{z3`0VYb>;~aiOSlJWkI9)b*)V})LyO!Mma+EE?A8$Bp#j-E@meA z4Qs{5{iWbw^9@vGPsyIN9DY&k8E(+9;r z_)SEODYiS60ZawjcNx?J@OsmX9TAT6gpq}s6w_tL(ES@}7rBVQF7C%*@DvQWWy~y? zw!yU|{@_*A&tM@6-m$l}R{-bm2TV2~41exiU)#5+TaPpHAKphN6e3IL>yp$J%&+F8 z`@k#6_&v^~WZzR%e&GNf-MJN>x19g~UY#{x3O(+w$agCAf%^TLb2So%L#IDNh%U^VR@RK>*u7W%%oq6 z3?)4`6n|r`7CT5vN-W9jAu-6`mW`0BU=g$V!j~w#_o%WEV|)BTD4}bx-q8nGUf$3x zsCaKej`qUwKlCq2i`M>i`qrjGNXj(HZj==lB%{(z${UDPWY4k~t?>+>3aUBMx;?E8 z0;ErzgBeuDPeFXeNj?=K6eC&)#b9;&b&qYwRyK?092;mzRpq`N}=Vu)e`Tfpx;>8~Gt{dSJu z6WQW_!tb;&zn^~RKgaJq6Wzdseq+V?{h9RF55Imp$M3mp@ju~r-!Q*}wwzy1XbCg_ z4yR1F<%BbrS1RCk{K@;1&)uM)=YiFDL6j@b=Om{Hz$d+d-vPj9{2fc!sNTwoO19vo zKf~qplxvY#2>l=k6(-1gy$jcGp-BpZ&YOO?S&IoxSIP(*xOvKn(nZ#RAJ819v6%z1 z?^r-bM{}t6;Q@1w_emB!qP&G)C&{{*g&@LvO5Sg=EpjKn?A_``-0B7Q?c@BWNWQa7 zTTOj6Ke-K7fhk}wc8upaeCAjD7IE`*yrLnc7yG62CN$gCo4f~E)rq_1v;pq%Ra{k_ zGf{>qvV8!K$Q8kNeFR;T5dF&77iwGVLIkpG$)3q3Wo<|J_z_*!Uwj5#6-1yS7mDN= zxZPWn_?m-kdm@vwkBZB0B}>w}g$uNnNY(tOXaGvuqf$+d1Jat$BD}@iU^a4E7!X*@ru?>SVCVBD|ZB%$fTftmSIxVL^g!Lt;ReIYK=_ zHRQ-UiA^Asq{V)TMUgC_;E+z<{a*pes*{zm$hA#LAWvC}i6|w~a!!9rJDb1XL<5Jo|BmeGHp~40{)g(i!%V_Ssp^ zxA=_SiyQV})QZl!e@-wVF4p@CQ0(_y)!7*^@GQa5d#g&ox1=|uc_H_{B?)}Iip$TkFSLuQOE(yQl z(4z1~<}-OghHEB_7``BLqlm6zz{0#&4W!c&`5g7;um8(U+}qprmwy-8pHGHpt9aFfSP35Cog-iqtj`y1icbTkn2^= z@&ny+Z)fiU408max=Dp~M35!eWj2kbsC=ifNwaQLm)_?Hhf6@7l5A83UEI7v zzXrl{_B-GK`$TwhT`abk8xbETXP7Xt>8ZQ~|p$!ZCHp!nUaqXlCfBY+Q+ufZbxPbX6 zoD<99v#N6riFa7vl7N|zxRg@(=w^v0xwy7XQ#u&SZ63&JSj~kP-2ok0?!a3)h`TKi z!DP%DB&Q-QWt+eDS~xPEg6;H6QstzaFq2BE@Cj_ahl+DA@0P$20c-e-OY>syr}uR zJz60^wYVx;wX1yvudOdfx;?A_6qyTQO4uT#0=a%2Oo{+Rt*=Vt6C~Y)CgvE~yvV4Z zUniD~)T`wl%y0HenmuC8etd;pCKw~xi=Fw7JyehR&-_4k|JZ9g0If&cD1$h8@c_&h zet-^d4Oi!2R*#cEUkDKx9_4aQ2!gO-fV_cSYIK)hZJ!t##J)-+Yk-yKNob%Oe!m1k z1dRm%d?zdGh`+A4$Hv{Q3C8d`Kr>KfZ+BcVEra>p>MZDph7zyu{Kw z0%M?;uTPL>R8M9#8}+hdD8ZIKFa{o=U4cQuW@v6=`I#%l`Twh*?H+;b15XAy)1jFtzHnW)=eLJ zy4)|7RjKs$Lzba8u>`=#GdphJ<_TZ0qLYInC1w|gqOWG6e9f2!M9*OCeta#MAH$a6 zk@;p399J(*w1h^^a4DJLutuKPaMCHcKiLFJzW zFmNqA@&~sh=02JM4anQS5#y=PnD$1;6cVi%Y#_=XKC%`@-rDhc`a=d>7< zAKOwU7?Z%5`}=1_;|a~=sF9ITB5W~##LH)DFY>xZoVVHI#4ug9RU*`<@}jOwTt+gA8xm^~Y?$*P%$Lo+4E^xj=N2n zquvXVv#BoJUGFo#EQ|C$QOJm>{y)Ws1i&pHHrP{|jkmv0e=P8u%f>P9Vl8nHXI#;F z3$O3$ifC&Z!Il3eQsm#@?N4j!&sv)9YJmq?0hf^Enw z0LNBu9$Z_-SCtd!WPV*Ys?5%OPYlbMGg*S#h`9iLQ?HobEzCTY;9(u@41Qfn5)a?d z73I&$W51GNrEBrtzsX{!6!u<+l%L*%@MjFpHmf>+S^>cHLVd&6H5kXiuX~jq67k|| zWy}gpa-1dE>JicXLLt%bru%IY#Djbgz;=GkjHy}!)38k3fjzkFPUvz_oH75zeh7U` zU>_48f(F8h2%4F0hKGP*+A!}BVJF2H{)CrrIffVE*pX5$>%9wc3-XG&JD}}kc-Szn z|MjZydMN2Ql)Sznyjr?Pkrs5{!tseea2K}b`U|uMelbrjixGbG-@A9b+JH z4DyKn=uIVBH@6C|g1*cDOPnc@PunAP*7ocdCg%mi1nm zqhZ8f73m^DYA=-u33#;abg|Uukvq|nyCh7}A(kT7LMt=o>O~^v!zt#&Nzwh%N|sv5 zYQ5>U6*~$mW`kvJp_sYwt}iCw%Ql7tyjv6*2!SczQUXk#Dk0e4*UBKmRo^c|B?A|C52dw^W&MV zh(0(`^?m`z+`HI%=ec3}9upTAu-pLZLBdfa;V82Felf1bAudPT+!*HS63OA~tlJ=k z?lW-U&z`M~BH{i&N4SfkP)rNubw@h2i3q2Ijt0`v08s>qOm&Y4_?Te_>I!#uw@>Q!Z`Hq6u>mMPalD>LR|#7M!^WHL3G z!Y%aF`E%QvR}_Do_|tUbCqJVlG3l|>-dPfe`TVoAJ`twqCx{$U%z_4eXl2H{`g5V^ zXi{`EDeB)-fBS2|T_EO?p879%3&*#?%Le3l>W{W9j;ge$o*l+E64-iQ6c+O|^7EIK z|9V^56PCl|Gqo}qE^J2>nZj``v=YDPhWI$tbqs}b425%!v?`cb8`r9*_E-&1y&#PI z6mfszsabEb&&vQq5(UB~>Z!xdSbtBrvo0k#@>u%=;&L2zjo}WKV_iq;T??(um;p-t zvFzwrQoq=aOBF~FPu;|exr%64RZ`4Tf3P8>;G2LyD{sR=9uc!`32Y@i^)ExHvZ<^P zB6yyz&ZP}x zMv>-=A2W}!cdVhXgqA<@kffC8=FF)=oD`;HVj);k=fJ8txCIr&pnw>l@lJ7X@Mf2D zdX8Gs1<~7UwVxKw%>n*fnzR_JSli>Du(smDUI0;y_1{m$Oh;4EqhV1)5DKHI>Af)? zzTf}{MKI+=_PkvD2Mhkrx>*1!peZFZ1#4m<^eE{+Xfan>X3PyY3KjdZt9{uOza+q` zWOsri5mJ->G8-OsPu@iFG*LXs-W3o$_e`zrkmMMs&c0x>dKhWbmpODl25?vbc94M` zbPE|)MknGBiQ{Ae3LDZuM%MU1G#l}0-r<}L+u%blwziSpPr}-GcfD>*k4D%2A#R!* z)5ov`u$&<^;$Gr}6=p1MkBIoZN`GPY2aO@4W{h_#WaUb|M)T1l3`FHw-9>nPiGo@rok%vZ z6=B_JTC+IZ{V`bl(bXvU!aA+Yn4c~b-H#)a<1CZ=2TVpwGZ`olwg&oa?>DYj1uj7$ z`P`qB^TAhO9Vcb0!0aJegmZAXNg+BE1F-{#tm~ycc0iUn(f)g!B33hILdncvR z?lUZP81+a>$k-Wb#!R@uo=a7AUWFcg_kv(Eyom>M+}AwQPC+pq3~VIQ+zh_0^);D& zFiR3O4gnf$2eg^0DQ>qo~;8FqXlZT zFAjlQ7=lC0rrpK23*`w>FcTw6?AOd!@?#nxdL5Aje#0YVgd5%R4kt>*e>&C&`3%I~ ztOxT6i#_3it0cuF{j{4)r61cBCkd;G(^(F%IEiR6DN9ZqRyvphi&Z^R&8mcXfu}hl z_l}(Otla!*duc?y`GiYvIfpD7zasrk;4d@H6_^8yS#3*_?7H17=Oe!1ut-iC)NG(J zo+rO*>4FZaNM=uvUK_m1_!9@FGlD~SK8DH|b5?3Fz&U>E%O=V~i z`Y_m&WjdV1l*3OLUheNuA(ey&A>ghd4U44Yu<4z`I}9`CyG^Qtq&a9S?bgX4r@xZz zxA)tRDrm3r6OSdzOPZ)0YvszBvgAJ}?=6KFspH)+Uux;fd*cPnI%`N!6(=l zzi?VR&#P5!=<`nVJY*-xu#$S*4GasBaqgRNzWq@F21_<^x-{MK)scu@BytDY@8yER zf9U;2A=NYW6*^QgdsqwIU4|;+t5_Y$$)xWP!l&w1hRYh|reynjfmO>BVuKXd4LkE3 zEQg^Y?fLuel$+$EwpK0YOrqjGezV%2bc$qh7HR*r!uHOd!mn=e=wjuY-1AM$`8~oG z$c1I16#U7UVetE$Qt{A3660pQgd17Xp$E9xf%zb-Lm;s4jL8&$+yxMOn!K66N0?84 zc@g@TaF)uUAMZHj(LX9g9E?0;Mizh!4uJ^$(E^ZnKiyLLWrFvde`zuk-%$C7e@}6d zu{xoso6HI>e!z|lr?~l%T?>U$L}KPt5TejGSZJ~FI1c-xMl!58^n#?4m>KD}IK#F` zie}NG+I++I5t@@@<}<_~t+IeOcexPqhv+D2(qDicRRJGy3>YEy82z%0DQAyDCVpQC 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 iMz=9)Bsf16uFFx=pyQ=j(qu0{;eFq9!i@ diff --git a/plugins/HexRaysPyTools.py b/plugins/HexRaysPyTools.py new file mode 100644 index 0000000..30e954a --- /dev/null +++ b/plugins/HexRaysPyTools.py @@ -0,0 +1,279 @@ +import logging +import HexRaysPyTools.Actions as Actions +import HexRaysPyTools.Core.Cache +from HexRaysPyTools.Core.TemporaryStructure import * +import HexRaysPyTools.Forms as Forms +import idaapi +import HexRaysPyTools.Core.NegativeOffsets as NegativeOffsets +import HexRaysPyTools.Core.Helper as Helper +import HexRaysPyTools.Core.Cache as Cache +import HexRaysPyTools.Core.Const as Const +from HexRaysPyTools.Core.SpaghettiCode import SpaghettiVisitor, SwapThenElseVisitor +from HexRaysPyTools.Core.StructXrefs import * + +potential_negatives = {} + + +def hexrays_events_callback(*args): + global potential_negatives + + hexrays_event = args[0] + + if hexrays_event == idaapi.hxe_populating_popup: + form, popup, hx_view = args[1:] + item = hx_view.item # current ctree_item_t + + if Actions.GuessAllocation.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.GuessAllocation.name, None) + + if Actions.RecastItemRight.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.RecastItemRight.name, None) + + if Actions.RecastItemLeft.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.RecastItemLeft.name, None) + + if Actions.RenameOther.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.RenameOther.name, None) + + if Actions.RenameInside.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.RenameInside.name, None) + + if Actions.RenameOutside.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.RenameOutside.name, None) + + if Actions.RenameUsingAssert.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.RenameUsingAssert.name, None) + + if Actions.SwapThenElse.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.SwapThenElse.name, None) + + if Actions.ShallowScanVariable.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.ShallowScanVariable.name, None) + idaapi.attach_action_to_popup(form, popup, Actions.DeepScanVariable.name, None) + idaapi.attach_action_to_popup(form, popup, Actions.RecognizeShape.name, None) + + if Actions.CreateNewField.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.CreateNewField.name, None) + + if Actions.FindFieldXrefs.check(item): + idaapi.attach_action_to_popup(form, popup, Actions.FindFieldXrefs.name, None) + + if Actions.PropagateName.check(hx_view.cfunc, item): + idaapi.attach_action_to_popup(form, popup, Actions.PropagateName.name, None) + + if item.citype == idaapi.VDI_FUNC: + # If we clicked on function + if not hx_view.cfunc.entry_ea == idaapi.BADADDR: # Probably never happen + idaapi.attach_action_to_popup(form, popup, Actions.AddRemoveReturn.name, None) + idaapi.attach_action_to_popup(form, popup, Actions.ConvertToUsercall.name, None) + if Actions.DeepScanReturn.check(hx_view): + idaapi.attach_action_to_popup(form, popup, Actions.DeepScanReturn.name, None) + + elif item.citype == idaapi.VDI_LVAR: + # If we clicked on argument + local_variable = hx_view.item.get_lvar() # idaapi.lvar_t + if local_variable.is_arg_var: + idaapi.attach_action_to_popup(form, popup, Actions.RemoveArgument.name, None) + + elif item.citype == idaapi.VDI_EXPR: + if item.e.op == idaapi.cot_num: + # number_format = item.e.n.nf # idaapi.number_format_t + # print "(number) flags: {0:#010X}, type_name: {1}, opnum: {2}".format( + # number_format.flags, + # number_format.type_name, + # number_format.opnum + # ) + idaapi.attach_action_to_popup(form, popup, Actions.GetStructureBySize.name, None) + elif item.e.op == idaapi.cot_var: + # Check if we clicked on variable that is a pointer to a structure that is potentially part of + # containing structure + if item.e.v.idx in potential_negatives: + idaapi.attach_action_to_popup(form, popup, Actions.SelectContainingStructure.name, None) + if Actions.ResetContainingStructure.check(hx_view.cfunc.get_lvars()[item.e.v.idx]): + idaapi.attach_action_to_popup(form, popup, Actions.ResetContainingStructure.name, None) + + elif hexrays_event == idaapi.hxe_double_click: + hx_view = args[1] + item = hx_view.item + if item.citype == idaapi.VDI_EXPR and item.e.op == idaapi.cot_memptr: + # Look if we double clicked on expression that is member pointer. Then get tinfo_t of the structure. + # After that remove pointer and get member name with the same offset + if item.e.x.op == idaapi.cot_memref and item.e.x.x.op == idaapi.cot_memptr: + vtable_tinfo = item.e.x.type.get_pointed_object() + method_offset = item.e.m + class_tinfo = item.e.x.x.x.type.get_pointed_object() + vtable_offset = item.e.x.x.m + elif item.e.x.op == idaapi.cot_memptr: + vtable_tinfo = item.e.x.type.get_pointed_object() + method_offset = item.e.m + class_tinfo = item.e.x.x.type.get_pointed_object() + vtable_offset = item.e.x.m + else: + func_offset = item.e.m + struct_tinfo = item.e.x.type.get_pointed_object() + func_ea = Helper.get_virtual_func_address(Helper.get_member_name(struct_tinfo, func_offset)) + if func_ea: + idaapi.jumpto(func_ea) + return 0 + + func_name = Helper.get_member_name(vtable_tinfo, method_offset) + func_ea = Helper.get_virtual_func_address(func_name, class_tinfo, vtable_offset) + if func_ea: + idaapi.open_pseudocode(func_ea, 0) + return 1 + + elif hexrays_event == idaapi.hxe_maturity: + cfunc, level_of_maturity = args[1:] + + if level_of_maturity == idaapi.CMAT_BUILT: + # print '=' * 40 + # print '=' * 15, "LEVEL", level_of_maturity, '=' * 16 + # print '=' * 40 + # print cfunc + + # First search for CONTAINING_RECORD made by Ida + visitor = NegativeOffsets.SearchVisitor(cfunc) + visitor.apply_to(cfunc.body, None) + negative_lvars = visitor.result + + # Second get saved information from comments + lvars = cfunc.get_lvars() + for idx in xrange(len(lvars)): + result = NegativeOffsets.parse_lvar_comment(lvars[idx]) + if result and result.tinfo.equals_to(lvars[idx].type().get_pointed_object()): + negative_lvars[idx] = result + + # Third make an analysis of local variables that a structure pointers and have reference that pass + # through structure boundaries. This variables will be considered as potential pointers to substructure + # and will get a menu on right click that helps to select Containing Structure from different libraries + + structure_pointer_variables = {} + for idx in set(range(len(lvars))) - set(negative_lvars.keys()): + if lvars[idx].type().is_ptr(): + pointed_tinfo = lvars[idx].type().get_pointed_object() + if pointed_tinfo.is_udt(): + structure_pointer_variables[idx] = pointed_tinfo + + if structure_pointer_variables: + visitor = NegativeOffsets.AnalyseVisitor(structure_pointer_variables, potential_negatives) + visitor.apply_to(cfunc.body, None) + + if negative_lvars: + visitor = NegativeOffsets.ReplaceVisitor(negative_lvars) + visitor.apply_to(cfunc.body, None) + + elif level_of_maturity == idaapi.CMAT_TRANS1: + + visitor = SwapThenElseVisitor(cfunc.entry_ea) + visitor.apply_to(cfunc.body, None) + + elif level_of_maturity == idaapi.CMAT_TRANS2: + # print '=' * 40 + # print '=' * 15, "LEVEL", level_of_maturity, '=' * 16 + # print '=' * 40 + # print cfunc + visitor = SpaghettiVisitor() + visitor.apply_to(cfunc.body, None) + + elif level_of_maturity == idaapi.CMAT_FINAL: + StructXrefVisitor(cfunc).process() + + return 0 + + +class MyPlugin(idaapi.plugin_t): + # flags = idaapi.PLUGIN_HIDE + flags = 0 + comment = "Plugin for automatic classes reconstruction" + help = "This is help" + wanted_name = "HexRaysPyTools" + wanted_hotkey = "Alt-F8" + + @staticmethod + def init(): + if not idaapi.init_hexrays_plugin(): + print "[ERROR] Failed to initialize Hex-Rays SDK" + return idaapi.PLUGIN_SKIP + + Cache.temporary_structure = TemporaryStructureModel() + # Actions.register(Actions.CreateVtable) + Actions.register(Actions.ShowGraph) + Actions.register(Actions.ShowClasses) + Actions.register(Actions.GetStructureBySize) + Actions.register(Actions.RemoveArgument) + Actions.register(Actions.AddRemoveReturn) + Actions.register(Actions.ConvertToUsercall) + Actions.register(Actions.ShallowScanVariable, Cache.temporary_structure) + Actions.register(Actions.DeepScanVariable, Cache.temporary_structure) + Actions.register(Actions.DeepScanReturn, Cache.temporary_structure) + Actions.register(Actions.DeepScanFunctions, Cache.temporary_structure) + Actions.register(Actions.RecognizeShape) + Actions.register(Actions.CreateNewField) + Actions.register(Actions.SelectContainingStructure, potential_negatives) + Actions.register(Actions.ResetContainingStructure) + Actions.register(Actions.RecastItemRight) + Actions.register(Actions.RecastItemLeft) + Actions.register(Actions.RenameOther) + Actions.register(Actions.RenameInside) + Actions.register(Actions.RenameOutside) + Actions.register(Actions.RenameUsingAssert) + Actions.register(Actions.SwapThenElse) + Actions.register(Actions.FindFieldXrefs) + Actions.register(Actions.PropagateName) + Actions.register(Actions.GuessAllocation) + + idaapi.attach_action_to_menu('View/Open subviews/Local types', Actions.ShowClasses.name, idaapi.SETMENU_APP) + idaapi.install_hexrays_callback(hexrays_events_callback) + + Const.init() + XrefStorage().open() + + return idaapi.PLUGIN_KEEP + + @staticmethod + def run(arg): + tform = idaapi.find_tform("Structure Builder") + if tform: + idaapi.switchto_tform(tform, True) + else: + Forms.StructureBuilder(Cache.temporary_structure).Show() + + @staticmethod + def term(): + if Cache.temporary_structure: + Cache.temporary_structure.clear() + # Actions.unregister(Actions.CreateVtable) + Actions.unregister(Actions.ShowGraph) + Actions.unregister(Actions.ShowClasses) + Actions.unregister(Actions.GetStructureBySize) + Actions.unregister(Actions.RemoveArgument) + Actions.unregister(Actions.AddRemoveReturn) + Actions.unregister(Actions.ConvertToUsercall) + Actions.unregister(Actions.ShallowScanVariable) + Actions.unregister(Actions.DeepScanVariable) + Actions.unregister(Actions.DeepScanReturn) + Actions.unregister(Actions.DeepScanFunctions) + Actions.unregister(Actions.RecognizeShape) + Actions.unregister(Actions.CreateNewField) + Actions.unregister(Actions.SelectContainingStructure) + Actions.unregister(Actions.ResetContainingStructure) + Actions.unregister(Actions.RecastItemRight) + Actions.unregister(Actions.RecastItemLeft) + Actions.unregister(Actions.RenameOther) + Actions.unregister(Actions.RenameInside) + Actions.unregister(Actions.RenameOutside) + Actions.unregister(Actions.RenameUsingAssert) + Actions.unregister(Actions.SwapThenElse) + Actions.unregister(Actions.FindFieldXrefs) + Actions.unregister(Actions.PropagateName) + Actions.unregister(Actions.GuessAllocation) + idaapi.term_hexrays_plugin() + XrefStorage().close() + + +def PLUGIN_ENTRY(): + idaapi.notify_when(idaapi.NW_OPENIDB, Cache.init_demangled_names) + idaapi.notify_when(idaapi.NW_OPENIDB, Cache.init_imported_ea) + idaapi.notify_when(idaapi.NW_OPENIDB, Cache.reset_touched_functions) + Helper.extend_ida() + return MyPlugin() diff --git a/plugins/HexRaysPyTools/Actions.py b/plugins/HexRaysPyTools/Actions.py new file mode 100644 index 0000000..503faaa --- /dev/null +++ b/plugins/HexRaysPyTools/Actions.py @@ -0,0 +1,1440 @@ +import ctypes +import sys +import re +import logging + +import idaapi +import idc + +import HexRaysPyTools.Forms as Forms +import HexRaysPyTools.Core.Const as Const +import HexRaysPyTools.Core.Helper as Helper +import HexRaysPyTools.Api as Api +import Settings +from HexRaysPyTools.Core.StructureGraph import StructureGraph +from HexRaysPyTools.Core.TemporaryStructure import VirtualTable, TemporaryStructureModel +from HexRaysPyTools.Core.VariableScanner import NewShallowSearchVisitor, NewDeepSearchVisitor, DeepReturnVisitor +from HexRaysPyTools.Core.Helper import FunctionTouchVisitor +from HexRaysPyTools.Core.SpaghettiCode import * +from HexRaysPyTools.Core.StructXrefs import XrefStorage + +RECAST_LOCAL_VARIABLE = 0 +RECAST_GLOBAL_VARIABLE = 1 +RECAST_ARGUMENT = 2 +RECAST_RETURN = 3 +RECAST_STRUCTURE = 4 + +logger = logging.getLogger(__name__) + + +def register(action, *args): + idaapi.register_action( + idaapi.action_desc_t( + action.name, + action.description, + action(*args), + action.hotkey + ) + ) + + +def unregister(action): + idaapi.unregister_action(action.name) + + +class TypeLibrary: + + class til_t(ctypes.Structure): + pass + + til_t._fields_ = [ + ("name", ctypes.c_char_p), + ("desc", ctypes.c_char_p), + ("nbases", ctypes.c_int), + ("base", ctypes.POINTER(ctypes.POINTER(til_t))) + ] + + def __init__(self): + pass + + @staticmethod + def enable_library_ordinals(library_num): + idaname = "ida64" if Const.EA64 else "ida" + if sys.platform == "win32": + dll = ctypes.windll[idaname + ".wll"] + elif sys.platform == "linux2": + dll = ctypes.cdll["lib" + idaname + ".so"] + elif sys.platform == "darwin": + dll = ctypes.cdll["lib" + idaname + ".dylib"] + else: + print "[ERROR] Failed to enable ordinals" + return + + idati = ctypes.POINTER(TypeLibrary.til_t).in_dll(dll, "idati") + dll.enable_numbered_types(idati.contents.base[library_num], True) + + @staticmethod + def choose_til(): + idati = idaapi.cvar.idati + list_type_library = [(idati, idati.name, idati.desc)] + for idx in xrange(idaapi.cvar.idati.nbases): + type_library = idaapi.cvar.idati.base(idx) # idaapi.til_t type + list_type_library.append((type_library, type_library.name, type_library.desc)) + + library_chooser = Forms.MyChoose( + list(map(lambda x: [x[1], x[2]], list_type_library)), + "Select Library", + [["Library", 10 | idaapi.Choose2.CHCOL_PLAIN], ["Description", 30 | idaapi.Choose2.CHCOL_PLAIN]], + 69 + ) + library_num = library_chooser.Show(True) + if library_num != -1: + selected_library = list_type_library[library_num][0] + max_ordinal = idaapi.get_ordinal_qty(selected_library) + if max_ordinal == idaapi.BADORD: + TypeLibrary.enable_library_ordinals(library_num - 1) + max_ordinal = idaapi.get_ordinal_qty(selected_library) + print "[DEBUG] Maximal ordinal of lib {0} = {1}".format(selected_library.name, max_ordinal) + return selected_library, max_ordinal, library_num == 0 + return None + + @staticmethod + def import_type(library, name): + if library.name != idaapi.cvar.idati.name: + last_ordinal = idaapi.get_ordinal_qty(idaapi.cvar.idati) + type_id = idaapi.import_type(library, -1, name) # tid_t + if type_id != idaapi.BADORD: + return last_ordinal + return None + + +class RemoveArgument(idaapi.action_handler_t): + + name = "my:RemoveArgument" + description = "Remove Argument" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + vu = idaapi.get_widget_vdui(ctx.widget) + function_tinfo = idaapi.tinfo_t() + if not vu.cfunc.get_func_type(function_tinfo): + return + function_details = idaapi.func_type_data_t() + function_tinfo.get_func_details(function_details) + del_arg = vu.item.get_lvar() # lvar_t + + function_details.erase(filter(lambda x: x.name == del_arg.name, function_details)[0]) + + function_tinfo.create_func(function_details) + idaapi.apply_tinfo2(vu.cfunc.entry_ea, function_tinfo, idaapi.TINFO_DEFINITE) + vu.refresh_view(True) + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +class AddRemoveReturn(idaapi.action_handler_t): + + name = "my:RemoveReturn" + description = "Add/Remove Return" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + # ctx - action_activation_ctx_t + vu = idaapi.get_widget_vdui(ctx.widget) + function_tinfo = idaapi.tinfo_t() + if not vu.cfunc.get_func_type(function_tinfo): + return + function_details = idaapi.func_type_data_t() + function_tinfo.get_func_details(function_details) + if function_details.rettype.equals_to(Const.VOID_TINFO): + function_details.rettype = idaapi.tinfo_t(Const.PVOID_TINFO) + else: + function_details.rettype = idaapi.tinfo_t(idaapi.BT_VOID) + function_tinfo.create_func(function_details) + idaapi.apply_tinfo2(vu.cfunc.entry_ea, function_tinfo, idaapi.TINFO_DEFINITE) + vu.refresh_view(True) + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +class ConvertToUsercall(idaapi.action_handler_t): + + name = "my:ConvertToUsercall" + description = "Convert to __usercall" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + # ctx - action_activation_ctx_t + vu = idaapi.get_widget_vdui(ctx.widget) + function_tinfo = idaapi.tinfo_t() + if not vu.cfunc.get_func_type(function_tinfo): + return + function_details = idaapi.func_type_data_t() + function_tinfo.get_func_details(function_details) + convention = idaapi.CM_CC_MASK & function_details.cc + if convention == idaapi.CM_CC_CDECL: + function_details.cc = idaapi.CM_CC_SPECIAL + elif convention in (idaapi.CM_CC_STDCALL, idaapi.CM_CC_FASTCALL, idaapi.CM_CC_PASCAL, idaapi.CM_CC_THISCALL): + function_details.cc = idaapi.CM_CC_SPECIALP + elif convention == idaapi.CM_CC_ELLIPSIS: + function_details.cc = idaapi.CM_CC_SPECIALE + else: + return + function_tinfo.create_func(function_details) + idaapi.apply_tinfo2(vu.cfunc.entry_ea, function_tinfo, idaapi.TINFO_DEFINITE) + vu.refresh_view(True) + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +class GetStructureBySize(idaapi.action_handler_t): + # TODO: apply type automatically if expression like `var = new(size)` + + name = "my:WhichStructHaveThisSize" + description = "Structures with this size" + hotkey = "W" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def select_structure_by_size(size): + result = TypeLibrary.choose_til() + if result: + selected_library, max_ordinal, is_local_type = result + matched_types = [] + tinfo = idaapi.tinfo_t() + for ordinal in xrange(1, max_ordinal): + tinfo.create_typedef(selected_library, ordinal) + if tinfo.get_size() == size: + name = tinfo.dstr() + description = idaapi.print_tinfo(None, 0, 0, idaapi.PRTYPE_DEF, tinfo, None, None) + matched_types.append([str(ordinal), name, description]) + + type_chooser = Forms.MyChoose( + matched_types, + "Select Type", + [["Ordinal", 5 | idaapi.Choose2.CHCOL_HEX], ["Type Name", 25], ["Declaration", 50]], + 165 + ) + selected_type = type_chooser.Show(True) + if selected_type != -1: + if is_local_type: + return int(matched_types[selected_type][0]) + return TypeLibrary.import_type(selected_library, matched_types[selected_type][1]) + return None + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + if hx_view.item.citype != idaapi.VDI_EXPR or hx_view.item.e.op != idaapi.cot_num: + return + ea = ctx.cur_ea + c_number = hx_view.item.e.n + number_value = c_number._value + ordinal = GetStructureBySize.select_structure_by_size(number_value) + if ordinal: + number_format_old = c_number.nf + number_format_new = idaapi.number_format_t() + number_format_new.flags = idaapi.FF_1STRO | idaapi.FF_0STRO + operand_number = number_format_old.opnum + number_format_new.opnum = operand_number + number_format_new.props = number_format_old.props + number_format_new.type_name = idaapi.create_numbered_type_name(ordinal) + + c_function = hx_view.cfunc + number_formats = c_function.numforms # idaapi.user_numforms_t + operand_locator = idaapi.operand_locator_t(ea, ord(operand_number) if operand_number else 0) + if operand_locator in number_formats: + del number_formats[operand_locator] + + number_formats[operand_locator] = number_format_new + c_function.save_user_numforms() + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class ShallowScanVariable(idaapi.action_handler_t): + + name = "my:ShallowScanVariable" + description = "Scan Variable" + hotkey = "F" + + def __init__(self, temporary_structure): + self.temporary_structure = temporary_structure + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + lvar = ctree_item.get_lvar() + if lvar is not None: + return Helper.is_legal_type(lvar.type()) + + if ctree_item.citype != idaapi.VDI_EXPR: + return False + + obj = Api.ScanObject.create(cfunc, ctree_item.e) + return obj and Helper.is_legal_type(obj.tinfo) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + cfunc = hx_view.cfunc + origin = self.temporary_structure.main_offset + + if self.check(cfunc, hx_view.item): + obj = Api.ScanObject.create(cfunc, hx_view.item) + visitor = NewShallowSearchVisitor(cfunc, origin, obj, self.temporary_structure) + visitor.process() + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class DeepScanVariable(idaapi.action_handler_t): + + name = "my:DeepScanVariable" + description = "Deep Scan Variable" + hotkey = "shift+F" + + def __init__(self, temporary_structure): + self.temporary_structure = temporary_structure + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + cfunc = hx_view.cfunc + origin = self.temporary_structure.main_offset + + if ShallowScanVariable.check(cfunc, hx_view.item): + obj = Api.ScanObject.create(cfunc, hx_view.item) + if FunctionTouchVisitor(cfunc).process(): + hx_view.refresh_view(True) + visitor = NewDeepSearchVisitor(hx_view.cfunc, origin, obj, self.temporary_structure) + visitor.process() + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class DeepScanReturn(idaapi.action_handler_t): + name = "my:DeepScanReturn" + description = "Deep Scan Returned Variables" + hotkey = None + + def __init__(self, temporary_structure): + self.temp_struct = temporary_structure + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(hx_view): + tinfo = idaapi.tinfo_t() + hx_view.cfunc.get_func_type(tinfo) + return not tinfo.get_rettype().equals_to(Const.VOID_TINFO) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + func_ea = hx_view.cfunc.entry_ea + + obj = Api.ReturnedObject(func_ea) + visitor = DeepReturnVisitor(hx_view.cfunc, self.temp_struct.main_offset, obj, self.temp_struct) + visitor.process() + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class DeepScanFunctions(idaapi.action_handler_t): + + name = "my:DeepScanFunctions" + description = "Scan First Argument" + hotkey = None + + def __init__(self, temporary_structure): + self.temporary_structure = temporary_structure + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + for idx in ctx.chooser_selection: + func_ea = idaapi.getn_func(idx - 1).startEA + cfunc = Api.decompile_function(func_ea) + obj = Api.VariableObject(cfunc.get_lvars()[0], 0) + if cfunc: + NewDeepSearchVisitor(cfunc, 0, obj, self.temporary_structure).process() + + def update(self, ctx): + if ctx.form_type == idaapi.BWN_FUNCS: + idaapi.attach_action_to_popup(ctx.widget, None, self.name) + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class RecognizeShape(idaapi.action_handler_t): + + name = "my:RecognizeShape" + description = "Recognize Shape" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + cfunc = hx_view.cfunc + + if not ShallowScanVariable.check(cfunc, hx_view.item): + return + + obj = Api.ScanObject.create(cfunc, hx_view.item) + temp_struct = TemporaryStructureModel() + visitor = NewShallowSearchVisitor(cfunc, 0, obj, temp_struct) + visitor.process() + tinfo = temp_struct.get_recognized_shape() + if tinfo: + tinfo.create_ptr(tinfo) + if obj.id == Api.SO_LOCAL_VARIABLE: + hx_view.set_lvar_type(obj.lvar, tinfo) + elif obj.id == Api.SO_GLOBAL_OBJECT: + idaapi.apply_tinfo2(obj.obj_ea, tinfo, idaapi.TINFO_DEFINITE) + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class CreateNewField(idaapi.action_handler_t): + name = "my:CreateNewField" + description = "Create New Field" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return + + item = ctree_item.it.to_specific_type + if item.op != idaapi.cot_memptr: + return + + parent = cfunc.body.find_parent_of(ctree_item.it).to_specific_type + if parent.op != idaapi.cot_idx or parent.y.op != idaapi.cot_num: + idx = 0 + else: + idx = parent.y.numval() + + struct_type = item.x.type.get_pointed_object() + udt_member = idaapi.udt_member_t() + udt_member.offset = item.m * 8 + struct_type.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) + if udt_member.name[0:3] != "gap": + return + + return struct_type, udt_member.offset // 8, idx + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + result = self.check(hx_view.cfunc, hx_view.item) + if result is None: + return + + struct_tinfo, offset, idx = result + ordinal = struct_tinfo.get_ordinal() + struct_name = struct_tinfo.dstr() + + if (offset + idx) % 2: + default_field_type = "_BYTE" + elif (offset + idx) % 4: + default_field_type = "_WORD" + else: + default_field_type = "_DWORD" + + declaration = idaapi.asktext( + 0x10000, "{0} field_{1:X}".format(default_field_type, offset + idx), "Enter new structure member:" + ) + if declaration is None: + return + + result = self.parse_declaration(declaration) + if result is None: + logger.warn("Bad member declaration") + return + + field_tinfo, field_name = result + field_size = field_tinfo.get_size() + udt_data = idaapi.udt_type_data_t() + udt_member = idaapi.udt_member_t() + + struct_tinfo.get_udt_details(udt_data) + udt_member.offset = offset * 8 + struct_tinfo.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) + gap_size = udt_member.size // 8 + + gap_leftover = gap_size - idx - field_size + + if gap_leftover < 0: + print "[ERROR] Too big size for the field. Type with maximum {0} bytes can be used".format(gap_size - idx) + return + + iterator = udt_data.find(udt_member) + iterator = udt_data.erase(iterator) + + if gap_leftover > 0: + udt_data.insert(iterator, TemporaryStructureModel.get_padding_member(offset + idx + field_size, gap_leftover)) + + udt_member = idaapi.udt_member_t() + udt_member.offset = offset * 8 + idx + udt_member.name = field_name + udt_member.type = field_tinfo + udt_member.size = field_size + + iterator = udt_data.insert(iterator, udt_member) + + if idx > 0: + udt_data.insert(iterator, TemporaryStructureModel.get_padding_member(offset, idx)) + + struct_tinfo.create_udt(udt_data, idaapi.BTF_STRUCT) + struct_tinfo.set_numbered_type(idaapi.cvar.idati, ordinal, idaapi.BTF_STRUCT, struct_name) + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + @staticmethod + def parse_declaration(declaration): + m = re.search(r"^(\w+[ *]+)(\w+)(\[(\d+)\])?$", declaration) + if m is None: + return + + type_name, field_name, _, arr_size = m.groups() + if field_name[0].isdigit(): + print "[ERROR] Bad field name" + return + + result = idc.ParseType(type_name, 0) + if result is None: + return + + _, tp, fld = result + tinfo = idaapi.tinfo_t() + tinfo.deserialize(idaapi.cvar.idati, tp, fld, None) + if arr_size: + assert tinfo.create_array(tinfo, int(arr_size)) + return tinfo, field_name + + +class ShowGraph(idaapi.action_handler_t): + name = "my:ShowGraph" + description = "Show graph" + hotkey = "G" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + self.graph = None + self.graph_view = None + + def activate(self, ctx): + """ + :param ctx: idaapi.action_activation_ctx_t + :return: None + """ + form = self.graph_view.GetTForm() if self.graph_view else None + if form: + self.graph_view.change_selected([sel + 1 for sel in ctx.chooser_selection]) + self.graph_view.Show() + else: + self.graph = StructureGraph([sel + 1 for sel in ctx.chooser_selection]) + self.graph_view = Forms.StructureGraphViewer("Structure Graph", self.graph) + self.graph_view.Show() + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_LOCTYPS: + idaapi.attach_action_to_popup(ctx.widget, None, self.name) + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class ShowClasses(idaapi.action_handler_t): + + name = "my:ShowClasses" + description = "Classes" + hotkey = "Alt+F1" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + """ + :param ctx: idaapi.action_activation_ctx_t + :return: None + """ + tform = idaapi.find_tform('Classes') + if not tform: + class_viewer = Forms.ClassViewer() + class_viewer.Show() + else: + idaapi.switchto_tform(tform, True) + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +class CreateVtable(idaapi.action_handler_t): + + name = "my:CreateVtable" + description = "Create Virtual Table" + hotkey = "V" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + ea = ctx.cur_ea + if ea != idaapi.BADADDR and VirtualTable.check_address(ea): + vtable = VirtualTable(0, ea) + vtable.import_to_structures(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_DISASM: + idaapi.attach_action_to_popup(ctx.widget, None, self.name) + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class SelectContainingStructure(idaapi.action_handler_t): + + name = "my:SelectContainingStructure" + description = "Select Containing Structure" + hotkey = None + + def __init__(self, potential_negatives): + idaapi.action_handler_t.__init__(self) + self.potential_negative = potential_negatives + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + result = TypeLibrary.choose_til() + if result: + selected_library, max_ordinal, is_local_types = result + lvar_idx = hx_view.item.e.v.idx + candidate = self.potential_negative[lvar_idx] + structures = candidate.find_containing_structures(selected_library) + items = map(lambda x: [str(x[0]), "0x{0:08X}".format(x[1]), x[2], x[3]], structures) + structure_chooser = Forms.MyChoose( + items, + "Select Containing Structure", + [["Ordinal", 5], ["Offset", 10], ["Member_name", 20], ["Structure Name", 20]], + 165 + ) + selected_idx = structure_chooser.Show(modal=True) + if selected_idx != -1: + if not is_local_types: + TypeLibrary.import_type(selected_library, items[selected_idx][3]) + lvar = hx_view.cfunc.get_lvars()[lvar_idx] + lvar_cmt = re.sub("```.*```", '', lvar.cmt) + hx_view.set_lvar_cmt( + lvar, + lvar_cmt + "```{0}+{1}```".format( + structures[selected_idx][3], + structures[selected_idx][1]) + ) + hx_view.refresh_view(True) + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +class ResetContainingStructure(idaapi.action_handler_t): + + name = "my:ResetContainingStructure" + description = "Reset Containing Structure" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(lvar): + return True if re.search("```.*```", lvar.cmt) else False + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + lvar = hx_view.cfunc.get_lvars()[hx_view.item.e.v.idx] + hx_view.set_lvar_cmt(lvar, re.sub("```.*```", '', lvar.cmt)) + hx_view.refresh_view(True) + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + +class RecastItemLeft(idaapi.action_handler_t): + + name = "my:RecastItemLeft" + description = "Recast Item" + hotkey = "Shift+L" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return + + expression = ctree_item.it.to_specific_type + child = None + + # Look through parents until we found Return, Assignment or Call + while expression and expression.op not in (idaapi.cot_asg, idaapi.cit_return, idaapi.cot_call): + child = expression.to_specific_type + expression = cfunc.body.find_parent_of(expression) + if not expression: + return + + expression = expression.to_specific_type + if expression.opname == 'asg': + + if expression.x.opname not in ('var', 'obj', 'memptr', 'memref'): + return + + right_expr = expression.y + right_tinfo = right_expr.x.type if right_expr.op == idaapi.cot_cast else right_expr.type + + # Check if both left and right parts of expression are of the same types. + # If not then we can recast then. + if right_tinfo.dstr() == expression.x.type.dstr(): + return + + if expression.x.op == idaapi.cot_var: + # var = (TYPE ) ...; + variable = cfunc.get_lvars()[expression.x.v.idx] + idaapi.update_action_label(RecastItemLeft.name, 'Recast Variable "{0}"'.format(variable.name)) + return RECAST_LOCAL_VARIABLE, right_tinfo, variable + elif expression.x.op == idaapi.cot_obj: + # g_var = (TYPE ) ...; + idaapi.update_action_label(RecastItemLeft.name, 'Recast Global') + return RECAST_GLOBAL_VARIABLE, right_tinfo, expression.x.obj_ea + elif expression.x.op == idaapi.cot_memptr: + # struct->member = (TYPE ) ...; + idaapi.update_action_label(RecastItemLeft.name, 'Recast Field') + return RECAST_STRUCTURE, expression.x.x.type.get_pointed_object().dstr(), expression.x.m, right_tinfo + elif expression.x.op == idaapi.cot_memref: + # struct.member = (TYPE ) ...; + idaapi.update_action_label(RecastItemLeft.name, 'Recast Field') + return RECAST_STRUCTURE, expression.x.x.type.dstr(), expression.x.m, right_tinfo + + elif expression.op == idaapi.cit_return: + + idaapi.update_action_label(RecastItemLeft.name, "Recast Return") + child = child or expression.creturn.expr + + if child.op == idaapi.cot_cast: + # return (TYPE) ...; + return RECAST_RETURN, child.x.type, None + + func_tinfo = idaapi.tinfo_t() + cfunc.get_func_type(func_tinfo) + rettype = func_tinfo.get_rettype() + + if func_tinfo.get_rettype().dstr() != child.type.dstr(): + # return ...; + # This's possible when returned type and value are both pointers to different types + return RECAST_RETURN, child.type, None + + elif expression.op == idaapi.cot_call: + if expression.x.op == idaapi.cot_memptr: + if expression.x == child: + return + + arg_index, arg_tinfo = Helper.get_func_argument_info(expression, child) + if child.op == idaapi.cot_cast: + # struct_ptr->func(..., (TYPE) var, ...); + new_arg_tinfo = child.x.type + else: + # struct_ptr->func(..., var, ...); When `var` and `arg` are different pointers + if arg_tinfo.equals_to(child.type): + return + new_arg_tinfo = child.type + + struct_type = expression.x.x.type.get_pointed_object() + funcptr_tinfo = expression.x.type + Helper.set_funcptr_argument(funcptr_tinfo, arg_index, new_arg_tinfo) + return RECAST_STRUCTURE, struct_type.dstr(), expression.x.m, funcptr_tinfo + + if child and child.op == idaapi.cot_cast: + if child.cexpr.x.op == idaapi.cot_memptr and expression.ea == idaapi.BADADDR: + idaapi.update_action_label(RecastItemLeft.name, 'Recast Virtual Function') + return RECAST_STRUCTURE, child.cexpr.x.x.type.get_pointed_object().dstr(), child.cexpr.x.m, child.type + + if expression.x == child.cexpr: + return + arg_index, _ = Helper.get_func_argument_info(expression, child.cexpr) + idaapi.update_action_label(RecastItemLeft.name, "Recast Argument") + return ( + RECAST_ARGUMENT, + arg_index, + expression.x.type.get_pointed_object(), + child.x.type, + expression.x.obj_ea + ) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + result = self.check(hx_view.cfunc, hx_view.item) + + if not result: + return + + if result[0] == RECAST_LOCAL_VARIABLE: + logger.debug("Recasting local variable. Type - %s", result[1].dstr()) + tinfo, lvar = result[1:] + if hx_view.set_lvar_type(lvar, tinfo): + hx_view.refresh_view(True) + + elif result[0] == RECAST_GLOBAL_VARIABLE: + logger.debug("Recasting global. Type - %s. Address - %s", result[1].dstr(), Helper.to_hex(result[2])) + tinfo, address = result[1:] + if idaapi.apply_tinfo2(address, tinfo, idaapi.TINFO_DEFINITE): + hx_view.refresh_view(True) + + elif result[0] == RECAST_ARGUMENT: + arg_index, func_tinfo, arg_tinfo, address = result[1:] + if arg_tinfo.is_array(): + arg_tinfo.convert_array_to_ptr() + + func_data = idaapi.func_type_data_t() + func_tinfo.get_func_details(func_data) + func_data[arg_index].type = arg_tinfo + new_func_tinfo = idaapi.tinfo_t() + new_func_tinfo.create_func(func_data) + if idaapi.apply_tinfo2(address, new_func_tinfo, idaapi.TINFO_DEFINITE): + hx_view.refresh_view(True) + + elif result[0] == RECAST_RETURN: + return_type, func_address = result[1:] + try: + cfunc = idaapi.decompile(func_address) if func_address else hx_view.cfunc + except idaapi.DecompilationFailure: + print "[ERROR] Ida failed to decompile function at 0x{0:08X}".format(func_address) + return + + function_tinfo = idaapi.tinfo_t() + cfunc.get_func_type(function_tinfo) + func_data = idaapi.func_type_data_t() + function_tinfo.get_func_details(func_data) + func_data.rettype = return_type + function_tinfo.create_func(func_data) + if idaapi.apply_tinfo2(cfunc.entry_ea, function_tinfo, idaapi.TINFO_DEFINITE): + hx_view.refresh_view(True) + + elif result[0] == RECAST_STRUCTURE: + structure_name, field_offset, new_type = result[1:] + tinfo = idaapi.tinfo_t() + tinfo.get_named_type(idaapi.cvar.idati, structure_name) + + ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, structure_name) + + if ordinal: + udt_member = idaapi.udt_member_t() + udt_member.offset = field_offset * 8 + idx = tinfo.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) + if udt_member.offset != field_offset * 8: + print "[Info] Can't handle with arrays yet" + elif udt_member.type.get_size() != new_type.get_size(): + print "[Info] Can't recast different sizes yet" + else: + udt_data = idaapi.udt_type_data_t() + tinfo.get_udt_details(udt_data) + udt_data[idx].type = new_type + tinfo.create_udt(udt_data, idaapi.BTF_STRUCT) + tinfo.set_numbered_type(idaapi.cvar.idati, ordinal, idaapi.NTF_REPLACE, structure_name) + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class RecastItemRight(RecastItemLeft): + + name = "my:RecastItemRight" + description = "Recast Item" + hotkey = "Shift+R" + + def __init__(self): + RecastItemLeft.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return + + expression = ctree_item.it + + result = RecastItemRight._check_potential_array(cfunc, expression) + if result: + return result + + # Look through parents until we found Cast + while expression and expression.op != idaapi.cot_cast: + expression = expression.to_specific_type + expression = cfunc.body.find_parent_of(expression) + if not expression: + return + + expression = expression.to_specific_type + + # Find `(TYPE) something;` or `(TYPE *) &something;` and calculate appropriate type for recast + if expression.x.op == idaapi.cot_ref: + new_type = expression.type.get_pointed_object() + expression = expression.x + else: + new_type = expression.type + + if expression.x.op == idaapi.cot_var: + # (TYPE) var; + variable = cfunc.get_lvars()[expression.x.v.idx] + idaapi.update_action_label(RecastItemRight.name, 'Recast Variable "{0}"'.format(variable.name)) + return RECAST_LOCAL_VARIABLE, new_type, variable + + elif expression.x.op == idaapi.cot_obj: + # (TYPE) g_var; + if Helper.is_code_ea(expression.x.obj_ea) and new_type.is_funcptr(): + # (TYPE) sub_XXXXXX; + new_type = new_type.get_pointed_object() + + idaapi.update_action_label(RecastItemRight.name, 'Recast Global') + return RECAST_GLOBAL_VARIABLE, new_type, expression.x.obj_ea + + elif expression.x.op == idaapi.cot_call: + # (TYPE) call(); + idaapi.update_action_label(RecastItemRight.name, "Recast Return") + return RECAST_RETURN, new_type, expression.x.x.obj_ea + + elif expression.x.op == idaapi.cot_memptr: + # (TYPE) var->member; + idaapi.update_action_label(RecastItemRight.name, "Recast Field") + return RECAST_STRUCTURE, expression.x.x.type.get_pointed_object().dstr(), expression.x.m, new_type + + @staticmethod + def _check_potential_array(cfunc, expr): + """ Checks `call(..., &buffer, ..., number)` and returns information for recasting """ + if expr.op != idaapi.cot_var: + return + + var_expr = expr.to_specific_type + parent = cfunc.body.find_parent_of(expr) + if parent.op != idaapi.cot_ref: + return + + parent = cfunc.body.find_parent_of(parent) + if parent.op != idaapi.cot_call: + return + + call_expr = parent.to_specific_type + for arg_expr in call_expr.a: + if arg_expr.op == idaapi.cot_num: + number = arg_expr.numval() + if number: + variable = cfunc.lvars[var_expr.v.idx] + char_array_tinfo = idaapi.tinfo_t() + char_array_tinfo.create_array(idaapi.tinfo_t(idaapi.BTF_CHAR), number) + idaapi.update_action_label(RecastItemRight.name, 'Recast Variable "{}" to "{}"'.format( + variable.name, char_array_tinfo.dstr() + )) + return RECAST_LOCAL_VARIABLE, char_array_tinfo, variable + + +class RenameOther(idaapi.action_handler_t): + name = "my:RenameOther" + description = "Take other name" + hotkey = "Ctrl+N" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return + + expression = ctree_item.it.to_specific_type + if expression.op != idaapi.cot_var: + return + + parent = cfunc.body.find_parent_of(expression).to_specific_type + if parent.op != idaapi.cot_asg: + return + + other = parent.theother(expression) + if other.op != idaapi.cot_var: + return + + this_lvar = ctree_item.get_lvar() + other_lvar = cfunc.get_lvars()[other.v.idx] + if (other_lvar.has_user_name or other_lvar.is_arg_var and re.search("a\d*$", other_lvar.name) is None) \ + and this_lvar.name.lstrip('_') != other_lvar.name.lstrip('_'): + return '_' + other_lvar.name, this_lvar + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + result = self.check(hx_view.cfunc, hx_view.item) + + if result: + name, lvar = result + hx_view.rename_lvar(lvar, name, True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class RenameInside(idaapi.action_handler_t): + name = "my:RenameInto" + description = "Rename inside argument" + hotkey = "Shift+N" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return False + + expression = ctree_item.it.to_specific_type + if expression.op == idaapi.cot_var: + lvar = ctree_item.get_lvar() + # Check if it's either variable with user name or argument with not standard `aX` name + if lvar.has_user_name or lvar.is_arg_var and re.search("a\d*$", lvar.name) is None: + parent = cfunc.body.find_parent_of(expression).to_specific_type + if parent.op == idaapi.cot_call: + arg_index, _ = Helper.get_func_argument_info(parent, expression) + func_tinfo = parent.x.type.get_pointed_object() + func_data = idaapi.func_type_data_t() + func_tinfo.get_func_details(func_data) + if arg_index < func_tinfo.get_nargs() and lvar.name.lstrip('_') != func_data[arg_index].name: + return func_tinfo, parent.x.obj_ea, arg_index, lvar.name.lstrip('_') + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + result = self.check(hx_view.cfunc, hx_view.item) + + if result: + func_tinfo, address, arg_index, name = result + + func_data = idaapi.func_type_data_t() + func_tinfo.get_func_details(func_data) + func_data[arg_index].name = name + new_func_tinfo = idaapi.tinfo_t() + new_func_tinfo.create_func(func_data) + idaapi.apply_tinfo2(address, new_func_tinfo, idaapi.TINFO_DEFINITE) + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class RenameOutside(idaapi.action_handler_t): + name = "my:RenameOutside" + description = "Take argument name" + hotkey = "Ctrl+Shift+N" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return False + + expression = ctree_item.it.to_specific_type + if expression.op == idaapi.cot_var: + lvar = ctree_item.get_lvar() + parent = cfunc.body.find_parent_of(expression).to_specific_type + + if parent.op == idaapi.cot_call: + arg_index, _ = Helper.get_func_argument_info(parent, expression) + func_tinfo = parent.x.type.get_pointed_object() + if func_tinfo.get_nargs() < arg_index: + return + func_data = idaapi.func_type_data_t() + func_tinfo.get_func_details(func_data) + name = func_data[arg_index].name + if name and re.search("a\d*$", name) is None and name != 'this' and name != lvar.name: + return name, lvar + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + result = self.check(hx_view.cfunc, hx_view.item) + + if result: + name, lvar = result + hx_view.rename_lvar(lvar, name, True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class RenameUsingAssertVisitor(idaapi.ctree_parentee_t): + + def __init__(self, cfunc, func_addr, arg_idx): + idaapi.ctree_parentee_t.__init__(self) + self.__cfunc = cfunc + self.__func_addr = func_addr + self.__arg_idx = arg_idx + self.__possible_names = set() + + def visit_expr(self, expr): + if expr.op == idaapi.cot_call and expr.x.op == idaapi.cot_obj and expr.x.obj_ea == self.__func_addr: + arg_expr = expr.a[self.__arg_idx] + if arg_expr.op != idaapi.cot_obj: + logger.error("Argument is not string at {}".format(Helper.to_hex(self._find_asm_address(expr)))) + return 1 + self.__add_func_name(arg_expr) + return 0 + + def process(self): + self.apply_to(self.__cfunc.body, None) + if len(self.__possible_names) == 1: + self.__rename_func() + else: + logger.error("Function at {} has more than one candidate for renaming: {}".format( + Helper.to_hex(self.__cfunc.entry_ea), ", ".join(self.__possible_names))) + + def __add_func_name(self, arg_expr): + new_name = idc.get_strlit_contents(arg_expr.obj_ea) + if not idaapi.is_valid_typename(new_name): + logger.warn("Argument has weird name `{}` at {}".format( + new_name, Helper.to_hex(self._find_asm_address(arg_expr)))) + return + + self.__possible_names.add(new_name) + + def __rename_func(self): + idc.set_name(self.__cfunc.entry_ea, self.__possible_names.pop()) + + +class RenameUsingAssert(idaapi.action_handler_t): + + name = "my:RenameUsingAssert" + description = "Rename as assert argument" + hotkey = None + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return False + + expression = ctree_item.it.to_specific_type + if expression.op != idaapi.cot_obj: + return False + + parent = cfunc.body.find_parent_of(expression).to_specific_type + if parent.op != idaapi.cot_call or parent.x.op != idaapi.cot_obj: + return False + + obj_ea = expression.obj_ea + if not Helper.is_code_ea(obj_ea) and idc.get_str_type(obj_ea) == idc.STRTYPE_C: + str_potential_name = idc.get_strlit_contents(obj_ea) + return idaapi.is_valid_typename(str_potential_name) + return False + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + cfunc = hx_view.cfunc + ctree_item = hx_view.item + if not self.check(cfunc, ctree_item): + return + + expr_arg = ctree_item.it.to_specific_type + expr_call = cfunc.body.find_parent_of(expr_arg).to_specific_type + + arg_idx, _ = Helper.get_func_argument_info(expr_call, expr_arg) + + assert_ea = expr_call.x.obj_ea + all_callers = Helper.get_funcs_calling_address(assert_ea) + + for caller_ea in all_callers: + try: + cfunc = idaapi.decompile(caller_ea) + if not cfunc: + raise idaapi.DecompilationFailure + + RenameUsingAssertVisitor(cfunc, assert_ea, arg_idx).process() + + except idaapi.DecompilationFailure: + logger.warn("IDA failed to decompile at {}".format(Helper.to_hex(caller_ea))) + + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class PropagateName(idaapi.action_handler_t): + name = "my:PropagateName" + description = "Propagate name" + hotkey = "P" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def callback_start(self): + hx_view, _ = self._data + hx_view.switch_to(self._cfunc, False) + + @staticmethod + def callback_manipulate(self, cexpr, obj): + if self.crippled: + logger.debug("Skipping crippled function at {}".format(Helper.to_hex(self._cfunc.entry_ea))) + return + + if obj.id == Api.SO_GLOBAL_OBJECT: + old_name = idaapi.get_short_name(cexpr.obj_ea) + if Settings.PROPAGATE_THROUGH_ALL_NAMES or PropagateName._is_default_name(old_name): + _, name = self._data + new_name = PropagateName.rename(lambda x: idaapi.set_name(cexpr.obj_ea, x), name) + logger.debug("Renamed global variable from {} to {}".format(old_name, new_name)) + elif obj.id == Api.SO_LOCAL_VARIABLE: + lvar = self._cfunc.get_lvars()[cexpr.v.idx] + old_name = lvar.name + if Settings.PROPAGATE_THROUGH_ALL_NAMES or PropagateName._is_default_name(old_name): + hx_view, name = self._data + new_name = PropagateName.rename(lambda x: hx_view.rename_lvar(lvar, x, True), name) + logger.debug("Renamed local variable from {} to {}".format(old_name, new_name)) + elif obj.id in (Api.SO_STRUCT_POINTER, Api.SO_STRUCT_REFERENCE): + struct_tinfo = cexpr.x.type + offset = cexpr.m + struct_tinfo.remove_ptr_or_array() + old_name = Helper.get_member_name(struct_tinfo, offset) + if Settings.PROPAGATE_THROUGH_ALL_NAMES or PropagateName._is_default_name(old_name): + _, name = self._data + new_name = PropagateName.rename(lambda x: Helper.change_member_name(struct_tinfo.dstr(), offset, x), name) + logger.debug("Renamed struct member from {} to {}".format(old_name, new_name)) + + @staticmethod + def rename(rename_func, name): + while not rename_func(name): + name = "_" + name + return name + + @staticmethod + def _is_default_name(string): + return re.match(r"[av]\d+$", string) is not None or \ + re.match(r"this|[qd]?word|field_|off_", string) is not None + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return + + obj = Api.ScanObject.create(cfunc, ctree_item) + if obj and not PropagateName._is_default_name(obj.name): + return obj + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + obj = self.check(hx_view.cfunc, hx_view.item) + if obj: + cfunc = hx_view.cfunc + visitor = Api.RecursiveObjectDownwardsVisitor(cfunc, obj, (hx_view, obj.name), True) + visitor.set_callbacks( + manipulate=PropagateName.callback_manipulate, + start_iteration=PropagateName.callback_start, + finish=lambda x: hx_view.switch_to(cfunc, True) + ) + visitor.process() + hx_view.refresh_view(True) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class GuessAllocation(idaapi.action_handler_t): + name = "my:ActionApi" + description = "Guess allocation" + hotkey = None + + class StructAllocChoose(Forms.MyChoose): + def __init__(self, items): + Forms.MyChoose.__init__( + self, items, "Possible structure allocations", + [["Function", 30], ["Variable", 10], ["Line", 50], ["Type", 10]] + ) + + def OnSelectLine(self, n): + idaapi.jumpto(self.items[n][0]) + + def OnGetLine(self, n): + func_ea, var, line, alloc_type = self.items[n] + return [Helper.to_nice_str(func_ea), var, line, alloc_type] + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return + return Api.ScanObject.create(cfunc, ctree_item) + + @staticmethod + def callback_manipulate(self, cexpr, obj): + if obj.id == Api.SO_LOCAL_VARIABLE: + parent = self.parent_expr() + if parent.op == idaapi.cot_asg: + alloc_obj = Api.MemoryAllocationObject.create(self._cfunc, self.parent_expr().y) + if alloc_obj: + self._data.append([alloc_obj.ea, obj.name, self._get_line(), "HEAP"]) + elif self.parent_expr().op == idaapi.cot_ref: + self._data.append([self._find_asm_address(cexpr), obj.name, self._get_line(), "STACK"]) + elif obj.id == Api.SO_GLOBAL_OBJECT: + self._data.append([self._find_asm_address(cexpr), obj.name, self._get_line(), "GLOBAL"]) + + @staticmethod + def callback_finish(self): + chooser = GuessAllocation.StructAllocChoose(self._data) + chooser.Show(False) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + item = hx_view.item + obj = GuessAllocation.check(hx_view.cfunc, item) + if obj: + visitor = Api.RecursiveObjectUpwardsVisitor(hx_view.cfunc, obj, data=[], skip_after_object=True) + visitor.set_callbacks( + manipulate=self.callback_manipulate, + finish=self.callback_finish + ) + visitor.process() + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class SwapThenElse(idaapi.action_handler_t): + name = "my:SwapIfElse" + description = "Swap then/else" + hotkey = "Shift+S" + + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @staticmethod + def check(cfunc, ctree_item): + if ctree_item.citype != idaapi.VDI_EXPR: + return False + + insn = ctree_item.it.to_specific_type + + if insn.op != idaapi.cit_if or insn.cif.ielse is None: + return False + + return insn.op == idaapi.cit_if and insn.cif.ielse + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + if self.check(hx_view.cfunc, hx_view.item): + insn = hx_view.item.it.to_specific_type + inverse_if(insn.cif) + hx_view.refresh_ctext() + + InversionInfo(hx_view.cfunc.entry_ea).switch_inverted(insn.ea) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM + + +class FindFieldXrefs(idaapi.action_handler_t): + name = "my:FindFieldXrefs" + description = "Field Xrefs" + hotkey = "Ctrl+X" + + @staticmethod + def check(ctree_item): + return ctree_item.citype == idaapi.VDI_EXPR and \ + ctree_item.it.to_specific_type.op in (idaapi.cot_memptr, idaapi.cot_memref) + + def activate(self, ctx): + hx_view = idaapi.get_widget_vdui(ctx.widget) + item = hx_view.item + + if not self.check(item): + return + + data = [] + offset = item.e.m + struct_type = idaapi.remove_pointer(item.e.x.type) + ordinal = struct_type.get_ordinal() + result = XrefStorage().get_structure_info(ordinal, offset) + for xref_info in result: + data.append([ + idaapi.get_short_name(xref_info.func_ea) + "+" + hex(int(xref_info.offset)), + xref_info.type, + xref_info.line + ]) + + field_name = Helper.get_member_name(struct_type, offset) + chooser = Forms.MyChoose( + data, + "Cross-references to {0}::{1}".format(struct_type.dstr(), field_name), + [["Function", 20 | idaapi.Choose2.CHCOL_PLAIN], + ["Type", 2 | idaapi.Choose2.CHCOL_PLAIN], + ["Line", 40 | idaapi.Choose2.CHCOL_PLAIN]] + ) + idx = chooser.Show(True) + if idx == -1: + return + + xref = result[idx] + idaapi.open_pseudocode(xref.func_ea + xref.offset, False) + + def update(self, ctx): + if ctx.widget_type == idaapi.BWN_PSEUDOCODE: + return idaapi.AST_ENABLE_FOR_FORM + return idaapi.AST_DISABLE_FOR_FORM diff --git a/plugins/HexRaysPyTools/Api.py b/plugins/HexRaysPyTools/Api.py new file mode 100644 index 0000000..0e1bbe7 --- /dev/null +++ b/plugins/HexRaysPyTools/Api.py @@ -0,0 +1,583 @@ +import logging +import idaapi +import idc +from Core.Helper import to_hex, get_member_name, get_func_argument_info, get_funcs_calling_address, is_imported_ea + +logger = logging.getLogger(__name__) + + +SETTING_START_FROM_CURRENT_EXPR = True + + +def decompile_function(address): + try: + cfunc = idaapi.decompile(address) + if cfunc: + return cfunc + except idaapi.DecompilationFailure: + pass + logger.warn("IDA failed to decompile function at 0x{address:08X}".format(address=address)) + + +class ScanObject(object): + def __init__(self): + self.ea = idaapi.BADADDR + self.name = None + self.tinfo = None + self.id = 0 + + @staticmethod + def create(cfunc, arg): + # Creates object suitable for scaning either from cexpr_t or ctree_item_t + if isinstance(arg, idaapi.ctree_item_t): + lvar = arg.get_lvar() + if lvar: + index = list(cfunc.get_lvars()).index(lvar) + result = VariableObject(lvar, index) + if arg.e: + result.ea = ScanObject.get_expression_address(cfunc, arg.e) + return result + cexpr = arg.e + else: + cexpr = arg + + if cexpr.op == idaapi.cot_var: + lvar = cfunc.get_lvars()[cexpr.v.idx] + result = VariableObject(lvar, cexpr.v.idx) + result.ea = ScanObject.get_expression_address(cfunc, cexpr) + return result + elif cexpr.op == idaapi.cot_memptr: + t = cexpr.x.type.get_pointed_object() + result = StructPtrObject(t.dstr(), cexpr.m) + result.name = get_member_name(t, cexpr.m) + elif cexpr.op == idaapi.cot_memref: + t = cexpr.x.type + result = StructRefObject(t.dstr(), cexpr.m) + result.name = get_member_name(t, cexpr.m) + elif cexpr.op == idaapi.cot_obj: + result = GlobalVariableObject(cexpr.obj_ea) + result.name = idaapi.get_short_name(cexpr.obj_ea) + else: + return + result.tinfo = cexpr.type + result.ea = ScanObject.get_expression_address(cfunc, cexpr) + return result + + @staticmethod + def get_expression_address(cfunc, cexpr): + expr = cexpr + + while expr and expr.ea == idaapi.BADADDR: + expr = expr.to_specific_type + expr = cfunc.body.find_parent_of(expr) + + assert expr is not None + return expr.ea + + def __hash__(self): + return hash((self.id, self.name)) + + def __eq__(self, other): + return self.id == other.id and self.name == other.name + + def __repr__(self): + return self.name + + +SO_LOCAL_VARIABLE = 1 # cexpr.op == idaapi.cot_var +SO_STRUCT_POINTER = 2 # cexpr.op == idaapi.cot_memptr +SO_STRUCT_REFERENCE = 3 # cexpr.op == idaapi.cot_memref +SO_GLOBAL_OBJECT = 4 # cexpr.op == idaapi.cot_obj +SO_CALL_ARGUMENT = 5 # cexpr.op == idaapi.cot_call +SO_MEMORY_ALLOCATOR = 6 +SO_RETURNED_OBJECT = 7 + + +class VariableObject(ScanObject): + # Represents `var` expression + def __init__(self, lvar, index): + super(VariableObject, self).__init__() + self.lvar = lvar + self.tinfo = lvar.type() + self.name = lvar.name + self.index = index + self.id = SO_LOCAL_VARIABLE + + def is_target(self, cexpr): + return cexpr.op == idaapi.cot_var and cexpr.v.idx == self.index + + +class StructPtrObject(ScanObject): + # Represents `x->m` expression + def __init__(self, struct_name, offset): + super(StructPtrObject, self).__init__() + self.struct_name = struct_name + self.offset = offset + self.id = SO_STRUCT_POINTER + + def is_target(self, cexpr): + return cexpr.op == idaapi.cot_memptr and cexpr.m == self.offset and \ + cexpr.x.type.get_pointed_object().dstr() == self.struct_name + + +class StructRefObject(ScanObject): + # Represents `x.m` expression + def __init__(self, struct_name, offset): + super(StructRefObject, self).__init__() + self.__struct_name = struct_name + self.__offset = offset + self.id = SO_STRUCT_REFERENCE + + def is_target(self, cexpr): + return cexpr.op == idaapi.cot_memref and cexpr.m == self.__offset and cexpr.x.type.dstr() == self.__struct_name + + +class GlobalVariableObject(ScanObject): + # Represents global object + def __init__(self, object_address): + super(GlobalVariableObject, self).__init__() + self.obj_ea = object_address + self.id = SO_GLOBAL_OBJECT + + def is_target(self, cexpr): + return cexpr.op == idaapi.cot_obj and self.obj_ea == cexpr.obj_ea + + +class CallArgObject(ScanObject): + # Represents call of function and specified argument + def __init__(self, func_address, arg_idx): + super(CallArgObject, self).__init__() + self.__func_ea = func_address + self.__arg_idx = arg_idx + self.id = SO_CALL_ARGUMENT + + def is_target(self, cexpr): + return cexpr.op == idaapi.cot_call and cexpr.x.obj_ea == self.__func_ea + + def create_scan_obj(self, cfunc, cexpr): + e = cexpr.a[self.__arg_idx] + while e.op in (idaapi.cot_cast, idaapi.cot_ref, idaapi.cot_add, idaapi.cot_sub, idaapi.cot_idx): + e = e.x + return ScanObject.create(cfunc, e) + + @staticmethod + def create(cfunc, arg_idx): + result = CallArgObject(cfunc.entry_ea, arg_idx) + result.name = cfunc.get_lvars()[arg_idx].name + result.tinfo = cfunc.type + return result + + def __repr__(self): + return "{}" + + +class ReturnedObject(ScanObject): + # Represents value returned by function + def __init__(self, func_address): + super(ReturnedObject, self).__init__() + self.__func_ea = func_address + self.id = SO_RETURNED_OBJECT + + def is_target(self, cexpr): + return cexpr.op == idaapi.cot_call and cexpr.x.obj_ea == self.__func_ea + + +class MemoryAllocationObject(ScanObject): + # Represents `operator new()' or `malloc' + def __init__(self, name, size): + super(MemoryAllocationObject, self).__init__() + self.name = name + self.size = size + self.id = SO_MEMORY_ALLOCATOR + + @staticmethod + def create(cfunc, cexpr): + if cexpr.op == idaapi.cot_call: + e = cexpr + elif cexpr.op == idaapi.cot_cast and cexpr.x.op == idaapi.cot_call: + e = cexpr.x + else: + return + + func_name = idaapi.get_short_name(e.x.obj_ea) + if "malloc" in func_name or "operator new" in func_name: + carg = e.a[0] + if carg.op == idaapi.cot_num: + size = carg.numval() + else: + size = 0 + result = MemoryAllocationObject(func_name, size) + result.ea = ScanObject.get_expression_address(cfunc, e) + return result + + +ASSIGNMENT_RIGHT = 1 +ASSIGNMENT_LEFT = 2 + + +class ObjectVisitor(idaapi.ctree_parentee_t): + def __init__(self, cfunc, obj, data, skip_until_object): + super(ObjectVisitor, self).__init__() + self._cfunc = cfunc + self._objects = [obj] + self._init_obj = obj + self._data = data + self._start_ea = obj.ea + self._skip = skip_until_object if self._start_ea != idaapi.BADADDR else False + self.crippled = False + + def process(self): + self.apply_to(self._cfunc.body, None) + + def set_callbacks(self, manipulate=None): + if manipulate: + self.__manipulate = manipulate.__get__(self, ObjectDownwardsVisitor) + + def _get_line(self): + for p in reversed(self.parents): + if not p.is_expr(): + return idaapi.tag_remove(p.print1(self._cfunc.__ref__())) + AssertionError("Parent instruction is not found") + + def _manipulate(self, cexpr, obj): + """ + Method called for every object having assignment relationship with starter object. This method should be + reimplemented in order to do something useful + + :param cexpr: idaapi_cexpr_t + :param id: one of the SO_* constants + :return: None + """ + self.__manipulate(cexpr, obj) + + def __manipulate(self, cexpr, obj): + logger.debug("Expression {} at {} Id - {}".format(cexpr.opname, to_hex(self._find_asm_address(cexpr)), obj.id)) + + +class ObjectDownwardsVisitor(ObjectVisitor): + def __init__(self, cfunc, obj, data=None, skip_until_object=False): + super(ObjectDownwardsVisitor, self).__init__(cfunc, obj, data, skip_until_object) + self.cv_flags |= idaapi.CV_POST + + def visit_expr(self, cexpr): + if self._skip: + if self._is_initial_object(cexpr): + self._skip = False + else: + return 0 + + if cexpr.op != idaapi.cot_asg: + return 0 + + x_cexpr = cexpr.x + if cexpr.y.op == idaapi.cot_cast: + y_cexpr = cexpr.y.x + else: + y_cexpr = cexpr.y + + for obj in self._objects: + if obj.is_target(x_cexpr): + if self.__is_object_overwritten(x_cexpr, obj, y_cexpr): + logger.info("Removed object {} from scanning at {}".format( + obj, to_hex(self._find_asm_address(x_cexpr)))) + self._objects.remove(obj) + return 0 + elif obj.is_target(y_cexpr): + new_obj = ScanObject.create(self._cfunc, x_cexpr) + if new_obj: + self._objects.append(new_obj) + return 0 + return 0 + + def leave_expr(self, cexpr): + if self._skip: + return 0 + + for obj in self._objects: + if obj.is_target(cexpr) and obj.id != SO_RETURNED_OBJECT: + self._manipulate(cexpr, obj) + return 0 + return 0 + + def _is_initial_object(self, cexpr): + if cexpr.op == idaapi.cot_asg: + cexpr = cexpr.y + if cexpr.op == idaapi.cot_cast: + cexpr = cexpr.x + return self._init_obj.is_target(cexpr) and self._find_asm_address(cexpr) == self._start_ea + + def __is_object_overwritten(self, x_cexpr, obj, y_cexpr): + if len(self._objects) < 2: + return False + + if y_cexpr.op == idaapi.cot_cast: + e = y_cexpr.x + else: + e = y_cexpr + + if e.op != idaapi.cot_call or len(e.a) == 0: + return True + + for obj in self._objects: + if obj.is_target(e. a[0]): + return False + return True + + +class ObjectUpwardsVisitor(ObjectVisitor): + STAGE_PREPARE = 1 + STAGE_PARSING = 2 + + def __init__(self, cfunc, obj, data=None, skip_after_object=False): + super(ObjectUpwardsVisitor, self).__init__(cfunc, obj, data, skip_after_object) + self._stage = self.STAGE_PREPARE + self._tree = {} + self._call_obj = obj if obj.id == SO_CALL_ARGUMENT else None + + def visit_expr(self, cexpr): + if self._stage == self.STAGE_PARSING: + return 0 + + if self._call_obj and self._call_obj.is_target(cexpr): + obj = self._call_obj.create_scan_obj(self._cfunc, cexpr) + if obj: + self._objects.append(obj) + return 0 + + if cexpr.op != idaapi.cot_asg: + return 0 + + x_cexpr = cexpr.x + if cexpr.y.op == idaapi.cot_cast: + y_cexpr = cexpr.y.x + else: + y_cexpr = cexpr.y + + obj_left = ScanObject.create(self._cfunc, x_cexpr) + obj_right = ScanObject.create(self._cfunc, y_cexpr) + if obj_left and obj_right: + self.__add_object_assignment(obj_left, obj_right) + + if self._skip and self._is_initial_object(cexpr): + return 1 + return 0 + + def leave_expr(self, cexpr): + if self._stage == self.STAGE_PREPARE: + return 0 + + if self._skip and self._is_initial_object(cexpr): + self._manipulate(cexpr, self._init_obj) + return 1 + + for obj in self._objects: + if obj.is_target(cexpr): + self._manipulate(cexpr, obj) + return 0 + return 0 + + def process(self): + self._stage = self.STAGE_PREPARE + self.cv_flags &= ~idaapi.CV_POST + super(ObjectUpwardsVisitor, self).process() + self._stage = self.STAGE_PARSING + self.cv_flags |= idaapi.CV_POST + self.__prepare() + super(ObjectUpwardsVisitor, self).process() + + def _is_initial_object(self, cexpr): + return self._init_obj.is_target(cexpr) and self._find_asm_address(cexpr) == self._start_ea + + def __add_object_assignment(self, from_obj, to_obj): + if from_obj in self._tree: + self._tree[from_obj].add(to_obj) + else: + self._tree[from_obj] = {to_obj} + + def __prepare(self): + result = set() + todo = set(self._objects) + while todo: + obj = todo.pop() + result.add(obj) + if obj.id == SO_CALL_ARGUMENT or obj not in self._tree: + continue + o = self._tree[obj] + todo |= o - result + result |= o + self._objects = list(result) + self._tree.clear() + + +class RecursiveObjectVisitor(ObjectVisitor): + def __init__(self, cfunc, obj, data=None, skip_until_object=False, visited=None): + super(RecursiveObjectVisitor, self).__init__(cfunc, obj, data, skip_until_object) + self._visited = visited if visited else set() + self._new_for_visit = set() + self.crippled = False + self._arg_idx = -1 + self._debug_scan_tree = {} + self.__debug_scan_tree_root = idc.Name(self._cfunc.entry_ea) + self.__debug_message = [] + + def visit_expr(self, cexpr): + return super(RecursiveObjectVisitor, self).visit_expr(cexpr) + + def set_callbacks(self, manipulate=None, start=None, start_iteration=None, finish=None, finish_iteration=None): + super(RecursiveObjectVisitor, self).set_callbacks(manipulate) + if start: + self._start = start.__get__(self, RecursiveObjectDownwardsVisitor) + if start_iteration: + self._start_iteration = start_iteration.__get__(self, RecursiveObjectDownwardsVisitor) + if finish: + self._finish = finish.__get__(self, RecursiveObjectDownwardsVisitor) + if finish_iteration: + self._finish_iteration = finish_iteration.__get__(self, RecursiveObjectDownwardsVisitor) + + def prepare_new_scan(self, cfunc, arg_idx, obj, skip=False): + self._cfunc = cfunc + self._arg_idx = arg_idx + self._objects = [obj] + self._init_obj = obj + self._skip = False + self.crippled = self.__is_func_crippled() + + def process(self): + self._start() + self._recursive_process() + self._finish() + self.dump_scan_tree() + + def dump_scan_tree(self): + self.__prepare_debug_message() + logger.info("{}\n---------------".format("\n".join(self.__debug_message))) + + def __prepare_debug_message(self, key=None, level=1): + if key is None: + key = (self.__debug_scan_tree_root, -1) + self.__debug_message.append("--- Scan Tree---\n{}".format(self.__debug_scan_tree_root)) + if key in self._debug_scan_tree: + for func_name, arg_idx in self._debug_scan_tree[key]: + prefix = " | " * (level - 1) + " |_ " + self.__debug_message.append("{}{} (idx: {})".format(prefix, func_name, arg_idx)) + self.__prepare_debug_message((func_name, arg_idx), level + 1) + + def _recursive_process(self): + self._start_iteration() + super(RecursiveObjectVisitor, self).process() + self._finish_iteration() + + def _manipulate(self, cexpr, obj): + self._check_call(cexpr) + super(RecursiveObjectVisitor, self)._manipulate(cexpr, obj) + + def _check_call(self, cexpr): + raise NotImplemented + + def _add_visit(self, func_ea, arg_idx): + if (func_ea, arg_idx) not in self._visited: + self._visited.add((func_ea, arg_idx)) + self._new_for_visit.add((func_ea, arg_idx)) + return True + return False + + def _add_scan_tree_info(self, func_ea, arg_idx): + head_node = (idc.Name(self._cfunc.entry_ea), self._arg_idx) + tail_node = (idc.Name(func_ea), arg_idx) + if head_node in self._debug_scan_tree: + self._debug_scan_tree[head_node].add(tail_node) + else: + self._debug_scan_tree[head_node] = {tail_node} + + def _start(self): + """ Called at the beginning of visiting """ + pass + + def _start_iteration(self): + """ Called every time new function visiting started """ + pass + + def _finish(self): + """ Called after all visiting happened """ + pass + + def _finish_iteration(self): + """ Called every time new function visiting finished """ + pass + + def __is_func_crippled(self): + # Check if function is just call to another function + b = self._cfunc.body.cblock + if b.size() == 1: + e = b.at(0) + return e.op == idaapi.cit_return or (e.op == idaapi.cit_expr and e.cexpr.op == idaapi.cot_call) + return False + + +class RecursiveObjectDownwardsVisitor(RecursiveObjectVisitor, ObjectDownwardsVisitor): + def __init__(self, cfunc, obj, data=None, skip_until_object=False, visited=None): + super(RecursiveObjectDownwardsVisitor, self).__init__(cfunc, obj, data, skip_until_object, visited) + + def _check_call(self, cexpr): + parent = self.parent_expr() + grandparent = self.parents.at(self.parents.size() - 2) + if parent.op == idaapi.cot_call: + call_cexpr = parent + arg_cexpr = cexpr + elif parent.op == idaapi.cot_cast and grandparent.op == idaapi.cot_call: + call_cexpr = grandparent.cexpr + arg_cexpr = parent + else: + return + idx, _ = get_func_argument_info(call_cexpr, arg_cexpr) + func_ea = call_cexpr.x.obj_ea + if func_ea == idaapi.BADADDR: + return + if self._add_visit(func_ea, idx): + self._add_scan_tree_info(func_ea, idx) + + def _recursive_process(self): + super(RecursiveObjectDownwardsVisitor, self)._recursive_process() + + while self._new_for_visit: + func_ea, arg_idx = self._new_for_visit.pop() + if is_imported_ea(func_ea): + continue + cfunc = decompile_function(func_ea) + if cfunc: + assert arg_idx < len(cfunc.get_lvars()), "Wrong argument at func {}".format(to_hex(func_ea)) + obj = VariableObject(cfunc.get_lvars()[arg_idx], arg_idx) + self.prepare_new_scan(cfunc, arg_idx, obj) + self._recursive_process() + + +class RecursiveObjectUpwardsVisitor(RecursiveObjectVisitor, ObjectUpwardsVisitor): + def __init__(self, cfunc, obj, data=None, skip_after_object=False, visited=None): + super(RecursiveObjectUpwardsVisitor, self).__init__(cfunc, obj, data, skip_after_object, visited) + + def prepare_new_scan(self, cfunc, arg_idx, obj, skip=False): + super(RecursiveObjectUpwardsVisitor, self).prepare_new_scan(cfunc, arg_idx, obj, skip) + self._call_obj = obj if obj.id == SO_CALL_ARGUMENT else None + + def _check_call(self, cexpr): + if cexpr.op == idaapi.cot_var and self._cfunc.get_lvars()[cexpr.v.idx].is_arg_var: + func_ea = self._cfunc.entry_ea + arg_idx = cexpr.v.idx + if self._add_visit(func_ea, arg_idx): + for callee_ea in get_funcs_calling_address(func_ea): + self._add_scan_tree_info(callee_ea, arg_idx) + + def _recursive_process(self): + super(RecursiveObjectUpwardsVisitor, self)._recursive_process() + + while self._new_for_visit: + new_visit = list(self._new_for_visit) + self._new_for_visit.clear() + for func_ea, arg_idx in new_visit: + funcs = get_funcs_calling_address(func_ea) + obj = CallArgObject.create(idaapi.decompile(func_ea), arg_idx) + for callee_ea in funcs: + cfunc = decompile_function(callee_ea) + if cfunc: + self.prepare_new_scan(cfunc, arg_idx, obj, False) + super(RecursiveObjectUpwardsVisitor, self)._recursive_process() diff --git a/plugins/HexRaysPyTools/Core/Cache.py b/plugins/HexRaysPyTools/Core/Cache.py new file mode 100644 index 0000000..479fbcf --- /dev/null +++ b/plugins/HexRaysPyTools/Core/Cache.py @@ -0,0 +1,62 @@ +# Information about explored functions +import re + +import idaapi +import idautils +import idc + + +imported_ea = set() +demangled_names = {} +touched_functions = set() +temporary_structure = None + + +def init_imported_ea(*args): + + def imp_cb(ea, name, ord): + imported_ea.add(ea) + # True -> Continue enumeration + # False -> Stop enumeration + return True + + print "[Info] Collecting information about imports" + imported_ea.clear() + nimps = idaapi.get_import_module_qty() + + for i in xrange(0, nimps): + name = idaapi.get_import_module_name(i) + if not name: + print "[Warning] Failed to get import module name for #%d" % i + continue + + # print "Walking-> %s" % name + idaapi.enum_import_names(i, imp_cb) + print "[Info] Done..." + + +def init_demangled_names(*args): + """ + Creates dictionary of demangled names => address, that will be used further at double click on methods got from + symbols. + """ + demangled_names.clear() + for address, name in idautils.Names(): + short_name = idc.Demangle(name, idc.GetLongPrm(idc.INF_SHORT_DN)) + if short_name: + demangled_names[short_name.split('(')[0]] = address - idaapi.get_imagebase() + + # Names can have templates and should be transformed before creating local type + name = re.sub(r'[<>]', '_t_', name) + + # Thunk functions with name like "[thunk]:CWarmupHostProvider::Release`adjustor{8}'" + result = re.search(r"(\[thunk\]:)?([^`]*)(.*\{(\d+)}.*)?", short_name) + name, adjustor = result.group(2), result.group(4) + if adjustor: + demangled_names[name + "_adj_" + adjustor] = address - idaapi.get_imagebase() + + print "[DEBUG] Demangled names have been initialized" + + +def reset_touched_functions(*args): + touched_functions = set() diff --git a/plugins/HexRaysPyTools/Core/Classes.py b/plugins/HexRaysPyTools/Core/Classes.py new file mode 100644 index 0000000..b5a6a4e --- /dev/null +++ b/plugins/HexRaysPyTools/Core/Classes.py @@ -0,0 +1,536 @@ +import idc +import idaapi +import HexRaysPyTools.Forms +import Helper + +# from PySide import QtGui, QtCore +from HexRaysPyTools.Cute import * + +all_virtual_functions = {} # name -> VirtualMethod +all_virtual_tables = {} # ordinal -> VirtualTable + + +class VirtualMethod(object): + def __init__(self, tinfo, name, parent): + self.tinfo = tinfo + self.tinfo_modified = False + self.name = name + self.class_name = None + self.name_modified = False + self.parents = [parent] + self.base_address = Helper.get_virtual_func_address(name) + if self.base_address: + self.base_address -= idaapi.get_imagebase() + + self.rowcount = 0 + self.children = [] + + @staticmethod + def create(tinfo, name, parent): + result = all_virtual_functions.get(name) + if result: + result.parents.append(parent) + return result + result = VirtualMethod(tinfo, name, parent) + all_virtual_functions[name] = result + return result + + def update(self, name, tinfo): + self.name = name + self.tinfo = tinfo + self.name_modified = False + self.tinfo_modified = False + + self.base_address = idc.LocByName(self.name) + if self.base_address != idaapi.BADADDR: + self.base_address -= idaapi.get_imagebase() + + def data(self, column): + if column == 0: + return self.name + elif column == 1: + return self.tinfo.get_pointed_object().dstr() + elif column == 2: + return "0x{0:08X}".format(self.address) if self.address else None + + def setData(self, column, value): + if column == 0: + if idaapi.isident(value) and self.name != value: + self.name = value + self.name_modified = True + for parent in self.parents: + parent.modified = True + return True + elif column == 1: + tinfo = idaapi.tinfo_t() + split = value.split('(') + if len(split) == 2: + value = split[0] + ' ' + self.name + '(' + split[1] + ';' + if idaapi.parse_decl2(idaapi.cvar.idati, value, '', tinfo, idaapi.PT_TYP): + if tinfo.is_func(): + tinfo.create_ptr(tinfo) + if tinfo.dstr() != self.tinfo.dstr(): + self.tinfo = tinfo + self.tinfo_modified = True + for parent in self.parents: + parent.modified = True + return True + return False + + def font(self, column): + if column == 0 and self.name_modified: + return QtGui.QFont("Consolas", 10, italic=True) + elif column == 1 and self.tinfo_modified: + return QtGui.QFont("Consolas", 10, italic=True) + return QtGui.QFont("Consolas", 10, 0) + + def flags(self, column): + if column != 2: + if self.address: + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled + + + @property + def color(self): + # return QtGui.QBrush(QtGui.QColor("#ffffb3")) + return QtGui.QColor("#fefbd8") + + @property + def tooltip(self): + return None + + @property + def address(self): + return self.base_address + idaapi.get_imagebase() if self.base_address else None + + def set_first_argument_type(self, name): + func_data = idaapi.func_type_data_t() + func_tinfo = self.tinfo.get_pointed_object() + class_tinfo = idaapi.tinfo_t() + if func_tinfo.get_func_details(func_data) and func_tinfo.get_nargs() and \ + class_tinfo.get_named_type(idaapi.cvar.idati, name): + class_tinfo.create_ptr(class_tinfo) + first_arg_tinfo = func_data[0].type + if (first_arg_tinfo.is_ptr() and first_arg_tinfo.get_pointed_object().is_udt()) or \ + Helper.is_legal_type(func_data[0].type): + func_data[0].type = class_tinfo + func_data[0].name = "this" + func_tinfo.create_func(func_data) + func_tinfo.create_ptr(func_tinfo) + if func_tinfo.dstr() != self.tinfo.dstr(): + self.tinfo = func_tinfo + self.tinfo_modified = True + for parent in self.parents: + parent.modified = True + else: + print "[Warning] function {0} probably have wrong type".format(self.name) + + def open_function(self): + if self.address: + if idaapi.decompile(self.address): + idaapi.open_pseudocode(self.address, 0) + else: + idaapi.jumpto(self.address) + + def commit(self): + if self.name_modified: + self.name_modified = False + if self.address: + idaapi.set_name(self.address, self.name) + if self.tinfo_modified: + self.tinfo_modified = False + if self.address: + idaapi.apply_tinfo2(self.address, self.tinfo.get_pointed_object(), idaapi.TINFO_DEFINITE) + + def __eq__(self, other): + return self.address == other.address + + def __repr__(self): + return self.name + + +class VirtualTable(object): + def __init__(self, ordinal, tinfo, class_): + self.ordinal = ordinal + self.tinfo = tinfo + self.class_ = [class_] + self.class_name = None + self.virtual_functions = [] + self.name = self.tinfo.dstr() + self._modified = False + + def update(self): + if self.modified: + vtable_tinfo = idaapi.tinfo_t() + udt_data = idaapi.udt_type_data_t() + vtable_tinfo.get_numbered_type(idaapi.cvar.idati, self.ordinal) + vtable_tinfo.get_udt_details(udt_data) + self.tinfo = vtable_tinfo + self.name = vtable_tinfo.dstr() + self.modified = False + if len(self.virtual_functions) == len(udt_data): + for current_function, other_function in zip(self.virtual_functions, udt_data): + current_function.update(other_function.name, other_function.type) + else: + print "[ERROR] Something have been modified in Local types. Please refresh this view" + + def update_local_type(self): + if self.modified: + final_tinfo = idaapi.tinfo_t() + udt_data = idaapi.udt_type_data_t() + self.tinfo.get_udt_details(udt_data) + if len(udt_data) == len(self.virtual_functions): + for udt_member, virtual_function in zip(udt_data, self.virtual_functions): + udt_member.name = virtual_function.name + udt_member.type = virtual_function.tinfo + virtual_function.commit() + final_tinfo.create_udt(udt_data, idaapi.BTF_STRUCT) + final_tinfo.set_numbered_type(idaapi.cvar.idati, self.ordinal, idaapi.NTF_REPLACE, self.name) + self.modified = False + else: + print "[ERROR] Something have been modified in Local types. Please refresh this view" + + def set_first_argument_type(self, class_name): + for function in self.virtual_functions: + function.set_first_argument_type(class_name) + + @property + def modified(self): + return self._modified + + @modified.setter + def modified(self, value): + self._modified = value + if value: + for class_ in self.class_: + class_.modified = True + + @staticmethod + def create(tinfo, class_): + ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, tinfo.dstr()) + result = all_virtual_tables.get(ordinal) + if result: + result.class_.append(class_) + else: + udt_data = idaapi.udt_type_data_t() + tinfo.get_udt_details(udt_data) + result = VirtualTable(ordinal, tinfo, class_) + virtual_functions = [VirtualMethod.create(func.type, func.name, result) for func in udt_data] + result.virtual_functions = virtual_functions + all_virtual_functions[ordinal] = result + return result + + def get_class_tinfo(self): + if len(self.class_) == 1: + return self.class_.tinfo + + def setData(self, column, value): + if column == 0: + if idaapi.isident(value) and self.name != value: + self.name = value + self.modified = True + return True + return False + + def data(self, column): + if column == 0: + return self.name + + @property + def color(self): + return QtGui.QColor("#d5f4e6") + + @property + def tooltip(self): + pass + + def font(self, column): + if self.modified: + return QtGui.QFont("Consolas", 12, italic=True) + return QtGui.QFont("Consolas", 12) + + def flags(self, column): + if column == 0: + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled + + @property + def children(self): + return self.virtual_functions + + def __repr__(self): + return str(self.virtual_functions) + + +class Class(object): + def __init__(self, name, tinfo, ordinal): + self.name = name + self.class_name = name + self.ordinal = ordinal + self.parent = None + self.vtables = {} + self.modified = False + + @staticmethod + def create_class(ordinal): + tinfo = idaapi.tinfo_t() + tinfo.get_numbered_type(idaapi.cvar.idati, ordinal) + vtables = {} + if tinfo.is_struct(): + udt_data = idaapi.udt_type_data_t() + tinfo.get_udt_details(udt_data) + for field_udt in udt_data: + if field_udt.type.is_ptr(): + possible_vtable = field_udt.type.get_pointed_object() + if possible_vtable.is_struct(): + v_udt_data = idaapi.udt_type_data_t() + possible_vtable.get_udt_details(v_udt_data) + for possible_func_udt in v_udt_data: + if not possible_func_udt.type.is_funcptr(): + break + else: + vtables[field_udt.offset / 8] = possible_vtable + if vtables: + class_ = Class(tinfo.dstr(), tinfo, ordinal) + for offset, vtable_tinfo in vtables.iteritems(): + vtables[offset] = VirtualTable.create(vtable_tinfo, class_) + class_.vtables = vtables + return class_ + + def update_from_local_types(self): + try: + if self.modified: + class_ = self.create_class(self.ordinal) + if class_: + self.name = class_.name + self.modified = False + for offset, vtable in class_.vtables.iteritems(): + self.vtables[offset].update() + else: + # TODO: drop class + raise IndexError + except IndexError: + print "[ERROR] Something have been modified in Local types. Please refresh this view" + + def update_local_type(self): + if self.modified: + for vtable in self.vtables.values(): + vtable.update_local_type() + udt_data = idaapi.udt_type_data_t() + tinfo = idaapi.tinfo_t() + self.tinfo.get_udt_details(udt_data) + tinfo.create_udt(udt_data, idaapi.BTF_STRUCT) + tinfo.set_numbered_type(idaapi.cvar.idati, self.ordinal, idaapi.NTF_REPLACE, self.name) + self.modified = False + + def set_first_argument_type(self, class_name): + if 0 in self.vtables: + self.vtables[0].set_first_argument_type(class_name) + + def has_function(self, regexp): + for vtable in self.vtables.values(): + if filter(lambda func: regexp.indexIn(func.name) >= 0, vtable.virtual_functions): + return True + return False + + def data(self, column): + if column == 0: + return self.name + + def setData(self, column, value): + if column == 0: + if idaapi.isident(value) and self.name != value: + self.name = value + self.modified = True + return True + return False + + def font(self, column): + if self.modified: + return QtGui.QFont("Consolas", 12, QtGui.QFont.Bold, italic=True) + return QtGui.QFont("Consolas", 12, QtGui.QFont.Bold) + + @property + def color(self): + # return QtGui.QBrush(QtGui.QColor("#ffb3ff")): + return QtGui.QColor("#80ced6") + + @property + def tooltip(self): + return None + + def flags(self, column): + if column == 0: + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled + + @property + def children(self): + return self.vtables.values() + + @property + def tinfo(self): + tinfo = idaapi.tinfo_t() + tinfo.get_numbered_type(idaapi.cvar.idati, self.ordinal) + return tinfo + + def open_function(self): + pass + + def __repr__(self): + return self.name + " ^_^ " + str(self.vtables) + + +class TreeItem: + def __init__(self, item, row, parent): + self.item = item + self.parent = parent + self.row = row + self.children = [] + + def __repr__(self): + return str(self.item) + + +class TreeModel(QtCore.QAbstractItemModel): + # TODO: Add higlighting if eip in function, consider setting breakpoints + + refreshed = QtCore.Signal() + + def __init__(self): + super(TreeModel, self).__init__() + self.tree_data = [] + self.headers = ["Name", "Declaration", "Address"] + + self.init() + # import pydevd + # pydevd.settrace("localhost", port=12345, stdoutToServer=True, stderrToServer=True) + + def init(self): + idaapi.show_wait_box("Looking for classes...") + all_virtual_functions.clear() + all_virtual_tables.clear() + + classes = [] + for ordinal in xrange(1, idaapi.get_ordinal_qty(idaapi.cvar.idati)): + result = Class.create_class(ordinal) + if result: + classes.append(result) + + for class_row, class_ in enumerate(classes): + class_item = TreeItem(class_, class_row, None) + for vtable_row, vtable in class_.vtables.iteritems(): + vtable_item = TreeItem(vtable, vtable_row, class_item) + vtable_item.children = [TreeItem(function, 0, vtable_item) for function in vtable.virtual_functions] + class_item.children.append(vtable_item) + self.tree_data.append(class_item) + + idaapi.hide_wait_box() + + def flags(self, index): + if index.isValid(): + return index.internalPointer().item.flags(index.column()) + + def index(self, row, column, parent=QtCore.QModelIndex()): + if parent.isValid(): + node = parent.internalPointer() + return self.createIndex(row, column, node.children[row]) + else: + return self.createIndex(row, column, self.tree_data[row]) + + def parent(self, index): + if index.isValid(): + node = index.internalPointer() + if node.parent: + return self.createIndex(0, 0, node.parent) + return QtCore.QModelIndex() + + def rowCount(self, index=QtCore.QModelIndex()): + if index.isValid(): + node = index.internalPointer() + if node: + return len(node.children) + return len(self.tree_data) + + def columnCount(self, index=QtCore.QModelIndex()): + return 3 + + def data(self, index, role=QtCore.Qt.DisplayRole): + if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole: + node = index.internalPointer() + return node.item.data(index.column()) + elif role == QtCore.Qt.FontRole: + return index.internalPointer().item.font(index.column()) + elif role == QtCore.Qt.ToolTipRole: + return index.internalPointer().item.tooltip + elif role == QtCore.Qt.BackgroundRole: + return index.internalPointer().item.color + elif role == QtCore.Qt.ForegroundRole: + return QtGui.QBrush(QtGui.QColor("#191919")) + return None + + def setData(self, index, value, role=QtCore.Qt.DisplayRole): + result = False + if role == QtCore.Qt.EditRole and value != "": + node = index.internalPointer() + result = node.item.setData(index.column(), str(value)) + return result + + def headerData(self, section, orientation, role): + if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal: + return self.headers[section] + + def set_first_argument_type(self, indexes): + indexes = filter(lambda x: x.column() == 0, indexes) + class_name = indexes[0].internalPointer().item.class_name + if not class_name: + classes = [[x.item.name] for x in self.tree_data] + class_chooser = HexRaysPyTools.Forms.MyChoose(classes, "Select Class", [["Name", 25]]) + idx = class_chooser.Show(True) + if idx != -1: + class_name = classes[idx][0] + if class_name: + for index in indexes: + index.internalPointer().item.set_first_argument_type(class_name) + + def refresh(self): + self.tree_data = [] + self.modelReset.emit() + self.init() + self.refreshed.emit() + + def rollback(self): + for class_item in self.tree_data: + class_item.item.update_from_local_types() + self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex()) + + def commit(self): + for class_item in self.tree_data: + if class_item.item.modified: + class_item.item.update_local_type() + + def open_function(self, index): + if index.column() == 2: + index.internalPointer().item.open_function() + + +class ProxyModel(QtGui.QSortFilterProxyModel): + def __init__(self): + super(ProxyModel, self).__init__() + self.filter_by_function = False + + def set_regexp_filter(self, regexp): + if regexp and regexp[0] == '!': + self.filter_by_function = True + self.setFilterRegExp(regexp[1:]) + else: + self.filter_by_function = False + self.setFilterRegExp(regexp) + + def filterAcceptsRow(self, row, parent): + if not parent.isValid() and self.filterRegExp(): + if self.filter_by_function: + return self.sourceModel().tree_data[row].item.has_function(self.filterRegExp()) + return self.filterRegExp().indexIn(self.sourceModel().tree_data[row].item.class_name) >= 0 + return True diff --git a/plugins/HexRaysPyTools/Core/Const.py b/plugins/HexRaysPyTools/Core/Const.py new file mode 100644 index 0000000..f27f019 --- /dev/null +++ b/plugins/HexRaysPyTools/Core/Const.py @@ -0,0 +1,58 @@ +import idaapi +import idc + +EA64 = idc.__EA64__ +EA_SIZE = 8 if EA64 else 4 + +COT_ARITHMETIC = (idaapi.cot_num, idaapi.cot_fnum, idaapi.cot_add, idaapi.cot_fadd, idaapi.cot_sub, idaapi.cot_fsub, + idaapi.cot_mul, idaapi.cot_fmul, idaapi.cot_fdiv) + +VOID_TINFO = None +PVOID_TINFO = idaapi.tinfo_t() +CONST_VOID_TINFO = None +CONST_PVOID_TINFO = idaapi.tinfo_t() +CHAR_TINFO = None +PCHAR_TINFO = idaapi.tinfo_t() +CONST_PCHAR_TINFO = idaapi.tinfo_t() +BYTE_TINFO = None +PBYTE_TINFO = None + +WORD_TINFO = None +PWORD_TINFO = idaapi.tinfo_t() + +X_WORD_TINFO = None # DWORD for x32 and QWORD for x64 +PX_WORD_TINFO = None + +DUMMY_FUNC = None + +LEGAL_TYPES = [] + + +def init(): + """ All tinfo should be reinitialized between session. Otherwise they could have wrong type """ + global VOID_TINFO, PVOID_TINFO, CONST_PVOID_TINFO, BYTE_TINFO, PBYTE_TINFO, LEGAL_TYPES, X_WORD_TINFO, \ + PX_WORD_TINFO, DUMMY_FUNC, CONST_PCHAR_TINFO, CHAR_TINFO, PCHAR_TINFO, CONST_VOID_TINFO, \ + WORD_TINFO, PWORD_TINFO + + VOID_TINFO = idaapi.tinfo_t(idaapi.BT_VOID) + PVOID_TINFO.create_ptr(VOID_TINFO) + CONST_VOID_TINFO = idaapi.tinfo_t(idaapi.BT_VOID | idaapi.BTM_CONST) + CONST_PVOID_TINFO.create_ptr(idaapi.tinfo_t(idaapi.BT_VOID | idaapi.BTM_CONST)) + CONST_PCHAR_TINFO.create_ptr(idaapi.tinfo_t(idaapi.BTF_CHAR | idaapi.BTM_CONST)) + CHAR_TINFO = idaapi.tinfo_t(idaapi.BTF_CHAR) + PCHAR_TINFO.create_ptr(idaapi.tinfo_t(idaapi.BTF_CHAR)) + BYTE_TINFO = idaapi.tinfo_t(idaapi.BTF_BYTE) + PBYTE_TINFO = idaapi.dummy_ptrtype(1, False) + X_WORD_TINFO = idaapi.get_unk_type(EA_SIZE) + PX_WORD_TINFO = idaapi.dummy_ptrtype(EA_SIZE, False) + + WORD_TINFO = idaapi.tinfo_t(idaapi.BT_UNK_WORD) + PWORD_TINFO.create_ptr(idaapi.tinfo_t(idaapi.BT_UNK_WORD)) + + func_data = idaapi.func_type_data_t() + func_data.rettype = PVOID_TINFO + func_data.cc = idaapi.CM_CC_UNKNOWN + DUMMY_FUNC = idaapi.tinfo_t() + DUMMY_FUNC.create_func(func_data, idaapi.BT_FUNC) + + LEGAL_TYPES = [PVOID_TINFO, PX_WORD_TINFO, PWORD_TINFO, PBYTE_TINFO, X_WORD_TINFO] diff --git a/plugins/HexRaysPyTools/Core/Helper.py b/plugins/HexRaysPyTools/Core/Helper.py new file mode 100644 index 0000000..7cd7e7d --- /dev/null +++ b/plugins/HexRaysPyTools/Core/Helper.py @@ -0,0 +1,312 @@ +import collections +import logging + +import idaapi +import idc + +import HexRaysPyTools.Core.Cache as Cache +import HexRaysPyTools.Core.Const as Const +import HexRaysPyTools.Settings as Settings + +logger = logging.getLogger(__name__) + + +def is_imported_ea(ea): + if idc.get_segm_name(ea) == ".plt": + return True + return ea in Cache.imported_ea + + +def is_code_ea(ea): + flags = idaapi.getFlags(ea) # flags_t + return idaapi.isCode(flags) + + +def is_rw_ea(ea): + seg = idaapi.getseg(ea) + return seg.perm & idaapi.SEGPERM_WRITE and seg.perm & idaapi.SEGPERM_READ + + +def get_virtual_func_address(name, tinfo=None, offset=None): + """ + :param name: method name + :param tinfo: class tinfo + :param offset: virtual table offset + :return: address of the method + """ + + address = idc.LocByName(name) + + if address != idaapi.BADADDR: + return address + + address = Cache.demangled_names.get(name, idaapi.BADADDR) + if address != idaapi.BADADDR: + return address + idaapi.get_imagebase() + + if tinfo is None or offset is None: + return + + offset *= 8 + udt_member = idaapi.udt_member_t() + while tinfo.is_struct(): + address = Cache.demangled_names.get(tinfo.dstr() + '::' + name, idaapi.BADADDR) + if address != idaapi.BADADDR: + return address + idaapi.get_imagebase() + udt_member.offset = offset + tinfo.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) + tinfo = udt_member.type + offset = offset - udt_member.offset + + +def get_func_argument_info(function, expression): + """ + Function is cexpr with opname == 'cot_call', expression is any son. Returns index of argument and it's type + + :param function: idaapi.cexpr_t + :param expression: idaapi.cexpr_t + :return: (int, idaapi.tinfo_t) + """ + for idx, argument in enumerate(function.a): + if expression == argument.cexpr: + func_tinfo = function.x.type + if idx < func_tinfo.get_nargs(): + return idx, func_tinfo.get_nth_arg(idx) + return idx, None + print "[ERROR] Wrong usage of 'Helper.get_func_argument_info()'" + + +def set_func_argument(func_tinfo, index, arg_tinfo): + func_data = idaapi.func_type_data_t() + func_tinfo.get_func_details(func_data) + func_data[index].type = arg_tinfo + func_tinfo.create_func(func_data) + + +def set_funcptr_argument(funcptr_tinfo, index, arg_tinfo): + func_tinfo = funcptr_tinfo.get_pointed_object() + set_func_argument(func_tinfo, index, arg_tinfo) + funcptr_tinfo.create_ptr(func_tinfo) + + +def get_nice_pointed_object(tinfo): + """ + Returns nice pointer name (if exist) or None. + For example if tinfo is PKSPIN_LOCK which is typedef of unsigned int *, then if in local types exist KSPIN_LOCK with + type unsigned int, this function returns KSPIN_LOCK + """ + try: + name = tinfo.dstr() + if name[0] == 'P': + pointed_tinfo = idaapi.tinfo_t() + if pointed_tinfo.get_named_type(idaapi.cvar.idati, name[1:]): + if tinfo.get_pointed_object().equals_to(pointed_tinfo): + return pointed_tinfo + except TypeError: + pass + + +def get_fields_at_offset(tinfo, offset): + """ + Given tinfo and offset of the structure or union, returns list of all tinfo at that offset. + This function helps to find appropriate structures by type of the offset + """ + result = [] + if offset == 0: + result.append(tinfo) + udt_data = idaapi.udt_type_data_t() + tinfo.get_udt_details(udt_data) + udt_member = idaapi.udt_member_t() + udt_member.offset = offset * 8 + idx = tinfo.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) + if idx != -1: + while idx < tinfo.get_udt_nmembers() and udt_data[idx].offset <= offset * 8: + udt_member = udt_data[idx] + if udt_member.offset == offset * 8: + if udt_member.type.is_ptr(): + result.append(idaapi.get_unk_type(Const.EA_SIZE)) + result.append(udt_member.type) + result.append(idaapi.dummy_ptrtype(Const.EA_SIZE, False)) + elif not udt_member.type.is_udt(): + result.append(udt_member.type) + if udt_member.type.is_array(): + if (offset - udt_member.offset / 8) % udt_member.type.get_array_element().get_size() == 0: + result.append(udt_member.type.get_array_element()) + elif udt_member.type.is_udt(): + result.extend(get_fields_at_offset(udt_member.type, offset - udt_member.offset / 8)) + idx += 1 + return result + + +def is_legal_type(tinfo): + tinfo.clr_const() + if tinfo.is_ptr() and tinfo.get_pointed_object().is_forward_decl(): + return tinfo.get_pointed_object().get_size() == idaapi.BADSIZE + return Settings.SCAN_ANY_TYPE or bool(filter(lambda x: x.equals_to(tinfo), Const.LEGAL_TYPES)) + + +def search_duplicate_fields(udt_data): + # Returns list of lists with duplicate fields + + default_dict = collections.defaultdict(list) + for idx, udt_member in enumerate(udt_data): + default_dict[udt_member.name].append(idx) + return [indices for indices in default_dict.values() if len(indices) > 1] + + +def get_member_name(tinfo, offset): + udt_member = idaapi.udt_member_t() + udt_member.offset = offset * 8 + tinfo.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) + return udt_member.name + + +def change_member_name(struct_name, offset, name): + return idc.set_member_name(idc.get_struc_id(struct_name), offset, name) + + +def import_structure(name, tinfo): + cdecl_typedef = idaapi.print_tinfo(None, 4, 5, idaapi.PRTYPE_MULTI | idaapi.PRTYPE_TYPE | idaapi.PRTYPE_SEMI, + tinfo, name, None) + if idc.parse_decl(cdecl_typedef, idaapi.PT_TYP) is None: + return 0 + + previous_ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, name) + if previous_ordinal: + idaapi.del_numbered_type(idaapi.cvar.idati, previous_ordinal) + ordinal = idaapi.idc_set_local_type(previous_ordinal, cdecl_typedef, idaapi.PT_TYP) + else: + ordinal = idaapi.idc_set_local_type(-1, cdecl_typedef, idaapi.PT_TYP) + return ordinal + + +def get_funcs_calling_address(ea): + """ Returns all addresses of functions which make call to a function at `ea`""" + xref_ea = idaapi.get_first_cref_to(ea) + xrefs = set() + while xref_ea != idaapi.BADADDR: + xref_func_ea = idc.GetFunctionAttr(xref_ea, idc.FUNCATTR_START) + if xref_func_ea != idaapi.BADADDR: + xrefs.add(xref_func_ea) + else: + print "[Warning] Function not found at 0x{0:08X}".format(xref_ea) + xref_ea = idaapi.get_next_cref_to(ea, xref_ea) + return xrefs + + +class FunctionTouchVisitor(idaapi.ctree_parentee_t): + def __init__(self, cfunc): + super(FunctionTouchVisitor, self).__init__() + self.functions = set() + self.cfunc = cfunc + + def visit_expr(self, expression): + if expression.op == idaapi.cot_call: + self.functions.add(expression.x.obj_ea) + return 0 + + def touch_all(self): + diff = self.functions.difference(Cache.touched_functions) + for address in diff: + if is_imported_ea(address): + continue + try: + cfunc = idaapi.decompile(address) + if cfunc: + FunctionTouchVisitor(cfunc).process() + except idaapi.DecompilationFailure: + logger.warn("IDA failed to decompile function at {}".format(to_hex(address))) + Cache.touched_functions.add(address) + idaapi.decompile(self.cfunc.entry_ea) + + def process(self): + if self.cfunc.entry_ea not in Cache.touched_functions: + Cache.touched_functions.add(self.cfunc.entry_ea) + self.apply_to(self.cfunc.body, None) + self.touch_all() + return True + return False + + +def to_hex(ea): + """ Formats address so it could be double clicked at console """ + if Const.EA64: + return "0x{:016X}".format(ea) + return "0x{:08X}".format(ea) + + +def to_nice_str(ea): + """ Shows address as function name + offset """ + func_start_ea = idc.get_func_attr(ea, idc.FUNCATTR_START) + func_name = idc.Name(func_start_ea) + offset = ea - func_start_ea + return "{}+0x{:X}".format(func_name, offset) + + +def save_long_str_to_idb(array_name, value): + """ Overwrites old array completely in process """ + id = idc.get_array_id(array_name) + if id != -1: + idc.delete_array(id) + id = idc.create_array(array_name) + r = [] + for idx in xrange(len(value) / 1024 + 1): + s = value[idx * 1024: (idx + 1) * 1024] + r.append(s) + idc.set_array_string(id, idx, s) + + +def load_long_str_from_idb(array_name): + id = idc.get_array_id(array_name) + if id == -1: + return None + max_idx = idc.get_last_index(idc.AR_STR, id) + result = [idc.get_array_element(idc.AR_STR, id, idx) for idx in xrange(max_idx + 1)] + return "".join(result) + + +# ====================================================================== +# Functions that extends IDA Pro capabilities +# ====================================================================== + + +def _find_asm_address(self, cexpr): + """ Returns most close virtual address corresponding to cexpr """ + + ea = cexpr.ea + if ea != idaapi.BADADDR: + return ea + + for p in reversed(self.parents): + if p.ea != idaapi.BADADDR: + return p.ea + + +def my_cexpr_t(*args, **kwargs): + """ Replacement of bugged cexpr_t() function """ + + if len(args) == 0: + return idaapi.cexpr_t() + + if len(args) != 1: + raise NotImplementedError + + cexpr = idaapi.cexpr_t() + cexpr.thisown = False + if type(args[0]) == idaapi.cexpr_t: + cexpr.assign(args[0]) + else: + op = args[0] + cexpr._set_op(op) + + if 'x' in kwargs: + cexpr._set_x(kwargs['x']) + if 'y' in kwargs: + cexpr._set_y(kwargs['y']) + if 'z' in kwargs: + cexpr._set_z(kwargs['z']) + return cexpr + + +def extend_ida(): + idaapi.ctree_parentee_t._find_asm_address = _find_asm_address diff --git a/plugins/HexRaysPyTools/Core/NegativeOffsets.py b/plugins/HexRaysPyTools/Core/NegativeOffsets.py new file mode 100644 index 0000000..567b8be --- /dev/null +++ b/plugins/HexRaysPyTools/Core/NegativeOffsets.py @@ -0,0 +1,255 @@ +import re +import logging +import idaapi +import idc +import Helper + +logger = logging.getLogger(__name__) + + +def parse_lvar_comment(lvar): + if lvar.type().is_ptr(): + m = re.search('```(.+)```', lvar.cmt) + if m: + structure_name, offset = m.group(1).split('+') + offset = int(offset) + parent_tinfo = idaapi.tinfo_t() + if parent_tinfo.get_named_type(idaapi.cvar.idati, structure_name) and parent_tinfo.get_size() > offset: + member_name = dict(find_deep_members(parent_tinfo, lvar.type().get_pointed_object())).get(offset, None) + if member_name: + return NegativeLocalInfo(lvar.type().get_pointed_object(), parent_tinfo, offset, member_name) + return None + + +def find_deep_members(parent_tinfo, target_tinfo): + udt_data = idaapi.udt_type_data_t() + parent_tinfo.get_udt_details(udt_data) + result = [] + for udt_member in udt_data: + if udt_member.type.equals_to(target_tinfo): + result.append((udt_member.offset / 8, udt_member.name)) + elif udt_member.type.is_udt(): + for offset, name in find_deep_members(udt_member.type, target_tinfo): + final_name = udt_member.name + '.' + name if udt_member.name else name + result.append((udt_member.offset / 8 + offset, final_name)) + return result + + +class NegativeLocalInfo: + def __init__(self, tinfo, parent_tinfo, offset, member_name): + self.tinfo = tinfo + self.size = tinfo.get_size() if tinfo.is_udt else 0 + self.parent_tinfo = parent_tinfo + self.offset = offset + self.member_name = member_name + + def __repr__(self): + return "Type - {0}, parent type - {1}, offset - {2}, member_name - {3}".format( + self.tinfo.dstr(), + self.parent_tinfo.dstr(), + self.offset, + self.member_name + ) + + +class NegativeLocalCandidate: + def __init__(self, tinfo, offset): + """ + Tinfo - type of the structure tha local variable points to. So it's stripped from pointer. Offset - is first + found offset that points outside of the structure. + :param tinfo: idaapi.tinfo_t + :param offset: int + """ + self.tinfo = tinfo + self.offsets = [offset] + + def __repr__(self): + return self.tinfo.dstr() + ' ' + str(self.offsets) + + def is_structure_offset(self, tinfo, offset): + # Checks if structure tinfo contains a member at given offset + # TODO:array checking + udt_member = idaapi.udt_member_t() + udt_member.offset = offset * 8 + if offset >= 0 and tinfo.find_udt_member(idaapi.STRMEM_OFFSET, udt_member) != -1: + if udt_member.type.is_udt(): + return self.is_structure_offset(udt_member.type, offset - udt_member.offset / 8) + return udt_member.offset == offset * 8 + return False + + def find_containing_structures(self, type_library): + """ + Given the type library creates a list of structures from this library, that contains this structure and + satisfy offset conditions. + :param type_library: idaapi.til_t + :returns: ordinal, offset, member_name, containing structure name + """ + + min_offset = min(self.offsets) + min_offset = min_offset if min_offset < 0 else 0 + max_offset = max(self.offsets) + max_offset = max_offset if max_offset > 0 else self.tinfo.get_size() + # TODO: Check if all offsets are legal + + # Least acceptable size of the containing structure + min_struct_size = max_offset - min_offset + result = [] + parent_tinfo = idaapi.tinfo_t() + target_tinfo = idaapi.tinfo_t() + if not target_tinfo.get_named_type(type_library, self.tinfo.dstr()): + print "[Warning] Such type doesn't exist in '{0}' library".format(type_library.name) + return result + for ordinal in xrange(1, idaapi.get_ordinal_qty(type_library)): + parent_tinfo.create_typedef(type_library, ordinal) + if parent_tinfo.get_size() >= min_struct_size: + for offset, name in find_deep_members(parent_tinfo, target_tinfo): + # print "[DEBUG] Found {0} at {1} in {2}".format(name, offset, parent_tinfo.dstr()) + if offset + min_offset >= 0 and offset + max_offset <= parent_tinfo.get_size(): + result.append((ordinal, offset, name, parent_tinfo.dstr())) + return result + + +class ReplaceVisitor(idaapi.ctree_parentee_t): + + def __init__(self, negative_lvars): + super(ReplaceVisitor, self).__init__() + self.negative_lvars = negative_lvars + self.pvoid_tinfo = idaapi.tinfo_t(idaapi.BT_VOID) + self.pvoid_tinfo.create_ptr(self.pvoid_tinfo) + + def visit_expr(self, expression): + if expression.op == idaapi.cot_add and expression.x.op == idaapi.cot_var and expression.y.op == idaapi.cot_num: + index = expression.x.v.idx + if index in self.negative_lvars: + offset = expression.y.numval() + if offset >= self.negative_lvars[index].size: + self.create_containing_record(expression, index, offset) + elif expression.op == idaapi.cot_sub and expression.x.op == idaapi.cot_var and expression.y.op == idaapi.cot_num: + index = expression.x.v.idx + if index in self.negative_lvars: + offset = -expression.y.n.value(idaapi.tinfo_t(idaapi.BT_INT)) + self.create_containing_record(expression, index, offset) + return 0 + + def create_containing_record(self, expression, index, offset): + negative_lvar = self.negative_lvars[index] + logger.debug("Creating CONTAINING_RECORD macro, offset: {}, negative offset: {}, TYPE: {}".format( + negative_lvar.offset, + offset, + negative_lvar.parent_tinfo.dstr() + )) + + arg_address = idaapi.carg_t() + if expression.op == idaapi.cot_var: + arg_address.assign(expression) + else: + arg_address.assign(expression.x) + + arg_type = idaapi.carg_t() + cexpr_helper = idaapi.create_helper(True, self.pvoid_tinfo, negative_lvar.parent_tinfo.dstr()) + arg_type.assign(cexpr_helper) + + arg_field = idaapi.carg_t() + cexpr_helper = idaapi.create_helper( + True, + self.pvoid_tinfo, + negative_lvar.member_name + ) + arg_field.assign(cexpr_helper) + return_tinfo = idaapi.tinfo_t(negative_lvar.parent_tinfo) + return_tinfo.create_ptr(return_tinfo) + new_cexpr_call = idaapi.call_helper(return_tinfo, None, "CONTAINING_RECORD") + new_cexpr_call.a.push_back(arg_address) + new_cexpr_call.a.push_back(arg_type) + new_cexpr_call.a.push_back(arg_field) + new_cexpr_call.thisown = False + + parent = reversed(self.parents).next().cexpr + + diff = negative_lvar.offset + offset + if diff: + number = idaapi.make_num(diff) + number.thisown = False + new_cexpr_add = Helper.my_cexpr_t(idaapi.cot_add, x=new_cexpr_call, y=number) + new_cexpr_add.type = return_tinfo + + if parent.op == idaapi.cot_ptr: + tmp_tinfo = idaapi.tinfo_t() + tmp_tinfo.create_ptr(parent.type) + new_cexpr_cast = Helper.my_cexpr_t(idaapi.cot_cast, x=new_cexpr_add) + new_cexpr_cast.thisown = False + new_cexpr_cast.type = tmp_tinfo + expression.assign(new_cexpr_cast) + else: + expression.assign(new_cexpr_add) + else: + if parent.op == idaapi.cot_ptr: + tmp_tinfo = idaapi.tinfo_t() + tmp_tinfo.create_ptr(parent.type) + new_cexpr_cast = Helper.my_cexpr_t(idaapi.cot_cast, x=new_cexpr_call) + new_cexpr_cast.thisown = False + new_cexpr_cast.type = tmp_tinfo + expression.assign(new_cexpr_cast) + else: + expression.assign(new_cexpr_call) + + +class SearchVisitor(idaapi.ctree_parentee_t): + def __init__(self, cfunc): + super(SearchVisitor, self).__init__() + self.cfunc = cfunc + self.result = {} + + def visit_expr(self, expression): + if expression.op == idaapi.cot_call and expression.x.op == idaapi.cot_helper and len(expression.a) == 3: + if expression.x.helper == "CONTAINING_RECORD": + if expression.a[0].op == idaapi.cot_var: + idx = expression.a[0].v.idx + if expression.a[1].op == idaapi.cot_helper and expression.a[2].op == idaapi.cot_helper: + parent_name = expression.a[1].helper + member_name = expression.a[2].helper + parent_tinfo = idaapi.tinfo_t() + if not parent_tinfo.get_named_type(idaapi.cvar.idati, parent_name): + return 0 + udt_data = idaapi.udt_type_data_t() + parent_tinfo.get_udt_details(udt_data) + udt_member = filter(lambda x: x.name == member_name, udt_data) + if udt_member: + tinfo = udt_member[0].type + self.result[idx] = NegativeLocalInfo( + tinfo, + parent_tinfo, + udt_member[0].offset / 8, + member_name + ) + return 1 + return 0 + + +class AnalyseVisitor(idaapi.ctree_parentee_t): + def __init__(self, candidates, potential_negatives): + super(AnalyseVisitor, self).__init__() + self.candidates = candidates + self.potential_negatives = potential_negatives + self.potential_negatives.clear() + + def visit_expr(self, expression): + if expression.op == idaapi.cot_add and expression.y.op == idaapi.cot_num: + if expression.x.op == idaapi.cot_var and expression.x.v.idx in self.candidates: + idx = expression.x.v.idx + number = expression.y.numval() + if self.candidates[idx].get_size() <= number: + if idx in self.potential_negatives: + self.potential_negatives[idx].offsets.append(number) + else: + self.potential_negatives[idx] = NegativeLocalCandidate(self.candidates[idx], number) + elif expression.op == idaapi.cot_sub and expression.y.op == idaapi.cot_num: + if expression.x.op == idaapi.cot_var and expression.x.v.idx in self.candidates: + idx = expression.x.v.idx + number = -expression.y.numval() + if idx in self.potential_negatives: + self.potential_negatives[idx].offsets.append(number) + else: + self.potential_negatives[idx] = NegativeLocalCandidate(self.candidates[idx], number) + + return 0 diff --git a/plugins/HexRaysPyTools/Core/SpaghettiCode.py b/plugins/HexRaysPyTools/Core/SpaghettiCode.py new file mode 100644 index 0000000..10633ee --- /dev/null +++ b/plugins/HexRaysPyTools/Core/SpaghettiCode.py @@ -0,0 +1,120 @@ +import idaapi +import idc + + +def inverse_if_condition(cif): + # cexpr_t has become broken but fortunately still exist `assing` method which copies one expr into another + cit_if_condition = cif.expr + tmp_cexpr = idaapi.cexpr_t() + tmp_cexpr.assign(cit_if_condition) + new_if_condition = idaapi.lnot(tmp_cexpr) + cif.expr.swap(new_if_condition) + del cit_if_condition + + +def inverse_if(cif): + inverse_if_condition(cif) + idaapi.qswap(cif.ithen, cif.ielse) + + +class InversionInfo(object): + ARRAY_NAME = "$HexRaysPyTools:IfThenElse:" + + def __init__(self, func_ea): + self.__name = InversionInfo.ARRAY_NAME + hex(int(func_ea)) + self.__id = idc.GetArrayId(self.__name) + + def get_inverted(self): + if self.__id != -1: + array = idc.GetArrayElement(idc.AR_STR, self.__id, 0) + return set(map(int, array.split())) + return set() + + def switch_inverted(self, address): + if self.__id == -1: + self.__id = idc.CreateArray(self.__name) + idc.SetArrayString(self.__id, 0, str(address)) + else: + inverted = self.get_inverted() + try: + inverted.remove(address) + if not inverted: + idc.DeleteArray(self.__id) + + except KeyError: + inverted.add(address) + + idc.SetArrayString(self.__id, 0, " ".join(map(str, inverted))) + + +class SpaghettiVisitor(idaapi.ctree_parentee_t): + def __init__(self): + super(SpaghettiVisitor, self).__init__() + + def visit_insn(self, instruction): + if instruction.op != idaapi.cit_block: + return 0 + + while True: + cblock = instruction.cblock + size = cblock.size() + # Find block that has "If" and "return" as last 2 statements + if size < 2: + break + + if cblock.at(size - 2).op != idaapi.cit_if: + break + + cif = cblock.at(size - 2).cif + if cblock.back().op != idaapi.cit_return or cif.ielse: + break + + cit_then = cif.ithen + + # Skip if only one (not "if") statement in "then" branch + if cit_then.cblock.size() == 1 and cit_then.cblock.front().op != idaapi.cit_if: + return 0 + + inverse_if_condition(cif) + + # Take return from list of statements and later put it back + cit_return = idaapi.cinsn_t() + cit_return.assign(instruction.cblock.back()) + cit_return.thisown = False + instruction.cblock.pop_back() + + # Fill main block with statements from "Then" branch + while cit_then.cblock: + instruction.cblock.push_back(cit_then.cblock.front()) + cit_then.cblock.pop_front() + + # Put back main return if there's no another return or "GOTO" already + if instruction.cblock.back().op not in (idaapi.cit_return, idaapi.cit_goto): + new_return = idaapi.cinsn_t() + new_return.thisown = False + new_return.assign(cit_return) + instruction.cblock.push_back(new_return) + + # Put return into "Then" branch + cit_then.cblock.push_back(cit_return) + return 0 + + +class SwapThenElseVisitor(idaapi.ctree_parentee_t): + def __init__(self, func_ea): + super(SwapThenElseVisitor, self).__init__() + self.__inversion_info = InversionInfo(func_ea) + self.__inverted = self.__inversion_info.get_inverted() + + def visit_insn(self, insn): + if insn.op != idaapi.cit_if or insn.cif.ielse is None: + return 0 + + if insn.ea in self.__inverted: + inverse_if(insn.cif) + + return 0 + + def apply_to(self, *args): + if self.__inverted: + super(SwapThenElseVisitor, self).apply_to(*args) diff --git a/plugins/HexRaysPyTools/Core/StructXrefs.py b/plugins/HexRaysPyTools/Core/StructXrefs.py new file mode 100644 index 0000000..9d0deb5 --- /dev/null +++ b/plugins/HexRaysPyTools/Core/StructXrefs.py @@ -0,0 +1,181 @@ +import time +import logging +from collections import namedtuple +import json + +import idaapi +import Helper +import HexRaysPyTools.Settings as Settings + + +logger = logging.getLogger(__name__) + +XrefInfo = namedtuple('XrefInfo', ['func_ea', 'offset', 'line', 'type']) + + +def singleton(cls): + instances = {} + + def get_instance(): + if cls not in instances: + instances[cls] = cls() + return instances[cls] + return get_instance + + +@singleton +class XrefStorage(object): + ARRAY_NAME = "$HexRaysPyTools:XrefStorage" + + def __init__(self): + self.storage = None + + def open(self): + if not Settings.STORE_XREFS: + self.storage = {} + return + + result = Helper.load_long_str_from_idb(self.ARRAY_NAME) + if result: + try: + self.storage = json.loads(result, object_hook=self.json_keys_to_str) + return + except ValueError: + logger.error("Failed to read previous info about Xrefs. Try Ctrl+F5 to cache data") + self.storage = {} + + def close(self): + self.save() + self.storage = None + + def save(self): + if not Settings.STORE_XREFS: + return + + if self.storage: + Helper.save_long_str_to_idb(self.ARRAY_NAME, json.dumps(self.storage)) + + def update_structure_info(self, ordinal, function_address, data): + """ Accepts data in form dictionary {structure offset -> list(offsets within function with field appealing) """ + if ordinal not in self.storage: + self.storage[ordinal] = {} + + image_base = idaapi.get_imagebase() + function_offset = function_address - image_base + self.storage[ordinal][function_offset] = data + + def get_structure_info(self, ordinal, struct_offset): + """ By given ordinal and offset within a structure returns dictionary {func_address -> list(offsets)} """ + result = [] + + if ordinal not in self.storage: + return result + + for func_offset, data in self.storage[ordinal].items(): + if struct_offset in data: + func_ea = func_offset + idaapi.get_imagebase() + for xref_info in data[struct_offset]: + offset, line, usage_type = xref_info + result.append(XrefInfo(func_ea, offset, line, usage_type)) + return result + + @staticmethod + def json_keys_to_str(x): + if isinstance(x, dict): + return {int(k): v for k, v in x.items()} + return x + + def __len__(self): + return len(str(self.storage)) + + +class StructXrefVisitor(idaapi.ctree_parentee_t): + def __init__(self, cfunc): + super(StructXrefVisitor, self).__init__() + self.__cfunc = cfunc + self.__image_base = idaapi.get_imagebase() + self.__function_address = cfunc.entry_ea + self.__result = {} + self.__storage = XrefStorage() + + def visit_expr(self, expression): + # Checks if expression is reference by pointer or by value + if expression.op == idaapi.cot_memptr: + struct_type = expression.x.type.get_pointed_object() + elif expression.op == idaapi.cot_memref: + struct_type = expression.x.type + else: + return 0 + + # Getting information about structure, field offset, address and one line corresponding to code + ordinal = struct_type.get_ordinal() + if ordinal == 0: + t = idaapi.tinfo_t() + struct_name = struct_type.dstr().split()[-1] # Get rid of `struct` prefix or something else + t.get_named_type(idaapi.cvar.idati, struct_name) + ordinal = t.get_ordinal() + + field_offset = expression.m + ea = self.__find_ref_address(expression) + usage_type = self.__get_type(expression) + + if ea == idaapi.BADADDR or not ordinal: + logger.warning("Failed to parse at address {0}, ordinal - {1}, type - {2}".format( + Helper.to_hex(ea), ordinal, struct_type.dstr() + )) + + one_line = self.__get_line() + + occurrence_offset = ea - self.__function_address + xref_info = (occurrence_offset, one_line, usage_type) + + # Saving results + if ordinal not in self.__result: + self.__result[ordinal] = {field_offset: [xref_info]} + elif field_offset not in self.__result[ordinal]: + self.__result[ordinal][field_offset] = [xref_info] + else: + self.__result[ordinal][field_offset].append(xref_info) + return 0 + + def process(self): + t = time.time() + self.apply_to(self.__cfunc.body, None) + for ordinal, data in self.__result.items(): + self.__storage.update_structure_info(ordinal, self.__function_address, data) + + storage_mb_size = len(self.__storage) * 1.0 / 1024 ** 2 + logger.debug("Xref processing: %f seconds passed, storage size - %.2f MB ", (time.time() - t), storage_mb_size) + + def __find_ref_address(self, cexpr): + """ Returns most close virtual address corresponding to cexpr """ + + ea = cexpr.ea + if ea != idaapi.BADADDR: + return ea + + for p in reversed(self.parents): + if p.ea != idaapi.BADADDR: + return p.ea + + def __get_type(self, cexpr): + """ Returns one of the following types: 'R' - read value, 'W' - write value, 'A' - function argument""" + child = cexpr + for p in reversed(self.parents): + assert p, "Failed to get type at " + Helper.to_hex(self.__function_address) + + if p.cexpr.op == idaapi.cot_call: + return 'Arg' + if not p.is_expr(): + return 'R' + if p.cexpr.op == idaapi.cot_asg: + if p.cexpr.x == child: + return 'W' + return 'R' + child = p.cexpr + + def __get_line(self): + for p in reversed(self.parents): + if not p.is_expr(): + return idaapi.tag_remove(p.print1(self.__cfunc)) + AssertionError("Parent instruction is not found") diff --git a/plugins/HexRaysPyTools/Core/StructureGraph.py b/plugins/HexRaysPyTools/Core/StructureGraph.py new file mode 100644 index 0000000..a7a0a30 --- /dev/null +++ b/plugins/HexRaysPyTools/Core/StructureGraph.py @@ -0,0 +1,184 @@ +import idaapi +import idc + + +class LocalType: + def __init__(self, name, members_ordinals, hint, is_selected=False, is_typedef=False, is_enum=False, is_union=False): + self.name = name + self.members_ordinals = members_ordinals + self.hint = hint + self.is_selected = is_selected + self.is_typedef = is_typedef + self.is_enum = is_enum + self.is_union = is_union + + def __call__(self): + return self.name, self.members_ordinals + + def __str__(self): + return "<{0}, {1}>".format(self.name, self.members_ordinals) + + def __repr__(self): + return self.__str__() + + @property + def name_and_color(self): + if self.is_selected: + return self.name, 0x0000FF + elif self.is_typedef: + return self.name, 0x99FFFF + elif self.is_enum: + return self.name, 0x33FF33 + elif self.is_union: + return self.name, 0xCCCC00 + return self.name, 0xffdd99 + + +class StructureGraph: + # TODO:Enum types display + def __init__(self, ordinal_list=None): + self.ordinal_list = ordinal_list if ordinal_list else xrange(1, idc.GetMaxLocalType()) + self.local_types = {} + self.edges = [] + self.final_edges = [] + self.visited_downward = [] + self.visited_upward = [] + self.downward_edges = {} + self.upward_edges = {} + self.initialize_nodes() + self.calculate_edges() + + def change_selected(self, selected): + self.visited_downward = [] + self.visited_upward = [] + self.final_edges = [] + for ordinal in self.ordinal_list: + self.local_types[ordinal].is_selected = False + self.ordinal_list = set(self.local_types).intersection(selected) + for ordinal in self.ordinal_list: + self.local_types[ordinal].is_selected = True + + @staticmethod + def get_ordinal(tinfo): + while tinfo.is_ptr() or tinfo.is_array(): + tinfo.remove_ptr_or_array() + if tinfo.is_udt(): + return tinfo.get_ordinal() + elif tinfo.is_enum(): + return tinfo.get_ordinal() + elif tinfo.is_typeref(): + typeref_ordinal = tinfo.get_ordinal() + if typeref_ordinal: + typeref_tinfo = StructureGraph.get_tinfo_by_ordinal(typeref_ordinal) + if typeref_tinfo.is_typeref() or typeref_tinfo.is_udt() or typeref_tinfo.is_ptr(): + return typeref_ordinal + else: + return 0 + + @staticmethod + def get_members_ordinals(tinfo): + ordinals = [] + if tinfo.is_udt(): + udt_data = idaapi.udt_type_data_t() + tinfo.get_udt_details(udt_data) + for udt_member in udt_data: + ordinal = StructureGraph.get_ordinal(udt_member.type) + if ordinal: + ordinals.append(ordinal) + return ordinals + + @staticmethod + def get_tinfo_by_ordinal(ordinal): + local_typestring = idc.GetLocalTinfo(ordinal) + if local_typestring: + p_type, fields = local_typestring + local_tinfo = idaapi.tinfo_t() + local_tinfo.deserialize(idaapi.cvar.idati, p_type, fields) + return local_tinfo + return None + + def initialize_nodes(self): + for ordinal in xrange(1, idc.GetMaxLocalType()): + # if ordinal == 15: + # import pydevd + # pydevd.settrace("localhost", port=12345, stdoutToServer=True, stderrToServer=True) + + local_tinfo = StructureGraph.get_tinfo_by_ordinal(ordinal) + if not local_tinfo: + return + name = idc.GetLocalTypeName(ordinal) + + if local_tinfo.is_typeref(): + typeref_ordinal = local_tinfo.get_ordinal() + members_ordinals = [] + if typeref_ordinal: + typeref_tinfo = StructureGraph.get_tinfo_by_ordinal(typeref_ordinal) + if typeref_tinfo.is_typeref() or typeref_tinfo.is_udt() or typeref_tinfo.is_ptr(): + members_ordinals = [typeref_ordinal] + cdecl_typedef = idaapi.print_tinfo(None, 4, 5, 0x3, local_tinfo, None, None) + self.local_types[ordinal] = LocalType(name, members_ordinals, cdecl_typedef, is_typedef=True) + elif local_tinfo.is_udt(): + # udt_data = idaapi.udt_type_data_t() + # local_tinfo.get_udt_details(udt_data) + members_ordinals = StructureGraph.get_members_ordinals(local_tinfo) + cdecl_typedef = idaapi.print_tinfo(None, 4, 5, 0x1, local_tinfo, None, None) + self.local_types[ordinal] = LocalType(name, members_ordinals, cdecl_typedef, is_union=local_tinfo.is_union()) + elif local_tinfo.is_ptr(): + typeref_ordinal = StructureGraph.get_ordinal(local_tinfo) + members_ordinals = [typeref_ordinal] if typeref_ordinal else [] + cdecl_typedef = idaapi.print_tinfo(None, 4, 5, 0x2, local_tinfo, None, None) + self.local_types[ordinal] = LocalType( + name, + members_ordinals, + cdecl_typedef + ' *', + is_typedef=True + ) + elif local_tinfo.is_enum(): + cdecl_typedef = idaapi.print_tinfo(None, 4, 5, 0x21, local_tinfo, None, None) + self.local_types[ordinal] = LocalType(name, [], cdecl_typedef, is_enum=True) + + self.ordinal_list = set(self.ordinal_list).intersection(self.local_types) + for ordinal in self.ordinal_list: + self.local_types[ordinal].is_selected = True + + def calculate_edges(self): + for first in self.local_types.keys(): + for second in self.local_types[first].members_ordinals: + self.edges.append((first, second)) + + self.downward_edges = {key: [] for key in self.local_types.keys()} + self.upward_edges = {key: [] for key in self.local_types.keys()} + + for key, value in self.edges: + self.downward_edges[key].append(value) + self.upward_edges[value].append(key) + + def generate_final_edges_down(self, node): + if node not in self.visited_downward: + self.visited_downward.append(node) + else: + return + for next_node in self.downward_edges[node]: + self.final_edges.append((node, next_node)) + for next_node in self.downward_edges[node]: + self.generate_final_edges_down(next_node) + + def generate_final_edges_up(self, node): + if node not in self.visited_upward: + self.visited_upward.append(node) + else: + return + for next_node in self.upward_edges[node]: + self.final_edges.append((next_node, node)) + for next_node in self.upward_edges[node]: + self.generate_final_edges_up(next_node) + + def get_nodes(self): + for ordinal in self.ordinal_list: + if ordinal in self.local_types: + self.generate_final_edges_down(ordinal) + self.generate_final_edges_up(ordinal) + return set([node for nodes in self.final_edges for node in nodes]) + + def get_edges(self): + return self.final_edges diff --git a/plugins/HexRaysPyTools/Core/TemporaryStructure.py b/plugins/HexRaysPyTools/Core/TemporaryStructure.py new file mode 100644 index 0000000..7e5415d --- /dev/null +++ b/plugins/HexRaysPyTools/Core/TemporaryStructure.py @@ -0,0 +1,864 @@ +import bisect +import idc +import idaapi +import re +import itertools +# import PySide.QtCore as QtCore +# import PySide.QtGui as QtGui +import Cache +from HexRaysPyTools.Cute import * +import Const +import Helper +import VariableScanner +import HexRaysPyTools.Api as Api +from HexRaysPyTools.Forms import MyChoose + + +SCORE_TABLE = dict((v, k) for k, v in enumerate( + ['unsigned __int8 *', 'unsigned __int8', '__int8 *', '__int8', '_BYTE', '_BYTE *', '_BYTE **', 'const char **', + 'signed __int16', 'unsigned __int16', '__int16', 'signed __int16 *', 'unsigned __int16 *', '__int16 *', + '_WORD *', '_WORD **', + 'signed int*', 'signed int', 'unsigned int *', 'unsigned int', 'int **', 'char **', 'int *', 'void **', + 'int', '_DWORD *', 'char', '_DWORD', '_WORD', 'void *', 'char *'] +)) + + +def parse_vtable_name(address): + name = idaapi.get_short_name(address) + if idaapi.is_valid_typename(name): + if name[0:3] == 'off': + # off_XXXXXXXX case + return "Vtable" + name[3:], False + elif "table" in name: + return name, True + print "[Warning] Weird virtual table name -", name + return "Vtable_" + name + else: + # Attempt to make nice and valid name from demangled RTTI name + try: + name = re.sub("^const ", "", name) + sliced_names = name.split("::") + name, for_part = "_for_".join(sliced_names[:-1]), sliced_names[-1] + print name, for_part + templates = re.search("<(.*)>", name) + if templates: + templates = templates.group(1) + name = re.sub("<.*>", "", name) + templates = re.sub("[^a-zA-Z0-9_*]", "_", templates) + templates = re.sub("\*", "PTR", templates) + name += '_' + templates + + for_part = re.search("\{for `(.*)'\}", for_part) + if for_part: + for_part = for_part.group(1) + name += '_' + for_part + + return 'Vtable_' + name, True + + except (AttributeError, IndexError): + print "[Warning] Unable to parse virtual table name - " + + return "Vtable_{0:X}".format(address), False + + +class AbstractMember: + def __init__(self, offset, scanned_variable, origin): + """ + Offset is the very very base of the structure + Origin is from which offset of the base structure the variable have been scanned + scanned_variable - information about context in which this variable was scanned. This is necessary for final + applying type after packing or finalizing structure. + + :param offset: int + :param scanned_variable: ScannedVariable + :param origin: int + """ + self.offset = offset + self.origin = origin + self.enabled = True + self.is_array = False + self.scanned_variables = {scanned_variable} if scanned_variable else set() + self.tinfo = None + + def type_equals_to(self, tinfo): + return self.tinfo.equals_to(tinfo) + + def switch_array_flag(self): + self.is_array ^= True + + def activate(self): + pass + + def set_enabled(self, enable): + self.enabled = enable + self.is_array = False + + def has_collision(self, other): + if self.offset <= other.offset: + return self.offset + self.size > other.offset + return other.offset + other.size >= self.offset + + @property + def score(self): + """ More score of the member - it better suits as candidate for this offset """ + try: + return SCORE_TABLE[self.type_name] + except KeyError: + if self.tinfo and self.tinfo.is_funcptr(): + return 0x1000 + len(self.tinfo.dstr()) + return 0xFFFF + + @property + def type_name(self): + return self.tinfo.dstr() + + @property + def size(self): + size = self.tinfo.get_size() + return size if size != idaapi.BADSIZE else 1 + + @property + def font(self): + return None + + def __repr__(self): + return hex(self.offset) + ' ' + self.type_name + + def __eq__(self, other): + """ I'm aware that it's dirty but have no time to refactor whole file to nice one """ + + if self.offset == other.offset and self.type_name == other.type_name: + self.scanned_variables |= other.scanned_variables + return True + return False + + __ne__ = lambda self, other: self.offset != other.offset or self.type_name != other.type_name + __lt__ = lambda self, other: self.offset < other.offset or \ + (self.offset == other.offset and self.type_name < other.type_name) + __le__ = lambda self, other: self.offset <= other.offset + __gt__ = lambda self, other: self.offset > other.offset or \ + (self.offset == other.offset and self.type_name < other.type_name) + __ge__ = lambda self, other: self.offset >= other.offset + + +class VirtualFunction: + def __init__(self, address, offset): + self.address = address + self.offset = offset + self.visited = False + + def get_ptr_tinfo(self): + # print self.tinfo.dstr() + ptr_tinfo = idaapi.tinfo_t() + ptr_tinfo.create_ptr(self.tinfo) + return ptr_tinfo + + def get_udt_member(self): + udt_member = idaapi.udt_member_t() + udt_member.type = self.get_ptr_tinfo() + udt_member.offset = self.offset + udt_member.name = self.name + udt_member.size = Const.EA_SIZE + return udt_member + + def get_information(self): + return [Helper.to_hex(self.address), self.name, self.tinfo.dstr()] + + @property + def name(self): + name = idaapi.get_short_name(self.address) + name = name.split('(')[0] + result = re.search(r"(\[thunk\]:)?([^`]*)(.*\{(\d+)}.*)?", name) + name, adjuster = result.group(2), result.group(4) + if adjuster: + name += "_adj_" + adjuster + name = name.translate(None, "`'").replace(':', '_').replace(' ', '_').replace(',', '_').replace('~', 'DESTR__') + name = name.replace("==", "__eq__") + name = name.replace("=", "__asg__") + name = re.sub(r'[<>]', '_t_', name) + return name + + @property + def tinfo(self): + try: + decompiled_function = idaapi.decompile(self.address) + if decompiled_function: + return idaapi.tinfo_t(decompiled_function.type) + return Const.DUMMY_FUNC + except idaapi.DecompilationFailure: + pass + print "[ERROR] Failed to decompile function at 0x{0:08X}".format(self.address) + return Const.DUMMY_FUNC + + def show_location(self): + idaapi.open_pseudocode(self.address, 1) + + +class ImportedVirtualFunction(VirtualFunction): + def __init__(self, address, offset): + VirtualFunction.__init__(self, address, offset) + + @property + def tinfo(self): + print "[INFO] Ignoring import function at 0x{0:08X}".format(self.address) + tinfo = idaapi.tinfo_t() + if idaapi.guess_tinfo2(self.address, tinfo): + return tinfo + return Const.DUMMY_FUNC + + def show_location(self): + idaapi.jumpto(self.address) + + +class VirtualTable(AbstractMember): + class VirtualTableChoose(MyChoose): + def __init__(self, items, temp_struct, virtual_table): + MyChoose.__init__( + self, + items, + "Select Virtual Function", + [["Address", 10], ["Name", 15], ["Declaration", 45]], + 13 + ) + self.popup_names = ["Scan All", "-", "Scan", "-"] + self.__temp_struct = temp_struct + self.__virtual_table = virtual_table + + def OnGetLineAttr(self, n): + return [0xd9d9d9, 0x0] if self.__virtual_table.virtual_functions[n].visited else [0xffffff, 0x0] + + def OnGetIcon(self, n): + return 32 if self.__virtual_table.virtual_functions[n].visited else 160 + + def OnInsertLine(self): + """ Scan All Functions menu """ + self.__virtual_table.scan_virtual_functions() + + def OnEditLine(self, n): + """ Scan menu """ + self.__virtual_table.scan_virtual_function(n) + + def __init__(self, offset, address, scanned_variable=None, origin=0): + AbstractMember.__init__(self, offset + origin, scanned_variable, origin) + self.address = address + self.virtual_functions = [] + self.name = "vtable" + ("_{0:X}".format(self.offset) if self.offset else '') + self.vtable_name, self.have_nice_name = parse_vtable_name(address) + self.populate() + + def populate(self): + address = self.address + while True: + if Const.EA64: + func_address = idaapi.get_64bit(address) + else: + func_address = idaapi.get_32bit(address) + + if Helper.is_code_ea(func_address): + self.virtual_functions.append(VirtualFunction(func_address, address - self.address)) + elif Helper.is_imported_ea(func_address): + self.virtual_functions.append(ImportedVirtualFunction(func_address, address - self.address)) + else: + break + address += Const.EA_SIZE + + if idaapi.get_first_dref_to(address) != idaapi.BADADDR: + break + + def create_tinfo(self): + # print "(Virtual table) at address: 0x{0:08X} name: {1}".format(self.address, self.name) + udt_data = idaapi.udt_type_data_t() + for function in self.virtual_functions: + udt_data.push_back(function.get_udt_member()) + + for duplicates in Helper.search_duplicate_fields(udt_data): + first_entry_idx = duplicates.pop(0) + print "[Warning] Found duplicate virtual functions", udt_data[first_entry_idx].name + for num, dup in enumerate(duplicates): + udt_data[dup].name = "duplicate_{0}_{1}".format(first_entry_idx, num + 1) + tinfo = idaapi.tinfo_t() + tinfo.create_ptr(Const.DUMMY_FUNC) + udt_data[dup].type = tinfo + + final_tinfo = idaapi.tinfo_t() + if final_tinfo.create_udt(udt_data, idaapi.BTF_STRUCT): + # print "\n\t(Final structure)\n" + idaapi.print_tinfo('\t', 4, 5, idaapi.PRTYPE_MULTI | idaapi.PRTYPE_TYPE + # | idaapi.PRTYPE_SEMI, final_tinfo, self.name, None) + return final_tinfo + print "[ERROR] Virtual table creation failed" + + def import_to_structures(self, ask=False): + """ + Imports virtual tables and returns tid_t of new structure + + :return: idaapi.tid_t + """ + cdecl_typedef = idaapi.print_tinfo(None, 4, 5, idaapi.PRTYPE_MULTI | idaapi.PRTYPE_TYPE | idaapi.PRTYPE_SEMI, + self.create_tinfo(), self.vtable_name, None) + if ask: + cdecl_typedef = idaapi.asktext(0x10000, cdecl_typedef, "The following new type will be created") + if not cdecl_typedef: + return + previous_ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, self.vtable_name) + if previous_ordinal: + idaapi.del_numbered_type(idaapi.cvar.idati, previous_ordinal) + ordinal = idaapi.idc_set_local_type(previous_ordinal, cdecl_typedef, idaapi.PT_TYP) + else: + ordinal = idaapi.idc_set_local_type(-1, cdecl_typedef, idaapi.PT_TYP) + + if ordinal: + print "[Info] Virtual table " + self.vtable_name + " added to Local Types" + return idaapi.import_type(idaapi.cvar.idati, -1, self.vtable_name) + else: + print "[Error] Failed to create virtual table " + self.vtable_name + print "*" * 100 + print cdecl_typedef + print "*" * 100 + + def show_virtual_functions(self): + function_chooser = self.VirtualTableChoose( + [function.get_information() for function in self.virtual_functions], Cache.temporary_structure, self) + + idx = function_chooser.Show(True) + if idx != -1: + virtual_function = self.virtual_functions[idx] + virtual_function.visited = True + virtual_function.show_location() + + def scan_virtual_function(self, index): + if Helper.is_imported_ea(self.virtual_functions[index].address): + print "[INFO] Ignoring import function at 0x{0:08X}".format(self.address) + return + try: + function = idaapi.decompile(self.virtual_functions[index].address) + except idaapi.DecompilationFailure: + print "[ERROR] Failed to decompile function at 0x{0:08X}".format(self.address) + return + if Helper.FunctionTouchVisitor(function).process(): + function = idaapi.decompile(self.virtual_functions[index].address) + if function.arguments and function.arguments[0].is_arg_var and Helper.is_legal_type(function.arguments[0].tif): + print "[Info] Scanning virtual function at 0x{0:08X}".format(function.entry_ea) + # TODO: Remove usage `temporary_structure' as global + obj = Api.VariableObject(function.get_lvars()[0], 0) + scanner = VariableScanner.NewDeepSearchVisitor(function, self.offset, obj, Cache.temporary_structure) + scanner.process() + else: + print "[Warning] Bad type of first argument in virtual function at 0x{0:08X}".format(function.entry_ea) + + def scan_virtual_functions(self): + for idx in xrange(len(self.virtual_functions)): + self.scan_virtual_function(idx) + + def get_udt_member(self, offset=0): + udt_member = idaapi.udt_member_t() + tid = self.import_to_structures() + if tid != idaapi.BADADDR: + udt_member.name = self.name + tmp_tinfo = idaapi.create_typedef(self.vtable_name) + tmp_tinfo.create_ptr(tmp_tinfo) + udt_member.type = tmp_tinfo + udt_member.offset = self.offset - offset + udt_member.size = Const.EA_SIZE + return udt_member + + def type_equals_to(self, tinfo): + udt_data = idaapi.udt_type_data_t() + if tinfo.is_ptr() and tinfo.get_pointed_object().get_udt_details(udt_data): + if udt_data[0].type.is_funcptr(): + return True + return False + + def switch_array_flag(self): + pass + + def activate(self): + self.show_virtual_functions() + + @staticmethod + def check_address(address): + # Checks if given address contains virtual table. Returns True if more than 2 function pointers found + # Also if table's addresses point to code in executable section, than tries to make functions at that addresses + functions_count = 0 + while True: + func_address = idaapi.get_64bit(address) if Const.EA64 else idaapi.get_32bit(address) + # print "[INFO] Address 0x{0:08X}".format(func_address) + if Helper.is_code_ea(func_address) or Helper.is_imported_ea(func_address): + functions_count += 1 + address += Const.EA_SIZE + else: + segment = idaapi.getseg(func_address) + if segment and segment.perm & idaapi.SEGPERM_EXEC: + idc.MakeUnknown(func_address, 1, idaapi.DOUNK_SIMPLE) + if idc.MakeFunction(func_address): + functions_count += 1 + address += Const.EA_SIZE + continue + break + idaapi.autoWait() + return functions_count + + @property + def type_name(self): + return self.vtable_name + " *" + + @property + def font(self): + return QtGui.QFont("Consolas", 10, QtGui.QFont.Bold) + + @property + def size(self): + return Const.EA_SIZE + + +class Member(AbstractMember): + def __init__(self, offset, tinfo, scanned_variable, origin=0): + AbstractMember.__init__(self, offset + origin, scanned_variable, origin) + self.tinfo = tinfo + self.name = "field_{0:X}".format(self.offset) + + def get_udt_member(self, array_size=0, offset=0): + udt_member = idaapi.udt_member_t() + udt_member.name = "field_{0:X}".format(self.offset - offset) if self.name[:6] == "field_" else self.name + udt_member.type = self.tinfo + if array_size: + tmp = idaapi.tinfo_t(self.tinfo) + tmp.create_array(self.tinfo, array_size) + udt_member.type = tmp + udt_member.offset = self.offset - offset + udt_member.size = self.size + return udt_member + + def activate(self): + new_type_declaration = idaapi.askstr(0x100, self.type_name, "Enter type:") + if new_type_declaration is None: + return + + result = idc.ParseType(new_type_declaration, 0) + if result is None: + return + _, tp, fld = result + tinfo = idaapi.tinfo_t() + tinfo.deserialize(idaapi.cvar.idati, tp, fld, None) + self.tinfo = tinfo + self.is_array = False + + +class VoidMember(Member): + def __init__(self, offset, scanned_variable, origin=0, char=False): + tinfo = Const.CHAR_TINFO if char else Const.BYTE_TINFO + Member.__init__(self, offset, tinfo, scanned_variable, origin) + self.is_array = True + + def type_equals_to(self, tinfo): + return True + + def switch_array_flag(self): + pass + + def set_enabled(self, enable): + self.enabled = enable + + @property + def font(self): + return QtGui.QFont("Consolas", 10, italic=True) + + +class TemporaryStructureModel(QtCore.QAbstractTableModel): + + def __init__(self, *args): + """ + Keeps information about currently found fields in possible structure + main_offset - is the base from where variables scanned. Can be set to different value if some field is passed by + reverence + items - array of candidates to fields + """ + super(TemporaryStructureModel, self).__init__(*args) + self.main_offset = 0 + self.headers = ["Offset", "Type", "Name"] + self.items = [] + self.collisions = [] + self.structure_name = "CHANGE_MY_NAME" + + # OVERLOADED METHODS # + + def rowCount(self, *args): + return len(self.items) + + def columnCount(self, *args): + return len(self.headers) + + def data(self, index, role): + row, col = index.row(), index.column() + item = self.items[row] + if role == QtCore.Qt.DisplayRole: + if col == 0: + return "0x{0:08X}".format(item.offset) + elif col == 1: + if item.is_array and item.size > 0: + array_size = self.calculate_array_size(row) + if array_size: + return item.type_name + "[{}]".format(array_size) + return item.type_name + elif col == 2: + return item.name + elif role == QtCore.Qt.ToolTipRole: + if col == 0: + return self.items[row].offset + elif col == 1: + return self.items[row].size * (self.calculate_array_size(row) if self.items[row].is_array else 1) + elif role == QtCore.Qt.EditRole: + if col == 2: + return self.items[row].name + elif role == QtCore.Qt.FontRole: + if col == 1: + return item.font + elif role == QtCore.Qt.BackgroundRole: + if not item.enabled: + return QtGui.QColor(QtCore.Qt.gray) + if item.offset == self.main_offset: + if col == 0: + return QtGui.QBrush(QtGui.QColor("#ff8080")) + if self.have_collision(row): + return QtGui.QBrush(QtGui.QColor("#ffff99")) + elif role == QtCore.Qt.ForegroundRole: + if self.have_collision(row): + return QtGui.QBrush(QtGui.QColor("#191919")) + + def setData(self, index, value, role): + row, col = index.row(), index.column() + if role == QtCore.Qt.EditRole and idaapi.isident(str(value)): + self.items[row].name = str(value) + self.dataChanged.emit(index, index) + return True + return False + + def headerData(self, section, orientation, role): + if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal: + return self.headers[section] + + def flags(self, index): + if index.column() == 2: + return super(TemporaryStructureModel, self).flags(index) | QtGui.QAbstractItemView.DoubleClicked + return super(TemporaryStructureModel, self).flags(index) + + # HELPER METHODS # + + def pack(self, start=0, stop=None): + if self.collisions[start:stop].count(True): + print "[Warning] Collisions detected" + return + + final_tinfo = idaapi.tinfo_t() + udt_data = idaapi.udt_type_data_t() + origin = self.items[start].offset if start else 0 + offset = origin + + for item in filter(lambda x: x.enabled, self.items[start:stop]): # Filter disabled members + gap_size = item.offset - offset + if gap_size: + udt_data.push_back(TemporaryStructureModel.get_padding_member(offset - origin, gap_size)) + if item.is_array: + array_size = self.calculate_array_size(bisect.bisect_left(self.items, item)) + if array_size: + udt_data.push_back(item.get_udt_member(array_size, offset=origin)) + offset = item.offset + item.size * array_size + continue + udt_data.push_back(item.get_udt_member(offset=origin)) + offset = item.offset + item.size + + final_tinfo.create_udt(udt_data, idaapi.BTF_STRUCT) + cdecl = idaapi.print_tinfo(None, 4, 5, idaapi.PRTYPE_MULTI | idaapi.PRTYPE_TYPE | idaapi.PRTYPE_SEMI, + final_tinfo, self.structure_name, None) + cdecl = idaapi.asktext(0x10000, '#pragma pack(push, 1)\n' + cdecl, "The following new type will be created") + + if cdecl: + structure_name = idaapi.idc_parse_decl(idaapi.cvar.idati, cdecl, idaapi.PT_TYP)[0] + previous_ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, structure_name) + + if previous_ordinal: + reply = QtGui.QMessageBox.question( + None, + "HexRaysPyTools", + "Structure already exist. Do you want to overwrite it?", + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No + ) + if reply == QtGui.QMessageBox.Yes: + idaapi.del_numbered_type(idaapi.cvar.idati, previous_ordinal) + ordinal = idaapi.idc_set_local_type(previous_ordinal, cdecl, idaapi.PT_TYP) + else: + return + else: + ordinal = idaapi.idc_set_local_type(-1, cdecl, idaapi.PT_TYP) + if ordinal: + print "[Info] New type {0} was added to Local Types".format(structure_name) + tid = idaapi.import_type(idaapi.cvar.idati, -1, structure_name) + if tid: + tinfo = idaapi.create_typedef(structure_name) + ptr_tinfo = idaapi.tinfo_t() + ptr_tinfo.create_ptr(tinfo) + for scanned_var in self.get_unique_scanned_variables(origin): + scanned_var.apply_type(ptr_tinfo) + return tinfo + else: + print "[ERROR] Structure {0} probably already exist".format(structure_name) + + def have_member(self, member): + if self.items: + idx = bisect.bisect_left(self.items, member) + if idx < self.rowCount(): + return self.items[idx] == member + return False + + def have_collision(self, row): + return self.collisions[row] + + def refresh_collisions(self): + self.collisions = [False for _ in xrange(len(self.items))] + if (len(self.items)) > 1: + curr = 0 + while curr < len(self.items): + if self.items[curr].enabled: + break + curr += 1 + next = curr + 1 + while next < len(self.items): + if self.items[next].enabled: + if self.items[curr].offset + self.items[curr].size > self.items[next].offset: + self.collisions[curr] = True + self.collisions[next] = True + if self.items[curr].offset + self.items[curr].size < self.items[next].offset + self.items[next].size: + curr = next + else: + curr = next + next += 1 + + def add_row(self, member): + if not self.have_member(member): + bisect.insort(self.items, member) + self.refresh_collisions() + self.modelReset.emit() + + def get_unique_scanned_variables(self, origin=0): + scan_objects = itertools.chain.from_iterable( + [list(item.scanned_variables) for item in self.items if item.origin == origin]) + return dict(((item.function_name, item.name), item) for item in scan_objects).values() + + def get_next_enabled(self, row): + row += 1 + while row < self.rowCount(): + if self.items[row].enabled: + return row + row += 1 + return None + + def calculate_array_size(self, row): + next_row = self.get_next_enabled(row) + if next_row: + return (self.items[next_row].offset - self.items[row].offset) / self.items[row].size + return 0 + + def get_recognized_shape(self, start=0, stop=-1): + if not self.items: + return None + result = [] + if stop != -1: + base = self.items[start].offset + enabled_items = filter(lambda x: x.enabled, self.items[start:stop]) + else: + base = 0 + enabled_items = filter(lambda x: x.enabled, self.items) + offsets = set(map(lambda x: x.offset, enabled_items)) + if not enabled_items: + return + min_size = enabled_items[-1].offset + enabled_items[-1].size - base + tinfo = idaapi.tinfo_t() + for ordinal in xrange(1, idaapi.get_ordinal_qty(idaapi.cvar.idati)): + tinfo.get_numbered_type(idaapi.cvar.idati, ordinal) + if tinfo.is_udt() and tinfo.get_size() >= min_size: + is_found = False + for offset in offsets: + is_found = False + items = filter(lambda x: x.offset == offset, enabled_items) + potential_members = Helper.get_fields_at_offset(tinfo, offset - base) + for item in items: + for potential_member in potential_members: + if item.type_equals_to(potential_member): + is_found = True + break + if is_found: + break + if not is_found: + break + if is_found: + result.append((ordinal, idaapi.tinfo_t(tinfo))) + chooser = MyChoose( + [[str(x), "0x{0:08X}".format(y.get_size()), y.dstr()] for x, y in result], + "Select Structure", + [["Ordinal", 5], ["Size", 10], ["Structure name", 50]] + ) + idx = chooser.Show(modal=True) + if idx != -1: + return result[idx][1] + return None + + @staticmethod + def get_padding_member(offset, size): + udt_member = idaapi.udt_member_t() + if size == 1: + udt_member.name = "gap_{0:X}".format(offset) + udt_member.type = Const.BYTE_TINFO + udt_member.size = Const.BYTE_TINFO.get_size() + udt_member.offset = offset + return udt_member + + array_data = idaapi.array_type_data_t() + array_data.base = 0 + array_data.elem_type = Const.BYTE_TINFO + array_data.nelems = size + tmp_tinfo = idaapi.tinfo_t() + tmp_tinfo.create_array(array_data) + + udt_member.name = "gap_{0:X}".format(offset) + udt_member.type = tmp_tinfo + udt_member.size = size + udt_member.offset = offset + return udt_member + + # SLOTS # + + def finalize(self): + if self.pack(): + self.clear() + + def disable_rows(self, indices): + for idx in indices: + if self.items[idx.row()].enabled: + self.items[idx.row()].set_enabled(False) + self.refresh_collisions() + self.modelReset.emit() + + def enable_rows(self, indices): + for idx in indices: + if not self.items[idx.row()].enabled: + self.items[idx.row()].enabled = True + self.refresh_collisions() + self.modelReset.emit() + + def set_origin(self, indices): + if indices: + self.main_offset = self.items[indices[0].row()].offset + self.modelReset.emit() + + def make_array(self, indices): + if indices: + self.items[indices[0].row()].switch_array_flag() + self.dataChanged.emit(indices[0], indices[0]) + + def pack_substructure(self, indices): + if indices: + indices = sorted(indices) + self.dataChanged.emit(indices[0], indices[-1]) + start, stop = indices[0].row(), indices[-1].row() + 1 + tinfo = self.pack(start, stop) + if tinfo: + offset = self.items[start].offset + self.items = self.items[0:start] + self.items[stop:] + self.add_row(Member(offset, tinfo, None)) + + def unpack_substructure(self, indices): + + if indices is None or len(indices) != 1: + return + + item = self.items[indices[0].row()] + if item.tinfo is not None and item.tinfo.is_udt(): + + self.remove_items(indices) + offset = item.offset + udt_data = idaapi.udt_type_data_t() + if item.tinfo.get_udt_details(udt_data): + for udt_item in udt_data: + member = Member(offset + udt_item.offset / 8, udt_item.type, None) + member.name = udt_item.name + self.add_row(member) + + def resolve_types(self): + current_item = None + current_item_score = 0 + + for item in self.items: + if not item.enabled: + continue + + if current_item is None: + current_item = item + current_item_score = current_item.score + continue + + item_score = item.score + if current_item.has_collision(item): + if item_score <= current_item_score: + item.set_enabled(False) + continue + elif item_score > current_item_score: + current_item.set_enabled(False) + + current_item = item + current_item_score = item_score + + self.refresh_collisions() + self.modelReset.emit() + + def remove_items(self, indices): + rows = map(lambda x: x.row(), indices) + if rows: + self.items = [item for item in self.items if self.items.index(item) not in rows] + self.modelReset.emit() + + def clear(self): + self.items = [] + self.main_offset = 0 + self.modelReset.emit() + + def recognize_shape(self, indices): + min_idx = max_idx = None + if indices: + min_idx, max_idx = min(indices), max(indices, key=lambda x: (x.row(), x.column())) + + if min_idx == max_idx: + tinfo = self.get_recognized_shape() + if tinfo: + tinfo.create_ptr(tinfo) + for scanned_var in self.get_unique_scanned_variables(origin=0): + scanned_var.apply_type(tinfo) + self.clear() + else: + # indices = sorted(indices) + start, stop = min_idx.row(), max_idx.row() + 1 + base = self.items[start].offset + tinfo = self.get_recognized_shape(start, stop) + if tinfo: + ptr_tinfo = idaapi.tinfo_t() + ptr_tinfo.create_ptr(tinfo) + for scanned_var in self.get_unique_scanned_variables(base): + scanned_var.apply_type(ptr_tinfo) + self.items = filter(lambda x: x.offset < base or x.offset >= base + tinfo.get_size(), self.items) + self.add_row(Member(base, tinfo, None)) + + def activated(self, index): + # Double click on offset, opens window with variables + if index.column() == 0: + item = self.items[index.row()] + scanned_variables = list(item.scanned_variables) + variable_chooser = MyChoose( + map(lambda x: x.to_list(), scanned_variables), + "Select Variable", + [["Origin", 4], ["Function name", 25], ["Variable name", 25], ["Expression address", 10]] + ) + row = variable_chooser.Show(modal=True) + if row != -1: + idaapi.open_pseudocode(scanned_variables[row].expression_address, 0) + + # Double click on type. If type is virtual table than opens windows with virtual methods + elif index.column() == 1: + self.items[index.row()].activate() diff --git a/plugins/HexRaysPyTools/Core/VariableScanner.py b/plugins/HexRaysPyTools/Core/VariableScanner.py new file mode 100644 index 0000000..524b4c2 --- /dev/null +++ b/plugins/HexRaysPyTools/Core/VariableScanner.py @@ -0,0 +1,339 @@ +import logging +import idaapi +import idc +import Const +import Helper +import TemporaryStructure +import HexRaysPyTools.Api as Api + +logger = logging.getLogger(__name__) + +# If disabled then recursion will be triggered only for variable passed as first argument to function +SETTING_SCAN_ALL_ARGUMENTS = True + +# Global set which is populated when deep scanning and cleared after completion +scanned_functions = set() +debug_scan_tree = [] + + +class ScannedObject(object): + def __init__(self, name, expression_address, origin, applicable=True): + """ + :param name: Object name + :param expression_address: ea_t + :param origin: which offset had structure at scan moment + :param applicable: whether to apply type after creating structure + """ + self.name = name + self.expression_address = expression_address + self.func_ea = idc.get_func_attr(self.expression_address, idc.FUNCATTR_START) + self.origin = origin + self._applicable = applicable + + @property + def function_name(self): + return idaapi.get_short_name(self.func_ea) + + def apply_type(self, tinfo): + """ Finally apply Class'es tinfo to this variable """ + raise NotImplemented + + @staticmethod + def create(obj, expression_address, origin, applicable): + """ Creates suitable instance of ScannedObject depending on obj """ + if obj.id == Api.SO_GLOBAL_OBJECT: + return ScannedGlobalObject(obj.ea, obj.name, expression_address, origin, applicable) + elif obj.id == Api.SO_LOCAL_VARIABLE: + return ScannedVariableObject(obj.lvar, obj.name, expression_address, origin, applicable) + elif obj.id in (Api.SO_STRUCT_REFERENCE, Api.SO_STRUCT_POINTER): + return ScannedStructureMemberObject(obj.struct_name, obj.offset, expression_address, origin, applicable) + else: + raise AssertionError + + def to_list(self): + """ Creates list that is acceptable to MyChoose2 viewer """ + return [ + "0x{0:04X}".format(self.origin), + self.function_name, + self.name, + Helper.to_hex(self.expression_address) + ] + + def __eq__(self, other): + return self.func_ea == other.func_ea and self.name == other.name and \ + self.expression_address == other.expression_address + + def __hash__(self): + return hash((self.func_ea, self.name, self.expression_address)) + + def __repr__(self): + return "{} : {}".format(self.name, Helper.to_hex(self.expression_address)) + + +class ScannedGlobalObject(ScannedObject): + def __init__(self, obj_ea, name, expression_address, origin, applicable=True): + super(ScannedGlobalObject, self).__init__(name, expression_address, origin, applicable) + self.__obj_ea = obj_ea + + def apply_type(self, tinfo): + if self._applicable: + idaapi.set_tinfo2(self.__obj_ea, tinfo) + + +class ScannedVariableObject(ScannedObject): + def __init__(self, lvar, name, expression_address, origin, applicable=True): + super(ScannedVariableObject, self).__init__(name, expression_address, origin, applicable) + self.__lvar = idaapi.lvar_locator_t(lvar.location, lvar.defea) + + def apply_type(self, tinfo): + if not self._applicable: + return + + hx_view = idaapi.open_pseudocode(self.func_ea, -1) + if hx_view: + logger.debug("Applying tinfo to variable {0} in function {1}".format(self.name, self.function_name)) + # Finding lvar of new window that have the same name that saved one and applying tinfo_t + lvar = filter(lambda x: x == self.__lvar, hx_view.cfunc.get_lvars()) + if lvar: + logger.debug("Successful") + hx_view.set_lvar_type(lvar[0], tinfo) + else: + logger.warn("Failed to find previously scanned local variable {} from {}".format( + self.name, Helper.to_hex(self.expression_address))) + + +class ScannedStructureMemberObject(ScannedObject): + def __init__(self, struct_name, struct_offset, name, expression_address, origin, applicable=True): + super(ScannedStructureMemberObject, self).__init__(name, expression_address, origin, applicable) + self.__struct_name = struct_name + self.__struct_offset = struct_offset + + def apply_type(self, tinfo): + if self._applicable: + logger.warn("Changing type of structure field is not yet implemented. Address - {}".format( + Helper.to_hex(self.expression_address))) + + +class SearchVisitor(Api.ObjectVisitor): + def __init__(self, cfunc, origin, obj, temporary_structure): + super(SearchVisitor, self).__init__(cfunc, obj, None, True) + self.__origin = origin + self.__temporary_structure = temporary_structure + + def _manipulate(self, cexpr, obj): + super(SearchVisitor, self)._manipulate(cexpr, obj) + + if obj.tinfo and not Helper.is_legal_type(obj.tinfo): + logger.warn("Variable obj.name has weird type at {}".format(Helper.to_hex(self._find_asm_address(cexpr)))) + return + if cexpr.type.is_ptr(): + member = self.__extract_member_from_pointer(cexpr, obj) + else: + member = self.__extract_member_from_xword(cexpr, obj) + if member: + logger.debug("\tCreating member with type {}, {}, offset - {}".format( + member.type_name, member.scanned_variables, member.offset)) + self.__temporary_structure.add_row(member) + + def _get_member(self, offset, cexpr, obj, tinfo=None, obj_ea=None): + if offset < 0: + logger.error("Considered to be imposible: offset - {}, obj - {}".format( + offset, Helper.to_hex(self._find_asm_address(cexpr)))) + raise AssertionError + + applicable = not self.crippled + cexpr_ea = self._find_asm_address(cexpr) + scan_obj = ScannedObject.create(obj, cexpr_ea, self.__origin, applicable) + if obj_ea: + if TemporaryStructure.VirtualTable.check_address(obj_ea): + return TemporaryStructure.VirtualTable(offset, obj_ea, scan_obj, self.__origin) + if Helper.is_code_ea(obj_ea): + cfunc = Api.decompile_function(obj_ea) + if cfunc: + tinfo = cfunc.type + tinfo.create_ptr(tinfo) + else: + tinfo = Const.DUMMY_FUNC + return TemporaryStructure.Member(offset, tinfo, scan_obj, self.__origin) + # logger.warn("Want to see this ea - {},".format(Helper.to_hex(cexpr_ea))) + + if not tinfo or tinfo.equals_to(Const.VOID_TINFO) or tinfo.equals_to(Const.CONST_VOID_TINFO): + return TemporaryStructure.VoidMember(offset, scan_obj, self.__origin) + + if tinfo.equals_to(Const.CHAR_TINFO): + return TemporaryStructure.VoidMember(offset, scan_obj, self.__origin, char=True) + + if tinfo.equals_to(Const.CONST_PCHAR_TINFO): + tinfo = Const.PCHAR_TINFO + elif tinfo.equals_to(Const.CONST_PVOID_TINFO): + tinfo = Const.PVOID_TINFO + else: + tinfo.clr_const() + return TemporaryStructure.Member(offset, tinfo, scan_obj, self.__origin) + + def _parse_call(self, call_cexpr, arg_cexpr, offset): + _, tinfo = Helper.get_func_argument_info(call_cexpr, arg_cexpr) + if tinfo: + return self.__deref_tinfo(tinfo) + # TODO: Find example with UTF-16 strings + return Const.CHAR_TINFO + + def _parse_left_assignee(self, cexpr, offset): + pass + + def __extract_member_from_pointer(self, cexpr, obj): + parents_type = map(lambda x: idaapi.get_ctype_name(x.cexpr.op), list(self.parents)[:0:-1]) + parents = map(lambda x: x.cexpr, list(self.parents)[:0:-1]) + + logger.debug("Parsing expression {}. Parents - {}".format(obj.name, parents_type)) + + # Extracting offset and removing expression parents making this offset + if parents_type[0] in ('idx', 'add'): + # `obj[idx]' or `(TYPE *) + x' + if parents[0].y.op != idaapi.cot_num: + # There's no way to handle with dynamic offset + return + offset = parents[0].y.numval() * cexpr.type.get_ptrarr_objsize() + cexpr = self.parent_expr() + if parents_type[0] == 'add': + del parents_type[0] + del parents[0] + elif parents_type[0:2] == ['cast', 'add']: + # (TYPE *)obj + offset or (TYPE)obj + offset + if parents[1].y.op != idaapi.cot_num: + return + if parents[0].type.is_ptr(): + size = parents[0].type.get_ptrarr_objsize() + else: + size = 1 + offset = parents[1].theother(parents[0]).numval() * size + cexpr = parents[1] + del parents_type[0:2] + del parents[0:2] + else: + offset = 0 + + return self.__extract_member(cexpr, obj, offset, parents, parents_type) + + def __extract_member_from_xword(self, cexpr, obj): + parents_type = map(lambda x: idaapi.get_ctype_name(x.cexpr.op), list(self.parents)[:0:-1]) + parents = map(lambda x: x.cexpr, list(self.parents)[:0:-1]) + + logger.debug("Parsing expression {}. Parents - {}".format(obj.name, parents_type)) + + if parents_type[0] == 'add': + if parents[0].theother(cexpr).op != idaapi.cot_num: + return + offset = parents[0].theother(cexpr).numval() + cexpr = self.parent_expr() + del parents_type[0] + del parents[0] + else: + offset = 0 + + return self.__extract_member(cexpr, obj, offset, parents, parents_type) + + def __extract_member(self, cexpr, obj, offset, parents, parents_type): + if parents_type[0] == 'cast': + default_tinfo = parents[0].type + cexpr = parents[0] + del parents_type[0] + del parents[0] + else: + default_tinfo = Const.PX_WORD_TINFO + + if parents_type[0] in ('idx', 'ptr'): + if parents_type[1] == 'cast': + default_tinfo = parents[1].type + cexpr = parents[0] + del parents_type[0] + del parents[0] + else: + default_tinfo = self.__deref_tinfo(default_tinfo) + + if parents_type[1] == 'asg': + if parents[1].x == parents[0]: + # *(TYPE *)(var + x) = ??? + obj_ea = self.__extract_obj_ea(parents[1].y) + return self._get_member(offset, cexpr, obj, default_tinfo, obj_ea) + return self._get_member(offset, cexpr, obj, parents[1].x.type) + elif parents_type[1] == 'call': + if parents[1].x == parents[0]: + # ((type (__some_call *)(..., ..., ...)var[idx])(..., ..., ...) + # ((type (__some_call *)(..., ..., ...)*(TYPE *)(var + x))(..., ..., ...) + return self._get_member(offset, cexpr, obj, parents[0].type) + _, tinfo = Helper.get_func_argument_info(parents[1], parents[0]) + if tinfo is None: + tinfo = Const.PCHAR_TINFO + return self._get_member(offset, cexpr, obj, tinfo) + return self._get_member(offset, cexpr, obj, default_tinfo) + + elif parents_type[0] == 'call': + # call(..., (TYPE)(var + x), ...) + tinfo = self._parse_call(parents[0], cexpr, offset) + return self._get_member(offset, cexpr, obj, tinfo) + + elif parents_type[0] == 'asg': + if parents[0].y == cexpr: + # other_obj = (TYPE) (var + offset) + self._parse_left_assignee(parents[1].x, offset) + return self._get_member(offset, cexpr, obj, self.__deref_tinfo(default_tinfo)) + + @staticmethod + def __extract_obj_ea(cexpr): + if cexpr.op == idaapi.cot_ref: + cexpr = cexpr.x + if cexpr.op == idaapi.cot_obj: + if cexpr.obj_ea != idaapi.BADADDR: + return cexpr.obj_ea + + @staticmethod + def __deref_tinfo(tinfo): + if tinfo.is_ptr(): + if tinfo.get_ptrarr_objsize() == 1: + if tinfo.equals_to(Const.PCHAR_TINFO) or tinfo.equals_to(Const.CONST_PCHAR_TINFO): + return Const.CHAR_TINFO + return None # Turns into VoidMember + return tinfo.get_pointed_object() + return tinfo + + +class NewShallowSearchVisitor(SearchVisitor, Api.ObjectDownwardsVisitor): + def __init__(self, cfunc, origin, obj, temporary_structure): + super(NewShallowSearchVisitor, self).__init__(cfunc, origin, obj, temporary_structure) + + +class NewDeepSearchVisitor(SearchVisitor, Api.RecursiveObjectDownwardsVisitor): + def __init__(self, cfunc, origin, obj, temporary_structure): + super(NewDeepSearchVisitor, self).__init__(cfunc, origin, obj, temporary_structure) + + +class DeepReturnVisitor(NewDeepSearchVisitor): + def __init__(self, cfunc, origin, obj, temporary_structure): + super(DeepReturnVisitor, self).__init__(cfunc, origin, obj, temporary_structure) + self.__callers_ea = Helper.get_funcs_calling_address(cfunc.entry_ea) + self.__call_obj = obj + + def _start(self): + for ea in self.__callers_ea: + self._add_scan_tree_info(ea, -1) + assert self.__prepare_scanner() + + def _finish(self): + if self.__prepare_scanner(): + self._recursive_process() + + def __prepare_scanner(self): + try: + cfunc = self.__iter_callers().next() + except StopIteration: + return False + + self.prepare_new_scan(cfunc, -1, self.__call_obj) + return True + + def __iter_callers(self): + for ea in self.__callers_ea: + cfunc = Api.decompile_function(ea) + if cfunc: + yield cfunc diff --git a/plugins/HexRaysPyTools/Core/__init__.py b/plugins/HexRaysPyTools/Core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/HexRaysPyTools/Cute.py b/plugins/HexRaysPyTools/Cute.py new file mode 100644 index 0000000..23466c5 --- /dev/null +++ b/plugins/HexRaysPyTools/Cute.py @@ -0,0 +1,116 @@ +""" +Cute - a crossQt compatibility module for IDAPython. +Feel free to copy this code into your own projects. +Latest version can be found at https://github.com/tmr232/Cute +The MIT License (MIT) +Copyright (c) 2015 Tamir Bahar +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import os +import sys + +import idaapi + +# This nasty piece of code is here to force the loading of IDA's PySide. +# Without it, Python attempts to load PySide from the site-packages directory, +# and failing, as it does not play nicely with IDA. +old_path = sys.path[:] +try: + ida_python_path = os.path.dirname(idaapi.__file__) + sys.path.insert(0, ida_python_path) + if idaapi.IDA_SDK_VERSION >= 690: + from PyQt5 import QtGui, QtCore, QtWidgets + import sip + + QtCore.Signal = QtCore.pyqtSignal + QtCore.Slot = QtCore.pyqtSlot + QtGui.QTreeView = QtWidgets.QTreeView + QtGui.QTableView = QtWidgets.QTableView + QtGui.QAction = QtWidgets.QAction + QtGui.QMenu = QtWidgets.QMenu + QtGui.QLabel = QtWidgets.QLabel + QtGui.QMessageBox = QtWidgets.QMessageBox + + QtGui.QHeaderView = QtWidgets.QHeaderView + QtGui.QAbstractItemView = QtWidgets.QAbstractItemView + QtGui.QVBoxLayout = QtWidgets.QVBoxLayout + QtGui.QHBoxLayout = QtWidgets.QHBoxLayout + QtGui.QGridLayout = QtWidgets.QGridLayout + QtGui.QPushButton = QtWidgets.QPushButton + QtGui.QSpacerItem = QtWidgets.QSpacerItem + QtGui.QSizePolicy = QtWidgets.QSizePolicy + QtGui.QSortFilterProxyModel = QtCore.QSortFilterProxyModel + QtGui.QLineEdit = QtWidgets.QLineEdit + + use_qt5 = True + else: + from PySide import QtGui, QtCore + from PySide import QtGui as QtWidgets + + QtGui.QHeaderView.setSectionResizeMode = QtGui.QHeaderView.setResizeMode + + use_qt5 = False +finally: + sys.path = old_path + + +def connect(sender, signal, callback): + """Connect a signal. + Use this function only in cases where code should work with both Qt5 and Qt4, as it is an ugly hack. + Args: + sender: The Qt object emitting the signal + signal: A string, containing the signal signature (as in Qt4 and PySide) + callback: The function to be called upon receiving the signal + """ + if use_qt5: + return getattr(sender, signal.split('(', 1)[0]).connect(callback) + else: + return sender.connect(QtCore.SIGNAL(signal), callback) + + +def disconnect(sender, signal, callback): + """Disconnect a signal. + Use this function only in cases where code should work with both Qt5 and Qt4, as it is an ugly hack. + Args: + sender: The Qt object emitting the signal + signal: A string, containing the signal signature (as in Qt4 and PySide) + callback: The function to be called upon receiving the signal + """ + if use_qt5: + return getattr(sender, signal.split('(', 1)[0]).disconnect(callback) + else: + return sender.disconnect(QtCore.SIGNAL(signal), callback) + + +def form_to_widget(tform): + """Get the tform's widget. + IDA has two different form-to-widget functions, one for PyQt and one for PySide. + This function uses the relevant one based on the version of IDA you are using. + Args: + tform: The IDA TForm to get the widget from. + """ + class Ctx(object): + QtGui = QtGui + if use_qt5: + QtWidgets = QtWidgets + sip = sip + + if use_qt5: + return idaapi.PluginForm.FormToPyQtWidget(tform, ctx=Ctx()) + else: + return idaapi.PluginForm.FormToPySideWidget(tform, ctx=Ctx()) \ No newline at end of file diff --git a/plugins/HexRaysPyTools/Forms.py b/plugins/HexRaysPyTools/Forms.py new file mode 100644 index 0000000..ebc3cc4 --- /dev/null +++ b/plugins/HexRaysPyTools/Forms.py @@ -0,0 +1,246 @@ +import idaapi +# import PySide.QtGui as QtGui +# import PySide.QtCore as QtCore +from HexRaysPyTools.Cute import * +import Core.Classes + + +class MyChoose(idaapi.Choose2): + def __init__(self, items, title, cols, icon=-1): + idaapi.Choose2.__init__(self, title, cols, flags=idaapi.Choose2.CH_MODAL, icon=icon) + self.items = items + + def OnClose(self): + pass + + def OnGetLine(self, n): + return self.items[n] + + def OnGetSize(self): + return len(self.items) + + +class StructureBuilder(idaapi.PluginForm): + def __init__(self, structure_model): + super(StructureBuilder, self).__init__() + self.structure_model = structure_model + self.parent = None + + def OnCreate(self, form): + self.parent = form_to_widget(form) + self.init_ui() + + def init_ui(self): + self.parent.setStyleSheet( + "QTableView {background-color: transparent; selection-background-color: #87bdd8;}" + "QHeaderView::section {background-color: transparent; border: 0.5px solid;}" + "QPushButton {width: 50px; height: 20px;}" + # "QPushButton::pressed {background-color: #ccccff}" + ) + self.parent.resize(400, 600) + self.parent.setWindowTitle('Structure Builder') + + btn_finalize = QtGui.QPushButton("&Finalize") + btn_disable = QtGui.QPushButton("&Disable") + btn_enable = QtGui.QPushButton("&Enable") + btn_origin = QtGui.QPushButton("&Origin") + btn_array = QtGui.QPushButton("&Array") + btn_pack = QtGui.QPushButton("&Pack") + btn_unpack = QtGui.QPushButton("&Unpack") + btn_remove = QtGui.QPushButton("&Remove") + btn_resolve = QtGui.QPushButton("Resolve") + btn_clear = QtGui.QPushButton("Clear") # Clear button doesn't have shortcut because it can fuck up all work + btn_recognize = QtGui.QPushButton("Recognize Shape") + btn_recognize.setStyleSheet("QPushButton {width: 100px; height: 20px;}") + + btn_finalize.setShortcut("f") + btn_disable.setShortcut("d") + btn_enable.setShortcut("e") + btn_origin.setShortcut("o") + btn_array.setShortcut("a") + btn_pack.setShortcut("p") + btn_unpack.setShortcut("u") + btn_remove.setShortcut("r") + + struct_view = QtGui.QTableView() + struct_view.setModel(self.structure_model) + # struct_view.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + + struct_view.verticalHeader().setVisible(False) + struct_view.verticalHeader().setDefaultSectionSize(24) + struct_view.horizontalHeader().setStretchLastSection(True) + struct_view.horizontalHeader().setSectionResizeMode(QtGui.QHeaderView.ResizeToContents) + + grid_box = QtGui.QGridLayout() + grid_box.setSpacing(0) + grid_box.addWidget(btn_finalize, 0, 0) + grid_box.addWidget(btn_enable, 0, 1) + grid_box.addWidget(btn_disable, 0, 2) + grid_box.addWidget(btn_origin, 0, 3) + grid_box.addItem(QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Expanding), 0, 5) + grid_box.addWidget(btn_array, 1, 0) + grid_box.addWidget(btn_pack, 1, 1) + grid_box.addWidget(btn_unpack, 1, 2) + grid_box.addWidget(btn_remove, 1, 3) + grid_box.addWidget(btn_resolve, 0, 4) + grid_box.addItem(QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Expanding), 1, 5) + grid_box.addWidget(btn_recognize, 0, 6) + grid_box.addWidget(btn_clear, 1, 6) + + vertical_box = QtGui.QVBoxLayout() + vertical_box.addWidget(struct_view) + vertical_box.addLayout(grid_box) + self.parent.setLayout(vertical_box) + + btn_finalize.clicked.connect(lambda: self.structure_model.finalize()) + btn_disable.clicked.connect(lambda: self.structure_model.disable_rows(struct_view.selectedIndexes())) + btn_enable.clicked.connect(lambda: self.structure_model.enable_rows(struct_view.selectedIndexes())) + btn_origin.clicked.connect(lambda: self.structure_model.set_origin(struct_view.selectedIndexes())) + btn_array.clicked.connect(lambda: self.structure_model.make_array(struct_view.selectedIndexes())) + btn_pack.clicked.connect(lambda: self.structure_model.pack_substructure(struct_view.selectedIndexes())) + btn_unpack.clicked.connect(lambda: self.structure_model.unpack_substructure(struct_view.selectedIndexes())) + btn_remove.clicked.connect(lambda: self.structure_model.remove_items(struct_view.selectedIndexes())) + btn_resolve.clicked.connect(lambda: self.structure_model.resolve_types()) + btn_clear.clicked.connect(lambda: self.structure_model.clear()) + btn_recognize.clicked.connect(lambda: self.structure_model.recognize_shape(struct_view.selectedIndexes())) + struct_view.activated[QtCore.QModelIndex].connect(self.structure_model.activated) + self.structure_model.dataChanged.connect(struct_view.clearSelection) + + def OnClose(self, form): + pass + + def Show(self, caption=None, options=0): + return idaapi.PluginForm.Show(self, caption, options=options) + + +class StructureGraphViewer(idaapi.GraphViewer): + def __init__(self, title, graph): + idaapi.GraphViewer.__init__(self, title) + self.graph = graph + self.nodes_id = {} + + def OnRefresh(self): + self.Clear() + self.nodes_id.clear() + for node in self.graph.get_nodes(): + self.nodes_id[node] = self.AddNode(node) + for first, second in self.graph.get_edges(): + self.AddEdge(self.nodes_id[first], self.nodes_id[second]) + return True + + def OnGetText(self, node_id): + return self.graph.local_types[self[node_id]].name_and_color + + def OnHint(self, node_id): + """ Try-catch clause because IDA sometimes attempts to use old information to get hint """ + try: + ordinal = self[node_id] + return self.graph.local_types[ordinal].hint + except KeyError: + return + + def OnDblClick(self, node_id): + self.change_selected([self[node_id]]) + + def change_selected(self, ordinals): + self.graph.change_selected(ordinals) + self.Refresh() + self.Select(self.nodes_id[ordinals[0]]) + + +class ClassViewer(idaapi.PluginForm): + def __init__(self): + super(ClassViewer, self).__init__() + self.parent = None + self.class_tree = QtGui.QTreeView() + self.line_edit_filter = QtGui.QLineEdit() + + self.action_collapse = QtGui.QAction("Collapse all", self.class_tree) + self.action_expand = QtGui.QAction("Expand all", self.class_tree) + self.action_set_arg = QtGui.QAction("Set First Argument Type", self.class_tree) + self.action_rollback = QtGui.QAction("Rollback", self.class_tree) + self.action_refresh = QtGui.QAction("Refresh", self.class_tree) + self.action_commit = QtGui.QAction("Commit", self.class_tree) + + self.menu = QtGui.QMenu(self.parent) + + self.proxy_model = Core.Classes.ProxyModel() + + def OnCreate(self, form): + # self.parent = self.FormToPySideWidget(form) + self.parent = form_to_widget(form) + self.init_ui() + + def init_ui(self): + self.parent.setWindowTitle('Classes') + self.parent.setStyleSheet( + # "QTreeView::item:!has-children { background-color: #fefbd8; border: 0.5px solid lightgray ;}" + # "QTreeView::item:has-children { background-color: #80ced6; border-top: 1px solid black ;}" + # "QTreeView::item:selected { background-color: #618685; show-decoration-selected: 1;}" + "QTreeView {background-color: transparent; }" + "QHeaderView::section {background-color: transparent; border: 1px solid;}" + ) + + hbox_layout = QtGui.QHBoxLayout() + label_filter = QtGui.QLabel("&Filter:") + label_filter.setBuddy(self.line_edit_filter) + hbox_layout.addWidget(label_filter) + hbox_layout.addWidget(self.line_edit_filter) + + class_model = Core.Classes.TreeModel() + self.proxy_model.setSourceModel(class_model) + self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) + self.class_tree.setModel(self.proxy_model) + self.class_tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.class_tree.expandAll() + self.class_tree.header().setStretchLastSection(True) + self.class_tree.header().setSectionResizeMode(QtGui.QHeaderView.ResizeToContents) + self.class_tree.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + + self.action_collapse.triggered.connect(self.class_tree.collapseAll) + self.action_expand.triggered.connect(self.class_tree.expandAll) + self.action_set_arg.triggered.connect( + lambda: class_model.set_first_argument_type( + map(self.proxy_model.mapToSource, self.class_tree.selectedIndexes()) + ) + ) + self.action_rollback.triggered.connect(lambda: class_model.rollback()) + self.action_refresh.triggered.connect(lambda: class_model.refresh()) + self.action_commit.triggered.connect(lambda: class_model.commit()) + class_model.refreshed.connect(self.class_tree.expandAll) + + self.menu.addAction(self.action_collapse) + self.menu.addAction(self.action_expand) + self.menu.addAction(self.action_refresh) + self.menu.addAction(self.action_set_arg) + self.menu.addAction(self.action_rollback) + self.menu.addAction(self.action_commit) + + vertical_box = QtGui.QVBoxLayout() + vertical_box.addWidget(self.class_tree) + vertical_box.addLayout(hbox_layout) + self.parent.setLayout(vertical_box) + + self.class_tree.activated[QtCore.QModelIndex].connect( + lambda x: class_model.open_function(self.proxy_model.mapToSource(x)) + ) + self.class_tree.customContextMenuRequested[QtCore.QPoint].connect(self.show_menu) + self.line_edit_filter.textChanged[str].connect(self.proxy_model.set_regexp_filter) + # proxy_model.rowsInserted[object].connect(lambda: self.class_tree.setExpanded(object, True)) + + def OnClose(self, form): + pass + + def Show(self, caption=None, options=0): + return idaapi.PluginForm.Show(self, caption, options=options) + + def show_menu(self, point): + self.action_set_arg.setEnabled(True) + indexes = map( + self.proxy_model.mapToSource, + filter(lambda x: x.column() == 0, self.class_tree.selectedIndexes()) + ) + if len(indexes) > 1: + if filter(lambda x: len(x.internalPointer().children) > 0, indexes): + self.action_set_arg.setEnabled(False) + self.menu.exec_(self.class_tree.mapToGlobal(point)) diff --git a/plugins/HexRaysPyTools/Settings.py b/plugins/HexRaysPyTools/Settings.py new file mode 100644 index 0000000..9a3141a --- /dev/null +++ b/plugins/HexRaysPyTools/Settings.py @@ -0,0 +1,57 @@ +import os +import ConfigParser +import idc + +import logging + +CONFIG_FILE_PATH = os.path.join(idc.GetIdaDirectory(), 'cfg', 'HexRaysPyTools.cfg') + +DEBUG_MESSAGE_LEVEL = logging.INFO +# Whether propagate names (Propagate name feature) through all names or only defaults like v11, a3, this, field_4 +PROPAGATE_THROUGH_ALL_NAMES = False +# Store Xref information in database. I don't know how much size it consumes yet +STORE_XREFS = True +# There're some types that can be pointers to structures like int, PVOID etc and by default plugin scans only them +# Full list can be found in `Const.LEGAL_TYPES`. +# But if set this option to True than variable of every type could be possible to scan +SCAN_ANY_TYPE = False + + +def add_default_settings(config): + updated = False + if not config.has_option("DEFAULT", "DEBUG_MESSAGE_LEVEL"): + print DEBUG_MESSAGE_LEVEL + config.set(None, 'DEBUG_MESSAGE_LEVEL', str(DEBUG_MESSAGE_LEVEL)) + updated = True + if not config.has_option("DEFAULT", "PROPAGATE_THROUGH_ALL_NAMES"): + config.set(None, 'PROPAGATE_THROUGH_ALL_NAMES', str(PROPAGATE_THROUGH_ALL_NAMES)) + updated = True + if not config.has_option("DEFAULT", "STORE_XREFS"): + config.set(None, 'STORE_XREFS', str(STORE_XREFS)) + updated = True + if not config.has_option("DEFAULT", "SCAN_ANY_TYPE"): + config.set(None, 'SCAN_ANY_TYPE', str(SCAN_ANY_TYPE)) + updated = True + + if updated: + try: + with open(CONFIG_FILE_PATH, "w") as f: + config.write(f) + except IOError: + print "[ERROR] Failed to write or update config file at {}. Default settings will be used instead.\n" \ + "Consider running IDA Pro under administrator once".format(CONFIG_FILE_PATH) + + +def load_settings(): + global DEBUG_MESSAGE_LEVEL, PROPAGATE_THROUGH_ALL_NAMES, STORE_XREFS, SCAN_ANY_TYPE + + config = ConfigParser.ConfigParser() + if os.path.isfile(CONFIG_FILE_PATH): + config.read(CONFIG_FILE_PATH) + + add_default_settings(config) + + DEBUG_MESSAGE_LEVEL = config.getint("DEFAULT", 'DEBUG_MESSAGE_LEVEL') + PROPAGATE_THROUGH_ALL_NAMES = config.getboolean("DEFAULT", 'PROPAGATE_THROUGH_ALL_NAMES') + STORE_XREFS = config.getboolean("DEFAULT", 'STORE_XREFS') + SCAN_ANY_TYPE = config.getboolean("DEFAULT", 'SCAN_ANY_TYPE') diff --git a/plugins/HexRaysPyTools/__init__.py b/plugins/HexRaysPyTools/__init__.py new file mode 100644 index 0000000..0545fcc --- /dev/null +++ b/plugins/HexRaysPyTools/__init__.py @@ -0,0 +1,7 @@ +import logging +import Settings + +Settings.load_settings() +logging.basicConfig(format='[%(levelname)s] %(message)s\t(%(module)s:%(funcName)s)') +logging.root.setLevel(Settings.DEBUG_MESSAGE_LEVEL) + diff --git a/plugins/HexraysInvertIf32.dll b/plugins/HexraysInvertIf32.dll deleted file mode 100644 index ccbd6c5bbfbf14ffed8dd97f2bb27e80f7b523e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 diff --git a/plugins/HexraysInvertIf64.dll b/plugins/HexraysInvertIf64.dll deleted file mode 100644 index f36e1d7cff160030cd37d4a1080b3441fe48479c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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�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 E0UxP$_5c6?