From 237aebdae521f601040b22b6ee2ae3ff2465e35b Mon Sep 17 00:00:00 2001 From: Alexandre Bartel Date: Sun, 15 Jul 2012 18:25:43 +0200 Subject: [PATCH] New -android-jars and -force-android-jar options to select Android API versions when analyzing Android applications --- ant.settings.template | 2 + build.xml | 1 + .../sable/soot/ui/PhaseOptionsDialog.java | 72 +++++++++ generated/options/soot/AntTask.java | 10 ++ generated/options/soot/options/Options.java | 42 +++++ .../options/soot/options/SparkOptions.java | 2 +- libs/AXMLPrinter2.jar | Bin 0 -> 28108 bytes src/soot/Scene.java | 149 +++++++++++++++++- src/soot/options/soot_options.xml | 29 ++++ 9 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 libs/AXMLPrinter2.jar diff --git a/ant.settings.template b/ant.settings.template index 6c28457785f..5b8ae67cf75 100644 --- a/ant.settings.template +++ b/ant.settings.template @@ -33,3 +33,5 @@ release.loc=lib ## javadoc comments. javaapi.url=http://java.sun.com/j2se/1.5.0/docs/api/ +# AXMLPrinter2.jar +axmlprinter2.jar=./libs/AXMLPrinter2.jar diff --git a/build.xml b/build.xml index 56303e9fb35..6b5822102f1 100644 --- a/build.xml +++ b/build.xml @@ -30,6 +30,7 @@ + diff --git a/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java b/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java index d4a89230d5c..a70b1b187e8 100644 --- a/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java +++ b/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java @@ -2093,6 +2093,24 @@ private boolean createNewConfig() { getConfig().put(getInput_Optionsprocess_dir_widget().getAlias(), stringRes); } + stringRes = getInput_Optionsandroid_jars_widget().getText().getText(); + + defStringRes = ""; + + + if ( (!(stringRes.equals(defStringRes))) && (stringRes != null) && (stringRes.length() != 0)) { + getConfig().put(getInput_Optionsandroid_jars_widget().getAlias(), stringRes); + } + + stringRes = getInput_Optionsforce_android_jar_widget().getText().getText(); + + defStringRes = ""; + + + if ( (!(stringRes.equals(defStringRes))) && (stringRes != null) && (stringRes.length() != 0)) { + getConfig().put(getInput_Optionsforce_android_jar_widget().getAlias(), stringRes); + } + stringRes = getInput_Optionsmain_class_widget().getText().getText(); defStringRes = ""; @@ -6572,6 +6590,30 @@ public StringOptionWidget getInput_Optionssoot_classpath_widget() { + private StringOptionWidget Input_Optionsandroid_jars_widget; + + private void setInput_Optionsandroid_jars_widget(StringOptionWidget widget) { + Input_Optionsandroid_jars_widget = widget; + } + + public StringOptionWidget getInput_Optionsandroid_jars_widget() { + return Input_Optionsandroid_jars_widget; + } + + + + private StringOptionWidget Input_Optionsforce_android_jar_widget; + + private void setInput_Optionsforce_android_jar_widget(StringOptionWidget widget) { + Input_Optionsforce_android_jar_widget = widget; + } + + public StringOptionWidget getInput_Optionsforce_android_jar_widget() { + return Input_Optionsforce_android_jar_widget; + } + + + private StringOptionWidget Input_Optionsmain_class_widget; private void setInput_Optionsmain_class_widget(StringOptionWidget widget) { @@ -10285,6 +10327,36 @@ private Composite Input_OptionsCreate(Composite parent) { setInput_Optionssoot_classpath_widget(new StringOptionWidget(editGroupInput_Options, SWT.NONE, new OptionData("Soot Classpath", "", "","cp", "\nUse PATH as the list of directories in which Soot should search \nfor classes. PATH should be a series of directories, separated \nby the path separator character for your system. If no classpath \nis set on the command line, but the system property \nsoot.class.path has been set, Soot uses its value as the \nclasspath. If neither the command line nor the system properties \nspecify a Soot classpath, Soot falls back on a default classpath \nconsisting of the value of the system property java.class.path \nfollowed java.home/lib/rt.jar, where java.home stands for the \ncontents of the system property java.home and / stands for the \nsystem file separator.", defaultString))); + defKey = ""+" "+""+" "+"android-jars"; + defKey = defKey.trim(); + + if (isInDefList(defKey)) { + defaultString = getStringDef(defKey); + } + else { + + defaultString = ""; + + } + + setInput_Optionsandroid_jars_widget(new StringOptionWidget(editGroupInput_Options, SWT.NONE, new OptionData("Path to Android jar files", "", "","android-jars", "\nUse PATH as the directory in which Soot should search for the \nappropriate android.jar file to use. The directory must contain \nsubdirectories named after the Android SDK version. Those \nsubdirectories must each contain one android.jar file. For \ninstance if the target directory is \n/home/user/androidSDK/platforms/ subdirectories containing \nandroid.jar for Android SDK 8 and 13 must be named android-8/ \nand android-13/ respectively. Note, that this options requires \nthat only one Android application is analyzed at a time. The \nAndroid application must contain the AndroidManifest.xml file. \n ", defaultString))); + + + defKey = ""+" "+""+" "+"force-android-jar"; + defKey = defKey.trim(); + + if (isInDefList(defKey)) { + defaultString = getStringDef(defKey); + } + else { + + defaultString = ""; + + } + + setInput_Optionsforce_android_jar_widget(new StringOptionWidget(editGroupInput_Options, SWT.NONE, new OptionData("Force specific Android jar file", "", "","force-android-jar", "\nUse PATH as the path to the android.jar file Soot should use. \nThis option overrides the "android-jars" option. ", defaultString))); + + defKey = ""+" "+""+" "+"main-class"; defKey = defKey.trim(); diff --git a/generated/options/soot/AntTask.java b/generated/options/soot/AntTask.java index bd61eb4439a..d303f1e4032 100644 --- a/generated/options/soot/AntTask.java +++ b/generated/options/soot/AntTask.java @@ -189,6 +189,16 @@ public void setoaat(boolean arg) { if(arg) addArg("-oaat"); } + public void setandroid_jars(String arg) { + addArg("-android-jars"); + addArg(arg); + } + + public void setforce_android_jar(String arg) { + addArg("-force-android-jar"); + addArg(arg); + } + public void setast_metrics(boolean arg) { if(arg) addArg("-ast-metrics"); } diff --git a/generated/options/soot/options/Options.java b/generated/options/soot/options/Options.java index f726cbcf9ad..da357775419 100644 --- a/generated/options/soot/options/Options.java +++ b/generated/options/soot/options/Options.java @@ -214,6 +214,40 @@ else if( false ) oaat = true; + else if( false + || option.equals( "android-jars" ) + ) { + if( !hasMoreOptions() ) { + G.v().out.println( "No value given for option -"+option ); + return false; + } + String value = nextOption(); + + if( android_jars.length() == 0 ) + android_jars = value; + else { + G.v().out.println( "Duplicate values "+android_jars+" and "+value+" for option -"+option ); + return false; + } + } + + else if( false + || option.equals( "force-android-jar" ) + ) { + if( !hasMoreOptions() ) { + G.v().out.println( "No value given for option -"+option ); + return false; + } + String value = nextOption(); + + if( force_android_jar.length() == 0 ) + force_android_jar = value; + else { + G.v().out.println( "Duplicate values "+force_android_jar+" and "+value+" for option -"+option ); + return false; + } + } + else if( false || option.equals( "ast-metrics" ) ) @@ -997,6 +1031,12 @@ public List process_dir() { private boolean oaat = false; public void set_oaat( boolean setting ) { oaat = setting; } + public String android_jars() { return android_jars; } + public void set_android_jars( String setting ) { android_jars = setting; } + private String android_jars = ""; + public String force_android_jar() { return force_android_jar; } + public void set_force_android_jar( String setting ) { force_android_jar = setting; } + private String force_android_jar = ""; public boolean ast_metrics() { return ast_metrics; } private boolean ast_metrics = false; public void set_ast_metrics( boolean setting ) { ast_metrics = setting; } @@ -1186,6 +1226,8 @@ public String getUsage() { +padOpt(" -pp -prepend-classpath", "Prepend the given soot classpath to the default classpath." ) +padOpt(" -process-path DIR -process-dir DIR", "Process all classes found in DIR" ) +padOpt(" -oaat", "From the process-dir, processes one class at a time." ) ++padOpt(" -android-jars PATH", "Use PATH as the path for finding the android.jar file" ) ++padOpt(" -force-android-jar PATH", "Force Soot to use PATH as the path for the android.jar file." ) +padOpt(" -ast-metrics", "Compute AST Metrics if performing java to jimple" ) +padOpt(" -src-prec FORMAT", "Sets source precedence to FORMAT files" ) +padVal(" c class (default)", "Favour class files as Soot source" ) diff --git a/generated/options/soot/options/SparkOptions.java b/generated/options/soot/options/SparkOptions.java index 5a5f9f8e880..fb9ee4a21b6 100644 --- a/generated/options/soot/options/SparkOptions.java +++ b/generated/options/soot/options/SparkOptions.java @@ -536,7 +536,7 @@ public String geom_dump_verbose() { * If you want to compare the precision of the points-to * results with other solvers (e.g. Paddle), you can use the - * "verify-file" to specify the list of methods (soot method + * 'verify-file' to specify the list of methods (soot method * signature format) that are reachable by that solver. Then, in * the internal evaluations (see the switch geom-eval), we only * consider the methods that are present to both solvers. diff --git a/libs/AXMLPrinter2.jar b/libs/AXMLPrinter2.jar new file mode 100644 index 0000000000000000000000000000000000000000..6498a955780f5a0d96951859b4886b85452c8147 GIT binary patch literal 28108 zcmagF1C%Vm+9leyZQHhO+qUgK?e5dIZQHiHPusR_oY!~muemdKUe=1LT$Q^rB36B| zbAJd0X*PP)Ab&n(MO6f8CFR8E75+CEs`VHb#n0t`G?f1dlNFSc z6cbfep_3JpHMFxdGj(>MRWo&RwzRk7B4DIrfD(2xHFPmGp%wDvB4D87-J9O{|^Mre;_<;Z5&)}Z2lie_Llu6Z0g|hGiN$u8$)O3Xcb#!Y*iFLJBT$| z4GBn;Ci*n1fGz~3bt_syDMEwte35<)pBMv|*k--y7B&na)a$a2euo~1uHJUrbp3MEpc4$Q zF%yHz!-{mVMlz%0$ByeMWtn}EKcU8FPgZoAMi6xAsCa_#U??Jg(7^^dL6J-hhf#tF znMI(zviU8RCpOnn%;)6#7gY+2v^vs0A_dL0TQSL?|3*@hPT2&wP|=5a>o)Krv#zKn zm`DzYhR(@V4VT!dw2>Y#0oVJ1s*?;wc$DS_s^4J+H5FpX_I!g54vlv$PsX+#A10J4 zNACiGnaCgvw$&j+B}mNKmPLcrs$(#1*&Qq<AQ! zl~hB=n}0aqvXgEaw7I1CLFu8N602ULeEs2RWtDcXl^UT96=0$}U=z+QVYAhXNOd>Y zb>bAXs|i~0w{jkmJY=oig+s%5KU+M2D&&$QQ6K&;2qdk?Fa9EJ&Ethv%$S7oLL}PB zkW~uCqJs{tZ>$W?SKm-`wTfaeJpS^-vPHf(5f{ZyYHZvqTTjhQsc>X(O|VFH1qH1N zxuatv=~~g_?ra&fhGM(pdvl(&a;>s6i^R(tUec1=^~u}KSKt$Rt8&~>tJE0@b3tzR z$VQeUor%RFtJ!Eo^of!u^>KO>Ya*t4h4EFXwObbB1wy?*ny`hJip0xqH)NxYu01t! zs`pNni#Su+(S#NuYhFmyR#)ev6BPDDX7%F2B85yM)n>tEM`8ngew=a_Y*h<%NvFD(W8s{ z7_x|C+Au~DRhHvn_(cU#Y-WsHkUKUS$+jpe19YlcL<+*Wx^OJ?fbi0m)sB?ngr$ruI zA!GQ?AbPf0J<6?b4a{$&Yx)KxfHEKEfbAH<8t&nGXVN%|3bjr8fM`62yw-Hy7_@b)M_35& zr1j5)ymKM-U!ebFw0{zZUUfSUHYfmq0W<&r(fd#u@=fN*6Xlk+v zy|O&UitVsBGXK1&zrJSi{Q>)+J_q`_LF_p6sNLsVHe*bWV$_}JH`FloCqqw^V}lHV zl*LEt;~QZ*lc}%p=fngIbZD1U9krJy3P_L@hXEIJzz@Noohh4=DqwhgM7Otp?SNTj*XtbIyzJbE z3KGyY*E}`I>bx{#M?+27qWfHIfIbUDP1ntC)A|(LEWyD=oHCMv_}lK0BmZv{0gd^uCFLz50X6x{eLJ zS$L;HeNVOlTN*)ww{bb2=ghh@)ZSEX18(M^m0uGwxL)Fxw~)A7y%8fiWgybbewDCL z@A%}Prm0jn=%~uc173AG(dR>UFrqp;^g%Pz)m`8#&r>sLJPALDIl^P6X^yjFA{Xqz88jPaZ7XXqV13m+4~RAx4~shxef3Ca<}k z*kMNF-xV@AcY4il{O+^?D`#4%ZCAMDN(`3v*N^;k$$@NUc!DAMu+#NgxtrtU@=-Z; zE!{77d;z%7ShRaxwg|z`0isIR4g&1y*OMHd*#|T+{DuYz92dvImnPjX+%d(%#SHcD zn=EmCcNsUKM$MxJ<*_ihfgFcDYHDjOXD+=Uw5i(;PIpQgX8Kpz!IXV*3SegjAW9LO z4=Er$qqxv|K0I+A#6fig`H@?ceHj?&@KWPfs{M3~98n+I&>c}8^uZOx>Cs!*A$H?e z-l0~bS2m+A(>q@{G^3AKggYac9nmh7!54&`*gbHIX<;}%F*DF~c7`cGT7ev_4`elK z;ek>uTa8zl%4mpO>*l0eg-XYP47Gz0`lfXiDUDHba+}>mbi6%hl~<%(%f0$%Zl5P@ z456VDE;NKOOl9$V1Lea#RgctSUrYyhN&Si`+1ML*~Z; zzn_OPh{^L!V8gE|WCfvVWsj6UD2CjNWeQRjDiq{tCkl|ZtbdR*s4hv;&~KUg7wH%} z>(Jjnz`MBmh={@;0_mua2y)6W&7^)){XQ0J7tGc{^dmakvpV+RLi0lZG zZqwP16q%L?n#G3}r)wM}qS)NMcv1!P&7YYVzF<%LPRpY(a!W&GK4iZb$WL2CuY`Ut zQ0%{x6*hlTi_S9`Zy1(7Pi?O0Dk^Si*V~=oXyieI4CY!QJdYUh$=ArF$6&`Dli#nD z*sDCPt{4lA4V*krrSEaj)a_or?sE)0pE!PBUcVnn36;vZgnga1II2H8pPR*M!;~eC z9tG;3U{l5=QcBhDw!3r9oGYo@L5B_&?qVMX>M_{8hp`k7JBu79w$e<>w+NtLM}4S< z&saQ4yMPB13Rv|F+)S8f3n-@BwBsUlz~(?42HMd1+K$kXVRJw#Q7Bu$ad~-7m`8-0 zEo>V7h0507Rw6KF6X9OVPFy`v)PckZzTqP>d9qmI0#U+_3yf=p0@sRLI_Hvt&B+LC zXWBc}=aJQSG1LJRd=`~aA6mo=GE7Q*&?~jIrMYv$?w!P6=r%=GP!CW%yY9%f#b4y8 z$AQD*OGsYf5PH9tE}|&(1+}3GHZ#Kc=qjIu!^e#*k+8~2>@Fp4r)V6$hpsWcns&cK zRd6%aW~`XPol>KOz-O3;_5cwu3d-CdFlMBnYt|Fs#R3uaG!A{vGuM>wXl;LQ>9YVK zzG&ez!lLi(u^?>M_Z?qYV*ilxNYP#XCzQA`%sl9y0{a7AknQ@6MsD(GU2_0K&E>2akD? zj$)RK#fUnjNv!;o4<$#wNfE@X+P)EapuxQhe9B7{kvTyWUINdAQwiqs zDL^+@GwJ1=;4R5y(P(a!OHPaf&!U{Z$0b2&q`1(fL9GK3R4Y@*xB$L}&BIjQnr*02 zb47L%i&=g*u6kWCY^A-NP7F1yPKZ3@J?4wp7Tf3XZD)zA4sjX}8m5Yxp7k!5&MdV( z3q5iER!~Nh^11Mx9dkjDUSjO*uCtp@N?aoz!|fYNU5|$Ey`P)UUKv`3ixgkYNGi;f zP`lEbDX_lcIByJ&6`c4~Qd!R>Ets9EVzWD0<7x!9V`f0@-o@sEXK&jCSJNQK#Ut&!;oKIw_6%Un}HY)uM(iE2%SB1vH)YfAGKMs7$MbfQ!+KSn4 zcD-!u7pm^BK?^>rTfq!wexNxE9o4*uLQHeslFp*|>$5XS)`=`UB(I58(EuisxnM=2 zSrG!xvhhplmxsb`wL}_`$%Vu|ix7*`U^)?CFdYgP19a*Gie|GYu7f_imI)UtxM{+* z`G@$bc81{5fbPIVu_0TD@)cqJQ>@JCbWC*QtY<_~4~rs%PYu#XYAw_saP$9@hn|F5I-9FvwAaG!p`fgm&cRO@Z z15Z>3dI-`~l-RAdsiVVSx{AouEBP^EGNc-mS+fQ!(0hn9;%JAAk4;D3mTM$D5_7Vp zH*h>u>qxG~W6$`mSonJleX^1^cuP5N$(I4YKhv{wXH5E|VUH8=oOtT%$P2HMl42C{ zGqSYjE@XlIhqa4z2o~+0%c*Q>Ep2FQRc^lRc5Od{bQ!Sp@nu;rjGoxEJ9}_$uwKcx z3oBwAibQ8~7WfPB$el$EJ$L$g@9^amdY+$Ej4?yb9?$-+iYG3GCWpnyZR-?wJCA`6 z?aBK+*W-bO_SlCyX44m5yv^JlVOx() z`*tu7UkVwFcW#gI5*G07X=@a15i0LfK}*%~dst^w^hI<$I$rPJ;;C`oC83OtIswvH zR2gd)g*AI$N%lHMhxK1NBSKL&0?IvB6noWf!)|Z_Kt@j{Ww7Ly^AoVnocKFx`9YdQ zReWs8A$qav8exnjx0E42^(QrRQJwAAnQ13ukd(Aro=aPgXi|o}udJnSQol^K{Gv+# z0;*Ok!%Nb!+P1!A#{{0x+0<5<7N;kGGg6rzW6{RAz5%O!kJ!Z%38|9T(nO_iu^MYy z<;@Hse~2$@?viI~OI+XEhr$w%HGftkQrSfq=}V7cC=f^w;b0uM?N~OG2?GJTK>*ug zIJ0sn+8vdYUl-G%R@pU^Wm2aN(LA4R0S9FQ8)HsSrgTW48A}n0dU1gzy)&&$o+(7F zti68fo{jhVnzUGdvkQ$5JMs!`;jUKBz)}Y6$~;Zm_1NzVp(a@J03P9BlIBCCz2CrT z4cN)l?#4$_E9Zb!N824B%jAC;f5Cb$MS!r`jdQr1g$s8gk_sNkwx2v7kdZQS25kGb zbrtgj$X^dG=|wt;0_8qyeK>}0bU?F1JnxCs%3gKaN+x(**?st-v|k;Lo1wJP+GU!e z_oP9SIwTrQ#*=TQ*y>Hngal^TZ%5Q3{kCQfXrqPn3F@ukPAEfaa~^i(Wa0$%zlb-$ z`fjBZ*4j=SkSnbk3|gI$4phU7xMY*H+6tgPc3D%}&*6X&FMfBpj=F-34Q zg?2bpN5Icx7Kcs|Q5e)UYW>DBCwY2mBfsIxBJIM~;0-HQ8lVd45%c1ogXjgQ{OSz_ zSHIjnB+(tk^{w|y{q#T`vwDT%i;MT2jkPNXVSCtivlsj3qAaVFyFbT8rDH)+GbcvM zm5Zu9>dyGlp5p3-N3N0|OVQB$RK`r{_vO&RMvR=2(=JLkFzwfn!`Y_O$kcB8g*o1@ zFTt!$l4;WqO(~?+Ud+%70(Abhs8tztK_Gr>O8#F!M_enlOhkvxf4*kbv?Hrp2bW{_ ziLCQau8Hm2X+@Vakv;50|HcpU+!jDhOh&#l)`(gnP~!LDKc8cKDh}&~Tr``sWqj-{ z)9c}X3sGPfP4{}x20>ffu}0g`QfH|Sb@>&p>zT~9BYU0$Ej>37X(KLt*z1LPJPcj9 zaJ%i}+UIb`qTF+R7(N|$K0F)S+PU^@ykXSHr2zJPrINNcieG6kyPGgWorUtWI^!3G zWQx9NGjL9q{zYooU>w>f>NM4Dt{yA7wc%|DO?WHM*;2Z0x$OJx)anY3bkvx3dbEL& zLwRcA-jUtp34T*-=>cKRDB)Qr0f?gnQQF2JD%a;iDb_C2hvR5mBQwIo2?=cql`=7| zL7G^nRx&`oP$yVoTsS3An^e~{edz9lc^wbs2dQq9Idv>y}p(Jr9#dCaSbHldO?CYMs%iYwHtqN7l8L`@myHes8lCE ztN;hC*b9fS(s`|dO5{fb#Lyv=hK(DWt{*r0*q&_o;Sk-)A6a4NECnTlcHTDOl~>91 z1sUZB!-6|jYP2^nN7T-D?*t6C3EB9hjzT;}=;AcNSLNNDDD{Rb7bhP6#sA-h=_77qBjjz)XAk^V7*k#vGznXSgwWRiXPwA@JgmIn11N;8V3|PJ zd;Y?)P%UHyb*7?cNm-UyMV_$u9Hj)GwjU7T3G9g9vm278F zaVc@+Y@IH3lzH(tXb|A;K(kUHtY%Nh%%sb{VH=9mJsgUZ2}bnIWYrLtn;;{4wV3bQ zQ|zyJfk>qtitmBTj7bB5L#bZuOkg>#lX@@P=^y!S#|Z!qHWOottmY0bj@?Bw3JHsC zg$}5y2f+d*^E);2Dw~;}AFFUAM3D40D<~0W_9QpO{OW0IXziKBd>Sccv}a)LDjBVc zYl?u)o|`9=brPfYu2-#Pn}s->tB6?2CheC6s)XQENDTpOc1TjywJa&7a*12~PG1Mf zP@imWw&*>mSB#kOBboDlk)r!cECKhCLz10r6P(SjMz@4S?)|>vUeeB!!U~a?s`92c zR5-ZDeSeRPT|kD=GdzGWD?^_|)G(4+s@cP>hh)2}4Q%GPDK`4-BlwY(owLVOLadE> zPch_)#_dN!+qeT`?}va zfo$%gA*TN1ZP*b30I>cOBB&uNqu^v|=klMHN@s6llB^Ex zp)!p6O}=(%mtx(}jucL4paVgcDFCDdLqZ55#NY-Ai7li?<&xM93H{;pLI;K~;VV$Q z_-;{Y(JWY;zijo3O;XSza#^dmWqm#Dr>(CqFZbl?5Bpzl@j!l2`0Qk_r8A%0<@(QL z@%-JcPXs{Q-2sx*l|DUky|U(+zB3S6*=K{Ov3s(ANe%mqzrJtA)ozyC3U#T8LCmBl z1u3bjthBe;=`!msU80U_sTU;Z>~Wl~AhSPMFPXl`#5%eU!R9S|@8UvrQHL~Pt*X>s z;}VDVxVp$hUX1 zoPrjksIIYv81;I<4dp$MKTu;Jp=~xwG=lNl4H^YK*Vdp)mN}rQ+<=kJogqysp#58d z01p;yg1e9fVQ%t3D-bYP_@YUK1@mSxKA~-$T(sH7qHVTN0 z2}?j$Jzs4U8F2?B-QL9r8YRl1E}axYkA#t>V!?=NlEYfPtXbzBj$`l$ctKgD^@Ef18Qr$5Txu&Q462d z{gYcwg6RWsj4(+L9xEvd z4;iCwG`&QX5<1HZ&3zLS3aEVAqrVeVgjesiy2EM`2hq5Z!kx5BM@o8CnN6$lue$N` z{-XLgYK#OX%U38!)^ktd#symIb;mccC}Sh5$?)@FVEH|gY_cRn{(5bvoFzY|xSr5j z={UK$wWGlqKH-Gu-)zAJemzgKOqPdRzw>e|qOq*aY%LUSae9vW*zdY%fMHK0hAl0R zHe5OiMp^E0B@;cA8V^VVqUii7`p;f0ikPutLYi`t5J77tt}I^zrdBL1PsEdQZK*s} zE8wJ;`1dX}Ww)+OLlS&K=BEbSwo6wiX(1;MwScBjFylJl4hJ*uui&F@u-!;pqUBe$ zyQb%g_y#2Ss>-OBis@nu+)c)#Ya!oGY+<}8{_jk%9 zHO)g>pA}RQ3`Vjdx;SJ%+7+-sOE} zYq@wtClKViAmFmG3UJ+g%lGmLWk&K*FL*}eI6-HZdXYv46~Dy~FtdZn>a zi=-Exw!70*HJ?;0lly>_Iof8Sf4T44oP2Sr?Yn3!Z|vgh)H;3QtgeXE$LC4pw*0q4tp#BdF6Z_QaA5m*-hex?*$|(k!oX ztcAx6fLhG(SJLOC$Zzua#dfB&@_xfy;>I5?_tS`R9zJO|Ll zj^gNsPTBxK8{HhknX~cOjHcx&UjjCIF3uWTK!qwUu}#IXQuc7&sk+--Pd0TQT4jq( z?{hs#lCw7^ecuFvTRdidQ`~DdOU=H%CkfBA+$j z)b)$2YiOs|;R|;0fVRqr)}qh9UQ5FZqYnSvQ)y5Tm&pGH+nO~j` zcK7ZZ@?|iD9!c`S&qNXW&o~z1uFi2lb&{nZkGRRWdHhKZvLLepbGSVW=jpx5si%c6P_*{9XlI*sFUF8wJ&b|KFVLARJsH(rYfLoRagL=Q zr)Lws^W6!bY4PR-ebt*$dqg}PA8v2dEJ>WfyoA~NWI&|~RPN$X>H1Cyl!gs-CK5mY zXl171lIItV@xsh-?sKKRNtDzp4d`GXaa)IM7R>TM+E>)n!3U8_P^G0p+Doo;id`OF zKa5{%LeNH9B7zfw*;{16FWwiTq463JtyTF}LvFTuS2zQ3e_i`SGx*3ozLqBVgy=91 zzL-o$kk(zxmIit!AI#1>uHxc3>NmJbf}(S;8Xeiz3_-v?lFV7IAr89dXQ=EM)b6bZAt zj{Ie}M3o=9yPjU|=&n!3%!rEHf2T>wJKa0a82tE{3Jylf_(>~MPl^$~q)_mx{ic6ap{SC^9F zMV)GGg7q)ie&c@M$MMfA_tZFNE*Eda%`7<@K^F)2H9bAS%HnD-+ub&Kg3hzCLYc6$ zRO@bC+G?$InF>26>hSp|n#<&>mj>oo1yqeZJyoucM66kN2=z-S=+njN>CD@eZ^LR( z0UBx&@uRwE2B5ac6cL&S#+Jxo&y+&f>v21k6Cy;dF(yJo#-NYEZpCQg1R-*pIm3iq zwJ89&=@i{2QZCJ)B8a-7ICh4Q(y8q-uuFkdv4%QBN#%1G2&VrM-NBN9D9afqVB(Qp z#gBrRqR1h7O>hfjYU$``m5w~?w-^c?26|v4`Q+77-Q#c#Va@Ij=kIB(W5=PogbQst za-%Vf%D0FWaJ)+g8`)q%mdJ!k*;fJ&Do07Xi3Qo8Gsx?rmI6lX=BXjY5a4LilNL$5 zqpKdqmzQ8t;Z&d%&bFk05u3!e?4d-GK4&R@8)RFAI+WWBY*PQt1)gFIIu|~|QV^et zRFvb$1+$i{K`P4{m4;PnZOViJC1E~BC+(P6{>B8poEPyn(R2``6Km1kA(%x5YCT8e zT5X)g>Wb8etHmSeW33yBrq%}4ETPUK(A`{9wB4OIuPV-=4QeAAkx?L`H~^Qx*eJfB zy|3k7r*7coIG%n-x%$h^(v%odV9(57Lwe{_gwECKTP07Xcr=XKM2&4OClmbkr^Vs-$rUF=aIE`0}oUeJBXZ=y+ST3Il0h(G?Q8%PU)oE71#z z2UJqo54`s6)fPER&)K;YbLiBplRwGCpm{jQnb`el(NHJy!i1|Pw`l8^L_aLquTIJ4 z0uWDUVrG{M(5z%sfUG(U^$!7+KbrJjhWXEa^|s zGg3r=VU&kU&1qH6%1MrqJuzH* zH5WN9VPa5gDw5tHu~Lih5^*R7T1`Emc|rNISgli z4^@z0{kCd%<0u_95{C2kQ%_xRnKc#H3`YqJ6PJP{jS1BmXiVpCQIV{s9>~l}#h06m z@1xWDh89y0mmI-!1|Yd)DF*$FS}$=bzf=f(Vf9l9vWpt2#j<=uNwAS^VMM=tE^B zxS9JgE*xmfZc1V(z4-NoTj2Cs<@uRd3jP(9`wA7@lnVcWmB!Ex?g=Q;H>l$DVcahH zT4gPyl<{VeG)g7{SKP5M_M;8_v{m*?nNzIXx>k?OLP_7Gc%K}tsYr7kc__c=R^~hE zPqV|d914TM8yGFefUN4bUM82&@|^>CxUL}gbP{1>ih^DF`}y7=etD~6r3{Mnfq28( z-(Vh%4ZQu~%ULk)R(EXv<`RkS?CVGH%^wGXZ`{qNw^*ctd}jn)Hden}4kyVX8-3PL zF$)JVj%qD!`a!;D^haSVcfmH4)$}{O^eSfy%$nbs7BAIuRb5-~0?wQ8M_sO`Z=L98 zC%b*(_>?M%v+y`6FL%tAp5ZuE1 zZMh(?#=`JKrQ2%AffW>?9_07A>7TUOdhMP0ly!3( z{`kKat(zTfrs|0`l~oyI;1ii-+1@GT$vnXnSQ}INyK4glt+ucXrcI`^vKyCI!m6>>BokAfWTxIo2I_8^qe^2sC zBz2v--B46x3+J8h)IM9jI^X__D*D&Ew%pw$3cYPo$8TitjU1j4UJeo6u_wZ8yMq)~ zgr3jTflXc~K>k2aN;N#W4gQ0|HKJ_r%j1`fK3U2>vqzi+eqXgmcq0r?#KoQFOch5U z)1#x=U}hPUIg#Mn7fG6Ad!Y%q1QWH+*oY1XBfe)_+K!P~RWM(&5oYtEA>hY`lg+S^ z&af(Osu4StBlu`1Wqqvt7~tX`H(IbYhTPqVM2!G+h1k^B57e680I;r*r7FnHE43}V;~(msa4R_`lD*0NDCA0jlidZ=m$8r#>~|su{|;=b*8}SeJi%G^oAQ5 z-2gTt!uBgCe(11F&vyJS6X1mEYeOG9h_CaL@nS(9K87>Dwp&pm%k5YiE5Hs7VxTPd z1t4w8g+Py(v4mNoSzc=d;O2X86VHHV_;jcDqkzCDzA2@!DUn&c5yj(-LG|>DQgA35 zrw45FwWcRSEdOqXD0FbBEKc9F8ow1LqKpK`3g5AuZg{t+BR`X}9ej$q7sHyUSSEg) zk=SVpc;jsw^XHo3at-LLlRFKB39lY{Rv7e9e)P>_~oGg`F{s#9K`$ zF%WS_*Gvw~su0Ex%=FP0(K0agg*=8E{u7!l0p38(Pdj4pR%UX-LxQP$bWuR>YtryU zUC5?}T)R;?8?y_XKDqCX<+15%no#exc|Hj?yu()xyCK8P;L^2;p0PXA%l?ZbFZ9+u z{wLX$`HcDF zJv}V(6T{Et>B+Cr=q#9hdSh14(DqreW57ZoF3-uo86A-&$KGc+|E{e1L3V0VMw*sx4py3$YG!J-QHf!ZdH3jEc4}(Dbw-kgu0@Fg zQ0jtH?DZW4#q>D22<^`4&Iu6U-wz1lr#C}86DNC1lm7}Q0s#E$uwnlP%GJg4ziX)> z06=pjIU7U)E8c(N4xHa4tVL4WfKxJ~;GG`gfJzxeWg!_@nqzj&3+zB(7P5=k~+(_?ocX z_vhCWHo$xyd><~*LW=eSZ)u7Zs2s1UpZ#jHa^-ddIDwSG@p+1O4po-!`qD1} z76E9$0;E%Wu%3e)DU=Ff`i zWe6EndI$XDc;1&p=k5FIzPnAb0Kx}j#qv|~3^471j%HJHrC2x)GY;QF4d3QHCuhjgz1=7 zd@X0IaR%y&8KfwraAwr@1XUBf*d(UrC`0MmW|{oFA7PkA!q88e1g({iQj!gA>>|%i zlnlr04Kf0nUg0wOdBxpgVY9bvOVflA$`VmaXak^(Nq#8QQ$~3-5mA%zLuT+x#U@Jy z7EE4AM)E|e=*d1g0u9`=@fm~B##?OT93|iq2x*s4!GAFW+O?m4Tu=7VLp-H~2CMdl z`ZbLk#0Bo?Ej-8C`0Z=N6>RLx)k(X0<#vPO;sc0aBfWXqC5EDw%pA)&?giH--`&be z>a2d?QP;p$5TZ%yQVzQY^+scafI*J2xZzN%80(8_dh-^0Epu0#o}y#b>i6;^?j&v_ zmNo~6L$l_;KCli6#$ecMS;AitnSZddeKiI=i=Jxs*TKJR_`nU~s)cc5T$lG|u*@%L zqIQ2Hi-MyfjTPd=5|VS`iRbKkAbI{iuspXx9ny!s^Ud!8&hKFt^0g0n<&N1Qmc}u^jrpUFTMI=+Ir6%t_XXLGKjkFSyDF0f zgj##o0?60L=nczyJn{E`_;)@(dkTbl2$8b@0RWhPb~51q>!J~KadEOVa&<9PHg)-r zy5K}v`2hih&{7DZG6*P$zY4$2})r72vmK`a!P%IzE`o17v$W4Qakt~l>Vp66Zg zV8q-OhdOD~%L6SFt{0HlK*&nU4wn3(T}8_Xz|GejgWL+? z&G#$^Ef-xy*GhOYEeB6fj{j0pNZu44=S|qmI9w)aC2JoQk|#M~QTiq`|HKP=_^<5Y z|4Ez(|KpD}w*Se(*!_3b=Kn3ANt^a90(dya`$}6b4;vMjsDM5@neG+g$Kw}NG4=PAeTNvnpNXE!E z4iF>+lPfI(5@HE}%J)?pC9?P|XVA>H1eO;(X<+L^WW@)Mk!Vfd zg@3>LbWb*|Gk-yT-SUcmWOcu+$2Q+QajW}t0UE&OFzz90BuqUYI-ljdtI%mTH4eXb z>6YD4951Z(OsDId$3e>GeHz?bH1H)(JukJo<2Nab25^SGpf6TGZ*K?c(m;4TlYvN; z454h2y$EA^zlYAEI$YSVwNzexe`R~S*&rgQ&{&U~LJym?v~j0nHg!V?LRffmuMyXA z&U0Z;h_tjNp1Aeodo4d$7g*$t(vB)W3oy#ElTm>jm#59Qzl(f4Lh`m8dfD*P(6H1C z=HnzG5KB_=LMBzFiTevq z_}U8Swx2K?cL1&wde5c?gO4YTvILyTjbjB{ z4?pHrF_IN&MGKI6-m7~Y46+XG0(kWoM?5(=*d@&*Hc=BcUFSEpPg`WQDsEy1b}^Z> zCVS>prdC`=G$6~K@@1dV3@1t&e!F6b`-xGs%=9sU3^?ssR;bL{x;h~UdTt;@vjdj?rCBsuHLR8T$w?t-J^Cli1(n^@nh5+^$mv5}c) z)DKlLXqrfYBl0ruk0QC-gFNl`#5cH)vAk^AH7GBxU%IQZ=XcL7GgsA0Vk7t|n@W3Q zS?}?Kh{(YB*zqyF07@D}K~!UTrw+o0X4go$fBpt%%Ht)%x(ElRL#X}~@%MUg402{1 z?+B90O{jRIb~kM|>6nq42!Y*-0LbrClF^GQOpBJ{(SBYv#pCPI6wf79^`fU`9mW=M ztHw-NPVcS9V^5V{R$E4td}g5imdw?d*i=k(9wj!1cgK^YZ+_ow7O9Th6Q{HXPpXQV zN7@YK+)Pz2xHF+U2HfXdzf-(-Ybv-zIu;+T^GNhznGjF(aTyoaQI`K=8p?4DX2(hv zV_^y9-WDi2XPJ1dl=}Lu^$sz$OW`8j+LY5~nSR`7Nz)zG?oC(dCNFA(Q%UO#b3t{D zGq`2E$`a$fUBGo?niOIt%^w|_S3Fy==|p`*n>+%FdfV-DeY^qQ7Idc3uTo{QTS*bY zz`6uMEG0>Mt{B#dC`|1BVno|f2N!F=D@PPVzfb3rT)NXImL&+heOOK12EJk8ioY;k zL>TbVmSP0-Qt6WVphoSE=jDaF(f%;MGHG!jS2&73HOg9Lo1InDFI8Ad=zV#z} zaIy0iCQp!O%$|UPCLg%2PF$x!RlKzGDN;v<$$jNWy?Y_sZ_zNL$4d;nGpuJh zwP=~|N+n}av-w`Nr)@YR?_YvfLq7W&0wAg|o}_p~R&q_Q9^nz&k+hqw`-UN2-9b~L zwCr@TB+Qn*Ubj3=Y8P2ufE$1hCkL`1|hq&Z+ zAC|5{(mQLCqIt+_^1`Q7yC*_Zv1)cI#l;Zrk*;ynwe(o0ZoZdo5YV~EW}QgH2qN?? zYLZNzJ!$qq8ibfmqt6>)w4ib}(;;U<6`hXa%wVixFU<2^7q6AmTY;dDbKdP@j|;&` z1jI3%Jk|^Z>iq!o^g?0fkVAv3e{v^qj-*faq)9+*Oyyx`E z?7Z2t?2<~+8)9w?raw4&h4Pae>4~k~6W;y{p+D+>OYbo9 zSPx__LK_p|<)hv_1c3c(IL8X|IxB_2!#ECu_j6r0#yDy)GMs|gXw8Flpmtq}{JU~U zbK-&3<`Vh$xbQ1vVSH!e7)5&QSm$dc%DwQ3;z(JBtcQgv-1RnAOUg-Ct0M|-LRh{= zB8fKYINAzLb*S%MSJem?+Xh8NXm<{lH2&a$a_XYtS{>aX03Y|8db0q0HvV&XT*2$w zxH=E>pfNQ#$Sttgz3{t#M4O(OtKQyU*jgvM9CKH{q4TbhQ#yv4$kizP#F|>inC^b4 z==d@EdLjD?7&cBsnZ$}zRPKpapR82j-yNurE7fJ#17xB`W^v4~!4rAne!n|O^iF1Y zdAVZtA{QLKezzj4cwb~-mUhzd-Zu`&p0aJnJIFDfo}#iIx3n44aq%7(^rxj;K8`fU zO&tq)j?U{Y&tNYHPZTD}Wo3SL7H5a<%)|5~mf2eJKeUGIXH*O_6_>YXw617!vEpJ@ z53SQqv)+$(U>Y@*3+x#n6lhQZA(;1APv8r zG?GX*;MbQGmRg>syht*w|9p0toR{T&18tzdVqvZy$>Yp37u$ta_4%%U$iKf&T(p-^*3sI0Kk*l1*@w<=|q zFGt6tKr1IgGMH$f6rk!vP#u&O5m*98VJxqkd@5FW7oyk~)?jz8zKp;4l}Iph<>xKAy^YJI>72 z@9q5yP#0SYRRVEQqWqxKS)yHY06n_GJGU$y9pijA)0zn-6g=$wP*~0`TUvv!J{xj` zJTPlB`mB#o(%23~sN!TTG|XaF&=$a8m~X?qFMA@a>QQReObKz{{(9`2BBE}Z`MGfh&slyO$CR@Ixf4}h?_OJFVj&wClL@zq5ZOr{g{Xh zvvDVum+oU{&~-g^Q6S5;8W=-T?Qis=w92x!XjxF~;MLX1c2V8=_MpyS%^hFbjvxp~ zZX#?KhyqCyCJs+vV zPHsdI81q>>&>5avW1|nmBExmgK@o|BE2Iib$gKyO`^mKG7Jf}+p6Mu99xQRal;00b z_qv9%Cy7O-ov~vpebo&x;3KS44N-7-x16KqKo1(b>XB7ptH{s>e2w}8_;xRKk37kr zKwn7pjnX7D`{R7ei92~ub*SbqYF1@8b*F?o;(I3?cVI*`i7=`AA6(?p52=PZmME`& zeS@h*O@drJeS>)w!aPR!jyeHPz+%Kaa0KrGKSm=E?f3jNf3}50#-z#jlnk~`#35bK)`{I`Jx01~NCb^hF89l?r(zXiSoK=n8U zQcNhyS;X0-hNC|vrKoLl$Oz)2or+rGboWUA^S8ci?cHYCl0jNAf!D@ z$|Ko`n7Hws355o|g`2XyeQU3$wY_$)zqY^m!f{Iutt_qWmv-kDmrgjybXrp&d2pU4 zCbI8OhIixLn_<2^0KM;q%8BN5t4H*D#aX_U?TPJ<#oNfMqp;d}a6}K!T?m#IyVcF^ z+4jf@K z3|#4f2;qQu)@I+Rij6!xu_K)DAtXX{TU3>=P|jiDZWmB$E9|Mv%=?&iJ~ZDjK{^lc zBhtgVyoRjGbOZL<8+HpU%uOf^mTV}EFkmoHQl4^_a1_`pd8t7{W{>sf|F5yH3X5x7 zm&H90+zGA?1ec&egS)$HaCdhI!5tch;O->2I|=UY4h@7$_P+bDPFB|b&foK4^h4Dc zJ-g@EQ>yAmAvPqgq0kD1s-(txWATF@4VN%N81I^XXR!%dTJg=S2KG^qi@JF87kCaP zQ3og5aPkv z(Tjhuz8AAQ4fNN^tKx);@(`NH=D@{y-#(bM*KpHG?c7KqUwk#0IzIqpsd2$|d5FsWH01%U zO+DbwRYI?^78@(~mOwn16t*>>N}Sx8K5x#ev()~5TiP-soZCTwe_{tJQYgb-!Kyg< zTX?OH0EjB>vvD@VHBLu)#f>rj`wwIV=KKcZUI19S9tmV3Zs9jh6OXuTzD*<9F%yt_ zh$en;5N*T9rY0$s1#-~5<*gQA}FL8CR3I~h+?lR4i%mZ#;NwGfuShDM6OSx zTFyMeS?s$kmledVF|u_GYL0dcubbF?(uvk@IWvP6CPUL?R;M>B5or2Kccin}c31XL z#R+jl@}QyVhw3~gKYf-y&v?6wVcC@tnG%;yn}4vac?C{|tj^k)!>n#>Yk!9lkg3;@ z5&hK>*(AtoIa`5tZPo`*NtA-fJvGyA0$biLqfm=^R~>|=7GQ)Rtscc6GH4q~18ake z+zmM{P9Qo~I=5G`H21CdHe68G*wHOk&M_4H4e*J5*A3K61AX!h#wBW{qo!wUN3F*; zGGUS^;>K2-LnMIEp6WoTx5A-wG55$yf1=$`j*AMfx0!+n59!1--*YAV&3J@(dBB?4 zH~|cJsBd5zM+s=Fl_C+&f!pF01HXRefk)NA86qYCE{WSPTDNKq^nY8zWLgAxh=H=>vsNxB>s<`rsA zZ%Z|LCvc99zugEAhLtf)lqcLDlzk8N2+g__MkzP??vF2^0EzsJC)h(MI4R6hz(N{U4 zUt0X$dcug)E&ClSo*;8<-1%t=a11=1ikv37DgSMzwAooaZ6F)NL=Y_p?_yLuHW9qK za;=zSb$h`;S4@`pXV_6$O=W=}iIq0*Vyudriy1GNzLIW(2Vct(bM>;BjGn{mtl>$O zoswG(4LJZ-Nb1a#iEX!_ix$UjMmNw@4fRu#vrzoXtBk(uu|!Ack$TCZwaCHNMfPlH zeI6=(zu#kRSu7N&|kM4ZGi-{v#C8#;r&u!@-YVjge#r`8Sn>!(=rDFfUs`lwlP3S_dbnn(-l3?<7!ar;F7Fyg}R>-_}WvW=f z8(SgvAv0&&PBUpGf4@b_E2;DZq0XjYe@tPeX6wePB#-vAJkii${KkN1CM)Epr=|}$ z2+{gcWS6o2Dk)pn@ZdY}q~OrWhWKw17ueTo z2)0S#cE?fV+P%r9PXfY|6ktVP#lcw@$lSHgi)dm5@EuONHrV(x=z@?i{k%`E^FcKG z?>;O{O-Bs)jk$ZWcM>xA%BzYGZf$=QNzr__C$ZizN+cPIUln}EaZ8j_AhhZF17j0) zo=;Hq4(j%_Z|0#2G4}|)_MIYY*5#Lk({9nXy3k`SQ0ryE0+y>JmTS-ll_tEa+7{>y zU(q;(1F~5Ior&5*P-l=1G$=q2qUQiiKEbYq2r@pqaA4urCp;vo7$@F_c3VXHvJd!`n6lSALQkM(;de*W^Ww(mRRooe{0vSGNyHlfBK`v7|vn|ck> z%!L$lS6uK%il1j#a_SPw?P!G$f&(_$Vbep3Yne|f(e6wXHwRh!=CVzidoe@ztzR3# z0&S^Cw`kM!t`LIIWp0r7y8M?vG;DWX;LM&1xnuuW*_JwM6DsBonuwTf;Yia)TwK}) zHlruFZN#fSw4>W!LT(p9y9P;p0O#!c$SL|d8r44tCg+|c@agS0Zlt6+M3*q)PEp&h zQ6B_bDFGDyfW{=6ehA@F-b$+bqmbQY0#fkE$k3<+z!c|5sYfuv{`k*RC_*0bo}tW~ zVB=jP(VN%wM$@w6eGNW|eDY1}@}OxNZj}cc^vhGI3|5hW=nZls+yK=hA?^iP{9W#_ z@22OqKx;By+B5qDrm!T7xbWWixw04`ig~hak$9twT0%vH=~2hNUv*G#;Xxmv^{1Ck**B-F=DU`Lhtm=d?k%#NM~W%XE>c@~5(qwnFAZ9C&U z7(f9`0&G-Pc#0l3D9XFJH3?c@?k z@FJc>h)&NjrLCh9UEL#209E73ymHnxS&hmqjBP6z!3xBcO~9Co9F-?F)yR!ClcuS9 zo{SlqEWAv?g_brv!R9#dgX-d_F6M#fsYf zD`k!FY4zAtx9J*@QU|q5m(bxAvWrLXoJX*v^l zEUJP^G`2VM)yefCdi^WXx}~o2f@1N z{1BNDJ&PK8gIV+WL2M)fz35$dEZ7$g(vwsvZ>;{YOy;}yL)BR-nGMY7k06WqX3Xl4 z*4bTg4{Zd2TuA3<+n)ilz%aBB<1lZ`cIcfF8P+61l{kbZkVw*sIV;J6=OSBvCOyuoKhga2cpfH(sK@_$Ar)IVH1s=*BHx*@Kzf`J{&^0 z-r9#c=SaU|!-~d36pI?q=6-c?k^kOt+JPniJ%K`>Wb)7!fk6a_d)5u&L0;5rHL$bER zrP*<;;%v>q*fhzFZ4c<`40r}};zl9R8ULDbU zy$nFwf-9ie%VE_-5$b%4grqOGa9?mK_c=&^S&8XOfA4F^V0pbpm}#^pjNm)P9$R_# zU`s!J{ICh@!mG|cg-et<6qRKKKVsUAg4Y{ff?l;J+|_h zu$LCB`mG)5H&cTaH?uVfaPBM);Ix8lbhfBbEoF?(8mClJxdBATLE52C{fWg9r5}u$ zM_a5}DTf%l^Vh3CV3$PEy0~vDPNgdX_Z+ciJgoMn4CSwi3T10Q>1}PG?lwpPJ9H8b zcM6{L@h$MZCTQo$_ni++lnR#>S!2uEgzDEdYr;=nsc_jGO;Y z_qfqycIva*29K9D9M;9Se2oRTPFck2Qdi0c%;sd?AET#PpS&u_5H8GXrbydATXcSp zs!MU?*t9!(J^sNNWc3jxA;wiISM0JK+D$gIOX!X4fMi8j*`(0D?fVSIaH4l&7}kn7 z4H1Mk;2S%mG4hGQoE$v~tap#6`v7H&u`O4Az_yRLLfVP^*tqmQjOJ-zqlHJC5fLCL1qHQdOKowpG!YK!bd)gU~`JPa+fZfVDqC4YF^Jcp&N_?Y-leG|*4}gwuWu|?b4p;#ZD*N`DL|4vcQF!0Y>9iioSE>FCq$rPtj0xZTaAz^kM{<2n zu#KYiL8&)7q^T;=XX2cfw46V5AVY?t&z>m-Vw)4?iu6 zytmy(bM83_-r;gnc}b&w`a?-Wdvm{6;CZQbF6>%ktr*vF%WC6HO0-9xIehIrrX37b zcLtUD_lcTZ<({FcmJ-)1s>7C(-s6(PCFP!|4J9Rbs`Os|RqzF68fa?j62#Pylx>lX z>B#HGJ4Ae}{+2IRm9X_mT!ZQ8>t15IPfjoymQsX^4m}g!JB~|(mnb%5Z>M$GJ7zp- z(ZO*}j}BnOUYE-U1J}E3`aacxjPI<>M-dATPtkPf3!iBgyA;?=&pGpGhj*Fy4< z_DIm0w^Yh&i^gzB+$JlX-uK^kf@Ry6X;yQY(%{Wvn0hszEnA4(=IefGFkg@~?yDm~ z=FZh~g@89jx96)P&9UdO=j_F|RK}R?aTVk!tSg-LJ!$;H{Z(5wV}8;lx4U+Sz~;e_ zh+q5r;c@&PsYipg8PzZfkVTc0E*Zo0_b|!JE1-IZlGP+5`ofh-m81=-kb7z9-Vtf} zeNP>T(k^$I!)(;o1vhaS5r4!F(Sm|E#^*7v^TzVcp5k8j7P1&MKX zW%)r99rXfl%=keN^FWcTU9tS-6zwurr5UzF(&hQY$?pPTO?YQDeI3k`Y15 zqO)4vJ+&|HzHhSgvGdNHf+yI_iI_V)U$y6TkJ%qPEk;w+0@sX#zX_3=mm?L|@3zlYJQf=Be>=(XI zlDPIfhK6a^8a#6-Zrr5IdX)p~1%7$#IuPeFi0Q!;>79;R#3D&?AyePjeTSyK(USl6 zZM~TI5v?noypq+3Jzl|Pi9d;aVHal%B9F7;WRIK)CQFmQF^d$xScPO>j$5=_EO%q5hG-)~e31umx5C75!-Q8RJKXVm#+S5a@Q%g4hkKQA{DF zq|9OAlLfvTWfs2o>_x`~0rui7)v=)u>0}j=J8Z0ng*zGl%~0NYh-bP__j?c%vOVAs z&}kTI?*S&QQ!US-PQK;=J93U1hhq3nmkcuS7T)npF|d1VoY-3@j%9tzN+d0frKgNw z3VlV?YD97~N$_kawWqnEaY>V>t0rv0L}#%TVyS`hT`F6@bhmi<(YGr)sO}1RD({HU z+YE`NO@ND{Sn?z?Wh^0VhU?wVG0QIgdA=%YQcFGM zeXXm~5K`T~ytHn=(WG*f`4_97le6MulVJ5#L<>&Q`j zAY591gMffRWA5FT@m{+?AHmV(-XddYKF5m|-a+0dORnIJkMte3 zmP{0fm{4ddf~xO3V$hLo_R0{HO4X|&yvzk1BufVg9l0hdyIjayPmGkY1%rcNuE4;3dH0I0`J zS~6I$8=$+!I4KIp%rd})cKr!raB@`!fi6{+_Ae^dF>%8|alM+i=~FYz1Gj0bISR@P zk^6LXu=oJe1I=Nl0Y0;}`juX)Y0hSl<+ z7C#QVL=WzmFRKJM5Bu6kjo4mll3I78x{Gw07FNP=7^fJitzUx19my-%n$~SHnyM~6yC6@j0o^m-X zw;bhw+yEy25z=v2*!KFSP!3mN9gA&kk8HeBc2KQyWK(9DsuzAKC!r6av`ipj%hDq) z#rJw{0wk_3OP-DrNywE4KURBd;|uM1tPKjjy=>-=5QLWA3#~2d?pQ zs8qY?T6dE{uOHZ-c@V|UnOGd-*KS`F;rsx83*EQbVDi=o@SGc;F*d}kQW=rcu2=Gz zp{jSa4S&*+J~PKc+r4;j@9=d8p+%e;{{(ub=$@$BMz#+Ib@&sxCv=SKcDcDn_1rrB zY<^BuX^%X(6qR0M(OF|DXB)^=bcNLPS9Fb+bVNAOtKtqqFQ?@hTTSKmpJb-|Dt5*H za+dH*yh12~fD!i`WyAj*jr~vJl@A{s6^!+b{*zskpu8fhXo}B5t)@0PM zpj%z49mTuCbyZ6&UiU>nZ`jj%bC0b17KP5k$xE zOJpJ|%+l$ny!^&RA3Fg(-+TUno+Td>>8{0dk9GuC#E;(4Mk*+tJ>=eIYiKS8kHYy|p^^7(XzMy)&9c?TUa5SGny7=BnDw-=-9l8F>|i8YG&{MQ6O6 zD}hoT4gP$GsHfuZN+<|7Cd=h@q(-+8DNwEvtx6pQdEzFfG)yW#>GUg@;*W)RnZ?d^ z6s{7xhmn);Z~`jVl<~SY$ds=`#^2|#xHD*{Zuku{8ubg{Jdh{Lr%dVPWG?|*$=T79 z@JV2&yA|h_QV`j;HZsbUNLGi& zrYNJtx{KsRY#(@dPf{F%Q=RsGj&OmFZ6mwdmOzb)p!&?!zQ3L&%Zi(C!R)KsnhT51 z(VF7v+-tXJCs`>L{thos2+cLp2$f8Xq0rHTyLd4CK`>KX2^g!-V6kSl(0&NR+!r&> z-wug47bukqjdg-Q7dG@OCKrwv?Pt$W;(ModKVG&uux@K=wx6g7Lr3So0g^< zl0dz;1u-_)%(LFWH$`Y=ZNu4^C!g{$=}k&UsreU*)h>}|cQ6sHmg*YCQjL6 zQZ;kP7-8Xsun*Lr_lz~SiuUHN-5 zm;)paZ`E-gMuoawk=x)YWpxE@lmaK<=ibfv1V zbn#R=Cf!hX3HJIvn8{sVAR5Or1aGaPdn+U2p1zL|eWPUZRfF)mS^Szoze<#2jdXF7 z)IiVX3YC5cu7!~_b%3}12dKog7#M6q(rW+PTc88mrPI2!Y@ar%EQf8CffDH3;kyR`Bbh3*rDNDl$zi<>+6Q@(2|%zNm*@;uT6q+ z3krqKNr&7s`LHN-548SH$@KPVU@mYq{acMtn7Yijs(IH=$|FGb9_j^o1uK2hH`VmU zBL!TMb=}u0F{e^TKv3{md)d2Q?1L`>?dQFh8ivlr&LoSa@6YGOlfr79H=L)r`HSvh zi&Ip>o}QhB;1CF4|J+07`SR~C^_Pw^{~PvuPnj1C)${OwV1MZ_^WP?ZmPG!g{GP|Z zI?24u_`ALFS1*~D%6T6CyR*zcBYyRkd8w}F;V*>i|8$!9XXLM5GcR@YJp3#2U!7!?14Y`rg$M%eEwVhZ^ZwWlK#&3dm#G@U-|RzAmo! zE-yUM&u2jYiuC&%&kM)j{bIk|#4n{v|Cb}>1@>om@e9oRudshOzi>I+U-tT+T*WVFuHT`*dyHRJ@o$m%?_SFns*g;+t>W*_ z%NMf0f23djzLyeXc_I6cfA7DcfBF4h%IOd2f4l!)_RF9Ae=iJ$|G@A!H{i>n{w$(@ zp{(Qi`$zh}E8(AH$1jBR0)Hm_ClT`R%)j5Yd|?(> v`@b^(@gC-1C;HQc&&xy~_5M83ZgLxe!csDJwga- literal 0 HcmV?d00001 diff --git a/src/soot/Scene.java b/src/soot/Scene.java index 7a1cfb672dd..d25d87b031b 100644 --- a/src/soot/Scene.java +++ b/src/soot/Scene.java @@ -30,7 +30,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; +import java.io.InputStream; import java.util.ArrayList; +import java.util.Enumeration; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -39,6 +41,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import soot.jimple.toolkits.callgraph.CallGraph; import soot.jimple.toolkits.callgraph.ContextSensitiveCallGraph; @@ -58,6 +62,10 @@ import soot.util.SingletonList; import soot.util.StringNumberer; +import org.xmlpull.v1.XmlPullParser; +import android.content.res.AXmlResourceParser; +import test.AXMLPrinter; + /** Manages the SootClasses of the application being analyzed. */ public class Scene //extends AbstractHost { @@ -223,9 +231,112 @@ public String getSootClassPath() } } + + return sootClassPath; } - + + public String getAndroidJarPath (String jars, String apk) { + File jarsF = new File (jars); + File apkF = new File (apk); + + int APIVersion = -1; + String jarPath = ""; + + if (!jarsF.exists()) + throw new RuntimeException("file '"+ jars +"' does not exist!"); + + if (!apkF.exists()) + throw new RuntimeException("file '"+ apk +"' does not exist!"); + + // get AndroidManifest + InputStream manifestIS = null; + try { + ZipFile archive = new ZipFile (apkF); + for (Enumeration entries = archive.entries(); entries.hasMoreElements(); ) { + ZipEntry entry = (ZipEntry) entries.nextElement(); + String entryName = entry.getName(); + // We are dealing with the Android manifest + if (entryName.equals("AndroidManifest.xml")) { + manifestIS = archive.getInputStream (entry); + break; + } + } + } catch(Exception e) { + throw new RuntimeException ("Error when looking for manifest in apk: "+ e); + } + if (manifestIS == null) + throw new RuntimeException ("Android manifest not found in apk ("+ apkF +")"); + + + // process AndroidManifest.xml + int sdkTargetVersion = -1; + int minSdkVersion = -1; + int defaultSdkVersion = 15; + try { + AXmlResourceParser parser=new AXmlResourceParser(); + parser.open(manifestIS); + StringBuilder indent=new StringBuilder(10); + final String indentStep=" "; + int depth = 0; + while (true) { + int type=parser.next(); + if (type==XmlPullParser.END_DOCUMENT) { + //throw new RuntimeException ("target sdk version not found in Android manifest ("+ apkF +")"); + break; + } + switch (type) { + case XmlPullParser.START_DOCUMENT: + { + break; + } + case XmlPullParser.START_TAG: + { + depth++; + String tagName = parser.getName(); + if (depth == 2 && tagName.equals("uses-sdk")) { + for (int i=0;i!=parser.getAttributeCount();++i) { + String attributeName = parser.getAttributeName (i); + String attributeValue = AXMLPrinter.getAttributeValue(parser,i); + if (attributeName.equals("targetSdkVersion")) { + sdkTargetVersion = Integer.parseInt (attributeValue); + } else if (attributeName.equals("minSdkVersion")) { + minSdkVersion = Integer.parseInt (attributeValue); + } + } + } + break; + } + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.TEXT: + break; + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + + if (sdkTargetVersion != -1) { + APIVersion = sdkTargetVersion; + } else if (minSdkVersion != -1) { + if (minSdkVersion <= 2) + minSdkVersion = 3; + APIVersion = minSdkVersion; + } else { + G.v().out.println ("Could not find sdk version in Android manifest! Using default."); + APIVersion = defaultSdkVersion; + } + + // get path to appropriate android.jar + jarPath = jars + File.separator + "android-" + APIVersion + File.separator + "android.jar"; + + return jarPath; + + } + public String defaultClassPath() { StringBuffer sb = new StringBuffer(); sb.append(System.getProperty("java.class.path")+File.pathSeparator); @@ -265,8 +376,42 @@ public String defaultClassPath() { sb.append(File.pathSeparator+ System.getProperty("java.home")+File.separator+"lib"+File.separator+"jce.jar"); } + + String defaultClassPath = sb.toString(); - return sb.toString(); + if (Options.v().src_prec() == Options.src_prec_apk) { + // check that android.jar is not in classpath + if (!defaultClassPath.contains ("android.jar")) { + String androidJars = Options.v().android_jars(); + String forceAndroidJar = Options.v().force_android_jar(); + if (androidJars.equals("") && forceAndroidJar.equals("")) { + throw new RuntimeException("You are analyzing an Android application but did not define android.jar. Options -android-jars or -force-android-jar should be used."); + } + + String jarPath = ""; + if (!forceAndroidJar.equals("")) { + jarPath = forceAndroidJar; + } else if (!androidJars.equals("")) { + Collection targetApks = Options.v().process_dir(); + if (targetApks.size() == 0 || targetApks.size() > 1) + throw new RuntimeException("only one Android application can be analyzed when using option -android-jars."); + jarPath = getAndroidJarPath (androidJars, (String)targetApks.toArray()[0]); + } + if (jarPath.equals("")) + throw new RuntimeException("android.jar not found."); + File f = new File (jarPath); + if (!f.exists()) + throw new RuntimeException("file '"+ jarPath +"' does not exist!"); + else + G.v().out.println("Using '"+ jarPath +"' as android.jar"); + defaultClassPath += File.pathSeparator + jarPath; + + } else { + G.v().out.println("warning: defaultClassPath contains android.jar! Options -android-jars and -force-android-jar are ignored!"); + } + } + + return defaultClassPath; } diff --git a/src/soot/options/soot_options.xml b/src/soot/options/soot_options.xml index bad1d5a6d2a..ae6f4bad5cc 100644 --- a/src/soot/options/soot_options.xml +++ b/src/soot/options/soot_options.xml @@ -320,6 +320,35 @@ will process one class at a time. Only body packs are run, no whole-program packs. + + Path to Android jar files + android-jars + path + Use as the path for finding the android.jar file + +Use as the directory in which Soot should search for the appropriate +android.jar file to use. +The directory must contain subdirectories named after the Android SDK version. Those +subdirectories must each contain one android.jar file. +For instance if the target directory is /home/user/androidSDK/platforms/ subdirectories +containing android.jar for Android SDK 8 and 13 must be named android-8/ and android-13/ +respectively. + +Note, that this options requires that only one Android application is analyzed at a time. +The Android application must contain the AndroidManifest.xml file. + + + + Force specific Android jar file + force-android-jar + path + Force Soot to use as the path for the android.jar file. + +Use as the path to the android.jar file Soot should use. +This option overrides the "android-jars" option. + + + Compute AST Metrics ast-metrics