From 433cf594e88dcbaac2580d8a3ef3222f97473c56 Mon Sep 17 00:00:00 2001 From: Aleks Matrosov Date: Sat, 21 Sep 2013 14:46:19 +0400 Subject: [PATCH] v1.0 source code upload --- .../2013}/HexRaysCodeXplorer v1.0.pdf | Bin .../2013}/HexRaysCodeXplorer.plw | Bin .../Beta1_21-07-2013/HexRaysCodeXplorer.plw | Bin 76800 -> 0 bytes .../Beta2_02-09-2013/HexRaysCodeXplorer.plw | Bin 81920 -> 0 bytes sources/HexRaysCodeXplorer.sln | 23 + sources/HexRaysCodeXplorer.suo | Bin 0 -> 32256 bytes sources/HexRaysCodeXplorer/CodeXplorer.cpp | 375 ++++++++++++++++ sources/HexRaysCodeXplorer/Common.h | 33 ++ sources/HexRaysCodeXplorer/GraphBuilder..cpp | 253 +++++++++++ sources/HexRaysCodeXplorer/GraphBuilder.cpp | 271 ++++++++++++ sources/HexRaysCodeXplorer/GraphBuilder.h | 122 ++++++ .../HexRaysCodeXplorer/HexRaysCodeXplorer.cpp | 297 +++++++++++++ .../HexRaysCodeXplorer/HexRaysCodeXplorer.h | 150 +++++++ .../HexRaysCodeXplorer.vcxproj | 107 +++++ .../HexRaysCodeXplorer.vcxproj.filters | 41 ++ .../HexRaysCodeXplorer.vcxproj.user | 11 + .../HexRaysCodeXplorer/IdaGraphBuilder.cpp | 294 +++++++++++++ sources/HexRaysCodeXplorer/IdaGraphBuilder.h | 29 ++ sources/HexRaysCodeXplorer/ObjectExplorer.cpp | 252 +++++++++++ sources/HexRaysCodeXplorer/ObjectExplorer.h | 74 ++++ sources/HexRaysCodeXplorer/ObjectType.cpp | 413 ++++++++++++++++++ sources/HexRaysCodeXplorer/ObjectType.h | 49 +++ 22 files changed, 2794 insertions(+) rename {Hex-Rays Plugin contest release => Hex-Rays Plugin contest/2013}/HexRaysCodeXplorer v1.0.pdf (100%) rename {Hex-Rays Plugin contest release => Hex-Rays Plugin contest/2013}/HexRaysCodeXplorer.plw (100%) delete mode 100644 beta-test/Beta1_21-07-2013/HexRaysCodeXplorer.plw delete mode 100644 beta-test/Beta2_02-09-2013/HexRaysCodeXplorer.plw create mode 100644 sources/HexRaysCodeXplorer.sln create mode 100644 sources/HexRaysCodeXplorer.suo create mode 100644 sources/HexRaysCodeXplorer/CodeXplorer.cpp create mode 100644 sources/HexRaysCodeXplorer/Common.h create mode 100644 sources/HexRaysCodeXplorer/GraphBuilder..cpp create mode 100644 sources/HexRaysCodeXplorer/GraphBuilder.cpp create mode 100644 sources/HexRaysCodeXplorer/GraphBuilder.h create mode 100644 sources/HexRaysCodeXplorer/HexRaysCodeXplorer.cpp create mode 100644 sources/HexRaysCodeXplorer/HexRaysCodeXplorer.h create mode 100644 sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj create mode 100644 sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.filters create mode 100644 sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.user create mode 100644 sources/HexRaysCodeXplorer/IdaGraphBuilder.cpp create mode 100644 sources/HexRaysCodeXplorer/IdaGraphBuilder.h create mode 100644 sources/HexRaysCodeXplorer/ObjectExplorer.cpp create mode 100644 sources/HexRaysCodeXplorer/ObjectExplorer.h create mode 100644 sources/HexRaysCodeXplorer/ObjectType.cpp create mode 100644 sources/HexRaysCodeXplorer/ObjectType.h diff --git a/Hex-Rays Plugin contest release/HexRaysCodeXplorer v1.0.pdf b/Hex-Rays Plugin contest/2013/HexRaysCodeXplorer v1.0.pdf similarity index 100% rename from Hex-Rays Plugin contest release/HexRaysCodeXplorer v1.0.pdf rename to Hex-Rays Plugin contest/2013/HexRaysCodeXplorer v1.0.pdf diff --git a/Hex-Rays Plugin contest release/HexRaysCodeXplorer.plw b/Hex-Rays Plugin contest/2013/HexRaysCodeXplorer.plw similarity index 100% rename from Hex-Rays Plugin contest release/HexRaysCodeXplorer.plw rename to Hex-Rays Plugin contest/2013/HexRaysCodeXplorer.plw diff --git a/beta-test/Beta1_21-07-2013/HexRaysCodeXplorer.plw b/beta-test/Beta1_21-07-2013/HexRaysCodeXplorer.plw deleted file mode 100644 index 869ba40275a5352b265d6d66c7ef3ea422afee29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76800 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 sIzObf?7I6Yc4C zr+t-ut$iI<9NX+I_7nC_d$&Ewp>gOPSs1A*F<#d@8XT)Icdv77rM|9)&QCUHt2L>b zV$E93I?Z%#u@(xSEDDR#5@S(ey%ldsu&6DGD9ad&#-hji%VtTnq@!e67N;c-YowW$ zVri9BWAR$*EsHD-tjudHYc1F968pl3PyT#c>4)(pC}q_n2Ar5sLaPdS=$3{(_A#m7r6b0na>RqCPNLvXzn>`Z69s;vz)uwTi2^@S;3o?FM1h|u R@Dl}oqQFlS_&1`!{|61j>dycG diff --git a/beta-test/Beta2_02-09-2013/HexRaysCodeXplorer.plw b/beta-test/Beta2_02-09-2013/HexRaysCodeXplorer.plw deleted file mode 100644 index 5f1bdf61c6aa5373d48c015939c6563339b05a7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81920 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 diff --git a/sources/HexRaysCodeXplorer.sln b/sources/HexRaysCodeXplorer.sln new file mode 100644 index 0000000..375b23b --- /dev/null +++ b/sources/HexRaysCodeXplorer.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HexRaysCodeXplorer", "HexRaysCodeXplorer\HexRaysCodeXplorer.vcxproj", "{F7E6B557-41F3-444A-BCA4-3527547DD665}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F7E6B557-41F3-444A-BCA4-3527547DD665}.Debug|Win32.ActiveCfg = Debug|Win32 + {F7E6B557-41F3-444A-BCA4-3527547DD665}.Debug|Win32.Build.0 = Debug|Win32 + {F7E6B557-41F3-444A-BCA4-3527547DD665}.Release|Win32.ActiveCfg = Release|Win32 + {F7E6B557-41F3-444A-BCA4-3527547DD665}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + VisualSVNWorkingCopyRoot = . + EndGlobalSection +EndGlobal diff --git a/sources/HexRaysCodeXplorer.suo b/sources/HexRaysCodeXplorer.suo new file mode 100644 index 0000000000000000000000000000000000000000..04be78e10bbc7b1f085d056a18ab9aeb6e70433b GIT binary patch literal 32256 zcmeHQ4Rl+@m3}4(2?>w@;V%RbNg#nls{U-}hY)PZP6GKc*f?3L0#g^-hW3tjdn*`=i$D1?PW;qa6FzBg~Irzh#@ zNlqL@IzCr3@4b0*@66n}bLY;T=k2c^we#-BChSrS;T&a{^2Of6ltXp*tB@wBVVt5I zf&}4`a6vV2mp(l!d_FfmDOiCmvY*Jq{8` z;%98}HI;Xnw^fNKDWz8lDp92wS4*rGpyeB{98yKRl#5pJGw&Yl{T5(ODc#B z5w5~$6L~+SjybFNInFt@88{Xw!`Ua41H6{NJd^{J0rDQl|C8Fa+4$!; zJVDDx`61uu_@^u&uh;;m0XY8oT#kRr1wt`{RFk1W-$FF<@Dpjo@V2yrW5nRfjT zh%W|wUAz7U;!6ORYS%%;ZCbh=aac=tBJR@CAk#D?k&k@B`t%}RX?X7nT(8mY^&`Gg zOJ9Zfx+gB1_~GO=i*Ea1;_LU`dH8F%Ra&S*c2dWfVK@^Ln^&jO;U0%C9*u@W=}0`5 zax4g@y3)b6Xm}(tj9wHEwl@Yt>3DKvYdD!gj#0jNPa>EMC&w|TFWw$*O2$`4+QZ3F zywM!FB0T*3@aoi|t-)w-81)@COc^m^%&2_07Rs`wo5p3VVDUAqOxR+y8LVd`WA{A8zaIY)Xbx;aEDz9%u@Nx`UnJ88)rG z=enE@r=t~|)1uA+QSf0L(zjWaKPw@RLMk_!*KHEy4|M>^pys*7Q|cI$H4H7llfW!M znU~UJU>I`&ar*%)G0QvE(J%5Ff6oGb%7G&Rq`~++-!&OfW+aX_$p23Kk%ncE$7x{i z!WG9Wm(dKk-dnE2qS+Fyl97wdpDgDsIn%Fz^w z#V8iki-x{*IF^dEMWT`PYDW_#0|ZGd6rR3FO@xztuNh(^OgVw=5LR(56B-`!SQqzz^B$jXjPq{BB=BLS0iYMs+etdmT9Uq$!Q9iAQ!GvS zJ(w`GylNaT9T+!Z{5jAcXJZ5=kd7(mz{)aOn~_$jvY;EiBv;B|diHHW~R!nFXvb>duD4yvo-lo+zi>& z<-eMU+dtPB$B%MDkZ?U}%crwsZWEqSCpDW1oxsm^FxR_Q@l*f$j0z)y8GdOC@S|nY zfPBGGhWyvhKT=m&in(qD+JL;(0NtoocN%UOZ?W z2WH@U+!w2-dgP)#Bhv=7yzE8>Z#9tl22aB>3Z}QKF+*Ub&`e~gyx*}N9hVgC(RtBQqDLV_d>zpiZGv;enri}T# zZu<8&FL>zn zuKuZQJ!}Sq(aJ}VL}QdcSNUf<@wcY2wxOoJy1Lfxbv1hIUaz;#Uhk{(+CA0o+G=mD z-(OQx-M@B0xbMQ?>J+Us%M($o#*%^DTaJ~XzC<#aEw!vpgdL$oV(r>BerJQLuG&{) zulH2XvU?l6F8i$d8mHaksi~gjcKh6QZg>A%6<1GP8<$eizf@dB=O=@SuKM0c6ehn? zYP!4PYART_p-+sQQtJ9;N_bI+MX3n|jAK zt#4+{nSq7=I$Mpy8;GYhyL^}=k{Y`F0JWVk2366$3B0w@8*mLB-HBbB+lba^7U zqTYHp`YTWNX3a4rsJuea&-|XCRKYs;QRari{Tv$X{FTSgS&d>@?Ca{TCPkAN@AA3= z`J~)A+E{&)Mmadt(Bch3yN-`s@@3U;@>hWlAu@2oh8uGJL8ur*stQTMWh~fqio8^& zKgYdrhQU;6NVO(GZsu21>GRBu0iDBtFfDfV^=Dc%%RR>1xb`13W*uYYMVq9hN{Yuv z15}yx)z`)IHwL#?WUwK@2B|>JraVnuR$Rt{KX%JY#r7Px$WB$S*FtVD#ZC$L&$%Z| z=+yQpxF^TGIk)OXJ@9%?q8$H+5K4J8r=;?4s2hLUPx+;k|7IoQ&v+tvus7F?9W#1S znC}^z?-|l-l^!MuNFn-EiJh+EzDT9-6Lw;UDXQ)ha+gxor-Qi?x zRRpF2>$?p`o)V*R88?Hpa30dfA??3iuCL3okYTl~TF5$RGt(?&u#DxmkfqYe-cVZ9 zgS)K%`CI>-M{3$Pc%NQ+=kVKiT*|#tJx?<&=@qf4$5EJdep7XzJm*U1*ZJz+aRN22 zht%WlG5ueXDhDM;?XsUg>|a0s@uu6JzrOd{Hu}h$w}EI0Jr&t)WbJB%3r@H^$Wmr7 z_dqnOu{h5|KRbFUN*@Q|XTiPoPTb{QyL^(LK=_|k(VvwopGVJ5R_1tsRs3H=-fsaIe5gJ5vQw(BXBd0=Mk++ULcchFq55!A z;#tM7_rH*S0Gw;}-lM`>fU~f9;Q0@(edtqT6+hQUA8Hz7vof<|{FX1aoU-V?JK7() z<&^hkm9~GZfJ-_{@&m};f4{({C_AykePFzA^`qC?oM$DM13Ua7wLhi*2^;&XUrSUH zX9e}1yL!)KJ{3XVhG?15%bxu6-#(~2&;QJQ)l*yg|1iMvyV76W<9llRQIAYe?%Fzb z@AL9~V_fKcnU7N5=$kERV9yX%0QRMK%3KqV4$>vtX$s~dp1|vd2O2$>x*>^dkVW)2 zabafSFGIH1cE)3N`Kv;iDA#NE#R{6gDVcNoQhKRfy=}6)_0)^|KeHcm`O$Uj{*PJ} zC89n^%~ke(P>R)&^7k+x%SG_UvwVyou-rkq4dFtF-jymy}JS|}K9=!-_KAu?! zsx8|-;4>%lk@w63=HIMVHp?rY>F2)%&@|dr+%ZUwD5z-!HBvJ(JNY6?8!Uc$tc5V@ zsDJRp0nZ*R(%Qq!?^4#Kob~T)yiDIX208yr&&woeNI~aX2ugS!HmS;m!a5lIPY2ch z^y9Y~s1TG{`^EWN?k1^c#5vNeymdA*@r)vanf<{J-bjjU1%Ijk=tp?LjJgNaNWa8G zYY0Z^YgO+OZ}Uuh>Fs|GUY@2EAkX>A^S?Z0#9pTtCAIQmBY^s6sr8=++|vJgTUF)g zUV0y8qrNO>fomIf?l@t>7GLz;*l(BgUfsyqQQtmHi zn9-!3t|0!;^jb`{+1#?D|NX#%-@o$pUvIc$cmKIRq8@J?>%ua~FA=<)ca>R5{IBo- z%=j0&-x(i!#=P*;s=6bdO)Ogf$G%D2(lxe)IC+tM5@W^C9p(tU()r}1Ew1;kn){PI z!}@-yT#WuQlBil=a&`Ow^n}d$6us*Ws zF|QmMinHSRtW{R+2Rw6?J@UmFENMSrP4#C&6Y6@1y92^KnCP` z?a#Fw&$L>t|2L8MQLWH?7By>Mt`O2_-2oi`nfa?rS@7_${2zfJL;1E{`LpUw>o#pJ#Bp*vWUmU#set-a`hjv7JUBS53l*n zp(|FN-Pix=shIS)a3z*4NAI zweR5JFSQKC@VmABw?gHWWB>P~uss&>7jq3^{n@`K1FY8ndgT4NhQHGHzcvi<_{&(% z@~uyAs_JWYfA_(YpMCzZBc6Wf{pNgokXplQQ7Qg*=D7Pl>3Q?~Z5!ODFa7wDd#BI7 zJ|FH}J4!MBpOsB+&Q&?%|8kV_gjP~9{H`KCK-7PyqO2JJtNj0cKDZRX@7(aKH`(@; z?0mFb&{BQRoX46!y=L4eZ?8LcbJf_*FPwm7rM_BBXV+?zX8$zWWyBZTVzAJx1ok?$ zbh)1$PkiKa>unKX% zlHW-Zo|HlZx$9K#o{bH!FNthj_24HvMt15sRMtn-W99xf<#nrs#o*Y-E|3j<`z5yy zKk^UXnbtP!^Z$9`i?MAZV5^mc$vC^A3tVaTb73wMyvh$QijNcVQ;*pnT+C;JxB4+8 zOZM{uWEQ+#T>hAAAm!*kw}Be22N{av_lk@Ke#^ms8Opj*E3X)SbL<)Y(}wcMI9jo$ z(Or0Iwf?oHC1voJ!nCwKab3=^-^X8mQ~HRvmjCRU_0zs_=kLG!#>2P0>LfX`3Nlvd zEB2EX;(Py%KRV;kDTg0MlhR||!+RI4*|hbl2NusB|4HD}I;nh1${80UTzh`qKGR>z z+gmBd|1R}EC={*?^^a1k{Q!!4L@TlwemB0!Wfq9vfeg~tV3t=tI~R#}wTt5Ti|c=r zP!`9j)ox#aydPS|UtIqqUfRj6;$MWkf6?&s^n;kQA71C3>z((&^`lq)%MJhB{=@JT ztwfe8gZ7dx8m#@el=Y9^c2mgqVa@+1Qvto!V#R+l^QK5uy8lpd{x_e2IRElI1-X=5 zVr-v$*G;(jj`l$MzooP<3381~jk*WQ+Cl(*AkBiD|E0a(EU$c~_dhk_r+qUH{5-vu zp+vr!8{_-ySrb=oj@&zD_~|`Q?>}qU)$eZ)3cv7w6kl|t2W}X+>21oBpv8O-Su=kB zp58Tw0<8A`FOXMwtryq-+!p2^?$J?8wTiz5d9B*t+*k*h1<8NJ&0v;SK3k5&PVJ)j z_%pYFmV-Y4+;aSJ-*f}GtrUNX{pmv^b~0}o@XzFE7>vh>wOtMKnWYPD#P8d07-Fi- z_FBXU+W4iRNNN${LqbE{Qu_YO>keY)}+g2cb{xoU{V5odSde=JN=!XKYL|E0*` za!$+}jj7`E7&jb%`saFiX2wVrxbh{9rlJ3zEW)#;l$8nl)2u$!kn;NbW&{>>ZEVJX2wFR<;f@ z0X6t_1=S#be^6?i2slvRANp#Yrxaf&x?zwr{l48L7vAR4LDE#p|DiEI194#Y+Qqkb J!0=^-{|B#6j#K~u literal 0 HcmV?d00001 diff --git a/sources/HexRaysCodeXplorer/CodeXplorer.cpp b/sources/HexRaysCodeXplorer/CodeXplorer.cpp new file mode 100644 index 0000000..775478d --- /dev/null +++ b/sources/HexRaysCodeXplorer/CodeXplorer.cpp @@ -0,0 +1,375 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#include "Common.h" +#include "GraphBuilder.h" +#include "ObjectExplorer.h" +#include "ObjectType.h" + + +// Hex-Rays API pointer +hexdsp_t *hexdsp = NULL; + +static bool inited = false; + +// Hotkey for the new command +static const char hotkey_dg[] = "G"; +static ushort hotcode_dg; + +static const char hotkey_ce[] = "E"; +static ushort hotcode_ce; + +static const char hotkey_rt[] = "R"; +static ushort hotcode_rt; + + + +//-------------------------------------------------------------------------- +// Helper class to build graph from ctree. +struct graph_builder_t : public ctree_parentee_t +{ + callgraph_t &cg; + std::map reverse; // Reverse mapping for tests and adding edges + + graph_builder_t(callgraph_t &_cg) : cg(_cg) {} + + // overriding functions + int add_node(citem_t *i); + int process(citem_t *i); + + // We treat expressions and statements the same way: add them to the graph + int idaapi visit_insn(cinsn_t *i) { return process(i); } + int idaapi visit_expr(cexpr_t *e) { return process(e); } +}; + +// Add a new node to the graph +int graph_builder_t::add_node(citem_t *i) +{ + // Check if the item has already been encountered during the traversal + if ( reverse.find(i) != reverse.end() ) + { + warning("bad ctree - duplicate nodes!"); + return -1; + } + + // Add a node to the graph + int n = cg.add(i); + + // Also remember the reverse mapping (citem_t* -> n) + reverse[i] = n; + + return n; +} + +// Process a ctree item +int graph_builder_t::process(citem_t *item) +{ + // Add a node for citem + int n = add_node(item); + if ( n == -1 ) + return -1; // error + + if ( parents.size() > 1 ) // The current item has a parent? + { + int p = reverse[parents.back()]; // Parent node number + // cg.add_edge(p, n); // Add edge from the parent to the current item + cg.create_edge(p, n); + } + + return 0; +} + +#define DECLARE_GI_VAR \ + graph_info_t *gi = (graph_info_t *) ud + +#define DECLARE_GI_VARS \ + DECLARE_GI_VAR; \ + callgraph_t *fg = &gi->fg + +//-------------------------------------------------------------------------- +static int idaapi gr_callback(void *ud, int code, va_list va) +{ + bool result = false; + switch ( code ) + { + // refresh user-defined graph nodes and edges + case grcode_user_refresh: + // in: mutable_graph_t *g + // out: success + { + DECLARE_GI_VARS; + func_t *f = get_func(gi->func_ea); + if (f == NULL) + break; + + graph_builder_t gb(*fg); // Graph builder helper class + //fg->walk_func(f); + gb.apply_to(&gi->vu->cfunc->body, NULL); + + mutable_graph_t *mg = va_arg(va, mutable_graph_t *); + + // we have to resize + mg->resize(fg->count()); + + callgraph_t::edge_iterator end = fg->end_edges(); + for ( callgraph_t::edge_iterator it=fg->begin_edges(); + it != end; + ++it ) + { + mg->add_edge(it->id1, it->id2, NULL); + } + + fg->clear_edges(); + result = true; + } + break; + + // retrieve text for user-defined graph node + case grcode_user_text: + //mutable_graph_t *g + // int node + // const char **result + // bgcolor_t *bg_color (maybe NULL) + // out: must return 0, result must be filled + // NB: do not use anything calling GDI! + { + DECLARE_GI_VARS; + va_arg(va, mutable_graph_t *); + int node = va_arg(va, int); + const char **text = va_arg(va, const char **); + bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); + + callgraph_t::nodeinfo_t *ni = fg->get_info(node); + result = ni != NULL; + if ( result ) + { + *text = ni->name.c_str(); + if ( bgcolor != NULL ) + *bgcolor = ni->color; + } + } + break; + } + return (int)result; +} + + +static bool idaapi display_graph(void *ud) +{ + vdui_t &vu = *(vdui_t *)ud; + + // Determine the ctree item to highlight + vu.get_current_item(USE_KEYBOARD); + citem_t *highlight = vu.item.is_citem() ? vu.item.e : NULL; + + graph_info_t *gi = graph_info_t::create(vu.cfunc->entry_ea, highlight); + + netnode id; + id.create(); + + // get function name + qstring title = gi->title;//"Funcion ctree graph"; + + HWND hwnd = NULL; + TForm *form = create_tform(title.c_str(), &hwnd); + + gi->vu = (vdui_t *)ud; + gi->form = form; + gi->gv = create_graph_viewer(form, id, gr_callback, gi, 0); + open_tform(form, FORM_TAB | FORM_MENU | FORM_QWIDGET); + viewer_fit_window(gi->gv); + + return true; +} + +// Get poinyter to func_t by routine name +func_t * get_func_by_name(const char *func_name) +{ + func_t * result_func = NULL; + size_t func_total = get_func_qty(); + if(func_total > 0) + { + char tmp[1024]; + for (unsigned int i = 0 ; i < func_total - 1 ; i ++) + { + func_t * func = getn_func(i); + if(func != NULL) + { + memset(tmp, 0x00, sizeof(tmp)); + char *func_n = get_func_name(func->startEA, tmp, sizeof(tmp)); + if(func_n != NULL) + { + if(!strcmp(func_name, func_n)) + { + result_func = func; + break; + } + } + } + } + } + return result_func; +} + +static bool idaapi decompile_func(vdui_t &vu) +{ + // Determine the ctree item to highlight + vu.get_current_item(USE_KEYBOARD); + citem_t *highlight = vu.item.is_citem() ? vu.item.e : NULL; + + if(highlight != NULL) + { + // if it is an expression + if(highlight->is_expr()) + { + cexpr_t *e = (cexpr_t *)highlight; + + // retireve the name of the routine + char tmp[1024]; + memset(tmp, 0x00, sizeof(tmp)); + e->print1(tmp, sizeof(tmp), NULL); + tag_remove(tmp, tmp, sizeof(tmp)); + + char *proc_name = tmp + strlen(tmp); + + while((proc_name > tmp) && (*(proc_name - 1) != '>')) + proc_name --; + + func_t * func = get_func_by_name(proc_name); + if(func != NULL) + { + vdui_t * decompiled_window = open_pseudocode(func->startEA, -1); + } + } + } + + return true; +} + +//display Class Explorer +static bool idaapi display_objects(void *ud) +{ + vdui_t &vu = *(vdui_t *)ud; + search_vtbl(); + custom_form_init(); + + return true; +} + +//-------------------------------------------------------------------------- +// This callback handles various hexrays events. +static int idaapi callback(void *, hexrays_event_t event, va_list va) +{ + switch ( event ) + { + case hxe_right_click: + { + vdui_t &vu = *va_arg(va, vdui_t *); + // add new command to the popup menu + add_custom_viewer_popup_item(vu.ct, "Display Graph", hotkey_dg, display_graph, &vu); + add_custom_viewer_popup_item(vu.ct, "Object Explorer", hotkey_ce, display_objects, &vu); + add_custom_viewer_popup_item(vu.ct, "REconstruct Type", hotkey_rt, reconstruct_type, &vu); + } + break; + + case hxe_keyboard: + { + vdui_t &vu = *va_arg(va, vdui_t *); + int keycode = va_arg(va, int); + int shift = va_arg(va, int); + // check for the hotkey + if ( lookup_key_code(keycode, shift, true) == hotcode_dg && shift == 0 ) + return display_graph(&vu); + if ( lookup_key_code(keycode, shift, true) == hotcode_dg && shift == 0 ) + return display_objects(&vu); + if ( lookup_key_code(keycode, shift, true) == hotcode_rt && shift == 0 ) + return reconstruct_type(&vu); + } + break; + case hxe_double_click: + { + vdui_t &vu = *va_arg(va, vdui_t *); + decompile_func(vu); + } + break; + default: + break; + } + return 0; +} + +//-------------------------------------------------------------------------- +// Initialize the plugin. +int idaapi init(void) +{ + if ( !init_hexrays_plugin() ) + return PLUGIN_SKIP; // no decompiler + install_hexrays_callback(callback, NULL); + const char *hxver = get_hexrays_version(); + msg("Hex-rays version %s has been detected, %s ready to use\n", hxver, PLUGIN.wanted_name); + inited = true; + hotcode_dg = get_key_code(hotkey_dg); // convert the hotkey to binary form + hotcode_rt = get_key_code(hotkey_rt); // convert the hotkey to binary form + + return PLUGIN_KEEP; +} + +//-------------------------------------------------------------------------- +void idaapi term(void) +{ + if ( inited ) + { + remove_hexrays_callback(callback, NULL); + term_hexrays_plugin(); + } +} + +//-------------------------------------------------------------------------- +void idaapi run(int) +{ + // This function won't be called because our plugin is invisible (no menu + // item in the Edit, Plugins menu) because of PLUGIN_HIDE +} + +//-------------------------------------------------------------------------- +static char comment[] = "HexRaysCodeXplorer plugin"; + +//-------------------------------------------------------------------------- +// +// PLUGIN DESCRIPTION BLOCK +// +//-------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_HIDE, // plugin flags + init, // initialize + term, // terminate. this pointer may be NULL. + run, // invoke plugin + comment, // long comment about the plugin + // it could appear in the status line or as a hint + "", // multiline help about the plugin + "HexRaysCodeXplorer", // the preferred short name of the plugin + "" // the preferred hotkey to run the plugin +}; diff --git a/sources/HexRaysCodeXplorer/Common.h b/sources/HexRaysCodeXplorer/Common.h new file mode 100644 index 0000000..ab95dcf --- /dev/null +++ b/sources/HexRaysCodeXplorer/Common.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#pragma warning (disable: 4996 4800 ) + +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/GraphBuilder..cpp b/sources/HexRaysCodeXplorer/GraphBuilder..cpp new file mode 100644 index 0000000..8134b6b --- /dev/null +++ b/sources/HexRaysCodeXplorer/GraphBuilder..cpp @@ -0,0 +1,253 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#pragma warning(disable: 4800 4018) + +#include + +#include +#include + +#include + +#include "GraphBuilder.h" + +bool callgraph_t::visited(citem_t *i, int *nid) +{ + ea_int_map_t::const_iterator it = ea2node.find(i); + if ( it != ea2node.end() ) + { + if ( nid != NULL ) + *nid = it->second; + return true; + } + return false; +} + + +void callgraph_t::create_edge(int id1, int id2) +{ + edges.push_back(edge_t(id1, id2)); +} + +char * callgraph_t::get_node_label(int n, char *buf, int bufsize) const +{ + int_ea_map_t::const_iterator it = node2ea.find(n); + + if ( it != node2ea.end() ) + { + const citem_t *item = it->second; + + char *ptr = buf; + char *endp = buf + bufsize; + + // Each node will have the element type at the first line + APPEND(ptr, endp, get_ctype_name(item->op)); + const cexpr_t *e = (const cexpr_t *)item; + const cinsn_t *i = (const cinsn_t *)item; + + // For some item types, display additional information + switch ( item->op ) + { + case cot_ptr : // *x + case cot_memptr : // x->m + // Display access size for pointers + ptr += qsnprintf(ptr, endp-ptr, ".%d", e->ptrsize); + if ( item->op == cot_ptr ) + break; + case cot_memref : // x.m + // Display member offset for structure fields + ptr += qsnprintf(ptr, endp-ptr, " (m=%d)", e->m); + break; + case cot_obj : // v + case cot_var : // l + // Display object size for local variables and global data + ptr += qsnprintf(ptr, endp-ptr, ".%d", e->refwidth); + case cot_num : // n + case cot_helper : // arbitrary name + case cot_str : // string constant + // Display helper names and number values + APPCHAR(ptr, endp, ' '); + e->print1(ptr, endp-ptr, NULL); + tag_remove(ptr, ptr, 0); + ptr = tail(ptr); + break; + case cit_goto: + // Display target label number for gotos + ptr += qsnprintf(ptr, endp-ptr, " LABEL_%d", i->cgoto->label_num); + break; + case cit_asm: + // Display instruction block address and size for asm-statements + ptr += qsnprintf(ptr, endp-ptr, " %a.%"FMT_Z, *i->casm->begin(), i->casm->size()); + break; + default: + break; + } + + // The second line of the node contains the item address + ptr += qsnprintf(ptr, endp-ptr, "\nea: %a", item->ea); + if ( item->is_expr() && !e->type.empty() ) + { + // For typed expressions, the third line will have + // the expression type in human readable form + APPCHAR(ptr, endp, '\n'); + if ( print_type_to_one_line(ptr, endp-ptr, idati, e->type.u_str()) != T_NORMAL ) + { // could not print the type? + APPCHAR(ptr, endp, '?'); + APPZERO(ptr, endp); + } + + if(e->type.is_ptr()) + { + typestring ptr_rem = remove_pointer(e->type); + if(ptr_rem.is_struct()) + { + qstring typenm; + + print_type_to_qstring(&typenm, "prefix ", 0,0, PRTYPE_MULTI | PRTYPE_TYPE | PRTYPE_SEMI, idati, ptr_rem.u_str()); + + // print_type_to_one_line(ptr, endp-ptr, idati, ptr_rem.u_str()); + } + } + } + } + + return buf; +} + +callgraph_t::nodeinfo_t *callgraph_t::get_info(int nid) +{ + nodeinfo_t *ret = NULL; + + do + { + // returned cached name + int_funcinfo_map_t::iterator it = cached_funcs.find(nid); + if ( it != cached_funcs.end() ) + { + ret = &it->second; + break; + } + + // node does not exist? + int_ea_map_t::const_iterator it_ea = node2ea.find(nid); + if ( it_ea == node2ea.end() ) + break; + + citem_t *pfn = it_ea->second; + if ( pfn == NULL ) + break; + + nodeinfo_t fi; + + // get name + char buf[MAXSTR]; + if(get_node_label(nid, buf, MAXSTR)) + fi.name = buf; + else + fi.name = "?"; + + // get color + if(pfn == highlighted) + fi.color = 2000; + else + fi.color = 1;//bgcolors.prolog_color;//calc_bg_color(pfn->startEA); + + //fi.ea = pfn->startEA; + fi.ea = 0; + + it = cached_funcs.insert(cached_funcs.end(), std::make_pair(nid, fi)); + ret = &it->second; + } while ( false ); + + return ret; +} + +//-------------------------------------------------------------------------- + +int callgraph_t::add(citem_t *i) +{ + // check if we are trying to add existing node + ea_int_map_t::const_iterator it = ea2node.find(i); + if ( it != ea2node.end() ) + return it->second; + + ea2node[i] = node_count; + node2ea[node_count] = i; + + int ret_val = node_count; + node_count ++; + return ret_val; +} + +//-------------------------------------------------------------------------- +callgraph_t::callgraph_t() : node_count(0) +{ + cur_text[0] = '\0'; +} + +//-------------------------------------------------------------------------- +void callgraph_t::clear_edges() +{ + edges.clear(); +} + +//-------------------------------------------------------------------------- +graph_info_t::graphinfo_list_t graph_info_t::instances; + +//-------------------------------------------------------------------------- +graph_info_t::graph_info_t() +{ + form = NULL; + gv = NULL; +} + +//-------------------------------------------------------------------------- +graph_info_t * graph_info_t::create(ea_t func_ea, citem_t *highlighted) +{ + graph_info_t *r; + + func_t *pfn = get_func(func_ea); + if ( pfn == NULL ) + return NULL; + + r = new graph_info_t(); + get_title(func_ea, &r->title); + r->func_ea = pfn->startEA; + r->fg.highlighted = highlighted; + instances.push_back(r); + + return r; +} + +//-------------------------------------------------------------------------- +bool graph_info_t::get_title(ea_t func_ea, qstring *out) +{ + // we should succeed in getting the name + char func_name[MAXSTR]; + if ( get_func_name(func_ea, func_name, sizeof(func_name)) == NULL ) + return false; + out->sprnt("Call graph of: %s", func_name); + return true; +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/GraphBuilder.cpp b/sources/HexRaysCodeXplorer/GraphBuilder.cpp new file mode 100644 index 0000000..9368a0f --- /dev/null +++ b/sources/HexRaysCodeXplorer/GraphBuilder.cpp @@ -0,0 +1,271 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "Common.h" +#include "GraphBuilder.h" + +#include +#include +#include + + +bool callgraph_t::visited(citem_t *i, int *nid) +{ + ea_int_map_t::const_iterator it = ea2node.find(i); + if ( it != ea2node.end() ) + { + if ( nid != NULL ) + *nid = it->second; + return true; + } + return false; +} + + +void callgraph_t::create_edge(int id1, int id2) +{ + edges.push_back(edge_t(id1, id2)); +} + +char * callgraph_t::get_node_label(int n, char *buf, int bufsize) const +{ + int_ea_map_t::const_iterator it = node2ea.find(n); + + if ( it != node2ea.end() ) + { + const citem_t *item = it->second; + + char *ptr = buf; + char *endp = buf + bufsize; + + // Each node will have the element type at the first line + APPEND(ptr, endp, get_ctype_name(item->op)); + const cexpr_t *e = (const cexpr_t *)item; + const cinsn_t *i = (const cinsn_t *)item; + + // For some item types, display additional information + switch ( item->op ) + { + case cot_ptr : // *x + case cot_memptr : // x->m + // Display access size for pointers + ptr += qsnprintf(ptr, endp-ptr, ".%d", e->ptrsize); + if ( item->op == cot_ptr ) + break; + case cot_memref : // x.m + // Display member offset for structure fields + ptr += qsnprintf(ptr, endp-ptr, " (m=%d)", e->m); + break; + case cot_obj : // v + case cot_var : // l + // Display object size for local variables and global data + ptr += qsnprintf(ptr, endp-ptr, ".%d", e->refwidth); + case cot_num : // n + case cot_helper : // arbitrary name + case cot_str : // string constant + // Display helper names and number values + APPCHAR(ptr, endp, ' '); + e->print1(ptr, endp-ptr, NULL); + tag_remove(ptr, ptr, 0); + ptr = tail(ptr); + break; + case cit_goto: + // Display target label number for gotos + ptr += qsnprintf(ptr, endp-ptr, " LABEL_%d", i->cgoto->label_num); + break; + case cit_asm: + // Display instruction block address and size for asm-statements + ptr += qsnprintf(ptr, endp-ptr, " %a.%"FMT_Z, *i->casm->begin(), i->casm->size()); + break; + default: + break; + } + + // The second line of the node contains the item address + ptr += qsnprintf(ptr, endp-ptr, "\nea: %a", item->ea); + if ( item->is_expr() && !e->type.empty() ) + { + // For typed expressions, the third line will have + // the expression type in human readable form + APPCHAR(ptr, endp, '\n'); + if ( print_type_to_one_line(ptr, endp-ptr, idati, e->type.u_str()) != T_NORMAL ) + { // could not print the type? + APPCHAR(ptr, endp, '?'); + APPZERO(ptr, endp); + } + + if(e->type.is_ptr()) + { + typestring ptr_rem = remove_pointer(e->type); + if(ptr_rem.is_struct()) + { + qstring typenm; + + print_type_to_qstring(&typenm, "prefix ", 0,0, PRTYPE_MULTI | PRTYPE_TYPE | PRTYPE_SEMI, idati, ptr_rem.u_str()); + } + } + } + } + + return buf; +} + +callgraph_t::nodeinfo_t *callgraph_t::get_info(int nid) +{ + nodeinfo_t *ret = NULL; + + do + { + // returned cached name + int_funcinfo_map_t::iterator it = cached_funcs.find(nid); + if ( it != cached_funcs.end() ) + { + ret = &it->second; + break; + } + + // node does not exist? + int_ea_map_t::const_iterator it_ea = node2ea.find(nid); + if ( it_ea == node2ea.end() ) + break; + + citem_t *pfn = it_ea->second; + if ( pfn == NULL ) + break; + + nodeinfo_t fi; + + // get name + char buf[MAXSTR]; + if(get_node_label(nid, buf, MAXSTR)) + fi.name = buf; + else + fi.name = "?"; + + // get color + if(pfn == highlighted) + fi.color = 2000; + else + fi.color = 1;//bgcolors.prolog_color;//calc_bg_color(pfn->startEA); + + //fi.ea = pfn->startEA; + fi.ea = 0; + + it = cached_funcs.insert(cached_funcs.end(), std::make_pair(nid, fi)); + ret = &it->second; + } while ( false ); + + return ret; +} + +//-------------------------------------------------------------------------- + +int callgraph_t::add(citem_t *i) +{ + // check if we are trying to add existing node + ea_int_map_t::const_iterator it = ea2node.find(i); + if ( it != ea2node.end() ) + return it->second; + + ea2node[i] = node_count; + node2ea[node_count] = i; + + int ret_val = node_count; + node_count ++; + return ret_val; +} + +//-------------------------------------------------------------------------- +callgraph_t::callgraph_t() : node_count(0) +{ + cur_text[0] = '\0'; +} + +//-------------------------------------------------------------------------- +void callgraph_t::clear_edges() +{ + edges.clear(); +} + +//-------------------------------------------------------------------------- +graph_info_t::graphinfo_list_t graph_info_t::instances; + +//-------------------------------------------------------------------------- +graph_info_t::graph_info_t() +{ + form = NULL; + gv = NULL; +} + +//-------------------------------------------------------------------------- +graph_info_t * graph_info_t::create(ea_t func_ea, citem_t *highlighted) +{ + graph_info_t *r; + + func_t *pfn = get_func(func_ea); + if ( pfn == NULL ) + return NULL; + + r = new graph_info_t(); + r->func_ea = pfn->startEA; + r->fg.highlighted = highlighted; + + size_t num_inst = 0; + for(graphinfo_list_t::iterator it = instances.begin() ; it != instances.end() ; it ++) + { + if(((*(it))->func_ea == func_ea) && (num_inst < (*(it))->func_instance_no)) + num_inst = (*(it))->func_instance_no; + } + + r->func_instance_no = ++ num_inst; + get_title(func_ea, num_inst, &r->title); + + instances.push_back(r); + + return r; +} + +//-------------------------------------------------------------------------- +bool graph_info_t::get_title(ea_t func_ea, size_t num_inst, qstring *out) +{ + // we should succeed in getting the name + char func_name[MAXSTR]; + if ( get_func_name(func_ea, func_name, sizeof(func_name)) == NULL ) + return false; + + out->sprnt("Call graph of: %s %d", func_name, num_inst); + + return true; +} + +void graph_info_t::destroy(graph_info_t *gi) +{ + for(graphinfo_list_t::iterator it = instances.begin() ; it != instances.end() ; it ++) + { + if((*(it)) == gi) + { + instances.erase(it); + break; + } + } +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/GraphBuilder.h b/sources/HexRaysCodeXplorer/GraphBuilder.h new file mode 100644 index 0000000..cd1c5d8 --- /dev/null +++ b/sources/HexRaysCodeXplorer/GraphBuilder.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +// function call graph creator class +class callgraph_t +{ + // total number of the nodes in the graph + int node_count; + + // node id to func addr and reverse lookup + typedef std::map ea_int_map_t; + typedef std::map int_ea_map_t; + ea_int_map_t ea2node; + int_ea_map_t node2ea; + + // current node search ptr + int cur_node; + char cur_text[MAXSTR]; + + bool visited(citem_t *i, int *nid); + +public: + + citem_t *highlighted; + + int add(citem_t *i); + // edge structure + struct edge_t + { + int id1; + int id2; + edge_t(int i1, int i2): id1(i1), id2(i2) { } + edge_t(): id1(0), id2(0) { } + }; + + typedef qlist edges_t; + + // edge manipulation + typedef edges_t::iterator edge_iterator; + void create_edge(int id1, int id2); + + edge_iterator begin_edges() { return edges.begin(); } + edge_iterator end_edges() { return edges.end(); } + + void clear_edges(); + + callgraph_t(); + + const int count() const { return node_count; } + + // node / func info + struct nodeinfo_t + { + qstring name; + bgcolor_t color; + ea_t ea; + }; + + typedef std::map int_funcinfo_map_t; + int_funcinfo_map_t cached_funcs; + nodeinfo_t *get_info(int nid); + + +// int walk_func(func_t *func); +private: + edges_t edges; + + char * get_node_label(int n, char *buf, int bufsize) const; +}; + + +// per function call graph context +class graph_info_t +{ +// Actual context variables +public: + callgraph_t fg; // associated graph maker + graph_viewer_t *gv; // associated graph_view + TForm *form; // associated TForm + vdui_t *vu; + + + ea_t func_ea; // function ea in question + qstring title; // the title + + size_t func_instance_no; +// Instance management +private: + typedef qlist graphinfo_list_t; + typedef graphinfo_list_t::iterator iterator; + + // Remove instance upon deletion of the objects + static graphinfo_list_t instances; + + + graph_info_t(); +public: + static graph_info_t *create(ea_t func_ea, citem_t *it); + static void destroy(graph_info_t *gi); + static bool get_title(ea_t func_ea, size_t num_inst, qstring *out); +}; \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.cpp b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.cpp new file mode 100644 index 0000000..4374666 --- /dev/null +++ b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.cpp @@ -0,0 +1,297 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#include "HexRaysCodeXplorer.h" + +itemrefs_t items; + + // Display a graph node. Feel free to modify this function to fine tune the node display. + char *idaapi get_node_label(int n, char *buf, int bufsize) + { + char *ptr = buf; + char *endp = buf + bufsize; + // Get the corresponding ctree item + const citem_t *item = items[n]; + // Each node will have the element type at the first line + APPEND(ptr, endp, get_ctype_name(item->op)); + const cexpr_t *e = (const cexpr_t *)item; + const cinsn_t *i = (const cinsn_t *)item; + // For some item types, display additional information + switch ( item->op ) + { + case cot_ptr : // *x + case cot_memptr : // x->m + // Display access size for pointers + ptr += qsnprintf(ptr, endp-ptr, ".%d", e->ptrsize); + if ( item->op == cot_ptr ) + break; + case cot_memref : // x.m + // Display member offset for structure fields + ptr += qsnprintf(ptr, endp-ptr, " (m=%d)", e->m); + break; + case cot_obj : // v + case cot_var : // l + // Display object size for local variables and global data + ptr += qsnprintf(ptr, endp-ptr, ".%d", e->refwidth); + case cot_num : // n + case cot_helper : // arbitrary name + case cot_str : // string constant + // Display helper names and number values + APPCHAR(ptr, endp, ' '); + e->print1(ptr, endp-ptr, NULL); + tag_remove(ptr, ptr, 0); + ptr = tail(ptr); + break; + case cit_goto: + // Display target label number for gotos + ptr += qsnprintf(ptr, endp-ptr, " LABEL_%d", i->cgoto->label_num); + break; + case cit_asm: + // Display instruction block address and size for asm-statements + ptr += qsnprintf(ptr, endp-ptr, " %a.%"FMT_Z, *i->casm->begin(), i->casm->size()); + break; + default: + break; + } + // The second line of the node contains the item address + ptr += qsnprintf(ptr, endp-ptr, "\nea: %a", item->ea); + if ( item->is_expr() && !e->type.empty() ) + { + // For typed expressions, the third line will have + // the expression type in human readable form + APPCHAR(ptr, endp, '\n'); + if ( print_type_to_one_line(ptr, endp-ptr, idati, e->type.u_str()) != T_NORMAL ) + { // could not print the type? + APPCHAR(ptr, endp, '?'); + APPZERO(ptr, endp); + } + + if(e->type.is_ptr()) + { + typestring ptr_rem = remove_pointer(e->type); + if(ptr_rem.is_struct()) + { + qstring typenm; + + print_type_to_qstring(&typenm, "prefix ", 0,0, PRTYPE_MULTI | PRTYPE_TYPE | PRTYPE_SEMI, idati, ptr_rem.u_str()); + +// print_type_to_one_line(ptr, endp-ptr, idati, ptr_rem.u_str()); + } + } + } + return buf; + } + + + // Display a graph edge. + bool idaapi print_edge(FILE *fp, int i, int j) const + { + qfprintf(fp, "edge: { sourcename: \"%d\" targetname: \"%d\" ", i, j); + const char *label = NULL; + const citem_t *a = items[i]; + const citem_t *b = items[j]; + if ( a->is_expr() ) // For expressions, add labels to the edges + { + cexpr_t *e = (cexpr_t *)a; + if ( e->x == b ) label = "x"; + if ( e->y == b ) label = "y"; + if ( e->z == b ) label = "z"; + } + if ( label != NULL ) + qfprintf(fp, "label: \"%s\" ", label); + qfprintf(fp, "}\n"); + return true; + } + // Determine the node color. Feel free to change it. + bgcolor_t idaapi get_node_color(int n) const + { + const citem_t *item = items[n]; + if ( item == highlight ) + return CL_GREEN; // Highlighted item + if ( item->is_expr() ) + { + char buf[MAXSTR]; + const cexpr_t *e = (const cexpr_t *)item; + if ( print_type_to_one_line(buf, sizeof(buf), idati, e->type.u_str()) != T_NORMAL ) + return CL_YELLOWGREEN; // Problematic type + } + + if(item->op == cot_call) + return CL_RED; + + return DEFCOLOR; + } + // Print the node color. + void idaapi print_node_attributes(FILE *fp, int n) const + { + bgcolor_t c = get_node_color(n); + if ( c != DEFCOLOR ) + qfprintf(fp, " color: %s", get_color_name(c)); + } +}; + +//-------------------------------------------------------------------------- +// Helper class to build graph from ctree. +struct graph_builder_t : public ctree_parentee_t +{ + cfunc_graph_t &cg; // Resulting graph + std::map reverse; // Reverse mapping for tests and adding edges + + graph_builder_t(cfunc_graph_t &_cg) : cg(_cg) {} + int add_node(citem_t *i); + int process(citem_t *i); + // We treat expressions and statements the same way: add them to the graph + int idaapi visit_insn(cinsn_t *i) { return process(i); } + int idaapi visit_expr(cexpr_t *e) { return process(e); } +}; + +// Add a new node to the graph +int graph_builder_t::add_node(citem_t *i) +{ + // Check if the item has already been encountered during the traversal + if ( reverse.find(i) != reverse.end() ) + { + warning("bad ctree - duplicate nodes!"); + return -1; + } + // Add a node to the graph + int n = cg.add_node(); + // Remember the pointer to the item, we will need it to generate GDL + // (in print_node_label) + if ( n <= cg.items.size() ) + cg.items.push_back(i); + cg.items[n] = i; + // Also remember the reverse mapping (citem_t* -> n) + reverse[i] = n; + return n; +} + +// Process a ctree item +int graph_builder_t::process(citem_t *item) +{ + // Add a node for citem + int n = add_node(item); + if ( n == -1 ) + return -1; // error + + if ( parents.size() > 1 ) // The current item has a parent? + { + int p = reverse[parents.back()]; // Parent node number + cg.add_edge(p, n); // Add edge from the parent to the current item + } + return 0; +} + +//-------------------------------------------------------------------------- +// Build and display graph for the ctree +static bool idaapi display_graph(void *ud) +{ + vdui_t &vu = *(vdui_t *)ud; + // Determine the ctree item to highlight + vu.get_current_item(USE_KEYBOARD); + citem_t *highlight = vu.item.is_citem() ? vu.item.e : NULL; + + cfunc_graph_t cg(highlight); // Graph to display + graph_builder_t gb(cg); // Graph builder helper class + // Build the graph by traversing the ctree + gb.apply_to(&vu.cfunc->body, NULL); + + // Our graph object 'cg' is ready. Now display it by converting it to GDL + // and calling wingraph32 + char fname[QMAXPATH]; + qtmpnam(fname, sizeof(fname)); // Generate temporary file name + gen_gdl(&cg, fname); // Generate GDL file from 'cg' graph + display_gdl(fname); // Display the GDL file + return true; // Success! +} + +func_t * get_func_by_name(const char *func_name) +{ + func_t * result_func = NULL; + size_t func_total = get_func_qty(); + if(func_total > 0) + { + char tmp[1024]; + for (int i = 0 ; i < func_total - 1 ; i ++) + { + func_t * func = getn_func(i); + if(func != NULL) + { + memset(tmp, 0x00, sizeof(tmp)); + char *func_n = get_func_name(func->startEA, tmp, sizeof(tmp)); + if(func_n != NULL) + { + if(!strcmp(func_name, func_n)) + { + result_func = func; + break; + } + } + } + } + } + return result_func; +} + +static bool idaapi decompile_func(vdui_t &vu) +{ + // Determine the ctree item to highlight + vu.get_current_item(USE_KEYBOARD); + citem_t *highlight = vu.item.is_citem() ? vu.item.e : NULL; + + if(highlight != NULL) + { + if(highlight->is_expr()) + //if(highlight->op == cot_call) + { + cexpr_t *e = (cexpr_t *)highlight; + char tmp[512]; + memset(tmp, 0x00, sizeof(tmp)); + e->print1(tmp, sizeof(tmp), NULL); + tag_remove(tmp, tmp, sizeof(tmp)); + + char *proc_name = tmp + strlen(tmp); + + + while((proc_name > tmp) && (*(proc_name - 1) != '>')) + proc_name --; + + msg("Function %s is choosen\n", proc_name); + + func_t * func = get_func_by_name(proc_name); + if(func != NULL) + { + vdui_t * decompiled_window = open_pseudocode(func->startEA, -1); + /* + hexrays_failure_t error; + cfuncptr_t decompiled = decompile(func, &error); + if(decompiled != NULL) + switch_to(decompiled); + */ + } + } + } + + return true; // Success! +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.h b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.h new file mode 100644 index 0000000..9765a11 --- /dev/null +++ b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#include +#include +#include + +// Hex-Rays API pointer +hexdsp_t *hexdsp = NULL; + +static bool inited = false; + +// Hotkey for the new command +static const char hotkey[] = "G"; +static ushort hotcode; + +//------------------------------------------------------------------------- +// red green blue +#define CL_WHITE ((255)+ (255<<8)+ (255<<16)) // 0 +#define CL_BLUE ((0 )+ (0 <<8)+ (255<<16)) // 1 +#define CL_RED ((255)+ (0 <<8)+ (0 <<16)) // 2 +#define CL_GREEN ((0 )+ (255<<8)+ (0 <<16)) // 3 +#define CL_YELLOW ((255)+ (255<<8)+ (0 <<16)) // 4 +#define CL_MAGENTA ((255)+ (0 <<8)+ (255<<16)) // 5 +#define CL_CYAN ((0 )+ (255<<8)+ (255<<16)) // 6 +#define CL_DARKGREY ((85 )+ (85 <<8)+ (85 <<16)) // 7 +#define CL_DARKBLUE ((0 )+ (0 <<8)+ (128<<16)) // 8 +#define CL_DARKRED ((128)+ (0 <<8)+ (0 <<16)) // 9 +#define CL_DARKGREEN ((0 )+ (128<<8)+ (0 <<16)) // 10 +#define CL_DARKYELLOW ((128)+ (128<<8)+ (0 <<16)) // 11 +#define CL_DARKMAGENTA ((128)+ (0 <<8)+ (128<<16)) // 12 +#define CL_DARKCYAN ((0 )+ (128<<8)+ (128<<16)) // 13 +#define CL_GOLD ((255)+ (215<<8)+ (0 <<16)) // 14 +#define CL_LIGHTGREY ((170)+ (170<<8)+ (170<<16)) // 15 +#define CL_LIGHTBLUE ((128)+ (128<<8)+ (255<<16)) // 16 +#define CL_LIGHTRED ((255)+ (128<<8)+ (128<<16)) // 17 +#define CL_LIGHTGREEN ((128)+ (255<<8)+ (128<<16)) // 18 +#define CL_LIGHTYELLOW ((255)+ (255<<8)+ (128<<16)) // 19 +#define CL_LIGHTMAGENTA ((255)+ (128<<8)+ (255<<16)) // 20 +#define CL_LIGHTCYAN ((128)+ (255<<8)+ (255<<16)) // 21 +#define CL_LILAC ((238)+ (130<<8)+ (238<<16)) // 22 +#define CL_TURQUOISE ((64 )+ (224<<8)+ (208<<16)) // 23 +#define CL_AQUAMARINE ((127)+ (255<<8)+ (212<<16)) // 24 +#define CL_KHAKI ((240)+ (230<<8)+ (140<<16)) // 25 +#define CL_PURPLE ((160)+ (32 <<8)+ (240<<16)) // 26 +#define CL_YELLOWGREEN ((154)+ (205<<8)+ (50 <<16)) // 27 +#define CL_PINK ((255)+ (192<<8)+ (203<<16)) // 28 +#define CL_ORANGE ((255)+ (165<<8)+ (0 <<16)) // 29 +#define CL_ORCHID ((218)+ (112<<8)+ (214<<16)) // 30 +#define CL_BLACK ((0 )+ (0 <<8)+ (0 <<16)) // 31 + +//------------------------------------------------------------------------- +// Convert internal background color code into textual form for GDL +static const char *get_color_name(bgcolor_t c) +{ + switch ( c ) + { + case CL_WHITE : return "white"; + case CL_BLUE : return "blue"; + case CL_RED : return "red"; + case CL_GREEN : return "green"; + case CL_YELLOW : return "yellow"; + case CL_MAGENTA : return "magenta"; + case CL_CYAN : return "cyan"; + case CL_DARKGREY : return "darkgrey"; + case CL_DARKBLUE : return "darkblue"; + case CL_DARKRED : return "darkred"; + case CL_DARKGREEN : return "darkgreen"; + case CL_DARKYELLOW : return "darkyellow"; + case CL_DARKMAGENTA : return "darkmagenta"; + case CL_DARKCYAN : return "darkcyan"; + case CL_GOLD : return "gold"; + case CL_LIGHTGREY : return "lightgrey"; + case CL_LIGHTBLUE : return "lightblue"; + case CL_LIGHTRED : return "lightred"; + case CL_LIGHTGREEN : return "lightgreen"; + case CL_LIGHTYELLOW : return "lightyellow"; + case CL_LIGHTMAGENTA: return "lightmagenta"; + case CL_LIGHTCYAN : return "lightcyan"; + case CL_LILAC : return "lilac"; + case CL_TURQUOISE : return "turquoise"; + case CL_AQUAMARINE : return "aquamarine"; + case CL_KHAKI : return "khaki"; + case CL_PURPLE : return "purple"; + case CL_YELLOWGREEN : return "yellowgreen"; + case CL_PINK : return "pink"; + case CL_ORANGE : return "orange"; + case CL_ORCHID : return "orchid"; + case CL_BLACK : return "black"; + } + return "?"; +} + +//-------------------------------------------------------------------------- +// Since we can not directly display cfunc_t as a graph, we build a graph +// object which will be saved as a GDL file and displayed with wingraph32. +class cfunc_graph_t : public gdl_graph_t +{ + typedef qvector itemrefs_t; + itemrefs_t items; + const citem_t *highlight; // item to highlight + friend struct graph_builder_t; + array_of_intseq_t succs; + array_of_intseq_t preds; + int idaapi nsucc(int b) const { return size() ? succs[b].size() : 0; } + int idaapi npred(int b) const { return size() ? preds[b].size() : 0; } + int idaapi succ(int b, int i) const { return succs[b][i]; } + int idaapi pred(int b, int i) const { return preds[b][i]; } +public: + cfunc_graph_t(const citem_t *_highlight) : highlight(_highlight) {} + int idaapi size(void) const { return preds.size(); } + int add_node(void) + { + int n = size(); + preds.resize(n+1); + succs.resize(n+1); + return n; + } + void add_edge(int x, int y) + { + preds[y].push_back(x); + succs[x].push_back(y); + } +}; + + +static bool idaapi display_graph(void *ud); +func_t* get_func_by_name(const char *func_name); +static bool idaapi decompile_func(vdui_t &vu); diff --git a/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj new file mode 100644 index 0000000..706057d --- /dev/null +++ b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj @@ -0,0 +1,107 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F7E6B557-41F3-444A-BCA4-3527547DD665} + Win32Proj + HexRaysCodeXplorer + HexRaysCodeXplorer + + + + DynamicLibrary + true + MultiByte + v100 + + + DynamicLibrary + false + true + MultiByte + v100 + + + + + + + + + + + + + true + build makefile + .plw + + + false + .plw + + + + + + Level3 + Disabled + __NT__;__IDP__;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(IDADIR)\idasdk64\include;$(IDADIR)\plugins\hexrays_sdk\include + MultiThreadedDebug + + + Console + true + ida.lib + /EXPORT:PLUGIN %(AdditionalOptions) + $(IDADIR)\idasdk64\lib\x86_win_vc_32 + + + + + Level3 + + + MaxSpeed + true + true + __NT__;__IDP__;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(IDADIR)\idasdk64\include;$(IDADIR)\plugins\hexrays_sdk\include + MultiThreaded + + + Console + true + true + true + ida.lib + /EXPORT:PLUGIN %(AdditionalOptions) + $(IDADIR)\idasdk64\lib\x86_win_vc_32 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.filters b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.filters new file mode 100644 index 0000000..f15c0d1 --- /dev/null +++ b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.filters @@ -0,0 +1,41 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.user b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.user new file mode 100644 index 0000000..f80fefa --- /dev/null +++ b/sources/HexRaysCodeXplorer/HexRaysCodeXplorer.vcxproj.user @@ -0,0 +1,11 @@ + + + + $(IDADIR)\idaq.exe + WindowsLocalDebugger + + + $(IDADIR)\idaq.exe + WindowsLocalDebugger + + \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/IdaGraphBuilder.cpp b/sources/HexRaysCodeXplorer/IdaGraphBuilder.cpp new file mode 100644 index 0000000..d45750d --- /dev/null +++ b/sources/HexRaysCodeXplorer/IdaGraphBuilder.cpp @@ -0,0 +1,294 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#include "IdaGraphBuilder.h" +#include "HexRaysCodeXplorer.h" + +#include + + + +//typedef std::map graph_nodes; + +typedef std::map basic_blocks_t; + +static std::vector graph_text; +static basic_blocks_t bbs; + + +static bool gather_basic_blocks(ea_t ea1, ea_t ea2) +{ + show_wait_box("Finding basic blocks"); + ea_t start = BADADDR; + bool ok = true; + int cnt = 0; + while ( ea1 != ea2 ) + { + if ( wasBreak() ) + { + ok = false; + break; + } + if ( start == BADADDR ) + { + start = ea1; + ea1 = nextthat(ea1, ea2, f_isCode, NULL); + if ( ea1 >= ea2 ) + break; + //start = ea1; + } + while ( ea1 < ea2 ) + { + if ( !ua_ana0(ea1) ) + break; + ea1 = get_item_end(ea1); + if ( is_basic_block_end(false) ) + break; + } + if ( ea1 != start ) + bbs[start] = ea1 - start; // remember the bb start and size + if ( !isCode(get_flags_novalue(ea1)) ) + start = BADADDR; + else + start = ea1; + } + hide_wait_box(); + return ok; +} + + +//-------------------------------------------------------------------------- +// wrapper for gather_basic_blocks() +static void update_basic_blocks(void) +{ + bbs.clear(); + func_t *f = NULL; + + f = get_func(get_screen_ea()); + if( f != NULL ) + { + if( gather_basic_blocks( f->startEA, f->endEA ) ) + { + //msg("List of basic blocks:\n"); + for ( basic_blocks_t::iterator p=bbs.begin(); p != bbs.end(); ++p ) + { + size_t i = p->first;// - base; + //msg("%08X: (end: %08X)\n",i,i+p->second); + } + } + + } + +} + + +//-------------------------------------------------------------------------- +// return number of instructions within two addresses +static size_t get_num_insns(ea_t start, ea_t end) +{ + ea_t cur = start; + size_t insns = 0; + + while( cur != BADADDR ) + { + if(isCode(getFlags(cur))) + insns++; + start = cur; + cur=next_head( start, end ); + } + + return insns; +} + +//-------------------------------------------------------------------------- +static int BuildGraph(void *, int code, va_list va) +{ + int result = 0; + switch ( code ) + { + + case grcode_user_refresh: // refresh user-defined graph nodes and edges + // in: mutable_graph_t *g + // out: success + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + msg("%x: refresh\n", g); + + if ( g->empty() ) + g->resize( (int)(bbs.size()) ); + + int j=0; + for ( basic_blocks_t::iterator p=bbs.begin(); p != bbs.end(); ++p ) + { + //size_t i = p->first;// - base; + //msg("%08X: (end: %08X)\n",i,i+p->second); + xrefblk_t xb; + for ( bool ok=xb.first_from(prevthat(p->first+p->second, 0, f_isCode, NULL), XREF_ALL); ok; ok=xb.next_from() ) + { + //xb.to - contains the referenced address + int k=0; + for ( basic_blocks_t::iterator p2=bbs.begin(); p2 != bbs.end(); ++p2 ) + { + if( xb.to == p2->first ) + { + g->add_edge(j, k, NULL); + msg("%08x: -> %08X\n", prevthat(p->first+p->second, 0, f_isCode, NULL), xb.to); + } + k++; + } + } + j++; + } + result = true; + } + break; + + case grcode_user_gentext: // generate text for user-defined graph nodes + // in: mutable_graph_t *g + // out: must return 0 + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + msg("%x: generate text for graph nodes\n", g); + graph_text.resize(g->size()); + + for ( node_iterator p=g->begin(); p != g->end(); ++p ) + { + int n = *p; + char buf[MAXSTR]; + + qsnprintf(buf,sizeof(buf),"Node %8d\n",n); + graph_text[n] = buf; + + int j=0; + for ( basic_blocks_t::iterator bbi=bbs.begin(); bbi != bbs.end(); ++bbi ) + { + if(n==j) + { + qsnprintf(buf, sizeof(buf), "StartEA %08X\n" + "EndEA %08X\n", + bbi->first, + bbi->first+bbi->second); + graph_text[n] += buf; + qsnprintf(buf,sizeof(buf),"Instr %8d\n",get_num_insns(bbi->first,bbi->first+bbi->second)); + graph_text[n] += buf; + break; + } + j++; + } + + qsnprintf(buf, sizeof(buf),"Indeg %8d\n" + "Outdeg %8d\n", + g->npred(n), + g->nsucc(n) + ); + + + graph_text[n] += buf; + + } + result = true; + } + break; + + case grcode_user_text: // retrieve text for user-defined graph node + // in: mutable_graph_t *g + // int node + // const char **result + // bgcolor_t *bg_color (maybe NULL) + // out: must return 0, result must be filled + // NB: do not use anything calling GDI! + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int node = va_argi(va, int); + const char **text = va_arg(va, const char **); + bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); + + int succ = g->nsucc(node); + int pred = g->npred(node); + + *text = graph_text[node].c_str(); + + if ( bgcolor != NULL ) + { + // same indegree as outdegree and != 0 ? + if(pred == succ && pred != 0) + *bgcolor = RGB(220, 220, 220); + + // a few edges only + else if( succ <= 2 && pred <= 2 ) + { + if( succ == 0 || pred == 0 ) + { + // nodes with no edges at all + if(pred == succ) + *bgcolor = RGB(255, 50, 50); + // nodes with either in- or outdegree edges only + else + *bgcolor = RGB(0, 130, 255); + } + // "normal" node, default color + else + *bgcolor = DEFCOLOR; + } + // in- or outdegree > 2 + else + *bgcolor = RGB( 255, 255, 0 ); + } + + result = true; + qnotused(g); + } + break; + + + case grcode_dblclicked: // a graph node has been double clicked + // in: graph_viewer_t *gv + // selection_item_t *current_item + // out: 0-ok, 1-ignore click + { + graph_viewer_t *v = va_arg(va, graph_viewer_t *); + selection_item_t *s = va_arg(va, selection_item_t *); + + if ( s->is_node ) + { + int j=0; + + for ( basic_blocks_t::iterator bbi=bbs.begin(); bbi != bbs.end(); ++bbi ) + { + if(s->node==j) + { + //jump to dblclicked node in disassembly/IDA graph view + jumpto(bbi->first,-1); + break; + } + j++; + } + } + + } + break; + + } + return result; +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/IdaGraphBuilder.h b/sources/HexRaysCodeXplorer/IdaGraphBuilder.h new file mode 100644 index 0000000..da9542a --- /dev/null +++ b/sources/HexRaysCodeXplorer/IdaGraphBuilder.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/ObjectExplorer.cpp b/sources/HexRaysCodeXplorer/ObjectExplorer.cpp new file mode 100644 index 0000000..f36d1ce --- /dev/null +++ b/sources/HexRaysCodeXplorer/ObjectExplorer.cpp @@ -0,0 +1,252 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "Common.h" +#include "ObjectExplorer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + + +LPCTSTR get_text_disasm(ea_t ea) +{ + static char disasm_buff[MAXSTR]; + disasm_buff[0] = disasm_buff[MAXSTR - 1] = 0; + + if(generate_disasm_line(ea, disasm_buff, (sizeof(disasm_buff) - 1))) + tag_remove(disasm_buff, disasm_buff, (sizeof(disasm_buff) - 1)); + + return(disasm_buff); +} + + +BOOL get_vtbl_info(ea_t ea_address, tVTBL_info &vtbl_info) +{ + flags_t flags = getFlags(ea_address); + if(!(hasRef(flags) || has_any_name(flags) && (isDwrd(flags) || isUnknown(flags)))) + return(FALSE); + else + { + BOOL is_move_xref = FALSE; + ea_t ea_code_ref = get_first_dref_to(ea_address); + if(ea_code_ref && (ea_code_ref != BADADDR)) + { + do + { + if(isCode(getFlags(ea_code_ref))) + { + LPCTSTR disasm_line = get_text_disasm(ea_code_ref); + if((*((PUINT) disasm_line) == 0x20766F6D /*"mov "*/) && (strstr(disasm_line+4, " offset ") != NULL)) + { + is_move_xref = TRUE; + break; + } + } + + ea_code_ref = get_next_dref_to(ea_address, ea_code_ref); + + } while(ea_code_ref && (ea_code_ref != BADADDR)); + } + if(!is_move_xref) + return(FALSE); + + ZeroMemory(&vtbl_info, sizeof(tVTBL_info)); + + get_name(BADADDR, ea_address, vtbl_info.name_size, (MAXSTR - 1)); + + ea_t ea_start = vtbl_info.ea_begin = ea_address; + while(TRUE) + { + flags_t index_flags = getFlags(ea_address); + if(!(hasValue(index_flags) && (isDwrd(index_flags) || isUnknown(index_flags)))) + break; + + ea_t ea_index_value = get_32bit(ea_address); + if(!(ea_index_value && (ea_index_value != BADADDR))) + break; + + if(ea_address != ea_start) + if(hasRef(index_flags)) + break; + + flags_t value_flags = getFlags(ea_index_value); + if(!isCode(value_flags)) + { + break; + } + else + if(isUnknown(index_flags)) + { + doDwrd(ea_address, sizeof(DWORD)); + } + + ea_address += sizeof(UINT); + }; + + if((vtbl_info.methods = ((ea_address - ea_start) / sizeof(UINT))) > 0) + { + vtbl_info.ea_end = ea_address; + return(TRUE); + } + else + { + return(FALSE); + } + } +} + + +qvector vtbl_list; +static BOOL process_vtbl(ea_t &ea_rdata) +{ + tVTBL_info vftable_info_t; + if(get_vtbl_info(ea_rdata, vftable_info_t)) + { + ea_rdata = vftable_info_t.ea_end; + ea_t eaAssumedCOL; + verify_32_t((vftable_info_t.ea_begin - 4), eaAssumedCOL); + + if(vftable_info_t.methods > 1) + { + if(has_user_name(getFlags(vftable_info_t.ea_begin))) + { + char szName[MAXSTR] = {0}; + get_short_name(BADADDR, vftable_info_t.ea_begin, szName, (MAXSTR - 1)); + + qstring vtbl_info; + vtbl_info.cat_sprnt("0x%x - 0x%x: \t %s \t method count: %u", vftable_info_t.ea_begin, vftable_info_t.ea_end, vftable_info_t.name_size, vftable_info_t.methods); + vtbl_list.push_back(vtbl_info); + + return(TRUE); + } + } + + return(FALSE); + } + + ea_rdata += sizeof(UINT); + return(FALSE); +} + + +void search_vtbl() +{ + segment_t *rdata_seg = get_segm_by_name(".rdata"); + ea_t ea_rdata = rdata_seg->startEA; + while(ea_rdata <= rdata_seg->endEA) + { + process_vtbl(ea_rdata); + }; +} + + + +//--------------------------------------------------------------------------- +// IDA Custom View Window Initialization +//--------------------------------------------------------------------------- +static bool idaapi ct_keyboard(TCustomControl * /*v*/, int key, int shift, void *ud) +{ + if ( shift == 0 ) + { + object_explorer_info_t *si = (object_explorer_info_t *)ud; + switch ( key ) + { + case IK_ESCAPE: + close_tform(si->form, FORM_SAVE | FORM_CLOSE_LATER); + return true; + } + } + return false; +} + + +static bool idaapi lines_linenum( + TCustomControl * /*cv*/, + const place_t *p, + uval_t *num, + void * /*ud*/) +{ + *num = p->touval(NULL) + 1; + return true; +} + + +int idaapi ui_callback(void *ud, int code, va_list va) +{ + object_explorer_info_t *si = (object_explorer_info_t *)ud; + switch ( code ) + { + case ui_tform_invisible: + { + TForm *f = va_arg(va, TForm *); + if ( f == si->form ) + { + delete si; + unhook_from_notification_point(HT_UI, ui_callback, NULL); + } + } + break; + } + return 0; +} + + +void custom_form_init() +{ + HWND hwnd = NULL; + TForm *form = create_tform("Object Explorer", &hwnd); + if ( hwnd == NULL ) + { + warning("Object Explorer window already open. Switching to it."); + form = find_tform("Object Explorer"); + if ( form != NULL ) + switchto_tform(form, true); + return; + } + + object_explorer_info_t *si = new object_explorer_info_t(form); + + qvector ::iterator vtbl_iter; + for ( vtbl_iter = vtbl_list.begin(); vtbl_iter != vtbl_list.end(); vtbl_iter++ ) + si->sv.push_back(simpleline_t(*vtbl_iter)); + + simpleline_place_t s1; + simpleline_place_t s2(si->sv.size()-1); + si->cv = create_custom_viewer("", NULL, &s1, &s2, &s1, 0, &si->sv); + si->codeview = create_code_viewer(form, si->cv, CDVF_NOLINES); + set_code_viewer_lines_icon_margin(si->codeview, 2); + hook_to_notification_point(HT_UI, ui_callback, si); + open_tform(form, FORM_TAB|FORM_MENU|FORM_RESTORE); +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/ObjectExplorer.h b/sources/HexRaysCodeXplorer/ObjectExplorer.h new file mode 100644 index 0000000..e5c970b --- /dev/null +++ b/sources/HexRaysCodeXplorer/ObjectExplorer.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include "ida.hpp" +#include "netnode.hpp" +#include + +#include + + + +struct object_explorer_info_t +{ + TForm *form; + TCustomControl *cv; + TCustomControl *codeview; + strvec_t sv; + object_explorer_info_t(TForm *f) : form(f), cv(NULL) {} +}; + +void custom_form_init(); + + +struct tVTBL_info +{ + ea_t ea_begin; + ea_t ea_end; + UINT methods; + char name_size[MAXSTR]; +}; + + +extern qvector vtbl_list; +extern qvector ::iterator vtbl_iter; + + +BOOL get_vtbl_info(ea_t eaAddress, tVTBL_info &rtInfo); +inline BOOL is_valid_name(LPCSTR pszName){ return(*((PDWORD) pszName) == 0x375F3F3F /*"??_7"*/); } +void parse_vft_members(LPCTSTR lpszName, ea_t eaStart, ea_t eaEnd); + +void search_vtbl(); + + +template BOOL verify_32_t(ea_t ea_ptr, T &rvalue) +{ + if(getFlags(ea_ptr)) + { + rvalue = (T) get_32bit(ea_ptr); + return(TRUE); + } + + return(FALSE); +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/ObjectType.cpp b/sources/HexRaysCodeXplorer/ObjectType.cpp new file mode 100644 index 0000000..46a92b7 --- /dev/null +++ b/sources/HexRaysCodeXplorer/ObjectType.cpp @@ -0,0 +1,413 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#include "Common.h" +#include "ObjectType.h" + +struct type_builder_t : public ctree_parentee_t +{ + cexpr_t *highl_expr; + + char highl_expr_name[MAXSTR]; + + struct struct_filed + { + int offset; + int size; + }; + + std::vector structure; + + + int idaapi visit_expr(cexpr_t *e); + + char * get_structure(char * name, char * buffer, int buffer_size); + + int get_structure_size(); + + bool idaapi check_memptr(struct_filed &str_fld); + + bool idaapi check_idx(struct_filed &str_fld); + + bool idaapi check_helper(citem_t *parent, int &offs, int &size); + + bool idaapi check_ptr(struct_filed &str_fld); +}; + +int get_idx_type_size(cexpr_t *idx_expr) +{ + char buff[MAXSTR]; + print_type_to_one_line(buff, MAXSTR, idati, idx_expr->type.u_str()); + + if(strstr(buff, "char")) + return 1; + else if(strstr(buff, "short")) + return 2; + else if(strstr(buff, "int")) + return 4; + + return 0; +} + +bool idaapi type_builder_t::check_helper(citem_t *parent, int &off, int &num) +{ + if(parent->op == cot_call) + { + cexpr_t *expr_2 = (cexpr_t *)parent; + if(!strcmp(get_ctype_name(expr_2->x->op), "helper")) + { + char buff[MAXSTR]; + expr_2->x->print1(buff, MAXSTR, NULL); + tag_remove(buff, buff, 0); + + if(!strcmp(buff, "LOBYTE")) + { + num = 1; + off = 0; + } + else if(!strcmp(buff, "HIBYTE") || !strcmp(buff, "BYTE3")) + { + num = 1; + off = 3; + } + else if(!strcmp(buff, "BYTE1")) + { + num = 1; + off = 1; + } + else if(!strcmp(buff, "BYTE2")) + { + num = 1; + off = 2; + } + else if(!strcmp(buff, "LOWORD")) + { + num = 2; + off = 0; + } + else if(!strcmp(buff, "HIWORD")) + { + num = 2; + off = 2; + } + else + { + return false; + } + + return true; + } + } + + return false; +} + +bool idaapi type_builder_t::check_memptr(struct_filed &str_fld) +{ + // check if it has at least two parents + if ( parents.size() > 2 ) + { + citem_t *parent_1 = parents.back(); + + // check if its parent is memptr + if(parent_1->is_expr() && (parent_1->op == cot_memptr)) + { + citem_t *parent_2 = parents[parents.size() - 2]; + citem_t *parent_3 = NULL; + + int num = 0; + int off = 0; + + // check presence of the helper block + bool bHelper = check_helper(parent_2, off, num); + if(bHelper) + parent_3 = parents[parents.size() - 3]; + else + parent_3 = parent_2; + + if(parent_2->is_expr() && (parent_2->op == cot_asg)) + { + cexpr_t *expr = (cexpr_t *)parent_1; + + if(bHelper) + { + str_fld.offset = expr->m + off; + str_fld.size = num; + } + else + { + str_fld.offset = expr->m; + str_fld.size = expr->ptrsize; + } + + return true; + } + } + } + + return false; +} + +bool idaapi type_builder_t::check_ptr(struct_filed &str_fld) +{ + // check if it has at least three parents + if ( parents.size() > 2 ) + { + citem_t *parent_1 = parents.back(); + int offset = 0; + int parent_idx = 1; + + // if its parent is addition + if(parent_1->is_expr() && (parent_1->op == cot_add)) + { + parent_idx ++; + cexpr_t *expr_2 = (cexpr_t *)parent_1; + + // get index_value + char buff[MAXSTR]; + expr_2->y->print1(buff, MAXSTR, NULL); + tag_remove(buff, buff, 0); + offset = atoi(buff); + } + + citem_t *parent_3 = parents[parents.size() - parent_idx]; + if(parent_3->is_expr() && (parent_3->op == cot_cast)) + parent_idx ++; + + citem_t *parent_4 = parents[parents.size() - parent_idx]; + if(parent_4->is_expr() && (parent_4->op == cot_ptr)) + { + parent_idx ++; + citem_t *parent_5 = parents[parents.size() - parent_idx]; + + int num_hlpr = 0; + int off_hlpr = 0; + + bool bHelper = check_helper(parent_5, off_hlpr, num_hlpr); + if(bHelper) + parent_idx ++; + + citem_t *parent_6 = parents[parents.size() - parent_idx]; + if(parent_6->is_expr() && (parent_6->op == cot_asg)) + { + cexpr_t *expr_4 = (cexpr_t *)parent_4; + + if(bHelper) + { + str_fld.offset = offset + off_hlpr; + str_fld.size = num_hlpr; + } + else + { + str_fld.offset = offset; + str_fld.size = expr_4->ptrsize; + } + + return true; + } + } + } + + return false; +} + +bool idaapi type_builder_t::check_idx(struct_filed &str_fld) +{ + // check if it has at least two parents + if ( parents.size() > 1 ) + { + citem_t *parent_1 = parents.back(); + + // if its parrent is + if(parent_1->is_expr() && (parent_1->op == cot_memptr)) + { + citem_t *parent_2 = parents[parents.size() - 2]; + if(parent_2->op == cot_idx) + { + cexpr_t *expr_2 = (cexpr_t *)parent_2; + + // get index_value + char buff[MAXSTR]; + expr_2->y->print1(buff, MAXSTR, NULL); + tag_remove(buff, buff, 0); + int num = atoi(buff); + + citem_t *parent_3 = parents[parents.size() - 3]; + if(parent_3->is_expr() && (parent_3->op == cot_asg)) + { + cexpr_t *expr_1 = (cexpr_t *)parent_1; + + str_fld.offset = expr_1->m + num; + str_fld.size = get_idx_type_size(expr_2); + + return true; + } + } + } + } + + return false; +} + +int idaapi type_builder_t::visit_expr(cexpr_t *e) +{ + // check if the expression being visited is variable + if(e->op == cot_var) + { + // get the variable name + char expr_name[MAXSTR]; + e->print1(expr_name, MAXSTR, NULL); + tag_remove(expr_name, expr_name, 0); + + // check for the target variable + if(!strcmp(expr_name, highl_expr_name)) + { + struct_filed str_fld; + + if(check_memptr(str_fld)) + structure.push_back(str_fld); + else if(check_idx(str_fld)) + structure.push_back(str_fld); + else if(check_ptr(str_fld)) + structure.push_back(str_fld); + + } + } + + return 0; +} + +int type_builder_t::get_structure_size() +{ + int highest_offset = 0; + int reference_size = 0; + + + for(std::vector::iterator i = structure.begin(); i != structure.end() ; i ++) + { + if(highest_offset < i ->offset) + { + highest_offset = i ->offset; + reference_size = i->size; + } + } + + return highest_offset + reference_size; +} + +char * get_type_nm(int sz) +{ + switch(sz) + { + case 1: + return "char"; + case 2: + return "short"; + case 4: + return "int"; + } + + return "unk"; +} + +void sort_fields(std::vector &un) +{ + for(unsigned int i = 0 ; i < un.size() ; i ++) + for(unsigned int j = 0 ; j < un.size() - i - 1 ; j ++) + { + if(un[j].offset > un[j + 1].offset) + { + type_builder_t::struct_filed tmp = un[j]; + un[j] = un[j + 1]; + un[j + 1] = tmp; + } + } +} + +char * type_builder_t::get_structure(char * name, char * bufferr, int buffer_size) +{ + sort_fields(structure); + char *buffer = bufferr; + int offs = 0; + + buffer += sprintf_s(buffer, buffer_size - (int)(buffer - bufferr), "struct %s {\r\n", name); + for(unsigned int i = 0 ; i < structure.size() ; i ++) + { + if(structure[i].offset > offs) + { + buffer += sprintf_s(buffer, buffer_size - (int)(buffer - bufferr), "\tchar\tfiller_%d[%d];\r\n", i, structure[i].offset - offs); + offs = structure[i].offset; + } + + if(structure[i].offset == offs) + { + buffer += sprintf_s(buffer, buffer_size - (int)(buffer - bufferr), "\t%s\tfield_%d;\r\n", get_type_nm(structure[i].size), i); + offs += structure[i].size; + } + } + + buffer += sprintf_s(buffer, buffer_size - (int)(buffer - bufferr), "}"); + + return NULL; +} + + +bool idaapi reconstruct_type(void *ud) +{ + vdui_t &vu = *(vdui_t *)ud; + + // Determine the ctree item to highlight + vu.get_current_item(USE_KEYBOARD); + citem_t *highlight = vu.item.is_citem() ? vu.item.e : NULL; + + // highlight == NULL might happen if one chooses variable at local variables declaration statement + if(highlight != NULL) + { + // the chosen item must be an expression and of 'variable' type + if(highlight->is_expr() && (highlight->op == cot_var)) + { + cexpr_t *highl_expr = (cexpr_t *)highlight; + + // initialize type rebuilder + type_builder_t type_bldr; + type_bldr.highl_expr = highl_expr; + + highl_expr->print1(type_bldr.highl_expr_name, MAXSTR, NULL); + tag_remove(type_bldr.highl_expr_name, type_bldr.highl_expr_name, 0); + + // traverse the ctree structure + type_bldr.apply_to(&vu.cfunc->body, NULL); + + // get the structure description + char buffr[MAXSTR*10]; + type_bldr.get_structure("STRUCTURE_TYPE", buffr, sizeof(buffr)); + msg("%s", buffr); + } + } + else + { + msg("Invalid item is choosen"); + } + + return true; +} \ No newline at end of file diff --git a/sources/HexRaysCodeXplorer/ObjectType.h b/sources/HexRaysCodeXplorer/ObjectType.h new file mode 100644 index 0000000..8cd946f --- /dev/null +++ b/sources/HexRaysCodeXplorer/ObjectType.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2013 + REhints + All rights reserved. + + ============================================================================ + + This file is part of HexRaysCodeXplorer + + HexRaysCodeXplorer is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; + +typedef std::basic_string TSTRING; +typedef std::basic_string WSTRING; +typedef std::basic_string ASTRING; +typedef std::vector BUFFER; + +#ifdef _UNICODE +#define tcout std::wcout +#else +#define tcout std::cout +#endif + + +bool idaapi reconstruct_type(void *ud); \ No newline at end of file