spytrap-adb-0.3.2/.cargo_vcs_info.json0000644000000001360000000000100132640ustar { "git": { "sha1": "70a2c54e3b264e7d615298cf8457435c7140191e" }, "path_in_vcs": "" }spytrap-adb-0.3.2/.dockerignore000064400000000000000000000000711046102023000145260ustar 00000000000000target Dockerfile .dockerignore .git .gitignore *.sw[op] spytrap-adb-0.3.2/.github/screenshot-device-list.png000064400000000000000000000362051046102023000205130ustar 00000000000000PNG  IHDR CiCCPICC profile(}=H@_JEZD ␡:ıVBЪɥ_Ф!Iqq\ ~,V\uupW'E)iExwqY8鶙N&lnU "00,cNR_.Ƴ9jbO$3ô7g6m>qdxܤ ?r]iόItIME 6>7GtEXtCommentCreated with GIMPW IDATxwX€3[XQDT{7j[]X+E5{"VHb,EM4yO̞930He9 Y`S ShPo ˂}o3@6  3?2q?.?ZXH!5f ׍]rI =Q*K=zwESMI@>:m\`H6KGnn5 3JϭӁ>7|n|is\s͵;4)P&hdsSZE&74:iRBUeTڨwE*]yk3(ev'>9:0 (%c6OyF}2kl}BQ~쉓=t%.?2\ꄝ:(]gΏ|n >荗;[zcGuPE8e/eH/7LY}/wz_Psb +Un==Ku&k(rO9{{>y>Ͳpa4k+V㏅BH+QD-i]whӾb]p, ,lYemǎժo#ït~']]mz 75襳o[130I*XtY4#ӥF'hudΟ}=9!YɖTytBÞ0sKhn)(+o(s.&%i$ y3O9'7*,o/~mZUB^Y2kG{˔ h ]*I~|?PFLz'DGGƦ%E ^e֟Bn\0ʜoJcxq#֭X}&3~ >Un:!{Z'z%Bȡv_j׶IZ?fnYXwO6!Q77;}gnGoJBJëaWFtaͤ-w|C_>{_̗][d,]C jVY%B [W ;|^<Ȱ粢p۟۟|Q,_ePbʐ?$gp(PȸƄmwCօl2}T~ʼ?N?d`mW'nY/Ǿyͽ[76~߿QW)I}q{xZy}Nv*dД($0RIBj?UKߺx)x%K(gS{L\z6˦/vKs1'sCC_ vvŔB}'M7BXk\'h&B[̥'/rwkTf|>Iy ɁO,ضZMUNBYB(LV.,ْe!B?BI;m&KAcM0ƫ~.xǨ^gM?/[NqҤĄE$㔭&)I5ԅ9\wS{]gߎ:TM@I]Fضu.0w3zm-,7s67 m/Ie~UB2É6wB5喝 =$n 5ԋuvsS6oln}>T(m6n٪uB!GGFP/F|L>Ƈws,@?m2_*)ur{Y!AĦd="=xy+*] ug}b5z!T%޸tjd3~vX]*]^WJSjެVI?vwhu+?v"B/$z ?}#QNE@>2e^ cIa+*=:8r|Ϫ6V훌[ LՒ66~CBswݟ_ E +]M1_JjܖB2cZIZCɪ^(cߪ`*#Bu2n8qhs*BUn7=jmVvQ!gojкdNmś'\캸U>d\{yY)ek! !\lyVo+ߦjq;Ϭ?c8A Tz#~|YyXCsaib񉶋 zZz]{1ӦMi\eԽCdS'۸j|<FJIb1RbZwSANCSmlГGWާ񔓣J?] ! N*8;F{G'ź)*0r(kKS !tާv+XѡTʔH;u(B=> ߷m\ֶpR䳇O>t_j3זc̴1wWVmtN7`u;2}a=^%Y55mܪ! o$ !|ΞpÌnTo`FX۲#ŏL;iNhwݝo_ L6i9|߅㫗4Z0\w?P+쟹ܿ#?[89;3wurcν)L9dM訵:qy$)rs)&FS1jO1SYX;wݜƾ:|i,9;T:;|߭Հa f> ׍(Knp] .&r[8mNˌ I>҆zkr 4:>      hhh4@@4@@4@@ }FĦ'7<5?Gˍ _FYvϝGq%wnn\@H 6ff+*$1?>9l[]N$}rt`@QK?-Z{^/m?_ WRs hESF8agٲcjZQZڷų^/=pYxPJ!PU[s۶ ~_3Gi7 !QcuD&/WȾ?xóN1Be7lop%0j0 ~[Rd-go?9hO{YJv:UUf弢$zk[,jt7lyIy*|\rغ)C)5ky' !.>84A/PÚT4 uql=&/2|z)lն;_УMEé׌p,AfCp؍I~AWIro7loTYZ!tF~?%/@@@j#Iڿ^`VRh=\]e!D ,oL h}򃋷ڠ`YFF/.S~M*ҥ%Yù'}|V撈/WJ%BVZ&+fǗB7{:f~Gǩz-9yS;6))ZVJ#^5;6o\DTAN/fCrXx,ٗ/ђ$ ]{?^|$NV~&Y >3sQߗG> T)z}>hRե=^~CN8f:T)ԼRľS6sZ4?.6ncxW ?J q %6x\33 36>ᱧVmRD!V/$9oHBD@rsg/>{˵^1Z!@/Y(UK=eb _ƣ&E uB,=`@N(Kը]@!0e\2B.97yHݲԻNˮ6ykb[ۏդXMxxC۠Jow2O챁{x{ )`==Slʖ("dSW^}[kؤ2#^,ʬxNj)QRŗ>⛚S6,aeQŢCM+KzMP`8"xN39΃s?Zc[8xHY9xac7\ԮW.oăGY=>zoN7aTR% z8& !}?aa]B\ κK,>xS..gmZ8IW"fI7\*+78 paǰ7I%}7ϸ9m6%#ٳ } =*v;Jza&6_vlo\Hϕ.qB#mR~M&TK'!SJhこ.]NڶbV%!_NACjj 3]eYgnj=2c.-(LJb^囑oIqO9)`ວ:!6yEOuvGL9;u Z7hcF؇y}mK렓L[dbx3㽿]'T-mˑccEl.6:NㆡS.M^7VąO#ʽVr豟_3yu7o0~Y9llڏ?s>e/pɗږtEU/]fM*"%<(/GE5vJXr#kz!5Vw hZk6n[jլf"U~}^ޑo@ҿxMTGzWkѡj{e$l[t+M/XN|ԂޯcS4[ 9,v>gb<7]VʯV͠!W,KyyBQM]]D}e۾}6kɪ/RH>S]_EJPBѼƩO\7/sKk`Y(ؘW;HtoXT!K))ŝ˖w:n(]A7-Xz!?w5Ysmݸo&&'՘i6esI.~W@n3BZF!RMo=99)՗wT޾ǶUWK[{k' !6u_/KUuړI96!X!oЩNnݮ]<W[# TKy0K,kԸǞݿqjrjyIKp?o}6&vPvyӋnVv6ވPrŀ2 I{{}7m"pd޹akd!0m>Și}dw@dxRfy~5,o4†zh#}a4PPa_|yVvN)N%%-sO1mM!3hE>|Wb,{\ڿvqϗo_p&6&I/0+Uhĝ[~:i;fk k\FqMBeũ[~w,սzB|yxr@u.Z/"f.I"J9eI& }<ڱ'Z!@&ί,к-d} cIDATMY4} j[A$D2Ys[yG h#d}l>1Wt bU&77]*OwBUAS綠|& TJvz?2fR&'[=Eixʦqs,B%g2SA\MɃ\u!=w]3mMhL*^81pMZ3q3#MC!LT$8+}V3?ZǿBͲYMFϸ .Lj&W]?M&􁷲&{]3~DG&m:kFQeA6 }H{_]5V!r6 `hw cQB2o8~=幰i9W683>ژV:ϩ^xrh5ܩ_q)w }Ԉ \չ4>C187OwY+*ٰ W p[?dTkϺHB(,摢D Vc4w1c< M~0*kR/Q;4ermWOk,Oaх/3ժŭe v܌ j|;SgC?B盗oM‡ʁgOҤZ(Pq>xuYNOy{'Po׹\v!"eiV ].OO?iB!n}>p6_Vr٣7 nUe!Y~ٰjIw|7,I.3޵CQ3=b"DjF/R"##_;Ooh =JytNs;PEh7ԤV Iow܈ vdrhi-,gPh+c]~0uMn۷oNR*B\/v[|HM+sqGG7gv{7B!kƪ} Bh v2cBؽs"ٻ,[X&mLk*߿{'R/DE;jo>{ G6:a&ՔF6/Eyz1́>3 l[йE#^~BN?6?1mմz,$IYC,}%V_K dR癩׊wMK[mln]^˖Y;Xs?Y6EA[e'/5] "oRVF?6oXLԱ"PCϟ]&V?+PIgKgvd^.?M $w]HD[qCsȥaZ}֥ Ɯa\@.3#瀜BёQjB kbFgǦS?|*(r8m DA#sty`y3V^I~$F/>(&?kg/7. wavbGxI֍zy ^h<8,}*!ςDb絵xܾŜM cNF? }dMFM쌎6BŌCx_#{yk7F@=%r5PR"kR"~ށ(H_M^8c^{1h]T.scѠ1z)w&AѲ۴+ik6i6TʚNKz!*tX}>yzW/F ꜃^M{!x+|Ҡqӿxgn/pYzki/p#1Wդ8f.Y9n$mZl17_ -K{ϡn/pLU*(/t5 U!ԅ(,7![4(TZĹqO|!dQG;k5Gq,B6:KYl.7Dq%}6V~k~{~?$<9bU6+\q{&8BHFӱrwSX;*;bRн ݇BZ4TvŔ"*#̍;m䰀@u'Qz!hR{)͔Y7"248Zx_w*HBHys<ז/<mUUـgAqrWgdfWM#Ey'Ea@;"Y  ! ip )٨j1秺2;Kv%aVQmW_ 5.ҋB[o㤘e;.yE+ؖ^Uu})Bc:j7ã?:c蛯f y.s#I2eL~qvJQ6=&XqNԪh):C/pu!F7-Z]oZ v09qiצS]g%CaӪGCݹϼ]R%Iid8UjUEπ>uǩZ|_^oNoP Kge yp~W;iWE̹ꜛ+W4J;c4`)$]qO+4&̕cMu A;ᚕee-ϞE+|Dm ۗ=F3G\J׿Ͳ;'2FFѻNǞc|Uܥ|Б%oSQL$!&?;&%o?tgQ`d_7Ÿo0|ci;SM!&lwױkN96Fg6ȾY62Ņz2rr/g4yzVG_>}kXږ.d+kR vfwO[7PBa~U$zg6@sv? qbȽSk;._zfx0;{ƓMeh436ז92bLy[G%\wbN"kXlvRnNBi6_f7&o?`k_FͲw,%{S5,PVMMͳu^~m,E+s;ldz-vZ}eUDF&?K]wɺ:ͻ'9>]ϒ3`_C&b(4)z5FQ|y;Ni2&G+  k26\a{Q?3E?#ۿ}0C`gyy306+]^&s| iMe]x-{H)ؤ<n>UXUh3{q} ~eǘ# ,ɲ䝓oힽ51 |M-1"w3XR'?}hڿyrdD_qA5Я/oR0    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@4@@3    @@4@@4@@4C  4@@4@@42|ݘdR@-6aq @@4@@4@@  0d3   hhh4@@4@@4@@ l?:q{ ~IENDB`spytrap-adb-0.3.2/.github/screenshot-findings-list.png000064400000000000000000003066201046102023000210560ustar 00000000000000PNG  IHDR CiCCPICC profile(}=H@_JEZD ␡:ıVBЪɥ_Ф!Iqq\ ~,V\uupW'E)iExwqY8鶙N&lnU "00,cNR_.Ƴ9jbO$3ô7g6m>qdxܤ ?r]iόItIME 6"QtEXtCommentCreated with GIMPW IDATxwXG+{&]TXQ bI,b4ƮQ:VTDA)J#N4=>Oξ3;YwG$hO] Ăh `|wm={BD)@@ 4@ 4>Y[ѷ͌ bD:Đ3l%oetmivVNFZO*ߝaINz/#,+M'#e]Ћ)b[=HO M?:W"Ѩb흦4* BS2nH]zs!D/SG8vk7u1^d M;FIyٰ5'\/le,9M)ie,W?;H գ԰ k/dpJDdPbSKh!D ;]oPw09|WdEv6GND>JyM WCL/l.XJ럗BS/\[Cܱywi9\gѴ8c&sbˆQZf4mlqki)W݈8X@a# l-n(PpܲoʿL7tّ y7{#5bOhh"Bd ,S]l[̽1`V勮!ǜCn {4kQ׹u͉"ZVB"\g?+e!DV!L~ qBUo Egd}\_bѺʻUv;xlo϶-8W6>5\ꦬKc_C=ku Ŵ%Od{82}{vt}!2-j1|P{wBkDV^TgCWVMv”}ɖw] 1W`g43jؑEhsdcw:8;7Ep֥N`h-B\nFša_J?`Rrk"/X@Ҫ4~L^^r#KX^-ІqYɏz\(;ހ§5\ZةZ>&G?q;mmyNNA2L(33#7/u_TՒ/3=jJ!֣/+m<~aM9àcGFMzKkʊK Kh17gƓ[s"E:u2$oJ~nZ7}ȓTbz~ ڋ. QU)>(1@*LmKO+}$| !,%Eܭ!P[|r텨7qG/"k$!?WnubOW(4'ƾJ}*1MjhlZ1No!XLh@NV ʛWMiJ*\ѵ_/J7DU[򜕓CB.}9d,BH m?l!L,Cw&>J7bSCq,OJȤ[%x;RŚm?:W( yQ{=VPEJǂ|4(ݤ/QBGSlx-*+Sd-%Bq|e’Vl|kH>9FM 3\Rd%kgm| J".p}gt4CхET14QB vjV`ٔ %=m.%U;q/t}CT{a {dMeߚs~T ڳ85- ~0_%wm O2+Hii SC1MKR!UV]w|8S$!%JnT48e_]3MN@TKw*II(urWK{f'&%&%&|i$8#-+=+Y^^8BՐS}0%mp6FJ MzK!\YUGdfxH61ޠ;X[V=Gɼ:V-Ƕ62 ǒߖyQ:H:Z P^xs#*0M- h4k3QPb?$Ta֧;頌 >KwETC) ߡ 04k>!%#%s>&v#$k]]bTI¶]B#LNU=$0%,p.99zv-nj,dQހ18l&1얍?sկ壌ǩSVQ0y%{bjݼnTIAqc^.HlxUG8{?i1gu%:'kBS" N콓KF? 0ĬIOӣi9 !TzB^kj[Yy4 )@)_rpտߋb_;bz~^liFZ訪f6GLy2|H*/QÆZihm⇋DTC`9lYWUn1 ϯKed-Zd@2в]-<ڕ^4Fl|qzq6V%v_ ailC] /Mv蹠7(9ɬy环$"0 !π-Wf?!2/mk9 ^r_iᚣug`BhKK BjHbvg-7PSd J2bn;TwEZK^ZFwMfe+Wp8៬TN ,pkA "@0E6 y!g !"ք5@n h h h$@ $@ $@ HHH@ 4@ 4@ 4hZNOm1=Fr$/q }v@cg >VY~ ?/fQM?ۥ6Ku5ni%Ѝh~IvfA ݔ@gտN_>?ք(l{]h>o|*Z=}s)M:Pڋ~Wx޿9Kdž"U{([jrBm5[E E%RiTYg_VZeh)Y^JxmiaF GR ie1]p+>2ZGqIECvO4%bZ.Y};p؃W;\~d^ڜJi.6 ^cBC22!c:] 3hQ3k֡Uu䎳p>L+V\ |Ϊ}o..\uѽM-uL#MǨE'3Wܹ8Ũivh1l1U?8|/^"m>3ЄoOitSɋ\ ]oE]cբk˖Z@,;M[HDS_= O,cwi݁Ypog2ۥ@`Bw5]۰GK vGS[WRK!pQ;Sʹ8@M ?ZwsB52yq/g^)l U=C# $MgKC;vߚ. B$@ $@ $@MȨkLikdmݞF_!t6mL;ˈeD2BύU$oX=,@0Mזak|͜7^x.+5`g3EٹU݊.0NǞ:fW0]g@woXwE*y1oY_WnJwfTVR}gGϨ9;uVD;bj\ cB}h!CEF1f5F& q&4rߟe7>73yjHI#&z05ϗQ\-6Fv& ;ش|BN{plGχO 3RϙC9$+ifXW1a/cC:͂!:Đ3lҪb[=HO M?:WlfOfZ/#S]~D[ {~a4I{()O:ָㄋ B0w~ɻTw09|f}G2®ϲrt2"YV {ܻt$\gѴ8b!US=bNY#h뭌SuHPz62m۞>>C5/y`3v}43.oA  h j@(Qe i\衭dBTRe? ɘ/:9Lϡtt~0;lIiTfpS!!~ޏmWafp.hcp۾FbsAyy+Nc;aʾd;WnJzSO]ܧ}tQ6\ O-$Sv^v|dЁF6I:1琛y%;vun]s"$H1bVҵg5d"92M`ۊs;yн˽\]{;ZvYy!D\2;Ƃٜ#j]qNhG,BTP>/jrT)f+QZ݊)+AU 4]W^RX:3U.}Ň!LO+D "D519NI5)G_3ǿ \GncPڦ"b[Y[o'[ )]ݽss ϧaW|rOn?FOG ,jk(z>?! a, rYLu5|*?kb.3;g㬻H2^{T.?-yޓn C 4uǡ;wb%ԛb1B36V}_EYg-vOKc%y-4bjjic!ď8~aUۇSOE2+OJȤ w)}9d,BH 罅$-3ޕn= ΍{K~: VFQͮ^MFyfŻ )w;)S͋kbTq\Az^m2 uxwP]6Rf.̷Wg\Uo]Pо-/Q'',_Nʢ o2_aƿ0T]9 @@"BE *dYl4襗od&д 6Umd1iQy鑷h.3rY?C5p7?hqw B}0E6 y!g  4es@ h h noiFY[_Q;V;HHi-O[{5Y;OoVʴ󡼌H^F$/#XL1O׹n#Aߐ{ğOزX~38IVJXR؉%]yg-LGFFǵe_3'ƍJ ٟ AeQfvn~sK/ӱ2m"LW?]?Vo0vO?$iꟌ7f}^:{}:c$RVJg%G_3D+םq4.D_|wA;fDҟg sP]7(3["Rzf=aw\\=T"żeul~]U~Sw;r:BiQYIK-O~=>g[iXms1KT[o+mYyV^GV~\aφ WgÔԘpGoHnMrW%oIq+v]?vSp=IƿG a/q1qhQs}7Ijoۼwދ2ȶ _ Էpg5TԸldcf|__c$ىk`ngHK*Gx6K~s3:děi;bw4zx:jنi$p`p~MаI~{F[Jp#B"d<t >UzLqЬ7N`/Ntx;0)141?[*kGSB#N[İ䓙ˈn=KqN.AH7a[F?wa pAs/5ywj&o|QClϓ^FYVΛNF=ˊ0w/7\gѴ8b!US=bNYTECTٱu&o9cЗO3v%qR\tY۲(-+/-8+$:p2$Ӓ4%SuHPz62mgult'9e#~VR=dEMv]>Pw2RBDQ*KѬH^FdSBd! L!Ė Wã^s 0h>Deu9^/whӍvWW [}B(*ExW^-@{J~;L\{式HV_&;v46|l keY|(zh+!TYOEuDt2&DCs(_d0L[zM#jn849Sj";l ŕm:εu7H[P^ʪN/rսIg)q1aԲ WS IqEᔝ;%Y5t럨wxNc2{9fwN[|GZ$ahfk1hwpC+Ca32voZGJh0mEf<`^.ֽ{["FclC j]qNhG,/6PRWxE4dƆ VM,Ġ)02 #}6USl>/KEʑ+~2.l Q5YyWHQNӧ6b =R?iaJ%<{WO )#rD9TWt4inZDx6'~suOb gŠVtK:4q/B`? !Ieffйyߏ"Y_QZUZQ|r텨2#}_7©e·խȚT_@u%E}6G\ʥϡ:)wEh񙡰SCZ>&G?q;9!|kԭwwlJ۔RPl+!"-۴;=ݺb!#6?EB!﷐V*D6\94lYp,Fwufu2 <{8 {; JӒP+HFu 4WzZ:PQ\Rpj--+*)$w\IEFS˶UwWoF+6OQUPjK,TMq9c2=xA.)&P1^y))m9|ԋ rZ]L4aX[/Mi6s2@Kx.&B55q t[w(&@fvYH^oo+|)GӲkZz2NL`]g.7y CL c4~clV*6gŠpUZnW4,CVKC#κ۶Ї^޸Yؒމ(= f%W4#Lz>Ot=;k7=rZ /W_)c_D~0Aގ\9HR~uͯĭ)#E+- Ru'TjH}B=lK<=) lW7ب4Ę@9ѕee |B > Q#1]UodӷǻlmwhC.<ͱuz j E~pu|~4γ{g0]?O^)).v+;t„5Lz:V#8Hi ii"?oLY-oҰw2hZ h㈂*kKgnxYGJNOTE9#ޥnodოaԴ Op ?+/H_˾5l3gqDo?eQ1ljk?:UV]w|8S o04tje yq[n#<4(]*+h^=xT#aSҴkSzdfd0vۏrI#{gӨ%JnT48eѥ-bnѐ|;ǭȴw;3,,# kW-@{ ɖj7<B#L~>pG\X^!g*ok1\&Bq8ڡaQ?ʡN&;8W3&t9Qbt0nYLGWggq--M>^D$q:rsy9r+:+'_2{jD \SfXvf֗( 1X|F@0GCm$O2?=eIb(~`nd[~̀}^SJM,e5DtljorZ榲M#;~ɯRG;g)ɼ:VmemeI{Еi*ZQqQTס/q%F)l%4TҹY? A4~qy8#-ى{S{m䨅 ԗ{!Q8Mwb`ߋ?#]Nk9n'e#-h!b TR^YE R[ǐүF8w==o6=2/f8 JHBrZbrZbR囑A . r݇ V41}D]#oM=1f~G#uE^hݺVzZf6S2j\̜a;ivkNSHLQS.U9[uUU7voఙd?.U]N˜U4uM;ȵ)W:\?DOYT BSVQ0y%00HfТuNсy%]S^Z;Vn}:njQg햮la8xI\~~^ZPxSVy6cVnͼ& xzqzq:- >,!>,FZY5\Dm6d e 5r--[yH+kOeN*ssg4Z:Γ7H]1\{V^-@{JmifzfjZ㟜_QXy3:aD\8^f/j3lvqeM6YwW"4a\/.QWiI0 qTFavzi-#]NHubd= ^'O <k-ۯN߰͘~錿n% _v'ʼ>gkoQ*x1=j1i-@M%(Ɉ_\fӹ-" ^Aui=oyo|UÓ]!le0U=X!"#h[5}4ǪC\Zus8iz}-mπ=GR$HGCh q30 !%qs}֥I[fB*z@QAK7!DtM)Ru,@D.,M5DvlFx]q\Az^mԗd$I"= ofW/uuv&<3oޅT*IƨVi|WSE'k Nw 6TM*+7dVfrվ꠩Cj~~^l !5yq}y~ڵ[9:Q&b{TϹqj`ۀYwG%^wm-e|LO6h(hҖSM(^=!fvW_ 4Ll8u=__#">{d@YSuu0Y]!W=g㪩^90/*z}̮pScNL^m}Uqյ$Ra RAޯ%h(()0i wPNO4W\==Rx9{'Mƞ߮%oÿp -NavmBBT<[١ C7˭C֖\!$H. -_ ~wʷ 4@ 4@ 4ϑu{}ӖJKoG>J K ;+!V*BF4X/W25-N;׌2ϩ<+o|N.bW9h܌H^F$/#" e'(/#_kOx# >C-/<?K߭vO?$if",ͯ%1e'o[o[=0qVrpT5C Lyк!Nn)Fڥh⸶ [3U^'g^Wqj51dh`Z_m T:?u39-"g)aմu}$%=K;pqWxy^JU;NVy173SO/\y_ɵse+ѕ<G6ޑ9tY?.u[.$ BetR%f>~xWZ5BLua ^blrI'VM>S-uЎM+; vO2{oyy6 ]q\;󖛓ItH~a#l+prNp5U@Z0?yh)x' ƚjM pĻ2TX"o@Euy7cu;nmv7ah:0 5Rj:KSC`:)w*]HYOW>#y`kv<&Lgk׭ZYqwޏ,Se|Z{V}vBrLp Џ_O! 馼[S4W!\vx᚜3{dRUOY iɕzX]:̒Ë%_‘=-4X{m?t'KzYsK9 3L$XC{ɾxto`B-@W<8B !~;%at8E%'ܻF!~s&h<ٻXH:/?'^\r}gx )ҊS8Z"*#5>\]`e\FX+?%__QZUQJ7TV hAmYQIaQIQYp%TZXL:.V8\TڼnYX4awJh!Cꊢ#utc32,FL6 _mxPQjϐwacʚ=MeD;RT! 9]uܤ,Y}~zg1{CHT0L=Gi8r-V!L0b;v@`S٬mo'{נt-G*< 6*_p죈Zq$s5_+Y{?yið*Vۘ)fu\x+2clyfv)vvf\ey{|SmlI @u9O;rG;WKfvYwgte( {w U[мΒ!yfEmվt4fgxrQ)Ę@!!>OSH #*7vԷ;mw=C>h( yQ{=VPEbf,-=2n9QS]}mIs<6s=TGHP_G8v(k0BN:v9'HR2+O"F!>4Bb0M1.ZS%rCT9Ahi)D4~1>DE .-2j)p1<܈@ЇB2"4dM, PN&ĕ4ͬ̉ʷ5]>).Ĵ;SضKhЩ*>3'z͑ a!&Sf%zqM 6& bl !8NgPT^XƏ-F`Z8B ]QŤ@IuSbb'Q O7!`˲ctL!sόL y5H[Ci[oCۢGZw֙;pZWAg}+q6y[1ܴUquQux; cOs#BxiX<;Gjisbnn Ÿ굮-bwwb*&et/K, ‚?3LfΜ3ZzwߤeS\9b$K &v,mZ4wg'LHMV̀䦧p пY1v3tfܗ'ٹ/EHޟ9cKǽ=y cs"$+1ov_2k4^[+gmET_9xq b_/ z7 Ma11Vj̲]wnğ{K,̸f[oЋ_]Zp[Qm&{!Լʙlk3]_ٗc:̜"~TnӗE/i:RT5u#ޱZޡM{\4sg4Dy;JޓII-Lp0O8.֜f/-jL35r;AssBᶃ4ՑzP|ywqq_9HMkި[F'6|o~*ȤKOTSک+O&7KuΣϰ1MM8w&p4:%i޷HJҒ) QźZ0% MYQW#K|@  /ticٟ,Z~N3'H>0m{r #yiBŒGD} &/Wv*V76}O3}OIK{[ࢂ.>+K˶ՍjG'Q,iV^iy1Aszo;cM* MO|%O ӱLhZypϳǔJB`lsWJ]09-c4 zhi~ꞑ6xEպ0nьrvRϿsmp:s>gG8$OP/&C:C ?lۚ7WNvw$4ViNg)i /ooPRJ rۇ"[H/meR(2+ 3nvOBb#NL҈ b4b+1S1f$ϜRu~`y^lpeCUOiT\>s6_G W$/@W)ô u7ħKlfw'F?8_(ڽOMаG^րP*? ¼|^\M7G۾/kNv|SC .kHhKZXW y~Ɔy {o^?yБa5/6;xyw˻Q|7d9+|B aG;>a/(+\1T-ʺEQ$K}xE5l;;fe[`6f v+?va2VY 0aO737|sSM"܉a&wqzX}[Gu/UquV=PzNNtvzS:4 cUaZN-qt^ ;ea7Q}pF7tǑ TN*? %ڱ 3:uoۥ_Qbnޚboہ⢂><>}R =tœiR_xЄ}5:}aH{ͅk Kam{vuI0ӹ_;ʻdݧ(9]nF]Fl;ڌP$OƅFHw#?K+6; eZ]+ĕ<7d;gk[9;l j'MN^󲢬x}ܣ58JvMHP_aHDnH *]), _/51tca*tRB҆OgabJ8C Ta EEdpRyӐ53f7K k7R}@)y" $:S 4d : cO(*&Tp#D*[JBYye<^)E+ U^=*sdG*pM]mÈ'n줊kiiTvRZIEvҘ'zt@ @ ~ }(7!,z{&l܄K#k/aէEw3TJB)1;/g݆P$o,߱2QU+lnY{ (zaҞ|HQiS{UPKIۍ{m<feH(Ii~vnVvnv~: sÁDnZ-{dTPe[Z3_.0~_XX2">Vū͑EČ~ڍ9\^CCS=AJJ~];>IT 0g. DV^U{PE^ʔX  E`@aDX0־[&ծj31P'qqJUˍZNdTve߀f;[ Rv(4$OaGsHIJqM]DQ_QOJ *JK F ߝ{|TRln?W6cE9=Dh yFTVUr:T5lqi:Qn/D$_߸Q-qcTpAViYJQdbTd0`.TOPuKRjI \ᶓ/Sd4`TQ0EN9^|m`2#9M7sRxx;ͱ}&FrS< џ%B ߌ?fہ,Ă}Ft3 Wvު!&KJL*L ->F`ֲFŔld%5(MON6rniYIS$r%ÜW&r' ) P94&=@UE!V+al^[Szi㦤r%Q4iQQ[q$,Bi4„BeAoʩF)a0k0"q%3~ڨgJb%@sj`m;fcdq̇D8%+ōڵ!BFgm:6UfsT/@FKAj 4G> 7})0S7 p0ѳ:e@WWV)'Ɣ9qu-X9_BAPcRT$ԊC|n:Y4u=mF-/p`7:6a]~%nsp2շp=gy  5-U&4zy+0+-WD+[3QWKi:SԙwnFM+5ME{4MA-!8{S5M 'zjeµks:iT9]yj橕}uՌŢ%|?Wz}K4/Ծ=e$^(0czK5/yHa&ѩqjK\ f~gb7 |T\=oKcSW_=7SIel灰vQBtpE]ߍz`N~@&BؼJտ3ghápWK{&[KLgdM  hZZmpJyT,64_EUS7兽HVUp#]hlC+ϚezIKő #z+;B["y\Kwߊ'\hb5rwg3p\2s PmKeRtJEzywHnd`t]N[gc x^O}`t|@ީsF M^0TV^"lqgxi~&n[n,k8D`r֧cbՍjG'QiV^iy1h {W}nRA :_W5a:Е@ Ms~=y]RI?lٹj*T߹+.1FXZ=4iiuHUj]UHh 9w;_68F93@p yjՔGt 'Wx1u H?:Xd-h`fonm]GuhbzݤңWB|'~gGp(*>,fw҈ /b4b+RKͨJ98:Yyy߱CSk:Vn6pmf'ǦZv|hأO/|ik@(JRW_Pda^>/WinA?>}}_֜r7ćr]}őЈI,>6$1 ^w+W{inEE>׌# MN*\~_eEk71.ƍ.0-.{4(,0)&8ɑc`wZ':2ņq¾aMy{'Sȧ'9i`˂B=zwk@G$9pC;}HRW׎ryLq~ZF~ېJy񒙗֫pacPc+K5GT".հyP>V`5bG{];tؕ'1j+,^[䰧DTn]$xu*x|캩3邊En> I-ZO^}:Hjoܲe+c.kӟ\.(SBsvp HxWy?[y/jH8r|Ϟ%}O6=[n񏷏W {t@)[ 7.o1*gti߭I=Ξ[[ َgtuoͩvR c|w^j9 0I.߱2QU+PnY{ (zaҞ|HQiS{UPKIۍ{mE|JäܤS'S@g4^]}%i,L HӋePDcڠY ߢƎo$ZѦ9 M㇭б{NBՍKL.&1M=m6ufRäɉi_*G]SC5 MyQ77K)dat[ _EIS#xm4;7w si7gN2235#ss3j-ɾ!|&X_ɠ0~_XX2}1GV5H(?_ %b :Wo[phwj2v.f 0 =mױkdly.r~}^`hAdF\2JMrn!sc>f,U2R)f A$99hSGK)IC{[NyP3{K⇵(eRwEB!TaA8d4jePuKKct&ݻwRc־k%{-U(CWSo۱5#c>$J)\)nԮ r7:mӱ.LM:6|~) XZWg91Oq>ctܧOQ|T|~Ԓ:z],؊<$՘#g:_:Ӡ+K(79jLXj*MqM^9ƵnӠͨE;~CثcvSk=m=M?0 u9 5-U&ٴ FSa1d~$235ټwFV-Gfժ\w2v۸L ڻ3Z|WAqe hfiЮ{Q/Y8HWuU>EɈEbb-bt4})LU-FM+5ME{4MA-!8{S5%N᥉fՌ"ͲsVh LwjunEkL{ I~%ў+`B0[ھzam Inً').<}ο)Rº3%&mt4eI&UY5L Hnz::@ uu}Znn|Q.j7@+6X$==ϻrov_2k4^[+gmEOrSa~s0;^H/^q*fI|५Zb8ave0T@쉗j$0wnğ{K|C [i2 XZX{~l=r$G_3jIƧ;walTr.9ˋT$w>ő\yEGL-3EiIVc^(Up==N]{`)ZۆPʉDf^B&B2yC3ٕi>[Gw JeB8u}9V)'K:}YtR#EUsߙmP-Ћjvpznj4pb#q'NuΣ!ٰ1MM8w&p4:%i޷HJҒ) QźZ0% MYQW#KЌ@ u0 >]Xg# ~n;5}H^zZM^0TV^Z"lqgo^3atrwdY!bԄi<{[ࢂ.>+K]3RNyXڭjc~Z{W}nRA |{_ZׄCWF-T'p>{,@)5+K^at=~cョ%~Sct*ܕٛ8ib2FfJ@0xEպ0nьrv NE@ no yjՔGt 'Wx1u H?-MpTw@ @ @ @ ЈZ%6z;V?@ دjzB1cuz[[Ae>\uC4KT ݏ&ǻפ C.lхjmz3K;b|S礥׫D }mKP֦;`V,˪7uS,gb.N#V8 y-Ҙ[ I,+v)@|tU8$}ҋKD:vCt6]\ܚ^ ~`Œ<>Ebt `gp(kMx^C Sw&0%{މr6t4n |uYeOCB(*2/Oăg6mqA}Vu+ ]@ ?L@c4LI9Qs%d4wlTONGim:7b#ݜX/)hzѽ^Cfg\Rn" y<^DQ‚55iFZLQvtM|ˮu=}\Zxa;Gŗ[/0TܲӹCmFmk:yNjXifTݻ},pGvYNj50)ĦmLa#:93x1`1sOQR/.X4' 8T]B_ڍ*3LHj9mˏdEYZA`-i8;>˫}!.Mթ{w]|ϫA9ȬCyo&rEӐޜ +%q$)%BgYXh5yRYa?I=T=n$w/ gOfjt! )%,#Rg *= K ͫ2w3yDV aG\|7uv0_R"&8< y)vN!@ ~djCKjqPgۮEیڂ<䳠a9{d!,K]UCO9Hb6:r*a xPf.Ĥn}bC 2sr'AvLUkOOd%Ӽ[26 f{8K˯dƋx// L"2oe驉E&z-QQdUlw\u9K[Q:Fu(3/ymCRB);(Ԕ-:ֿWqx1%ikv ؓd2)l$(I}f3_c gi1)%IhY)6L'Wgm kYr/.ltOq|.Zd~|&Mb:{QnjҠ[u]8Y-򆓠Qyi\~Endb`Pꨎc4@|=֕!)%z*ʝX/ltzzEbV* [K>" I OiԻ90I.޳/o$h5̾wP>~k{Ra=%|&/':&S8 11)}⣷qG7?j5t40ҩ18ɣwRK&pĦaIYϩbC PP#R<b^+%(/M^:a'_yPP4JM~]δWP"@XWFqʄxȦ '{kYNJ:QB);(\[6~ӴK03E4i#.))|%iǀ/(͘!VˠR㓞f!8 >4L]5G6Vz OXNl.LvS:8jo)_b#Moh^@ 'fcՂ`a $YhܠCG9}lZV]\̓I(4-[+-UpȌ4. !Sx⚛1R# +*M ffYR /D8 L[]s;gg6/ENN),Db hȈ.(wCBוw^YWf̘}92;.xIޝ|Yok&iWp-[]Fo%]@WҠ.%mPVC@ jaR 0hRR( cjgP^oe0̛KX4wClR3AE %^)mmS3HLCS /)k[hV,LP hv+lBSPm"R'̻&&C޾C K OƗohtgQY,IRv'^ʳJ$1IkK-++e'JeA_fQX$Ig7Ff*x~թ#6kњIb.=06δay9$ڷkؘ2I*;ŤIfݮ!6oׄ%q1iRqzZ5oaN&ybQqӪ~qCA}B@ ~&Äg$zYNƥKć7Co}MqmS!-Oakq^w*9I5Gvٰ%55s࿸xrҢ-L 3-jiw[uQ6T4;#^_[׬TCxZ:m۠KSt*XjJy)ts9 c>o+KFJ^?`3cm-ݲcs´Z۵Nv:dnNԔLfK 55tXb rikkmظ(kPNա~P*(;3e`-WМDgLe8Sc(V:ޚ>3N#h'Sse `ilM*{Yz5v}{0jυ@jkZ؆%|eֶ߈Igw7͆/7yDߖFt<,Ri1[]^t)afoFE&RQYiUv)hTFz6z@ ݥ#;T&YxKsb8T@?x5g[;Ǐ@}r#l~K˷^,$)?W4m5HK򸑗?[?c򿇦ҋS?6lZ[yaxӄ[Z."IvT=CG(d泀Vuޫ:spoǡa,Dvqި=$|S1ʼ[~e4qKd%\T^[f}v$V!Eƌ@W..Hx#)tz>9CR>u2J deKMk.KRJy%KR 1r*Q ep3$8 EXÙ#1:^ S"A*S݅a{"'5@5dUT.Vo 7WΣǵ8+(x8a//5>IwCqw̥&ѵSkq}>PM˥hFDI|LraV|&Dܴ*',7dG$@|+xKldsMS.@ GU@ @ @ @ @ @ @ @ ] X:l#;ʾfL葭#5:2Ӄk1X f}6q ~{m3u*M&U'采.2j?f2f p܈9 5! ;i6>?G|M`Qz@IQvbrjFq}|5am]/JwfLcVv&ի^Y63@ X\S,(Ġ2_؟G:M_Z\_~$ yO@ $yEh;1z10/J^y4`ceF1棽m.C:lf0j+,^[䰧D,58BA%'$żsb:6-.{4(,0)&8ɑc+=õ[!IQ_\];;"LߧPhe9x!7ol0_aMy{'Sȧ'9iWQyE@xoݖ<ݸ|oʿ9e$}#^kQ|5(dhO<{恎Oq151އC^'FkET8O L&<4#ϓ".-kM< ĸPnB7N*OSe%Y"l~rlJeχ=蜶#zOcBî?%z[ij ?3(/`Pf^F ]8۸jVH96,պ:TZ 4Fr@Vc w"Nͱ(ubRA6_E+R_P_aHDnH *]), _o'cT*8 560?O@dـ'__8rx[lv\佨M(OyĬy^cFwk /b%Vvy?7Q" -G oc'37n񏷏W {t@)[ 7FrCsTaב_ܓJ2o-:j|{-ˋִ֙qbvQOGhd:{ ֤ϞTgϭ{޺K#;d׊4z{NSb_ѵiߋ:/y˦KyCN}SڵjEjƽ$4]:ϸuoHHny ѩǖBuNmz:@aPʠ5Yyk(2  Y&*}lƌLJ6l9%ON_t"{e͑]xc1xBp*|p/ [.|Y^TrHA :aRߴ%$SES1ORxx`mzK֗_v^  ,&sn/ky[!iиMOY>VqnfHH!Ny+)zߝ<{M0F \?< }].-,jװ7$dXs!/JPB  h몠J,yz (^ش镓X9ˆ2"]2>MmpYuP|~WPR}Cib˂Czp 8n۾+׮TRϽ܍:_TXW,YEyϲUV-,GÅ.LlMZh&O^`RxM)JrEJAA yA4Mp>x`Uik=m=T,~5hP#?{fV_˒Xlg>HIY} 9LD29/ xoNʼ13Yhɫ lA8a_ oJK0&)Iŀx91^ yEq,P/&y2$+ŮC4@NUMeD *9X D ֻKuz Mʋ}~M a E G?}4CõoދX?[}h|~imC.eڻKhs(0Dugڐk[3{=Ynە7 X5R3o&wWF Rꩻ6]y9$Wg=J<l^SC۞{v$ w?}yyU/0Sm,)7/0.'~Mc`y`ؤXI<֝kԦn}n^"fo ?܂R/NhmU2D2H~^PXGcٹ @ڴCT [D-DWY6nZ+W+ &^?{zNQb_,#;9ֆ1TBl,wH(|y2yV]m"M?6o=R(շѰJCj  ׏SI_yLP\ kg*F/\5:bƭsQD[EtWܸa5' !Tt!0xOT;.%p)Ʈzw>46,Ie`4ok Pܗ5uo$h4WR_s]vۋO6^9#!EQ2jF\.BwO)I9ݹ;oo?%E=Cݻ*-\5Ui#7DBwojݝVro~w}3Xp3}z@4h4@wGЦ.d,Fڛ%kB׏I)'u^!S7I B]cR лE! "PFtx//v;\uO+I}%߅fzǷѦ̖IJ쨊[U/,oe ɴkRT.6ߠL>XNEǾXKJ@Ϩl]s_$ē:)[+Gm=ظ"+6'+.Uo1hx`ލ+Nw6؃C)KCU._=(㠫^ 3*[D= @Z{ہ;Y/O@5yvrm6%1Ulva*s?p6#D]Y~^#7[;B2YZM{ڎcgӓrR2t2Q(/~qFj^Vk{` G{l-Р'S2{|m2Ze`r×w+u?V) % +el^[:O8y_L{cw-ГmIӯz Όl_Y]F ldL^>eˉ2bo5iɛ8>Na?Woq7tN*Vy:僼Q^chNMc1Y;G>}\t-8 ]WŴ;^8iz~cVn='5;z̉2.>{CvJYt#5dn! ,`dF %$`Q5,,ڞMN p6 Ҷu_-reidhD})(% ޼hjׂFg()ms. Bl-68'd J\ @q#$@~\be"TvL/'( k^ VĠHI"z.4]EC r!†C E2AC"zX)0V|-`816ujb!PVk)o,ESEPU516)5y$ǯ=1F%ؙ1GVT6],b.JLhnⲣIUCf'U|W%|I7!9|kBc-@=-n'9` p8| .H87us2T\c߼}WGB9OA Eka_ZZCpp߃H~Fm* !d-2m{(δJK݈)I A jI'dW62FgtĮ($Q~!|A(Anֿl}(ɦg ?/dhAD `XE3%wsLxYN};9 쒒u-,:pMhzceB'͂A8iV g_5 4H Y2맽i6K;aVߚ7G4F!?F$Cm7yKӎ_:Γ3!Z_ xQ%hR6WvQ rsa'o8E. Bـ|-RmtP 鰰;/؀ ,.,|"I+ܠA!M"aRh^&"/D7^'' [ہ Z*)_ m}~&jr\@5h50b Ưyq Y<ʀq%2#S ŝzQG+n[}pH^XF%:HOo1?\Dl[r AΦ 38C[?)h)6I!Utq!_aqeC'&sWDϩbn$IS3A~R ad >HGy"WS2ҕo>F30mܨg=x}i+F3|@#$kP~=aw(0r39LZip0CTmDŮ("$=Pv,^`"ꋡimjW С\&5U>3@hY-Y{8cwu!42Bfoq:MJC ՀWXJ<MhYyMOհLK5tr_%Z'JtC}mHo Q~:mo^ `umںb :nhђgW9bPMU - s,ĭ rkROGYX3aZfv[vbB-Lfo qHcknr[j6@EMj&{dKLJ^QoikÁKUb1} C+*]ݝ^ۇgD9[ (xLb`әO!<3c$@`n 㬠k /7#b{ukyl;|MݰEK##foM$y ^yE~v+.T"$?/(( xlߺ9j+'$mhf^Qޘ/UJ~+Li"f",HׂriEE =3/oQ$016ay 0bcƋ<7<@ۼK}^@>Adx3n-sC tO_aأ%WRO-8l}|B@czhD#| `Ix(~i19b0+vy.~6TW]QYCzƍ[\ 8@IRw:_mzkfZ׽RqڲUSEV.;=rL4x! Qg^ݧ GFwx{3}h4@#  hF@@@@@@@@@@twmBF:b *["&~{Y\9ypR";=_~d`-J\5&%[ @BQ " Ul4@7RksWjiE9\Ь_x:mxzlyHˎܪRf~aquw}a3n%Ou_=8(8tIgqRXu*:EE*=uM~OLl1pL`RnwڜP߲ί(gWcz>[^V^,&W)lRT.6]ܿz.Q ?;AW?,%%gT2z<1Iwr_$ \=JjEl8KIcؼT=~lҁG(FnwdI$'d=dqsGQ6_/Լć8Fm(F[A)O>Me>.d/q3Va6SJ&DyYEyEWؼ2#u6p;WZ'ߓ_fɿ2oGpNw|ʖgSgešReh/]1HRYp~B!>>O~#*gc^,ys$+ǟ#ʧ ҴtR +ߜݧ?j@=`ܜ2yG '{&W0qcX-@K-9A抣!>6چ-Z0@#kǏxX!MfSѫC^{"Ea=n:/\j9нufcgz l Z0mtPi.^38iw;5"lQs: d,<XZt,S VqS"7(b~A|-E(h(A ؿQ( kR`H+[/m0ϫ:XmovŇ/h}j3?NQU9[[P7 y$rE-!xLj6Ks9M;so a>PG.Yd O9٧7U<\m_Z[E2lUV=(#x{|t#ޅDZ,ֺt!  H- MiZ X2jeebsE518Aɥ@aBtR^#?qL={SڲXW>qYTӛ5TU欠Hgx5?N=P}ϝ@шR&QXKy:?Ֆ ՍQPR&9\"!ZmpO(A@ʹ|-)vFI:!DŶD۩$>^NP׼{7ĭ(AY D]iWv B /"dD.EV`R`7a!lZpblB>*-5NSVD D1Y<^UQjblR'j*:ID _{b8J3cKlJ5?Y:x04}j]==$c р xp|eG? >X[O+vK^e@o8TCrL#T)Z 0D;[r:z[ Or<}=@T7p@\`I9~qn~梱 d8!x-5y28kr΃j<^M?+|.  8i/]x=4_%"T"CZdwP i 3^66SPv'֓(NȮl>e#]Q0EJI,T=C j!Q~= 8Qj=FM*~^ ] R87{ Ђ6v1CfJ1L5\h'ѩv$s%%ZR SYtM?z%1+9@>ʶOz3qҭξjh#kPeO{ӸmWvì$η5omiԍB~Hnz>8n#8uu'gB JZץmj;$ij8.Np\"Z`aaw.(_ Y\.YYDWABD/9| мDMVE_oO:bO\xTRF7cژMd0jٶ&{>Megp4SRlCΫRCBVA˿ʆNL Sٛa+,9keu]K N]@;K!:Lo3+ uڊ߼,0 7۴uŘAuܺѢ%9BsxġZ,sYۉ[פ,pݱf첥Ň2*[GÅ.LlMZm 4v"y$FRPb­Er^x5Տ j4mmGWOF ꯢ6xxcUw rY))6$ H!PZ= L"a" 7ٖݖTQ՚e:-uCCQ5x rPl<ټt5LmLmcǧ_oq/5Eym0^[^¦逆 (:'~φknrGꖃ8mܱ䵭14PK訨 o$}]=ܝC~'E]=rEK0{P}ն+JVw;sɝ'o^Yc:|zs׆b(WT*wx{;/ψsVePm#*O03w5䝷C.ykghyIyYAqIׂ,^nFł;c v4SaJ? FF6fH2p , 섳W\D2H~^#B).@-QP6@T'qؾus='WNI4|ō~1_vW>U%E%D YҊ Azf_b!)H<`bDkmt.`zkƌy^Goyy1-@)!a}(.53Y]#fZ>(AZ;=GJ",ßb[={q&#v%ЗxFzP!bOs`t%W }3j\68mz5r/q(瓤z1#t:ڢ:ʹ{We"]vzFhCB@@@@vϼOAR:g:3_ hF@@@@@@@@@@4ۅt#H{4UDP{M(8 is<%+Dv{ӿ&tZ=>kLJzh7!ADhn@nTӯlӊrR_?twYu>I)UkUf`KL{p(Q: Euiqj W$㈥nTt싈TzFe(''%ԙLbm]<)\6;E7q/ 6ulsƕE{8-`ץoJ ЕL.ܠG!ӿO|5{^HK^cn3&/ّ6@!B|}GTҟ=|=rǺXIV?GO;0Wi-xV9#7Or z^ !dg9Ie᳏NrX0L:a*gj[xف6Zs<wG +]C28P}l 'O9o[ҵ` 5G֎m=2tC! wW6Z[-D̋zt^rơ{ s4;w? A"`F$Mx1\fvqLY[Jyvj%DtXx*?vYnj&EnP$Z0!\QPi=!Q@KIׂ ;!S4upaCߋm '.SI}(28(y!o2[Q"$(av%<RE_4D ]^#[an.CZzgEHש|@UZjZbxMuC%#VؤNUt擈p`gYRٔjJtYtE `4iڛԦ0zz*u32Iˎ&T A|TW_嗼b%ph-FR `vu$󣟷2yz8(*/oK( rEcPqpC;/[k|"e^q2 <'Zy~~W]~A hi Ap="|^>#zhKED0;/(ȴ8f*m.. lv#$A*N36 'Q]|F`(GYz@aC z(Zq).?z&U*@q(n/m1acj#̔c͙j2"g :NΣS?H(4KJ>Ե7˛~JbVr}荕m 6 g⤥[/8}( G2.fXq.܇YIok Ҩ ."},qFN;~q8;O΄0k}93@FKv^IډgG-p\+Ω D2e"HC)fc\P`&\$ů(r"4_r(Jy ԿQxtĞH?.nm*@k ~nǚ/1arx` 812:)(d(Əe[Oi(dwUxD{Fn=mTq#{aw =Mr}moM}>7TW9 2{h 5ny$W2:Ņŕ y^=~ik$M 0=W'!BJۂn(Yiv6mn&g} f7==WN3WX,9r }ݗ hm+9>v&CtOf W-dD}yY*`pթni1TuEKs?R_C5U5 'Y@*ȝI=Ydc̈́!jmeKeU05B7| !9\m5mF'/ 0)yEiE y%9I" Cń[ j7_hZO~ƿ0 f4_Emƪ޷ٳ$-ۙRRVl0H B ơz@(E" Dթn-׻-75׍tZ/yVP[Y*֛~Y.AKAi?C7cCQThGD}7B 5Gk|@tk+v_yWyj:6~ǎO; 9)zq㾧_jJ `*++$M mQ(1tNQ# -~}+䈏ā-qZ۸ck[ey5%/B=bnqiqQQ=Rx(!7HT'zz;D&YCOv{䊖<`zmW{m^n%F1UaOgj;o?\P8qs5󎳂㒮;/X܌ȋ3vՑɻ\h6u -~m6e4{Y. gPd  \GR\ [l N}+{O8h{F?xcT +I0}K%J$"] !Ac ,ľCDSx<0ڄ]2֊/mcZ.yRCP\ kg*ѳFG̸|Q:е:wD_IY?^{L>bG೹-J  $KK?/KB_-&$B;,xJdflqP]uEe }k 7nr3(^Q'IbF|uEu@i]Ji>WMEZȍ~G3<⅀ .4fGyuu@@# ugh4@#  ) ,F:&hDoP#p$fqAxJIW$M邵z|(q}טxg|5);Me5Ԉ 07 " Q4@ K~ǯΩ_:ǧ夾~pB~봩!i#}R.;sJ͸;<} PtD;*,i@ /i%utIaIbթ {W%V/R>=$.do Q[6.jhv e=-rv5Ʈ*eU l»q={p({F:YW]]V7\@~wЏz- ~XKz[QT<1Iwr_$ \=JjEl8KIcؼYT=~lҁG(FnwdI$'d=d%E| 3R^hS=ޣ`mt>4{nzϸ[YS_'LY(eU_YV.cʌyb_{klOڞ~{gXtvf\fu'ʼ2BV`';e\fB)[NM{IM!L?$q eMxKtR=Ÿti/佌C8uhfn΢9BkA/\5n.y™M[ňjw9cN,7\w{deSTtšReh/]1HRYp~B!>>O~#*gc^,ys$+ǟ#ʧ ҴtR +ߜݧ?j@=`ܜ2yG '{&W0qcX-@K-9A抣!>6چ-Z0@#kǏxX!MefSѫC^{"Ea=n:/\j9нufcgz l Z0mtPi.^38iw;5"lQD!=/H*XVd@.l6 !$( aH0ZZPOѓr(D_(BZziJw nu67a|Isr>_-reidhD})(% ޼hY[4T7D=CIip`8dkA>$K]W*g|CNi$†%rw{ Y)nE@ekW4CHPj DxЉh( PFY:*[,(BZzgJ4@c#]p_&Uiiv&Z Q4U ZUc~8QWљO"JXniTsd_JeS* e)C%0ѤioS=$Klƃ{S..;7dzRw^_*͗JágJ*4`!ْ;ѓ̏~hx//} \K!s[7 \ C5l!͋ޗx^+t>Pk}Wj]vy,5Y^ЩraGB9]R%!1E]WCol[7gpApy6 Uz7pui>K|hÜF!?$Cm7Qy #ӎ_:Γ3!Z_xQ%hR6WvQ rsa'o8E. Bـ|-RmtP 鰰H&NT/!R!^(yDD@tĞH?.nmH@.r5rk^)3DMV SY9/?75.DFbDJC!S#j3rymk èPi>˘蓅&{>Megp4SRlCΫRCBVA˿ʆNLHGy"WS2ҕo>F30m׳_<>sid#|>JH|R@ vCҵzf(;DT'1ij+[gꨰ^*;"ꋡimjW\niWQaz%I edN]Hq ܽ%c7wѿ]r 4) 'T^aA>n(Yiv6mn&g}-f7==WN3WX,9r }ݗ hC+9>v&CtOf W-dD}yY*`pթni1TuEKa< ~jjjhNen'25#,\w0D3-lia M;\8QrlMnKi[md0l?yII+Jm7-H+Ib)*&Z$WSՙ}Fz{t5 X4kѠ*jG7Vx͞ %i|`Ardr^0B)>-Q_AJK0&)Ch?T6m!(AkSJ< /4UAQ'QXuGg\歧ajdj;>7'62gx ?~Y)+%h񪬬,6M4mOFP9y3G|6\w;#>Vl|~imMN'mՔ]BGEH]} Qd =)+Zy]TVŜۙK7<}ʚM=թlӛ6P(اO@9$S}xyFt*nTy*v?!ra\?C=FK ; K`p3"/WG&^Ƕs͛j6u -~m6e4{Y. gPd  \GR\ [ * Zk"NV1 X7w4 ?$5Y:  8ˆ"=3oQ01tzњ0}KFFR4`Ƌ<7<@ۼK}^@>Adx3n-sC qt@J2)߳Wg8|6e>D@#46,-; }k:@/-f4[ FQq@tEm˦ UWTз^ƀqV.1?5* IDAT⅀)}>I3B竣-ƮwU*N[j(eGn;1//ј{} rjtF@@@@@@@@@@@# |- hF@@@@@@@@@@4#hSo2Yt#MTA5GH₤̃:I)O$k!P1)ݢBp BǴѾ3@^^ ;~vN7"9>('K|eMO-O铔rQl[U/,/l ɴkOT=Mw QߠL>KNEǾX[HgԨ\7KĞxPg2eѷugR5u[|愲nv~E9ce*g6ݸ~Htg=8}2mҼˈNsA?0aI/IQs@@֞v &NnVSGPMwuwqQ4oC[TD1PU PT E;EQP_8$$n'yfwnنqYG6lQ[_ 0m 4߽u': ؓ b$?9n*eKHpxuB,3LZ 2ֆ<}uQGd6}mc\JhHwu!^g6o`ee@XQZ.e u;ё)O_o/Ugo8qCx}tlG>HMɴ0uq2uLvRFQ!Qq6KzL C!r !qxVfDžp"^g蓀#|\Tg֦OeǦ =:Ղ٧R8M |ʊgeŞxC"w,beLIDȚ|O<.f{C(W hsۣAD:b,D"C|mJHDFm"E W62TT+k v9:#$ /;b"kԟFX@q{]dMՋ %k0IVQb2yC=^Irr$_CL(  gs^]^:4U;Vmpk@~!S.c7Cӛ~`kM ϐl>q~{O͟~3KZΔ^9lXuQsMRo`%> w?}_xɾ^me6/l~"Yʝn%/-z:R)'DjJw +n(o @do<ÖcXi8~&6ob5f#c?isلg&TV}%pg|{Oo+T69dө VWWLlj>N%E&bCu7o~S⅃+~m矄<㼫1;6ඪ Q9 /DIDj"r!›Qj.J`2* X.ፅ 1J5LĹA71CC@O[>g-Y*$sr}} 8e A.ٽy CQe_| CTCG_ 2¢T$^=;!KPU%lGui9!7`> r*ʸx`S0Q\& hkk UxqA[v_5k7㋸")!ѼuH0o )Eִy ) "rT A| Dbg,DC|:V-DF^\TLڨJQetL-vQ< ;)Ό˓ 7jI\ϦpEvHjy' -߬c_ }5|~C3/!(/ #kE.^m-k-c}[ L4KFeAѣs5mS;1(&3;uחwy |j BU[]vPcq.E:]a^dm$Bz8| }ݗJLMmh;?bLX@m=q׆hoZ`b_- '<ƅA$q]|Ty9T-7۴4Lœ!d1-->kRXW@9r-^՚/ZH S-]K;GfSʞzߢLg BaU;ާBn l\xRy]5T;LY>V _Sz_KYAyF'Ję694-U.htOՋa6KWN6WSTֱ^tvO!N @$hMIT4 !IH™Wnkǘ+(کӫDq̑=;+(*k4Q~ng`8[tW0,p:$EAb)@ܮN#Ul:Z!Ѳ!Bz8GSh"0({a.PQ_):8 "OӢM O);Q\)^/QheE,d.6OOB"Cd^wgkK5\oT$x>B#zN~X-}IH*NAmdlz;|#yqݾ|"3Bwo E?|:q"smۦ˨f>=\æZ(,\[_fKPv3K <~t>QC)|BCyPX)-ٶ׎Ef:sFnRlu0nAT|(MDEw߮AE|L/^jK8_J8J@L&x>]MǢM{y_@|{>V2~/{!AMu8)p 2vkmYGPU)ѕU@M i%݁[!55E8.?=Ë+W3g>? lÓ?9K7pt[8=.(ن8j7A8BՖ0s2dBA{3tQ'Mel I./Ne>~ycI+Dڐ9PBwܦoc߹mӚK Mw@.K!mr+JإlAs7n=4:230cl6 gBҒÃ9.<~35aR"38ޔQϻM aww;6B;NƾN c(}(@!$;#ʌX 5 }0wϙq^^:J3"tɣش[GZs9AjgYIYYؓOuHd0<E) Ysb߅loH"@^[bbݕmNw:x{?W"\HD8t B |(M(&^&jųTu $.GrDcrARw3( hquqVv!*SL&o:k4INN3b0 ݻa`BalvΫ+]'rfjǪ NC6t \ܯ4}e>W^~,chzp '>ou~~cIKҙw+ .JqߡImp$}\gVu/ /k1ճ zobXd3Kӭ\evՠEO]X:DCH\.yaYyCM~W[:`زsA mτmYL6l美:FV&Cv=~{$?ײmo䝇j/ S,`?}ļ:0z 3%2Pt1DyBVn(2& ټXg ֞G\Z"Z@y.PҪ5NoI?8wm&38w:UBꊳjMSMbXYDbtH" ޯcJpu%ү>bw4Zb&PV!*'D"UBp UmPD.Dx"0 ZE "^&@ETkׅ2bc F)Fi8WZ<&z(}'T8kJjlb$gV(wBfyGHJ!cT\iLb%KEB||NwX>!,8H%7!]r(싯ujKDUX`Ubgc'd Jě-𨺮>-'}&6GA.XeY',+,2| W ,i\p9|@'ZhAdRF#/U|MlX_ ^6jWdmm- /.;h+=xf|Wd2%t1:$ TW7Ț6/!aq\DJб;h!RVHlLفpHoSjC(bKiV3Q5W) }4ٙŽG A,p+ii8WZLWA 6 r#rBe!+3C_:1~ݣ2]TTR]WJ_]:'*d'ܙ|yޜA{c[M^5I-b|@a>35Ww+8·oohq4:VÀPeasyū]x|MeoZ{qh (zcƺMG^w[y"buH  7~}S۱;fo͝ ?]pá- 1"+]TLoGIXOڱKD,p&S(a xNMk L}舰*=1[c Hj iEt=H= d4_٨_O陀Ȭ &$cNNձglrGi3)Q >dT4>ՖwaIzJ1HW WP适giY5=Q)u ! _{|@usk)+;D8&Fſ Xmzq5fj:KNn)"ՉACVmi!<ʐ!$p?/ IDAT :iX8"Wmu%sW;uzu=(t9qgEe&J’lu,@gr* V"ҡ~S~?[,EhC>ucU^;@:$Z6_ZhCt mu^FeV;R*+8_DiZV#)E~4J+k%3骚99q#.|lr|l9Tgc 255(  AH$wȜ.l-ý}f76rߟv]URꢜЇ\P|ydy@ޱc)zsiՙQ7=Wj%I~+]'/<;e3z \BfgqEBڴ0# iyPa*"6-_hYBȱg7I)職 Moo0/NO$<@u_͠Wg^ty?>XD"pr aţgלkT eӗk+vKBl nfIAO9g;|(Obu?/0бP6;۶ڱh1լ[}mWj9qq"p7 oCH5V@KRm K)Q\)oX4i/cKȟvO?ߧXTe/3$hؽ2P1YF3n m?Hj^{?e" !;7|k:`8@~@rX@ݐG/_:mhW=YyUE^s C@{A %XYv( cT [JE=5e)uil= 1h/| 7-xeIU^-@C @ 4@ 4@  h@ @ ,!@ X@C~-#e*]'dJ+ױ[k tu lWQ'?nOpQ$=6ٔQ;Qw|kƫz}ۿVuZS&}-7aG߄?5{IM3bQ{o@~~#Wׂ\+h&I2FZ-_U%_;pq^{S*g`p%ڮ)3I>g.G~xy~*&QEkYiyYy%VKقf坺̔7՗jhyHTnf+yt3 ^0}P@Bwܦoc߹mӚK D%GϥıYY9]HN!$;#ʌ~;rԄ=βrYY'G5g֦OeǦ =:Ղ.CBjm΄%/s\xfjÐ=Hwu!^g6oV&E"Mva< ռp|^%vme{w4tRH?BKy R3:N 0@뀪>z$4*cF1:ꋤ Ԇ#krfɀAn.VΜ{A[Rg]QщO@6~6>N:=Ja5Lv5Fb @ X@j}hKfMv Tk۶UlSPMD5RI"OMd_}\hwW϶vT6daQ$ qgn,_shm6d@o=O?c?kmwL:(3-7美:FV&Cv=h0z˾:rkky4boxjy-;7I13/r>}ʌ<ػ6mB^UYq=xAM璢*.ձKKK+jb2gggL{MPTp(-LLeq2>̗TVe b6c^>$1 Ҟ/`&b"ٍ&3?'DbW M %}>|ܯJ{c šL検OemP(QgDY$ȚXt{Cܰ#8/U@ju2m7sl9UBDVI^[& ';ٯUwa p@ ȯ¯AnB bh=Z6֭E /݁d#}fhSazvᚢ0Ԭ0Z(H7:Qէ儼o'M:mf^4DAdJ㧄E)l!'e[ v3۱ʲ2.<OXVYd<@Bɀ`UbgcH!ߔsJue.&}V#xyhJQϩ& Q@DŽ$TxqۺjcT<%&'Ntc=?q֔20,ISQH,Q251b9Gesjj$P. (:8{x&Ox!UyHH !S)M%m~Iک-WeBjy= ~R)!(zcƺMG^w[yoʬ-lU|M?&!I+n_0ambXб VTT`>ƽTgt#d;Dw[;D]AxN^]8&AS=u>9̝WltD jHub"lv*?KWMYIs_UK~ Dq[]sDeI ܲ"`8[tW0\Qx#:rh2ӵ'Nڰ~ Z7M LEGb޸p5T$N򹋏 /7Gw7妿X3<&Z*B֧O@~7pNFS<~;a~˹6meT3^s*3A8؛}fT/Z~{w]Tx0vF$}/`Ux8/:3*ÞӿAHJՙ!7">ʏ_ymo <~t>QC)|˵;Q%Y k?Cd/z5k͵J VڻO"Aƛ$s.狛M醣(9 ^^JAPe_N :dh+kruGY2 $(Jtc./xYȼvpyC{kr o=1IYT-Mˉ ;"bɧa+wi9O~ȖSIu|f1F@+SS3ʊRYP]m0xTL"Fr5\!ˀL|YES:ďj;<ȉ}vOѵmm3lQ݅x 7PM?pOձm;~v8:챃xO0Q7jU!H' M5NY߈.ɖo^1Loh}׭BOoR@ 䫺S+HjN;q=L[a^(o|D+uCZqqI"r2Hmm-C<ⰋksjEjջ&-6UC*bhow4uK?@ ~FR/-DdZIʣ=&'!]n[>\C\ŊuؠG?m(A_t#A߃@ /ǯp@ g!@ X@C @ @ @` @ @ 4ρx$&yIYyżՊB my˵D<]=s b~ 1Έ̓ JOߨnJ_j으*4X6&^u^1n)榮CV@TuJ?m@ۯ;}#-#2**>GNEFGdF|b %ъ$]zB!j9a/ԃ[6OAXsQ-}s[G2紻CGꕕPF)/cWr~Yi%|]㒃oМ:`e s vGFvTMrxs%JbՉJ}5I?$uݗڴFKRtoFtL*YN9aߞ &,~vt!yqBܤ ;#4V8y RtB\A$jdu.XU#'Jg`p%ڮQ3I>g.G~xy~*&`ee@XQZ.e7O$y 큗^'2SͤEc{htdfaKjm΄%/s\xfjÐ=D A(fzLS2H>:6uȊgcG&dڨad줰;F額£FCr3XYσ9l8ӇB <B3XY-EdEF5x.6]ȍM7$$@O~/C3g˓beba]n}ڇQe&ݬFP66*wRf|\V!CA8_YgdNi5F{j i84f deGm2fI*Ag~C01_*(uW=V1qd.Gy@Hj(ub蕻B"3RK l#qYG65Lv5Fa:@ X@5>0-'14іͯAK-SeonW;OBE7%> w?})ȆxOˏE}s]=m^PِF)[lf)w~Kޮ詺KQ8lvΫ+]'rfjǪAFtbpfDf&̓+-q0l 9֍ qgn,_shm6dPxs_G#+!}u`,2ǾpH:uo"R/6IgʎK]6x(Ź~&DYYta}1_7hJ/0޺!BN@{R 0h '4A84Bs<.]P:W0ntnr_`ڠ!&3KZxiRB9k'|8SA$q{]dMՋ %k0IVQb2yC=^Irr$_5 >Sk!??XYiid^ճ,/uCo+@XZ8;TdQ]hk,!F{.!8OKf{xR tQڳXt& 7ʮg7eN,nzxX珙ʪ ws_濽{;=Td| 7QH߸1`pێd9|e#iZvnn!GK"Qk=O/(LTд1umPfzg$faQ@lQb+߳5MrW*f<:d:ܨB(Dw*D "/ 24PR wʽW7~\KXPY63[]jK~\pŦGuCTs !=IsIfO.xj "8/U@ju2m7sl9UBDVI^[& ';rٯUwa/:@ _ïAnB bh=Z6֭E /݁d#}fhSazv0X"2Y@[[ H+־OH}SޫrB7M&6GA.XeY',+,2| u|!&ZT IMd4>\(ZT cb`7=^״IL|Eq W^BJqC)aWk`UbgJrU5duhT[֢Fq3zpˤO0 x%%QēPXRL7ҕ,c6Tp:Xq9&)Ѣj2H(2@@-zQ ݄u%"oQoQoy,a ՠ Ta\ :ZtN)"+jJNt@FQDܘLMk L}舰*=1[c Hj iEt=H_vKIZO W|iխfr# %ԨʍǔP%ACYQ5](XrlrJ6Sn~?IQʎ)GtH=g&]fݺ0I۴%3wy]i>20C%{G2rJFJgĝҵGX| &$ՙ!7V?|:q"FaEUfZI_J{zIG!xwE|qt{,6{v }-&7 (?y+M"i\:,u4M>׉lcMᡥq*8{]%,P=QUiF鉗m JQ^6 ":ʋgP)yt5R̨K7(96ʩݢJxnhZT͜͜]qK>]][dCOˁhu~GJT31ZQVBNnn"DjO 1 w56uL/vwxeǓٟEGU5U|n{uo㘉g:TР!AMu8 ??(Qʗ /1rK79Ibl:uiӅ0:!嫺S+e@N\4Ӭ-V?FW/whj +xvȌ1˷P^=b/fi;QASG+?t#!/W8O8H VӖ²Xށ̆ Qm7=( ^.9ls䏛 siag󉺼yxGP7'Fj@ X@>uM_^@ ?T@  h@ @ ,!@ X@C @ $ﶢv#)+7ZXghLa.ȻeeųYYAd4Iĉ ׁ f&Ұ ;pgM3eȎy x @ sMʊ}٧-o"TsV+g$Ȁa;~tyҞAsM ΐ_YWP$JϻPnqw4uʈܐ1Ѿ |HgDqLџuKVi*~iq̐_TAUp9r*2:"#5X(V #7W}W/Q {yd|( ,9{dܖɍnu޾-#Vxyуs]Z#J(vzJuBvi9.q?hNܣGK&v0-'14іͯAK-SeonW;OBE7V%> w?})ȆxOˏE}s]=m^PِF)[lf)w~Kޮ詺KQ8lvΫ+]'rfjǪAFtbpfDf&̓+-q0l 9֍ qgn,_shm6dPxs_G#+!}u`,⍁^){ZOhhb^+ḵ/8>i'>{Eҙw+ .JqߡI \+n58m[[79ڼ;Dԉ}@(t`O*aVtDr9?P2yå J 3>#&- `Xz\X]T\1D~fI_qIXC(2=upg6<w\9ǟKA]TDbO];b;9<׊7w&lTl( Vef?(z|?a&Od_g&>aב0&Ljʝ'-?=[єTWB 2r*C^utG @ 0팏%mϢqMS ' (749й\ƉO +v\̷5$IZm_zO0b_'R˸1Vo-r?#9I׶Tj&OlXXR]'f<,t)HOOy<.(q;/.ÑE3&0,9q_d']{2f'jYF]:2_>N~jWfTFUJ!y|G Ibk["T0LU ^yb[3n@ ڶxں[sU p{OwѲPfhwUB3PFj~}xG U:qԙ^"Lr+MϬ 1%uο8coUQaDfz;VY[}Ⱥu/1Lf)GSDO%;e (ǬUr-yyz==] I-72R~ @޷50beDn9;G}/^ouRVPZ*Ĕ iMV7 Cg3J40H CM&GM&9',kуI(( :p-ܐ4s|rkЀFT9z-}ɌO,*b v4sf,WԺknfUTmPXylTl\H46fxyIE"NUff4;qNf>AK%  (v?A=~Є 7a- Y4|:љ7^J(HOQT !%"ژM;ʟ_0uuxz (;.( w@,}.QuHv,y}ymxב&=|=gkQd@ԌBV'V_ ͡W7),Ϛ[(9NWSe.Z*HP'xn.E&UU EQkb5huV~*{p]8?~9~:50*̜T2/VpEgJOjnjkb@ ~~hd`%N9MRNE"*gTj":c}&@7mMzȊ(+Oдl)QI\>Ui/;s8PUUi?_ZsI/I#rܼUnTzMZ9"u[x!jrŇo&Z(b@Xmk ןr҂o&2ɜW~?IԶ /ƭU!i *1C4}'82/;5f峨捯0y,VP jӥLS2\!-2>P11W' J[{==ʿ}K.)tfÈF`4] *9sm% nVVjV-m&SWT&Q#➣*d*15M\zשyIZUGP: օ,-J޾g,t{œl*ВcX43էAK\Ao?Ԅ=3iJ77gUXv+د xUN0qvJfP^TAUϒ|Sݰ~ jy?O of•}wGnuSC^Ti[wҡ 8VDvV&d Ϟ$QYdZvF][ o ?X1;yn'0zğˆ8,5!*3\qaώL#\`ʁG=oo_8rFmOmOȴ*a4F4zfk]RڡkfI]eË}\[=ad A%SG=X+BnW4dfT.Ӱ7m"S\b_{2Y3W\\K7nJLևy ^:lw,|ҧ~)_],͞zytV!w*+ߍR"с]J4==XŹ4%欩rڜ"ގn/‹f |긡@tqWEPZ5h+: GLJa}]\rDq̰,MwѪn;Tsn4-]Xۗފ=]?:wW}ܡ׳(sA#>u~m*cYAgMկx=[F4u]{ zں&.-|xvc'nMXaYk0!KpN-ˡl[ q{on)smW/∤m;/OvuԪ`CܨV_&I!tv=6eːew9}44f[4ݩ;'9fF_y&qCv)[D+,.l I(sooDqLyǵiAؚQ‡T&͚Jc{aC@TZZ @ŭi91eVVR`OK֟E?-.゛ާ؋: OQoh ssF+T@V$츘o=j1.5I. ۾"aֿN߸1Vo-r?#9I׶TTj&OlXXR]'f<d$P}s߼g[)휸s߁/2=g3A V)00v $c'_\?:97C#I.fL)-J\èKG = Q%{EE^.CZ0 S`iU>>+Wީ )dẁJS齧;hYC3fsWU h״[6ÒN6n*VıSg{%dw2A4=ĔUT{8`o)WE9C?|Zid՞^@ ʷ> u_cͰSΧJ7w6APYl_[#D1+zz8$Z&odV,>A𬡈0,X ₷$(E@åi*11kܔ̪z="KpMm]<%6,W%E"S^m/wE/)W Mմ&~%ZIS9UިuLPZ*Ĕ Ojde7|8>j\7p9a\:JDED@Qk +6xJ0Up4p Yˆ|Q|5G/b2`Xm:?oW'{Wh!WQhQU:rzp!Otژ*%U98UQҨ9X--@ $Hi;vȏԟ O?q͆؛0,MXp[u 3 'o)!P$$(B,׋Bj@m̦K/:ZM~ux:`Dq 6ގ*s,\EUِ@?)2#T]ee_alz#ސ-SyZ?0*̜T2/VpEgJOjkkb@ &?M 4n2tnG & rF wE&zXFz&!5ڹ23kaa\kmX?,LԔVE7w2@og$C0&+CifRa\@Ze|W= cJ15238J,a\."a]^da'7U[&xIL&&c{c7BPBbqMbh ~p46nݕa, /_XG???%rpmFW%>RNE"J}ŸCgrUO[0}OQ@U&&nN)ӝ QV6it3&SB}:M_qtT/10q]@wKz6eRʫWO\: dry[Uus(>tu}Z(b@۶}-t m :|2U{jjY.ra:&S^׾%sY]8Zj1c "|H4/*!c)q%x,hl.-f3GwVU7U 7ua10b5dDDa!j*m ٦N̿}gy⢑>[gjNYI;yLrz7Bs 9J( +q{mW<^@TOVZaX!<,*WܻRScZ|d;)̟[it3Yr3 ϫ0WU:|o&̠G.ԕqBF7^:p W>9ӮKi҈f9\t7š1$\=q=5B`ue8ʾE_O;wMptXYnɔIRom#&j!oޠ $wb*00ra? qɳYkB1Uf;D* |vdS\^>g>?|@x <7.oC/M5o|@&a40zfk]RڡkfIBeË}\[=ad A%SG=X8-tVqeICv+n6/I=Wy*^dlK)3q X-({}vݱwJ~ǔ^7H?9bse[#v-ݩēqťO~)PdIIne Rvi9YP1YY̮:n4H|7FTHDvU(:DTRcjДujhߊt{;/W]&9@wc[]Cj ̢=UFuq\fvŕRVC͠Zi?[ݺsմtb"l_z+t ΟŦ^Үʜ'&' ji|ڒx9]Ux kҷnH[Tm`:ls)= orJR5U4W_׈q?/xqλ?xKB<}—}')2ˎ.ǒnKçH&QF {MC ̈́p:S^ ?3R5v߼,/( UrR#47];ENfr}'Ӹk+-p562e /&.zPBUq>kzuQ*Odu>g< i@g䊚lTylPak{!?|'r@ @q$@ O @ @@ @@ @@ @@ @372a-o$gz[慩L:OӢF)|ݓnd(烩;3G TƤmrD5V$ƀ 7랔;JD \@36Ie_]^L/LhIhFs2^lC[x.k#ORG 3s+ğz#m 5u? , ӟe%L}8^T#}.?jS6<`edZ'Տ\bIMrWkmlLӀE\Ӫ#Rb3f2&5ܶ{/LM1;yw!*f:A(;Iϲ\{+.-\rVʟݾoZ8<: 1,.4wdL,OIqiVZCan}KZmi_.LYSpn$hEC0Ŕ E 077]O0y1/.ΐ`<ךX~Z?zۜ<ߜpñH⪤<76hPr5kD7 ݽԂi8-u?-nu]dd}ˁFbW&G%lݐ&iǩk^E5J&KHhԿㄱG?]8>vײȗy{Ha#7VqlP^[m0~x'vWSgKfZ,|U÷+. h#>:_iQ̆dth]ֆw.C's]xTFɜj8Ön mt5wyU*K$ǦTsPq"{FPOQXM"ލd4^ۈsl9ttqd]\z$],6-A>}Π׳(sA#>u~m*cYAgMկx=[F4u]{ zں(.-|xvc'nMXaYk0!KpN-ˡl[ q{on)smWB$mWy}q/NcgC"Ƌ75=M|^|جu>jB_-Cz 1tɍ\.f4y T?6}Qc% N5تnSϤJ ;=^n`7iں}ϳkxOڳԱl=-Mz4۰NlSw"+Os͌"}92uRPm/ƉVX <]ٸ)lqa{Q0 .yc\!=kӰFQɱ5Wsɛ5u=%I 5F]GNØ2uq+wt`l1FSRSaOXwdG_y *ȩ{=CA)h7X~Z\Ҷ,iq>^i~Cc5^Wq2ȊG 2ƥ&Ieaۗ^ ׉Ԇqe7{$%ӛb(3jo{D*?7+&/bL1JNLQD094$&OJ*Uw͘Ĝܼ'AN?UYȳ;# D찀XY'6z,k[\j:dj&OlXXR]'f<Ϥb$Ѽ%8*~[_X!r捏{^RE U)quqAa~AaAqtJg0もնD_;'\w{kOٌqåVJs [%F]:2_>Nx/n dͨ< ,)*rĨʷD(`˭Nn)9_齽NŶfݐ5@mMu%cV缫5*N20e; ͘,0ZZwU*/׉cEd(KQF |Z`+wzzWYHfeAOODBrR䍌Ԫ'|ƈ50bej\7p9a\N(( :p-ܐ4s＀rkЀFT9z-d2S$!WQqpQU:rzp!Otژ*%U98UQҨ9X--@ H@;:-`S߳'Ӹ{ѢE+n+wNAct&ahVB$, C6!%2CٴcSW@i鍲I*BGUWd7yOȒW\+ٌ{^>ysФa>u s_tZT늲37j/Q)͡W7Ϛ[(9NWSe.Z*HP'x:2E&Uy"5:qðKq #s3sj0SVʼ[y)a$?5wfjkb@41и)K& r0yΆL;gz?33ߎda附ԤV(bŇA DNjrbƲ2RSZݴgޱDtOkՙpUo]^4yKwI@g2`G,a\../5Gg7y pbtt _pM 82/(ćLCϚٿÀƭ2[&800(OPOk3*r2-q].n'ΏeRbӦ` =tbdEtB'hZ6݌ɔФ|qΪ4Avzr@O]~^ճ/R^%J|zra@}"פ#۪ E[rDq ݶukk`ln3hx]Hue9}KWCUu ֱ442RKud1C*,`o7xWa 9E 06O(/Ә3t1k\,{K̿}gy⢑t*Us 9JUVjۮnoyV"ȩ4'°Bx$X\EÇ/T\w* m1 o&\^* wRș?ff:0@dWaXtK+-Yօ2NA~_kB0KHjݸkOoz2@pe"˧;R&H8q:L7dJ~$)ҷ AKuې7oP@qΫ)u îGz_;Or\'V%$#S6rWZ<иnWXx'm:w6Q^]͗uhX]Y"ȟ]t/vQ!}ny/߶/?wqпgԖglx?yvgaO4qgo%E&[gp IDAT֟%?l[hrbt=T.\o뉯b2lrUbXpm^w6y"v-:Ƿnu%W* gɢ^2Gc {a?GgX^k~s늋7cwoم[]Cl /]s˫TN!ZnX|7FTHDvUMO Sv$N ӔMIJiht{;9@U|TGQ UܐC\U/kfўW GF2: p2}r3NV$5o:EӇ5-]sd[ 77:Զ^w*s2 (ILL)NMc :YYMЏY['T!@`c<Յk[7-606m၄79%W*Ϋztkĸ9xqλ?xMBhIW>'|Y7~"mr,>^d0|dt qxOI(៿h\gKgfXrC<2>ު$!TAKY^v,u:8G|>-MB @А @ $@ $@ $@ $@ $ʤQ8~Z?-o‡'zD>ޖsyu|~j|yNcҶS9txcu ;N{&]94a휴8~Z\e9PkŤ#f4/uȆ>/=ozȉ6<$0"SE'nkЭfέgfV!ɒ>UƔXQ ?IjhO`ct/Tl9(2՝Y`!CwH8m7+>RCas@ ߮(j:p=_>ǬɠJ??}߮-V:BƠ]8_<%ťť5diQN~dͨ< ,ٗZ)*rĨʷD(`˭Nn)9_齽NŶfݐ5@mMu%cV缫k*N20eQ͘$6}/^Lk 4wU*/׉cS_~\oͷCIyIhQ^j*Ϛ[(9NWSe.Zn$Y({"EQkbw-hu[u8Jm,5'윎SD׭|1  hSjtR sL #YMjkb@ >~puS|8:M4a v6 ~hgfXS0123 I-&Z[*֏k,K+,-5E 7i"Wa zJ}͑/ l~VOvn=`V&tpK,?:cuސK2$Й̏^RBbqMbh ~p46nݕa,/NLn  ßd9D 6[a)'"JHMvb'CaoP $ݹY<ee M7c2%*4)kܧ*M438@ ߝ_b`Sna珞vlˤWI)uP5if,wQ|xVSudNūfPղr]¸u,$CNS4AQgK-X6{;uv=&-/p`{xt^F`)%,hl.]8Gӣϴt5L{9O\4Ґd^v.GK-6֓y¤/ᆒq1Y;U5 6da[Gv(!/ -N#gY}ƺZSVimNY*9mmڸnU r'M+IjL W +]t~JBv[ ĩy1WW>Jf}rO㭴qg,LmUq+*R>CK*g=ualsU'm;uu}^礽Zճ7c͟^#qVIۼm\][Dquyá{G 'NUIFLYYϞ$%QVXfq8 kU_I^^-UȝFʊwcDHt`W,.MOAt~O%5Vq6M9k\Hۋety7:n(]UQ1Tqp,>ȑ`؁&({}*q~׹[7t䚖.S,]KoŞ.|s~s+s_ڵw5PQ@1ĔD>6YYM>ÏY[/'Tq@ >\3 .l]Kߺ!MoQM69ϥo $)HVt^}գӿ^#7^xmTB aK6ˣD_JFa˺>JYT.ˎ.ǒj[zm;I"t u~f,Gyk8y9ģ+叵*+ wmWw11 Gh~Y.tpw9;y3g_#@ [elkm@Ue^LGm]6iǫć?)Ժ]4$wE;*g@7 ~i;}ϸyh@ ߪ&~֒+jCZզ,b5%FM^Rw'*zDn@ fb@ @ @| @ @ @ @ @ @ @ @ q` ٖ7|re-?A"|=*#_CcBj3, i"(ňF5-{F{/JXh HP-.$ǝ9ٝgϜvFr(9\b.kfr(9[铫;IGGpm ܚjW)iY _?(A6 <MɒdjkS:ح?ij27nHsaWo'rv,#6żM>^\$sʒGޢ)>`Cux1(/nCS岂77EWSwQ'8]uH?{ u(Q߻۴˅˓hqbe'5K֣KE*R,c},-5zRsf띑81KEj1K8d= ˪`ꘙk׿ S{#M=Fv&ώ|ȵu]{]6q{|ް5'·?xآ^m膚/"7yo {Fg1#]+UsUW~Z{3>ҒQ{ӥRC^XPx`jBpwMTjwhꠥO<X˚P*Ӷ y+#󴙋g`zR(Vk M'GA a^뙒&J%[ Jq5(Γ†֐0~'!)Q7o2zsOYaoP>dDǯhz,U4q/SÎ.uL~kݬԣɡW9G3j;'kX~]3pwYXk>:~Hm<_sk+|μ8dB;Wz̳ w8{٬F}\\;?1.(:Ȯ9[ӄ+qu0. !,WjUi)/Q)N4O-bCyݸ7FP=lzpLٺ>pZ&=}ɘmt^oи]'{z%_*JɐP,5- ŔǶWfkW%5S҄R"A @7w0Qrx{k2Ӝ RҊeZk / `g&PWi)o+ MxZbܵ 9RԯHSSYjׯ?a1mfh}'U0o?歶tV,;#[\SE@&qRMMG)kEEƊ;`KbE2Ę&0EyybYޫ2*гGw2w }5.E(AcdYi5yBBlZGOɫFmY^ڨ'IFIN5Xftx=ƺTR$q M^>$qsjg4ɫ!ghV3dYγY&TTYT}zT!\. 2 IDAT{2OIoXJBb0P&.m!,woJS@ڴ6H+r!4QY&0h긨Fuy(@H>;yuHЍk߅$nF4<ZeVV:"k1S7K[[ZXf:O$3-꤫"RЍɞ{yXR!2ۓR~čJ4f_: |"Ɋ,GJFh=Nw5Brm1SV+Z_H!*YvtD-Ԣ K{g}H3ER$mVEګoO!%+NN3DRMYbo|ФoMVEFJoO t__tܦ~&lK7=O|<鹫tA5&yWh|ǶrE>458gӮ [w0qU$ә}]Wuuc9wN$7j__#Б 2x݆hcd=u$FL^)[lJW]-mCۅ&}Qj#_EMPg_ vUQWQ,M5n_rƥOh3{}:oc`1UKi7kx7vZz]l,Zr_WM jXrfEglZݗYQ`Td ׯuwݝ^XQ\Je3v77W󖔲QKonnu`=Km)"I&wfc.Fa` U4M\yl{H3$ѥpWnP;N84elGw.oq#9u$4j37V!%acih=^O]vҗ"vFg.z{`L{VE쩳~D&;3]UdԊzo#|Пvy3\[A^#VM.K"OO'gK+;U;ˬ9*Ml`w/ylVА ' Yˍ^(U4O"M~zcl߶%L.?vɫ\nHWy'1b7ypy{GM{{їlER4.Ed)gW|*H8n7CMXV^UC)m˷n> :{d2{&mRPC8`A^ r? 0ǐ&>Xv(g_b̳s'ʸfDr7T"+I&Ubn/-r+"] description = "Test a phone for stalkerware using adb and usb debugging to scan for suspicious apps and configuration" readme = "README.md" categories = ["command-line-utilities"] license = "GPL-3.0-or-later" repository = "https://github.com/spytrap-org/spytrap-adb" [package.metadata.deb] depends = "$auto, adb" priority = "optional" section = "utils" [dependencies.anyhow] version = "1.0.44" [dependencies.bstr] version = "1.9.1" [dependencies.chrono] version = "0.4.19" features = ["clock"] default-features = false [dependencies.clap] version = "4" features = [ "derive", "env", ] [dependencies.clap_complete] version = "4.2.1" [dependencies.crossterm] version = "0.27" features = ["event-stream"] [dependencies.dirs] version = "5.0.0" [dependencies.env_logger] version = "0.11" [dependencies.forensic-adb] version = "0.7" [dependencies.hex] version = "0.4.3" [dependencies.indexmap] version = "2" features = ["serde"] [dependencies.log] version = "0.4.14" [dependencies.ratatui] version = "0.27" [dependencies.regex] version = "1.5.4" [dependencies.reqwest] version = "0.12" features = [ "rustls-tls-native-roots", "brotli", "gzip", "deflate", "json", ] default-features = false [dependencies.serde] version = "1.0.130" features = ["derive"] [dependencies.serde_json] version = "1.0.95" [dependencies.serde_yaml] version = "0.9" [dependencies.sha2] version = "0.10.6" [dependencies.shell-escape] version = "0.1.5" [dependencies.stalkerware-indicators] version = "0.2" [dependencies.tokio] version = "1.26.0" features = [ "macros", "rt-multi-thread", "process", ] [dependencies.tokio-stream] version = "0.1.12" [dev-dependencies.maplit] version = "1.0.2" spytrap-adb-0.3.2/Cargo.toml.orig000064400000000000000000000026071046102023000147500ustar 00000000000000[package] name = "spytrap-adb" version = "0.3.2" description = "Test a phone for stalkerware using adb and usb debugging to scan for suspicious apps and configuration" authors = ["kpcyrd "] license = "GPL-3.0-or-later" repository = "https://github.com/spytrap-org/spytrap-adb" categories = ["command-line-utilities"] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [package.metadata.deb] section = "utils" priority = "optional" depends = "$auto, adb" [dependencies] anyhow = "1.0.44" bstr = "1.9.1" chrono = { version = "0.4.19", default-features = false, features = ["clock"] } clap = { version = "4", features = ["derive", "env"] } clap_complete = "4.2.1" crossterm = { version = "0.27", features = ["event-stream"] } dirs = "5.0.0" env_logger = "0.11" forensic-adb = "0.7" hex = "0.4.3" indexmap = { version = "2", features = ["serde"] } log = "0.4.14" ratatui = "0.27" regex = "1.5.4" reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "brotli", "gzip", "deflate", "json"] } serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.95" serde_yaml = "0.9" sha2 = "0.10.6" shell-escape = "0.1.5" stalkerware-indicators = "0.2" tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread", "process"] } tokio-stream = "0.1.12" [dev-dependencies] maplit = "1.0.2" spytrap-adb-0.3.2/Dockerfile000064400000000000000000000004141046102023000140450ustar 00000000000000FROM docker.io/library/rust:1-alpine3.20 RUN apk add --no-cache musl-dev WORKDIR /app COPY . . RUN cargo build --release --locked FROM docker.io/library/alpine:3.20 RUN apk add android-tools COPY --from=0 /app/target/release/spytrap-adb / ENTRYPOINT ["/spytrap-adb"] spytrap-adb-0.3.2/LICENSE000064400000000000000000001045061046102023000130670ustar 00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . spytrap-adb-0.3.2/Makefile000064400000000000000000000002171046102023000135140ustar 00000000000000build: repro-env build -- sh -c ' \ RUSTFLAGS="-C strip=symbols" \ cargo build --target x86_64-unknown-linux-musl --release' .PHONY: build spytrap-adb-0.3.2/README.md000064400000000000000000000073711046102023000133430ustar 00000000000000# spytrap-adb Test a phone for stalkerware using adb and usb debugging to scan for suspicious apps and configuration. Based on [stalkerware-indicators] data provided by [Echap]. [stalkerware-indicators]: https://github.com/AssoEchap/stalkerware-indicators [Echap]: https://github.com/AssoEchap [![](.github/screenshot-device-list.png)](.github/screenshot-device-list.png) [![](.github/screenshot-findings-list.png)](.github/screenshot-findings-list.png) ## Usage There's an interactive UI when running the command with no arguments: ```sh ./spytrap-adb ``` Enable usb debugging on the phone and connect it to the computer over usb. You can also invoke some commands directly for non-interactive use: ```sh # list available devices ./spytrap-adb list # download indicators of compromise from https://github.com/AssoEchap/stalkerware-indicators ./spytrap-adb download-ioc # scan the first connected device ./spytrap-adb scan ``` ## Building from source ```sh # install rustup curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # clone the repository git clone https://github.com/spytrap-org/spytrap-adb # enter the directory cd spytrap-adb/ # compile the project cargo build --release --locked # this is the release binary ./target/release/spytrap-adb ``` ## Example output ``` % target/release/spytrap-adb scan [2021-12-19T21:26:41Z INFO spytrap_adb] Loaded 130 rules from "/home/user/.local/share/spytrap-adb/ioc.yaml" [2021-12-19T21:26:41Z INFO spytrap_adb] Device is not rooted [2021-12-19T21:26:41Z INFO spytrap_adb] Fetching remote clock [2021-12-19T21:26:41Z INFO spytrap_adb] Local time is 2021-12-19 21:26:41.847457823 UTC, remote time is 2021-12-19 21:26:42.318497288 UTC, drift=PT0.471039465S [2021-12-19T21:26:41Z INFO spytrap_adb] Comparing list of installed apps with known stalkerware ids [2021-12-19T21:26:41Z INFO spytrap_adb] Scanning installed apps (0/192) [2021-12-19T21:26:41Z WARN spytrap_adb] Suspicious Medium: Package "org.jitsi.meet" was manually installed [2021-12-19T21:26:42Z WARN spytrap_adb] Suspicious Medium: Package "com.android.gpstest.osmdroid" was manually installed [2021-12-19T21:26:46Z WARN spytrap_adb] Suspicious Medium: Package "org.fdroid.fdroid" was manually installed [2021-12-19T21:26:46Z INFO spytrap_adb] Scanning installed apps (100/192) [2021-12-19T21:26:48Z WARN spytrap_adb] Suspicious Medium: Package "com.wifi0" was manually installed [2021-12-19T21:26:50Z INFO spytrap_adb] Enumerating service list [2021-12-19T21:26:50Z INFO spytrap_adb] Reading accessibility settings [2021-12-19T21:26:50Z INFO spytrap_adb::accessibility] Reading accessibility settings [2021-12-19T21:26:50Z WARN spytrap_adb::accessibility] Found bound accessibility services: "Service[label=WiFi, feedbackType[FEEDBACK_SPOKEN, FEEDBACK_HAPTIC, FEEDBACK_AUDIBLE, FEEDBACK_VISUAL, FEEDBACK_GENERIC, FEEDBACK_BRAILLE], capabilities=1, eventTypes=TYPES_ALL_MASK, notificationTimeout=1000, requestA11yBtn=false]" [2021-12-19T21:26:50Z WARN spytrap_adb::accessibility] Found enabled accessibility services: "{com.wifi0/com.wifi0.AccessibilityReceiver4}" [2021-12-19T21:26:50Z WARN spytrap_adb] Suspicious High: An accessibility service is bound [2021-12-19T21:26:50Z WARN spytrap_adb] Suspicious High: An accessibility service is enabled: "{com.wifi0/com.wifi0.AccessibilityReceiver4}" [2021-12-19T21:26:50Z INFO spytrap_adb] Scan finished ``` ## FAQ ### `Error: Failed to list devices: Connection refused (os error 111)` The adb server is not running correctly ### Installing adb on MacOS brew install android-platform-tools ### Installing adb on Arch Linux pacman -S android-tools ### Installing adb on Debian/Ubuntu apt install adb ## Similar work - [MVT (Mobile Verification Toolkit)](https://github.com/mvt-project/mvt) ## License GPLv3+ spytrap-adb-0.3.2/repro-env.lock000064400000000000000000000230521046102023000146450ustar 00000000000000[container] image = "docker.io/library/archlinux@sha256:76b4733d4c59cdfcae4a5dced7a6611e8621a502f863631328b362c85ac405c9" [[package]] name = "binutils" version = "2.42+r91+g6224493e457-1" system = "archlinux" url = "https://archive.archlinux.org/packages/b/binutils/binutils-2.42+r91+g6224493e457-1-x86_64.pkg.tar.zst" sha256 = "d38f9e7fefdf4101d1ab77739356d5735658c316d45f805879e9b8502b228b1d" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZjqNXF8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCgpMAQDMEB8dNeJKS2ls2nLniecgjynnznoocjQ67ObHUv8uWQEAsWZ6IV4bUsjNLdD8ACNQrZVr+41FL1UjbINOHOkfWQg=" [[package]] name = "device-mapper" version = "2.03.25-2" system = "archlinux" url = "https://archive.archlinux.org/packages/d/device-mapper/device-mapper-2.03.25-2-x86_64.pkg.tar.zst" sha256 = "2b44e1fc72ab24bd4007fb3fe36d602b750ad1061bb1d7df449ee36734a4dd5f" signature = "iHUEABYKAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZpLxdgAKCRBtQr3RFuAGjw8fAQCw/U36/echw6+YVJaq4NkmDalAigiDufv4bOAQVDBjFQEA4g7/Ffw6892VMJjVSXJcCYDtKiuKKBIvX1c7hfvbHQE=" [[package]] name = "gc" version = "8.2.6-1" system = "archlinux" url = "https://archive.archlinux.org/packages/g/gc/gc-8.2.6-1-x86_64.pkg.tar.zst" sha256 = "6210a1e7e00d3162f175f9ab318a3da928495210b1ea411dcb66bf5e1f382daf" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZb9oiF8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCmQdAP0fleaFihboUqrAdlMxvO5OUJiXXxLtimhQj2e3rD9L3gEA0YzMCzi4GS6GlV1pa/hZ68bPL2gYz2goni9/aLXhMwY=" [[package]] name = "gcc" version = "14.1.1+r58+gfc9fb69ad62-1" system = "archlinux" url = "https://archive.archlinux.org/packages/g/gcc/gcc-14.1.1+r58+gfc9fb69ad62-1-x86_64.pkg.tar.zst" sha256 = "5f15e2830d965a3c42a9e73829723aa44870cdfc240d0c228ecc9e876feffa15" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZk3zsF8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaClx1APwIlpIm93K+RD0Q9K2C2wmVBzyGM3mn7pQc8VAzJDmWRAEAhyxZQ8g9fDJykLMsIWn1k2rOPVnQJ93bDQGT5MuYGAM=" [[package]] name = "guile" version = "3.0.10-1" system = "archlinux" url = "https://archive.archlinux.org/packages/g/guile/guile-3.0.10-1-x86_64.pkg.tar.zst" sha256 = "bf4d1b474045a88c7ad97b1ce56e8dd5786e5a10de02a8d33dc8f041edc8d01d" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZnkshF8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCruHAQDnjJsHGhq4cae34rHS1z+Hr6yzjVQSxvA6SE9XpredlAD/cstobLSMUsHswxf5df94XkoDPJdJs44uXE/iu9gsgQg=" [[package]] name = "iproute2" version = "6.10.0-1" system = "archlinux" url = "https://archive.archlinux.org/packages/i/iproute2/iproute2-6.10.0-1-x86_64.pkg.tar.zst" sha256 = "745c0867cbdc8ef7834154d6fa4d02339fbec48af31f5bdb3e23c741214bf661" signature = "iHUEABYKAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZpdqOAAKCRBtQr3RFuAGjycDAQD1g+nPTDsOdyRx9WHkhq2vecaAW60cbalTJ8m8TgxX4gD/TMBu1IpSxud5fItidhMbFTG/EcNqQe3zLo+x1417ZQ4=" [[package]] name = "jansson" version = "2.14-4" system = "archlinux" url = "https://archive.archlinux.org/packages/j/jansson/jansson-2.14-4-x86_64.pkg.tar.zst" sha256 = "a67ab57d4a9b4caa2e718652c392345cdd9c2496d835ab1d0ff1e78ae4429fde" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZjJful8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCpfCAP4sFnTjSEIn5wDbLWGZOivsWT2SWPG6AgofrnUjaK/UMAD9E2u6D7WJbVTYrvDhVUz3WN6k8bVB8ZODyoIF66GWEw0=" [[package]] name = "libedit" version = "20240517_3.1-1" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libedit/libedit-20240517_3.1-1-x86_64.pkg.tar.zst" sha256 = "fa17f759180233be8343ccccb1c3e4ac01cf5367ab141f36ca8830151b7c12be" signature = "iQIzBAABCgAdFiEE4kC1fixGMLp2ji8m/BtUfI2BcsgFAmZRDMAACgkQ/BtUfI2BcsjfBg/9GJ/xfxU7NKehazazuulK0VWaeXwc2SydHS4lpnqZggkcNtVlJIXzIrIBAK+A06M99NLqJzdrLsCssa5RjlMSxtB8nZYjZIE7M4fivBHgx4N6xUzzSu7PN8G9A1fvnbRFqtcvywP/FeWnmBIsf404BeKhwHB0PkG9RebYmJAKpg1IHhHNBJ/NkYC04Wov4oskzv/HehibIbnvTu6EZQ2f2NyXJVTqUsEkEw5xSEsyob+fgAtMdfufs6xpFskYV9NWniHKxWN9JKy0HwJ764JrxRW5VrrR2Ua/zWQYBi0r71zfrt/mILjkJwWNe0+LzJWpOqD59sjllW2iBeTeH+0A4lG7beaKdhCAAQQUzAPINuCwhpOGvvFhOn04icVPgqFKVegbNbXN+Wad+60HKgQw4AI0XeHo8uTiJOKolObV27OxaQTK2s5qmsrali4zsLlC+G2lYUvrqvWlrtuIIm+6XGNSMDoYovVd2EoBGRrwFCDlfdah8kFa6PY9J1UWQY7mDPJerpgB0IZ0KycxpXvB5I5XzYLKbSEjWZ331jnCWbR+poQfYwMYI2H9ZFy2YN0R/VohaIU70Ux5F24gjUaLYc1Ld9ZzrV1MSMj+D20vdxLCmNoDLfNwwceBbCyisT5GnxAFVWvaX3WwhI/D/jPnBcuEw11FfsHLUaGNIfM+mQQ=" [[package]] name = "libisl" version = "0.26-2" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libisl/libisl-0.26-2-x86_64.pkg.tar.zst" sha256 = "c9e75e062e0c63ea4ce8ff3b74ed09400c58e70323362ea98039e8ca89d7dce3" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZjJful8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCkLXAQC49BDxyJqogwtJnhf2IuViUWKWh8D9STZdlmc27Kwy0wEA3at28YnTA1thVrPW3SmnqteeRH+CEwIh7klsYADidAs=" [[package]] name = "libmpc" version = "1.3.1-2" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libmpc/libmpc-1.3.1-2-x86_64.pkg.tar.zst" sha256 = "10554706eb15ed2186fbd55fd5db8fa5919788ac9a75d26c0b9ef5bdfca9d470" signature = "iQEzBAABCAAdFiEEFRnVq6Zb9vwrc8dWek52CV2KUuQFAmaHCZwACgkQek52CV2KUuRjbAf/TmL9cCMXYTGsNmR2om6kT7XjczfZoFR9AM0FSTUNy5AuNZNUUCfm6ie6F4rFwHc3nETOSojScOZauZlcNiLrVriHYYFSZ8EhKiCJCo68a7KTbceBNoBuT6AEIpGMpIMti/cvWhu8VXl5JIRo8LNPBzDih05aCDUJ2nayKPYNHalDayu87sDXzJYXuzC8KD7XmUa7Kirn0ZpA4VZXc8CUjYYvn0wvRwLKQP4WMpszxmblQWjMVbQxh7dwC+gPtqIANVjoGKMRfr6QpT03XXIEaTBKbMsxeYFvwn+y2JN30t6oFI6LJADZ22VwimEzu06u+x9c+3+pr4k7Py43OCOGmg==" [[package]] name = "libnftnl" version = "1.2.7-1" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libnftnl/libnftnl-1.2.7-1-x86_64.pkg.tar.zst" sha256 = "a64c3f0c74ab3f0d7fb5492a61ae94c771ccf3eae8d01b2bffb90014e862e6df" signature = "iHUEABYKAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZpbtuQAKCRBtQr3RFuAGj87sAP99D+yjZ9PDCPTjiGgu5ueCy4mNVgD2KMBrSAXqs8bqDgEAinRtmbkvB4/SifYQH887mzRNntUivcMtCn5AkpfalQ8=" [[package]] name = "llvm-libs" version = "18.1.8-4" system = "archlinux" url = "https://archive.archlinux.org/packages/l/llvm-libs/llvm-libs-18.1.8-4-x86_64.pkg.tar.zst" sha256 = "bb417925759c69584e557666528c728af776b8e815cdd45d3125c725ddf8d9db" signature = "iQEzBAABCAAdFiEEhs/8qRjPOvRxR1iAUeixSKmZnDQFAmaYGg4ACgkQUeixSKmZnDR4ygf/S2itjSyaPvaiabUUZulYP+rlyQb8hxgYKfAIEBYU1xHRIYBqKuy+9VQMF7o57Jgawbdpk6wRgf2h5rvMj9LY/fpkaPCbK/t13c2zmEAROihuXLTGd6DJwhH9aGVAdDtCs1Nn3w6BnJI78buUCAEi0OGjxOC6RQiAUlPk7g8TKixvJiVgREyrm72TpFeaxzD2UuCw9X0Jj3rlFqmWNqjxoKfQptPUYk2pLZhcJAzFw0tZTd/5zCM+9/gE2AAd0C4lBEF/Xi37RSI3Y80oYFCL91b2x37S1XlYZDAXN/MZqOABCfMXI41HHL8wUX6xARqcTRvaMk3922f2oMZ8ISQyYg==" [[package]] name = "make" version = "4.4.1-2" system = "archlinux" url = "https://archive.archlinux.org/packages/m/make/make-4.4.1-2-x86_64.pkg.tar.zst" sha256 = "7c1bc0d882f7c8d1bcb305eb7efaabc19ed6afa5a9a443575fa0d5ed57985535" signature = "iHUEABYKAB0WIQSZH24/B2XPYpWIhYYTmwnaW/DTOAUCZBT0lQAKCRATmwnaW/DTOKwJAP9/kfT3rO0NhbD8wuI6ajzjyKtrl4SfuU0yu/PKByXQOgD/aYSvsyXylJhCadU2smO4LbP2Vj9fnIvEwPvbXbesVQ0=" [[package]] name = "musl" version = "1.2.5-2" system = "archlinux" url = "https://archive.archlinux.org/packages/m/musl/musl-1.2.5-2-x86_64.pkg.tar.zst" sha256 = "146b60543069d4eb92fd9086ffd6f442ee0a1f41f724445adc29af80693ce2da" signature = "iQJLBAABCAA1FiEEiee5MxxK59f699MFwTIpOVS75K0FAmaUdzsXHHNwdXB5a2luQGFyY2hsaW51eC5vcmcACgkQwTIpOVS75K2C+g//ddIjb7CKai01nuuzLd9hZQBP51iBe+lYdhv5lxlKaSzQZXHVUGXDAZdb7TaV9pbzdGPE+l5V2ZngivXdR5twUDWOQzvorXdxLgX7trBTghLDTBIKu6jPtrH2bNWrL8A908RumQVeWsOxUcompgAsRpQ4DhZkzk8325k9et0Nv1oQic96B5G1WefE85IYc2PDJeRhfyfnZmQ15aoVhohyhF0XSudlJSc/hqMZePW8tmyYz4ltvgOs4EF3smNwOaZVOew+w/5vOCYaSre5MFwHiilvYJaWhCji10m0MrBzLhRx0ZfRlBwTGbsToRbmczuwMXoYIiJsI2OiXsZD5/X9T5yPjl7pWP7uLaNTXCcbfrCZIWMVBXYQFxnWhUbKFZu8E7eXU2z75GukkC7GjR+VKw+SFH06Xdq73JltIXURRKe03uigC8ciME1xHOMv5kc1pQukQC/hTaR4GPF32pOX27CPQkNQ0dhRi1Cgyt47V+3GncI+mhlY3gJsCv31+Z0kM+WornVJYFSQnO0/frFPJaZUt0ONO7lHU2UMTe2tHm+zFHuHh6xOGaMjdRslrzq0Pby0xMb6nT0HsT1VXfSW30rywN1UPLd3TxAXGkG3SS8szftrLrmK6NAsK5tWfd5H2tiwSibYNnYYYVB720LKBl7bgpGFicdROohXJKqXsvA=" [[package]] name = "pacman-mirrorlist" version = "20240717-1" system = "archlinux" url = "https://archive.archlinux.org/packages/p/pacman-mirrorlist/pacman-mirrorlist-20240717-1-any.pkg.tar.zst" sha256 = "a07daec736defd8f90475b904da2bda446be3cdd3c2ef4e0fa85372ea8679d2f" signature = "iHUEABYKAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZpd5PAAKCRBtQr3RFuAGj7AtAQDnEAC7v38gwOs9DnBrS2c82YRRXT9w4ZlC5iRzT1S6vwD/bNyB8/QdOwL9xbOBUH5SDL+oMSfz4Bjca7vfa8Sa3A8=" [[package]] name = "rust" version = "1:1.79.0-3" system = "archlinux" url = "https://archive.archlinux.org/packages/r/rust/rust-1:1.79.0-3-x86_64.pkg.tar.zst" sha256 = "1dcf3958ed9b65c7716acd99f12ec3c4fabcaea04d007e9902e9309bb07d8e90" signature = "iHUEABYKAB0WIQSDvIiJNRtd67toQW64rAhgDxCM3wUCZoY9IgAKCRC4rAhgDxCM3+nxAP4wvfkUMX1qMbcU/1khKbOFp2G8JZFLtMJyjlsZbBUIowEAvsiyM0w+FvlG9hqoB5C36fBQZc6CIYqv4qyA6H2wbQc=" [[package]] name = "rust-musl" version = "1:1.79.0-3" system = "archlinux" url = "https://archive.archlinux.org/packages/r/rust-musl/rust-musl-1:1.79.0-3-x86_64.pkg.tar.zst" sha256 = "1f6aa451bf0ae5dc5f8a9a2fb574520314f9841036a96a02f0ccd8bad47113da" signature = "iHUEABYKAB0WIQSDvIiJNRtd67toQW64rAhgDxCM3wUCZoY9JAAKCRC4rAhgDxCM3y2iAQDmCdeTYYYTQmsyubkE3HCCZkSniDoW2kdlk0PJZ/eMYAD/SdU7d8Wf0m+Z4ieyEtvezox96UJ4jwZviuqSQ40dbAU=" spytrap-adb-0.3.2/repro-env.toml000064400000000000000000000002001046102023000146560ustar 00000000000000[container] image = "docker.io/library/archlinux" [packages] system = "archlinux" dependencies = ["make", "musl", "rust-musl"] spytrap-adb-0.3.2/src/accessibility.rs000064400000000000000000000045601046102023000160450ustar 00000000000000use crate::dumpsys; use crate::errors::*; use crate::ioc::{Suspicion, SuspicionLevel}; use crate::parsers::accessibility::Accessibility; use forensic_adb::Device; pub async fn dump(device: &Device) -> Result { info!("Reading accessibility settings"); let out = dumpsys::dump_service(device, "accessibility").await?; out.parse::() .context("Failed to parse accessibility service output") } impl Accessibility { pub fn audit(&self) -> Vec { let mut sus = Vec::new(); if let Some(services) = &self.bound_services { warn!("Found bound accessibility services: {:?}", services); sus.push(Suspicion { level: SuspicionLevel::High, description: "An accessibility service is bound".to_string(), }); } if let Some(services) = &self.enabled_services { warn!("Found enabled accessibility services: {:?}", services); sus.push(Suspicion { level: SuspicionLevel::High, description: format!("An accessibility service is enabled: {:?}", services), }); } sus } } #[cfg(test)] mod tests { use super::*; #[test] fn test_audit_accessibility_plain() { let data = include_str!("../test_data/dumpsys/accessibility/plain.txt"); let a = data.parse::().unwrap(); let sus = a.audit(); assert_eq!(&sus, &[]); } #[test] fn test_audit_accessibility_plain2() { let data = include_str!("../test_data/dumpsys/accessibility/plain2.txt"); let a = data.parse::().unwrap(); let sus = a.audit(); assert_eq!(&sus, &[]); } #[test] fn test_audit_accessibility_spylive360() { let data = include_str!("../test_data/dumpsys/accessibility/spylive360.txt"); let a = data.parse::().unwrap(); let sus = a.audit(); assert_eq!(&sus, &[ Suspicion { level: SuspicionLevel::High, description: "An accessibility service is bound".to_string(), }, Suspicion { level: SuspicionLevel::High, description: "An accessibility service is enabled: \"{com.wifi0/com.wifi0.AccessibilityReceiver4}\"".to_string(), }, ]); } } spytrap-adb-0.3.2/src/args.rs000064400000000000000000000034271046102023000141530ustar 00000000000000use crate::errors::*; use clap::{ArgAction, CommandFactory, Parser}; use clap_complete::Shell; use std::io::stdout; use std::path::PathBuf; #[derive(Debug, Parser)] #[command(version)] pub struct Args { /// More verbose logs #[arg(short, long, global = true, action(ArgAction::Count))] pub verbose: u8, /// Configure if an adb server should be started if needed #[arg( long, global = true, value_name = "choice", env = "SPYTRAP_START_ADB_SERVER", default_value = "auto" )] pub start_adb_server: AdbServerChoice, #[command(subcommand)] pub subcommand: Option, } #[derive(Debug, Clone, Copy, PartialEq, clap::ValueEnum)] pub enum AdbServerChoice { Auto, Always, Never, } #[derive(Debug, Parser)] pub enum SubCommand { Scan(Scan), List(List), DownloadIoc(DownloadIoc), Completions(Completions), } /// Run a scan on a given device #[derive(Debug, Parser)] pub struct Scan { pub serial: Option, /// Use specific rule files instead of latest downloaded #[arg(long)] pub rules: Vec, #[arg(long)] pub test_load_only: bool, /// Do not scan apps for suspicious permissions #[arg(long)] pub skip_apps: bool, } /// List all available devices #[derive(Debug, Parser)] pub struct List {} /// Download the latest version of stalkerware-indicators ioc.yaml #[derive(Debug, Parser)] pub struct DownloadIoc {} /// Generate shell completions #[derive(Debug, Parser)] pub struct Completions { pub shell: Shell, } impl Completions { pub fn generate(&self) -> Result<()> { clap_complete::generate( self.shell, &mut Args::command(), "spytrap-adb", &mut stdout(), ); Ok(()) } } spytrap-adb-0.3.2/src/dumpsys.rs000064400000000000000000000023031046102023000147130ustar 00000000000000use crate::errors::*; use bstr::ByteSlice; use forensic_adb::Device; use std::collections::HashSet; use std::str; const CMD_LIST_SERVICES: &str = "dumpsys -l"; pub async fn list_services(device: &Device) -> Result> { let cmd = CMD_LIST_SERVICES; debug!("Executing {:?}", cmd); let output = device .execute_host_exec_out_command(cmd) .await .with_context(|| anyhow!("Failed to run: {:?}", cmd))?; let mut services = HashSet::new(); for line in output.lines() { if line.is_empty() { continue; } let line = String::from_utf8_lossy(line); if let Some(service) = line.strip_prefix(" ") { debug!("Found service: {:?}", service); services.insert(service.to_string()); } } Ok(services) } pub async fn dump_service(device: &Device, service: &str) -> Result { let cmd = format!("dumpsys {}", service); debug!("Executing {:?}", cmd); let output = device .execute_host_exec_out_command(&cmd) .await .with_context(|| anyhow!("Failed to run: {:?}", cmd))?; let output = String::from_utf8_lossy(&output); Ok(output.into_owned()) } spytrap-adb-0.3.2/src/errors.rs000064400000000000000000000001551046102023000145260ustar 00000000000000pub use anyhow::{anyhow, bail, Context as _, Error, Result}; pub use log::{debug, error, info, trace, warn}; spytrap-adb-0.3.2/src/http.rs000064400000000000000000000065271046102023000142020ustar 00000000000000use crate::errors::*; use chrono::{offset::Utc, DateTime}; use serde::{Deserialize, Serialize}; use std::time::Duration; const APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); const CONNECT_TIMEOUT: Duration = Duration::from_secs(15); const READ_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Debug, Clone)] pub struct Client { http: reqwest::Client, } impl Client { pub fn new() -> Result { let http = reqwest::Client::builder() .user_agent(APP_USER_AGENT) .connect_timeout(CONNECT_TIMEOUT) .read_timeout(READ_TIMEOUT) .build() .context("Failed to setup http client")?; Ok(Self { http }) } pub async fn get(&self, url: &str) -> Result { let req = self .http .get(url) .send() .await .context("Failed to send HTTP request")?; let status = req.status(); let headers = req.headers(); trace!("Received response from server: status={status:?}, headers={headers:?}"); let req = req.error_for_status()?; Ok(req) } pub async fn github_branch_metadata(&self, base_url: &str, branch: &str) -> Result { let url = format!("{}/{}", base_url, branch); info!("Fetching git repository meta data: {url:?}..."); let metadata = self .get(&url) .await? .json::() .await .context("Failed to receive http response")?; let commit = metadata.commit; debug!("Found github commit for branch {branch:?}: {commit:?}"); Ok(commit) } pub async fn github_download_file( &self, base_url: &str, commit: &GithubRef, filename: &str, ) -> Result { let url = base_url .replace("{{commit}}", &commit.sha) .replace("{{filename}}", filename); info!("Downloading IOC file from {url:?}..."); let body = self .get(&url) .await? .text() .await .context("Failed to download HTTP response")?; Ok(body) } } #[derive(Debug, Deserialize, Serialize)] pub struct GithubBranch { pub name: String, pub commit: GithubRef, } #[derive(Debug, Deserialize, Serialize)] pub struct GithubRef { pub sha: String, pub commit: GithubCommit, } #[derive(Debug, Deserialize, Serialize)] pub struct GithubCommit { pub committer: GithubGitAuthor, } impl GithubCommit { pub fn release_timestamp(&self) -> Result { let datetime = &self.committer.date; let timestamp = parse_datetime(datetime) .with_context(|| anyhow!("Failed to parse datetime from github: {datetime:?}"))?; Ok(timestamp) } } #[derive(Debug, Deserialize, Serialize)] pub struct GithubGitAuthor { pub name: String, pub email: String, pub date: String, } fn parse_datetime(datetime: &str) -> Result { let dt = DateTime::parse_from_rfc3339(datetime)?; let dt = DateTime::::from(dt); Ok(dt.timestamp()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_datetime() { let timestamp = parse_datetime("2024-07-02T23:34:14Z").unwrap(); assert_eq!(timestamp, 1719963254); } } spytrap-adb-0.3.2/src/ioc.rs000064400000000000000000000135731046102023000137740ustar 00000000000000use crate::errors::*; use crate::http; use crate::rules; use crate::utils; use indexmap::IndexMap; use ratatui::style::{Color, Modifier, Style}; use ratatui::text::Span; use serde::{Deserialize, Serialize}; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use tokio::fs; // query the latest commit to detect if we need to update pub const IOC_GIT_BRANCHES_URL: &str = "https://api.github.com/repos/AssoEchap/stalkerware-indicators/branches"; pub const IOC_GIT_BRANCH: &str = "master"; const IOC_DOWNLOAD_URL: &str = "https://github.com/AssoEchap/stalkerware-indicators/raw/{{commit}}/{{filename}}"; const IOC_DB_FILES: &[&str] = &["ioc.yaml", "watchware.yaml"]; #[derive(Debug, PartialEq, Eq)] pub struct Suspicion { pub level: SuspicionLevel, pub description: String, } impl Suspicion { pub fn to_terminal(&self) -> Vec { vec![ Span::styled( match self.level { SuspicionLevel::High => "high", SuspicionLevel::Medium => "medium", SuspicionLevel::Low => "low", SuspicionLevel::Good => "good", }, self.level.terminal_color(), ), Span::raw(": "), Span::raw(&self.description), ] } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub enum SuspicionLevel { Good, Low, Medium, High, } impl SuspicionLevel { pub fn terminal_color(&self) -> Style { match self { SuspicionLevel::High => Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), SuspicionLevel::Medium => Style::default() .fg(Color::Yellow) .add_modifier(Modifier::BOLD), SuspicionLevel::Low => Style::default().add_modifier(Modifier::BOLD), SuspicionLevel::Good => Style::default() .fg(Color::Green) .add_modifier(Modifier::BOLD), } } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RepositoryContent { pub last_update_check: i64, #[serde(default)] pub update_available: bool, pub released: i64, pub git_commit: String, pub files: IndexMap, } #[derive(Debug, Clone)] pub struct Repository { pub path: PathBuf, pub content: Option, pub client: http::Client, } impl Repository { pub fn data_path() -> Result { let dir = dirs::data_local_dir().context("Failed to find local data dir")?; let dir = dir.join("spytrap-adb"); Ok(dir) } pub async fn init() -> Result { let dir = Self::data_path()?; Self::init_at(&dir).await } pub async fn init_at(path: &Path) -> Result { debug!("Opening repository at {path:?}..."); fs::create_dir_all(path) .await .with_context(|| anyhow!("Failed to create directory at {path:?}"))?; let db_path = path.join("update.json"); let content = match fs::read(&db_path).await { Ok(buf) => serde_json::from_slice::(&buf).ok(), Err(err) if err.kind() == ErrorKind::NotFound => None, Err(err) => { return Err(err) .with_context(|| anyhow!("Failed to read database file at {db_path:?}")) } }; let client = http::Client::new()?; Ok(Self { path: db_path, content, client, }) } pub async fn query_latest_branch(&self) -> Result { let branch = self .client .github_branch_metadata(IOC_GIT_BRANCHES_URL, IOC_GIT_BRANCH) .await?; Ok(branch) } pub async fn download_ioc_db(&mut self) -> Result<()> { let branch = self.query_latest_branch().await?; if let Some(content) = &mut self.content { if content.git_commit == branch.sha { info!( "We're still on most recent git commit, marking as fresh... (commit={:?})", branch.sha ); content.update_available = false; content.last_update_check = utils::now(); self.write_database_file() .await .context("Failed to write database file")?; return Ok(()); } } let now = utils::now(); let released = branch.commit.release_timestamp()?; let mut files = IndexMap::new(); for filename in IOC_DB_FILES { let data = self .client .github_download_file(IOC_DOWNLOAD_URL, &branch, filename) .await?; files.insert(filename.to_string(), data); } self.content = Some(RepositoryContent { last_update_check: now, update_available: false, released, git_commit: branch.sha, files, }); self.write_database_file() .await .context("Failed to write database file")?; Ok(()) } pub async fn write_database_file(&self) -> Result<()> { let path = &self.path; let mut buf = serde_json::to_vec(&self.content)?; buf.push(b'\n'); debug!("Writing database file to {path:?}..."); fs::write(&path, &buf) .await .with_context(|| anyhow!("Failed to write database file at {path:?}"))?; Ok(()) } pub fn parse_rules(&self) -> Result { let content = self .content .as_ref() .context("Local IOC repository does not have any content downloaded yet")?; let mut rules = rules::Rules::default(); for (name, data) in &content.files { rules.load_yaml(name, data.as_bytes())?; } Ok(rules) } } spytrap-adb-0.3.2/src/lib.rs000064400000000000000000000003601046102023000137560ustar 00000000000000pub mod accessibility; pub mod args; pub mod dumpsys; pub mod errors; pub mod http; pub mod ioc; pub mod package; pub mod parsers; pub mod pm; pub mod remote_clock; pub mod rules; pub mod scan; pub mod settings; pub mod tui; pub mod utils; spytrap-adb-0.3.2/src/main.rs000064400000000000000000000100201046102023000141260ustar 00000000000000use clap::Parser; use env_logger::Env; use forensic_adb::{AndroidStorageInput, Host}; use spytrap_adb::args::{self, Args, SubCommand}; use spytrap_adb::errors::*; use spytrap_adb::ioc; use spytrap_adb::rules; use spytrap_adb::scan; use spytrap_adb::tui; use spytrap_adb::utils; use tokio::fs; use tokio::process::Command; async fn ensure_adb_running(choice: &args::AdbServerChoice) -> Result<()> { if *choice != args::AdbServerChoice::Never { debug!("Making sure adb server is running..."); let status = Command::new("adb") .arg("start-server") .status() .await .context("Failed to start adb binary")?; if !status.success() { bail!("Failed to ensure adb server is running: exited with status={status:?}"); } } Ok(()) } #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); if args.subcommand.is_some() { let logging = match args.verbose { 0 => "info", 1 => "spytrap_adb=debug,info", 2 => "spytrap_adb=trace,debug", _ => "trace", }; env_logger::init_from_env(Env::default().default_filter_or(logging)); } let adb_host = Host::default(); match args.subcommand { Some(SubCommand::Scan(scan)) => { ensure_adb_running(&args.start_adb_server).await?; let rules = if scan.rules.is_empty() { let repo = ioc::Repository::init().await?; repo.parse_rules()? } else { let mut rules = rules::Rules::default(); for path in &scan.rules { let buf = fs::read(&path) .await .with_context(|| anyhow!("Failed to read rules from file: {path:?}"))?; rules.load_yaml(path, &buf)?; } rules }; if scan.test_load_only { info!("Rules loaded successfully"); return Ok(()); } let device = adb_host .device_or_default(scan.serial.as_ref(), AndroidStorageInput::Auto) .await .with_context(|| anyhow!("Failed to access device: {:?}", scan.serial))?; scan::run( &device, &rules, &scan::Settings::from(&scan), &mut scan::ScanNotifier::Null, ) .await?; } Some(SubCommand::List(_)) => { ensure_adb_running(&args.start_adb_server).await?; debug!("Listing devices from adb..."); let devices = adb_host .devices::>() .await .map_err(|e| anyhow!("Failed to list devices from adb: {}", e))?; for device in devices { debug!("Found device: {:?}", device); println!( "{:30} device={:?}, model={:?}, product={:?}", device.serial, utils::human_option_str(device.info.get("device")), utils::human_option_str(device.info.get("model")), utils::human_option_str(device.info.get("product")), ); } } Some(SubCommand::DownloadIoc(_download)) => { let mut repo = ioc::Repository::init().await?; repo.download_ioc_db() .await .context("Failed to download stalkerware-indicators ioc.yaml")?; } Some(SubCommand::Completions(completions)) => { completions.generate()?; } None => { ensure_adb_running(&args.start_adb_server).await?; let repo = ioc::Repository::init().await?; let mut app = tui::App::new(adb_host, repo); app.init().await?; let mut terminal = tui::setup()?; let ret = tui::run(&mut terminal, &mut app).await; tui::cleanup(&mut terminal).ok(); ret?; } } Ok(()) } spytrap-adb-0.3.2/src/package.rs000064400000000000000000000434701046102023000146140ustar 00000000000000use crate::errors::*; use crate::ioc::{Suspicion, SuspicionLevel}; use crate::parsers::{self, package::PackageInfo, package::Permission}; use forensic_adb::Device; use std::borrow::Cow; pub async fn dump(device: &Device, package: &str) -> Result { let cmd = format!( "dumpsys package {}", shell_escape::escape(Cow::Borrowed(package)) ); debug!("Executing {:?}", cmd); let output = device .execute_host_exec_out_command(&cmd) .await .with_context(|| anyhow!("Failed to run: {:?}", cmd))?; parsers::package::parse_output(&output, package) } fn is_permission_suspcious(permission: &Permission) -> Option { match permission.name.as_str() { // sus: high "ACTION_NOTIFICATION_LISTENER_SETTINGS" => Some(SuspicionLevel::High), "android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" => Some(SuspicionLevel::High), "android.permission.BIND_ACCESSIBILITY_SERVICE" => Some(SuspicionLevel::High), // sus: medium "android.permission.ACCESS_BACKGROUND_LOCATION" => Some(SuspicionLevel::Medium), "android.permission.READ_SMS" => Some(SuspicionLevel::Medium), "android.permission.RECEIVE_SMS" => Some(SuspicionLevel::Medium), "android.permission.BIND_DEVICE_ADMIN" => Some(SuspicionLevel::Medium), // sus: low "android.permission.ACCESS_COARSE_LOCATION" => Some(SuspicionLevel::Low), "android.permission.ACCESS_FINE_LOCATION" => Some(SuspicionLevel::Low), "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" => Some(SuspicionLevel::Low), "android.permission.CAMERA" => Some(SuspicionLevel::Low), "android.permission.QUERY_ALL_PACKAGES" => Some(SuspicionLevel::Low), "android.permission.READ_CALL_LOG" => Some(SuspicionLevel::Low), "android.permission.READ_CONTACTS" => Some(SuspicionLevel::Low), "android.permission.RECORD_AUDIO" => Some(SuspicionLevel::Low), "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" => Some(SuspicionLevel::Low), "android.permission.MODIFY_AUDIO_SETTINGS" => Some(SuspicionLevel::Low), // none _ => None, } } impl PackageInfo { pub fn audit(&self) -> Vec { debug!("Scanning package: {:?}", self.id); let mut sus = Vec::new(); match self.installer_package_name() { Some("com.android.vending") => { // TODO: authenticate this application is a legitimate google play store .apk } Some("com.android.packageinstaller") => { sus.push(Suspicion { level: SuspicionLevel::High, description: format!("Package {:?} was manually installed", self.id), }); } Some(installer) => { sus.push(Suspicion { level: SuspicionLevel::High, description: format!( "Package {:?} was manually installed by an unknown installer: {:?}", self.id, installer ), }); } None => (), } for permission in &self.requested_permissions { // warn!("requested permission: {:?}", permission); // println!("permission {:?}", permission.name); if let Some(level) = is_permission_suspcious(permission) { sus.push(Suspicion { level, description: format!( "Package {:?} has requested permission {:?}", self.id, permission ), }); } } for permission in &self.install_permissions { // warn!("install permission: {:?}", permission); // println!("permission {:?}", permission.name); if let Some(level) = is_permission_suspcious(permission) { sus.push(Suspicion { level, description: format!( "Package {:?} has install permission {:?}", self.id, permission ), }); } } for permission in &self.runtime_permissions { // warn!("runtime permission: {:?}", permission); // println!("permission {:?}", permission.name); if let Some(level) = is_permission_suspcious(permission) { sus.push(Suspicion { level, description: format!( "Package {:?} has runtime permission {:?}", self.id, permission ), }); } } sus } pub fn installer_package_name(&self) -> Option<&str> { self.fields.get("installerPackageName").map(String::as_str) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_audit_package_spylive360() { let data = include_bytes!("../test_data/dumpsys/package/spylive360.txt"); let pkginfo = parsers::package::parse_output(data, "com.wifi0").unwrap(); let sus = pkginfo.audit(); assert_eq!(&sus, &[ Suspicion { level: SuspicionLevel::High, description: "Package \"com.wifi0\" was manually installed".to_string(), }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.ACCESS_FINE_LOCATION\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.ACCESS_COARSE_LOCATION\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.ACCESS_BACKGROUND_LOCATION\", fields: {\"restricted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::High, description: "Package \"com.wifi0\" has requested permission Permission { name: \"ACTION_NOTIFICATION_LISTENER_SETTINGS\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.READ_SMS\", fields: {\"restricted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.READ_CONTACTS\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.READ_CALL_LOG\", fields: {\"restricted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.CAMERA\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::High, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.ACTION_MANAGE_OVERLAY_PERMISSION\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.RECEIVE_SMS\", fields: {\"restricted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.RECORD_AUDIO\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::High, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.BIND_ACCESSIBILITY_SERVICE\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has requested permission Permission { name: \"android.permission.QUERY_ALL_PACKAGES\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has install permission Permission { name: \"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\", fields: {\"granted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has install permission Permission { name: \"android.permission.QUERY_ALL_PACKAGES\", fields: {\"granted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.READ_SMS\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.READ_CALL_LOG\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.ACCESS_FINE_LOCATION\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.RECEIVE_SMS\", fields: {\"flags\": \"[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.ACCESS_COARSE_LOCATION\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.CAMERA\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.RECORD_AUDIO\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.READ_CONTACTS\", fields: {\"flags\": \"[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"com.wifi0\" has runtime permission Permission { name: \"android.permission.ACCESS_BACKGROUND_LOCATION\", fields: {\"flags\": \"[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]\", \"granted\": \"false\"} }".to_string() }, ]); } #[test] fn test_audit_package_contacts() { let data = include_bytes!("../test_data/dumpsys/package/contacts.txt"); let pkginfo = parsers::package::parse_output(data, "com.android.contacts").unwrap(); let sus = pkginfo.audit(); assert_eq!(&sus, &[ Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.contacts\" has requested permission Permission { name: \"android.permission.READ_CONTACTS\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.contacts\" has runtime permission Permission { name: \"android.permission.READ_CONTACTS\", fields: {\"flags\": \"[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"true\"} }".to_string() }, ]); } #[test] fn test_audit_package_fdroid() { let data = include_bytes!("../test_data/dumpsys/package/fdroid.txt"); let pkginfo = parsers::package::parse_output(data, "org.fdroid.fdroid").unwrap(); let sus = pkginfo.audit(); assert_eq!(&sus, &[ Suspicion { level: SuspicionLevel::High, description: "Package \"org.fdroid.fdroid\" was manually installed".to_string(), }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.fdroid.fdroid\" has requested permission Permission { name: \"android.permission.ACCESS_COARSE_LOCATION\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"org.fdroid.fdroid\" has requested permission Permission { name: \"android.permission.ACCESS_BACKGROUND_LOCATION\", fields: {\"restricted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.fdroid.fdroid\" has runtime permission Permission { name: \"android.permission.ACCESS_COARSE_LOCATION\", fields: {\"flags\": \"[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Medium, description: "Package \"org.fdroid.fdroid\" has runtime permission Permission { name: \"android.permission.ACCESS_BACKGROUND_LOCATION\", fields: {\"flags\": \"[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]\", \"granted\": \"false\"} }".to_string() }, ]); } #[test] fn test_audit_package_gpstest() { let data = include_bytes!("../test_data/dumpsys/package/gpstest.txt"); let pkginfo = parsers::package::parse_output(data, "com.android.gpstest.osmdroid").unwrap(); let sus = pkginfo.audit(); assert_eq!(&sus, &[ Suspicion { level: SuspicionLevel::High, description: "Package \"com.android.gpstest.osmdroid\" was manually installed".to_string(), }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.gpstest.osmdroid\" has requested permission Permission { name: \"android.permission.ACCESS_FINE_LOCATION\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.gpstest.osmdroid\" has requested permission Permission { name: \"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.gpstest.osmdroid\" has requested permission Permission { name: \"android.permission.ACCESS_COARSE_LOCATION\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.gpstest.osmdroid\" has install permission Permission { name: \"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\", fields: {\"granted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.gpstest.osmdroid\" has runtime permission Permission { name: \"android.permission.ACCESS_FINE_LOCATION\", fields: {\"flags\": \"[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"com.android.gpstest.osmdroid\" has runtime permission Permission { name: \"android.permission.ACCESS_COARSE_LOCATION\", fields: {\"flags\": \"[ USER_SET|REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"true\"} }".to_string() }, ]); } #[test] fn test_audit_package_jitsi() { let data = include_bytes!("../test_data/dumpsys/package/jitsi.txt"); let pkginfo = parsers::package::parse_output(data, "org.jitsi.meet").unwrap(); let sus = pkginfo.audit(); assert_eq!(&sus, &[ Suspicion { level: SuspicionLevel::High, description: "Package \"org.jitsi.meet\" was manually installed".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.jitsi.meet\" has requested permission Permission { name: \"android.permission.CAMERA\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.jitsi.meet\" has requested permission Permission { name: \"android.permission.MODIFY_AUDIO_SETTINGS\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.jitsi.meet\" has requested permission Permission { name: \"android.permission.RECORD_AUDIO\", fields: {} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.jitsi.meet\" has install permission Permission { name: \"android.permission.MODIFY_AUDIO_SETTINGS\", fields: {\"granted\": \"true\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.jitsi.meet\" has runtime permission Permission { name: \"android.permission.CAMERA\", fields: {\"flags\": \"[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"false\"} }".to_string() }, Suspicion { level: SuspicionLevel::Low, description: "Package \"org.jitsi.meet\" has runtime permission Permission { name: \"android.permission.RECORD_AUDIO\", fields: {\"flags\": \"[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]\", \"granted\": \"true\"} }".to_string() }, ]); } } spytrap-adb-0.3.2/src/parsers/accessibility.rs000064400000000000000000000154271046102023000175300ustar 00000000000000use crate::errors::*; use regex::Regex; use std::collections::HashMap; use std::str::FromStr; #[derive(Debug, PartialEq, Default)] pub struct Accessibility { attributes: HashMap, shortcut_key: Option, button: Option, button_target: Option, pub bound_services: Option, pub enabled_services: Option, binding_services: Option, crashed_services: Option, } impl Accessibility { fn add_attribute(&mut self, key: &str, value: &str) { self.attributes.insert(key.to_string(), value.to_string()); } } impl FromStr for Accessibility { type Err = Error; fn from_str(s: &str) -> Result { let mut out = Accessibility::default(); let (_, s) = s .split_once("User state[") .context("Failed to find `User state[` needle")?; let re = Regex::new(r"^\s*(.+):\{(.*)\}]?$").unwrap(); for line in s.lines() { debug!("Parsing line of accessibility output: {:?}", line); if let Some(cap) = re.captures(line) { let key = &cap[1]; let values = &cap[2]; match key { "attributes" => { for attr in values.split(", ") { if let Some((key, value)) = attr.split_once('=') { out.add_attribute(key, value); } } } "shortcut key" => out.shortcut_key = values_to_option(values), "button" => out.button = values_to_option(values), "button target" => out.button_target = values_to_option(values), "Bound services" => out.bound_services = values_to_option(values), "Enabled services" => out.enabled_services = values_to_option(values), "Binding services" => out.binding_services = values_to_option(values), "Crashed services" => out.crashed_services = values_to_option(values), _ => warn!("Found unexpected key in output: {:?}", key), } } } debug!("Parsed accessibility settings: {:?}", out); Ok(out) } } fn values_to_option(s: &str) -> Option { if s.is_empty() { None } else { Some(s.to_string()) } } #[cfg(test)] mod tests { use super::*; use maplit::hashmap; fn init() { let _ = env_logger::builder().is_test(true).try_init(); } #[test] fn test_parse_plain() { init(); let data = include_str!("../../test_data/dumpsys/accessibility/plain.txt"); let a = data.parse::().unwrap(); assert_eq!( a, Accessibility { attributes: hashmap![ "id".to_string() => "0".to_string(), "touchExplorationEnabled".to_string() => "false".to_string(), "serviceHandlesDoubleTap".to_string() => "false".to_string(), "requestMultiFingerGestures".to_string() => "false".to_string(), "requestTwoFingerPassthrough".to_string() => "false".to_string(), "displayMagnificationEnabled".to_string() => "false".to_string(), "autoclickEnabled".to_string() => "false".to_string(), "nonInteractiveUiTimeout".to_string() => "0".to_string(), "interactiveUiTimeout".to_string() => "0".to_string(), "installedServiceCount".to_string() => "0".to_string(), ], shortcut_key: None, button: None, button_target: Some("null".to_string()), bound_services: None, enabled_services: None, binding_services: None, crashed_services: None, } ); } #[test] fn test_parse_plain2() { init(); let data = include_str!("../../test_data/dumpsys/accessibility/plain2.txt"); let a = data.parse::().unwrap(); assert_eq!( a, Accessibility { attributes: hashmap![ "id".to_string() => "0".to_string(), "currentUser".to_string() => "true".to_string(), "touchExplorationEnabled".to_string() => "false".to_string(), "displayMagnificationEnabled".to_string() => "false".to_string(), "navBarMagnificationEnabled".to_string() => "false".to_string(), "autoclickEnabled".to_string() => "false".to_string(), "nonInteractiveUiTimeout".to_string() => "0".to_string(), "interactiveUiTimeout".to_string() => "0".to_string(), "installedServiceCount".to_string() => "4".to_string(), ], shortcut_key: None, button: None, button_target: None, bound_services: None, enabled_services: None, binding_services: None, crashed_services: None, } ); } #[test] fn test_parse_spylive360() { init(); let data = include_str!("../../test_data/dumpsys/accessibility/spylive360.txt"); let a = data.parse::().unwrap(); assert_eq!(a, Accessibility { attributes: hashmap![ "id".to_string() => "0".to_string(), "touchExplorationEnabled".to_string() => "false".to_string(), "serviceHandlesDoubleTap".to_string() => "false".to_string(), "requestMultiFingerGestures".to_string() => "false".to_string(), "requestTwoFingerPassthrough".to_string() => "false".to_string(), "displayMagnificationEnabled".to_string() => "false".to_string(), "autoclickEnabled".to_string() => "false".to_string(), "nonInteractiveUiTimeout".to_string() => "0".to_string(), "interactiveUiTimeout".to_string() => "0".to_string(), "installedServiceCount".to_string() => "1".to_string(), ], shortcut_key: None, button: None, button_target: Some("null".to_string()), bound_services: Some("Service[label=WiFi, feedbackType[FEEDBACK_SPOKEN, FEEDBACK_HAPTIC, FEEDBACK_AUDIBLE, FEEDBACK_VISUAL, FEEDBACK_GENERIC, FEEDBACK_BRAILLE], capabilities=1, eventTypes=TYPES_ALL_MASK, notificationTimeout=1000, requestA11yBtn=false]".to_string()), enabled_services: Some("{com.wifi0/com.wifi0.AccessibilityReceiver4}".to_string()), binding_services: None, crashed_services: None, }); } } spytrap-adb-0.3.2/src/parsers/mod.rs000064400000000000000000000000501046102023000154420ustar 00000000000000pub mod accessibility; pub mod package; spytrap-adb-0.3.2/src/parsers/package.rs000064400000000000000000001101261046102023000162640ustar 00000000000000use crate::errors::*; use bstr::ByteSlice; use std::collections::BTreeMap; use std::str; use std::str::FromStr; #[derive(Debug, PartialEq, Default)] pub struct PackageInfo { pub id: String, pub fields: BTreeMap, pub requested_permissions: Vec, pub install_permissions: Vec, pub runtime_permissions: Vec, } #[derive(Debug, PartialEq, Default)] pub struct Permission { pub name: String, pub fields: BTreeMap, } impl FromStr for Permission { type Err = Error; fn from_str(s: &str) -> Result { let mut permission = Permission::default(); if let Some((key, value)) = s.split_once(": ") { permission.name = key.to_string(); for field in value.split(", ") { if let Some((key, value)) = field.split_once('=') { permission.fields.insert(key.into(), value.into()); } } } else { permission.name = s.to_string(); } Ok(permission) } } fn count_whitespace_prefix(line: &str) -> usize { line.chars().take_while(|ch| *ch == ' ').count() } pub fn parse_output(output: &[u8], package: &str) -> Result { let mut prev_line = None; let mut section_stack = Vec::new(); let mut info = PackageInfo { id: package.to_string(), ..Default::default() }; let mut indent = 0; for line in output.lines() { let line = String::from_utf8_lossy(line); let trimmed_line = line.trim(); match count_whitespace_prefix(&line) { i if i < indent => { section_stack.truncate(i / 2); indent = i; } i if i > indent => { if let Some(prev_line) = prev_line { section_stack.push(prev_line); } indent = i; } i if i == indent => (), // unreachable _ => (), } prev_line = Some(trimmed_line.to_owned()); match section_stack.last().map(|x| x.as_str()) { Some("requested permissions:") => { debug!("requested permission: {:?}", trimmed_line); info.requested_permissions.push(trimmed_line.parse()?); } Some("install permissions:") => { debug!("install permission: {:?}", trimmed_line); info.install_permissions.push(trimmed_line.parse()?); } Some("runtime permissions:") => { debug!("runtime permission: {:?}", trimmed_line); info.runtime_permissions.push(trimmed_line.parse()?); } _ => { if let Some("Packages:") = section_stack.first().map(|x| x.as_str()) { trace!("package line: {:?}", line); if let Some((key, value)) = trimmed_line.split_once('=') { trace!( "discovered for package {:?}: key={:?}, value={:?}", package, key, value ); info.fields.insert(key.to_string(), value.to_string()); } } } } } Ok(info) } #[cfg(test)] mod tests { use super::*; use maplit::btreemap; #[test] fn count_whitespace() { assert_eq!(0, count_whitespace_prefix("")); assert_eq!(1, count_whitespace_prefix(" ")); assert_eq!(1, count_whitespace_prefix(" a")); assert_eq!(2, count_whitespace_prefix(" ")); assert_eq!(2, count_whitespace_prefix(" a")); assert_eq!(3, count_whitespace_prefix(" ab c d e f")); assert_eq!(3, count_whitespace_prefix(" User 0:")); } #[test] fn parse_package_spylive360() { let data = include_bytes!("../../test_data/dumpsys/package/spylive360.txt"); let pkginfo = parse_output(data, "com.wifi0").unwrap(); assert_eq!(pkginfo, PackageInfo { id: "com.wifi0".to_string(), fields: btreemap![ "userId".to_string() => "10155".to_string(), "pkg".to_string() => "Package{19806ac com.wifi0}".to_string(), "codePath".to_string() => "/data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA==".to_string(), "resourcePath".to_string() => "/data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA==".to_string(), "legacyNativeLibraryDir".to_string() => "/data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA==/lib".to_string(), "primaryCpuAbi".to_string() => "null".to_string(), "secondaryCpuAbi".to_string() => "null".to_string(), "versionCode".to_string() => "140 minSdk=19 targetSdk=30".to_string(), "versionName".to_string() => "1.4.0".to_string(), "splits".to_string() => "[base]".to_string(), "apkSigningVersion".to_string() => "2".to_string(), "applicationInfo".to_string() => "ApplicationInfo{19806ac com.wifi0}".to_string(), "flags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ]".to_string(), "privateFlags".to_string() => "[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE PARTIALLY_DIRECT_BOOT_AWARE PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ]".to_string(), "forceQueryable".to_string() => "false".to_string(), "queriesPackages".to_string() => "[]".to_string(), "dataDir".to_string() => "/data/user/0/com.wifi0".to_string(), "supportsScreens".to_string() => "[small, medium, large, xlarge, resizeable, anyDensity]".to_string(), "timeStamp".to_string() => "2021-12-15 17:52:55".to_string(), "firstInstallTime".to_string() => "2021-12-15 17:51:52".to_string(), "lastUpdateTime".to_string() => "2021-12-15 17:52:55".to_string(), "installerPackageName".to_string() => "com.android.packageinstaller".to_string(), "signatures".to_string() => "PackageSignatures{4cb6f75 version:2, signatures:[74831dfd], past signatures:[]}".to_string(), "installPermissionsFixed".to_string() => "true".to_string(), "pkgFlags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ]".to_string(), "User 0: ceDataInode".to_string() => "261852 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false".to_string(), "gids".to_string() => "[3003]".to_string(), ], requested_permissions: [ "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_BACKGROUND_LOCATION: restricted=true", "android.permission.INTERNET", "ACTION_NOTIFICATION_LISTENER_SETTINGS", "android.permission.READ_SMS: restricted=true", "android.permission.READ_CONTACTS", "android.permission.READ_CALL_LOG: restricted=true", "android.permission.READ_PHONE_STATE", "android.permission.WRITE_EXTERNAL_STORAGE: restricted=true", "android.permission.CAMERA", "android.permission.SYSTEM_ALERT_WINDOW", "android.permission.ACTION_MANAGE_OVERLAY_PERMISSION", "android.permission.ACCESS_WIFI_STATE", "android.permission.CHANGE_WIFI_STATE", "android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", "com.android.browser.permission.READ_HISTORY_BOOKMARKS", "android.permission.READ_EXTERNAL_STORAGE: restricted=true", "android.permission.RECEIVE_SMS: restricted=true", "android.permission.RECORD_AUDIO", "android.permission.BIND_ACCESSIBILITY_SERVICE", "com.huawei.systemmanager.permission.ACCESS_INTERFACE", "android.permission.QUERY_ALL_PACKAGES", "android.permission.ACCESS_MEDIA_LOCATION", "android.permission.WRITE_SETTINGS", "android.permission.MANAGE_EXTERNAL_STORAGE", "android.permission.WAKE_LOCK", "com.google.android.c2dm.permission.RECEIVE", "com.google.android.providers.gsf.permission.READ_GSERVICES", "com.google.android.gms.permission.ACTIVITY_RECOGNITION", "android.permission.FOREGROUND_SERVICE", "com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE", ].into_iter().map(|s| s.parse().unwrap()).collect(), install_permissions: [ "android.permission.FOREGROUND_SERVICE: granted=true", "android.permission.RECEIVE_BOOT_COMPLETED: granted=true", "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: granted=true", "android.permission.INTERNET: granted=true", "com.android.browser.permission.READ_HISTORY_BOOKMARKS: granted=true", "android.permission.CHANGE_WIFI_STATE: granted=true", "android.permission.ACCESS_NETWORK_STATE: granted=true", "android.permission.ACCESS_WIFI_STATE: granted=true", "android.permission.QUERY_ALL_PACKAGES: granted=true", "android.permission.WAKE_LOCK: granted=true", ].into_iter().map(|s| s.parse().unwrap()).collect(), runtime_permissions: [ "android.permission.READ_SMS: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.READ_CALL_LOG: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.RECEIVE_SMS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_COARSE_LOCATION: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.READ_PHONE_STATE: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.CAMERA: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.RECORD_AUDIO: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.READ_CONTACTS: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_MEDIA_LOCATION: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", ].into_iter().map(|s| s.parse().unwrap()).collect(), }); } #[test] fn parse_package_contacts() { let data = include_bytes!("../../test_data/dumpsys/package/contacts.txt"); let pkginfo = parse_output(data, "com.android.contacts").unwrap(); assert_eq!(pkginfo, PackageInfo { id: "com.android.contacts".to_string(), fields: btreemap![ "userId".to_string() => "10118".to_string(), "pkg".to_string() => "Package{647faae com.android.contacts}".to_string(), "codePath".to_string() => "/system/product/priv-app/Contacts".to_string(), "resourcePath".to_string() => "/system/product/priv-app/Contacts".to_string(), "legacyNativeLibraryDir".to_string() => "/system/product/priv-app/Contacts/lib".to_string(), "primaryCpuAbi".to_string() => "null".to_string(), "secondaryCpuAbi".to_string() => "null".to_string(), "versionCode".to_string() => "10731 minSdk=26 targetSdk=29".to_string(), "versionName".to_string() => "1.7.31".to_string(), "splits".to_string() => "[base]".to_string(), "apkSigningVersion".to_string() => "3".to_string(), "applicationInfo".to_string() => "ApplicationInfo{647faae com.android.contacts}".to_string(), "flags".to_string() => "[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "privateFlags".to_string() => "[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE PRIVILEGED PRODUCT PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ]".to_string(), "forceQueryable".to_string() => "false".to_string(), "queriesPackages".to_string() => "[]".to_string(), "dataDir".to_string() => "/data/user/0/com.android.contacts".to_string(), "supportsScreens".to_string() => "[small, medium, large, xlarge, resizeable, anyDensity]".to_string(), "timeStamp".to_string() => "2009-01-01 01:00:00".to_string(), "firstInstallTime".to_string() => "2009-01-01 01:00:00".to_string(), "lastUpdateTime".to_string() => "2009-01-01 01:00:00".to_string(), "signatures".to_string() => "PackageSignatures{b9af04f version:3, signatures:[ed46f865], past signatures:[]}".to_string(), "installPermissionsFixed".to_string() => "true".to_string(), "pkgFlags".to_string() => "[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "User 0: ceDataInode".to_string() => "261398 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false".to_string(), "gids".to_string() => "[3003]".to_string(), ], requested_permissions: [ "android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS", "android.permission.GET_ACCOUNTS", "android.permission.GET_ACCOUNTS_PRIVILEGED", "android.permission.MANAGE_ACCOUNTS", "android.permission.ACCESS_NETWORK_STATE", "android.permission.CALL_PHONE", "android.permission.READ_PROFILE", "android.permission.WRITE_PROFILE", "android.permission.INTERNET", "android.permission.NFC", "android.permission.READ_PHONE_STATE", "android.permission.WAKE_LOCK", "android.permission.WRITE_SETTINGS", "android.permission.USE_CREDENTIALS", "android.permission.VIBRATE", "android.permission.READ_SYNC_SETTINGS", "android.permission.READ_EXTERNAL_STORAGE: restricted=true", "com.android.launcher.permission.INSTALL_SHORTCUT", "android.permission.WRITE_SYNC_SETTINGS", "android.permission.READ_SYNC_STATS", "android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.FOREGROUND_SERVICE", "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS", ].into_iter().map(|s| s.parse().unwrap()).collect(), install_permissions: [ "android.permission.WRITE_SETTINGS: granted=true", "android.permission.USE_CREDENTIALS: granted=true", "android.permission.MANAGE_ACCOUNTS: granted=true", "android.permission.NFC: granted=true", "android.permission.FOREGROUND_SERVICE: granted=true", "android.permission.WRITE_SYNC_SETTINGS: granted=true", "android.permission.RECEIVE_BOOT_COMPLETED: granted=true", "android.permission.READ_PROFILE: granted=true", "android.permission.INTERNET: granted=true", "android.permission.WRITE_PROFILE: granted=true", "android.permission.GET_ACCOUNTS_PRIVILEGED: granted=true", "android.permission.ACCESS_NETWORK_STATE: granted=true", "android.permission.READ_SYNC_STATS: granted=true", "android.permission.READ_SYNC_SETTINGS: granted=true", "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS: granted=true", "android.permission.VIBRATE: granted=true", "com.android.launcher.permission.INSTALL_SHORTCUT: granted=true", "android.permission.WAKE_LOCK: granted=true", ].into_iter().map(|s| s.parse().unwrap()).collect(), runtime_permissions: [ "android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_UPGRADE_EXEMPT]", "android.permission.READ_PHONE_STATE: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.CALL_PHONE: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.WRITE_CONTACTS: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.GET_ACCOUNTS: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.READ_CONTACTS: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", ].into_iter().map(|s| s.parse().unwrap()).collect(), }); } #[test] fn parse_package_fdroid() { let data = include_bytes!("../../test_data/dumpsys/package/fdroid.txt"); let pkginfo = parse_output(data, "org.fdroid.fdroid").unwrap(); assert_eq!(pkginfo, PackageInfo { id: "org.fdroid.fdroid".to_string(), fields: btreemap![ "userId".to_string() => "10151".to_string(), "pkg".to_string() => "Package{7013942 org.fdroid.fdroid}".to_string(), "codePath".to_string() => "/data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A==".to_string(), "resourcePath".to_string() => "/data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A==".to_string(), "legacyNativeLibraryDir".to_string() => "/data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A==/lib".to_string(), "primaryCpuAbi".to_string() => "null".to_string(), "secondaryCpuAbi".to_string() => "null".to_string(), "versionCode".to_string() => "1013051 minSdk=22 targetSdk=25".to_string(), "versionName".to_string() => "1.13.1".to_string(), "splits".to_string() => "[base]".to_string(), "apkSigningVersion".to_string() => "3".to_string(), "applicationInfo".to_string() => "ApplicationInfo{7013942 org.fdroid.fdroid}".to_string(), "flags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "privateFlags".to_string() => "[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE HAS_DOMAIN_URLS PARTIALLY_DIRECT_BOOT_AWARE PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ]".to_string(), "forceQueryable".to_string() => "false".to_string(), "queriesPackages".to_string() => "[]".to_string(), "dataDir".to_string() => "/data/user/0/org.fdroid.fdroid".to_string(), "supportsScreens".to_string() => "[small, medium, large, xlarge, resizeable, anyDensity]".to_string(), "timeStamp".to_string() => "2021-11-04 20:12:50".to_string(), "firstInstallTime".to_string() => "2021-11-04 20:12:50".to_string(), "lastUpdateTime".to_string() => "2021-11-04 20:12:50".to_string(), "installerPackageName".to_string() => "com.android.packageinstaller".to_string(), "signatures".to_string() => "PackageSignatures{603ad53 version:3, signatures:[8b8a3ff5], past signatures:[]}".to_string(), "installPermissionsFixed".to_string() => "true".to_string(), "pkgFlags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "User 0: ceDataInode".to_string() => "392899 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false".to_string(), "gids".to_string() => "[3002, 3003, 3001]".to_string(), ], requested_permissions: [ "android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.CHANGE_WIFI_MULTICAST_STATE", "android.permission.CHANGE_NETWORK_STATE", "android.permission.CHANGE_WIFI_STATE", "android.permission.BLUETOOTH", "android.permission.BLUETOOTH_ADMIN", "android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.READ_EXTERNAL_STORAGE: restricted=true", "android.permission.WRITE_EXTERNAL_STORAGE: restricted=true", "android.permission.WRITE_SETTINGS", "android.permission.NFC", "android.permission.WAKE_LOCK", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.FOREGROUND_SERVICE", "android.permission.ACCESS_BACKGROUND_LOCATION: restricted=true", "android.permission.ACCESS_MEDIA_LOCATION", ].into_iter().map(|s| s.parse().unwrap()).collect(), install_permissions: [ "android.permission.NFC: granted=true", "android.permission.CHANGE_NETWORK_STATE: granted=true", "android.permission.FOREGROUND_SERVICE: granted=true", "android.permission.RECEIVE_BOOT_COMPLETED: granted=true", "android.permission.BLUETOOTH: granted=true", "android.permission.CHANGE_WIFI_MULTICAST_STATE: granted=true", "android.permission.INTERNET: granted=true", "android.permission.BLUETOOTH_ADMIN: granted=true", "android.permission.CHANGE_WIFI_STATE: granted=true", "android.permission.ACCESS_NETWORK_STATE: granted=true", "android.permission.ACCESS_WIFI_STATE: granted=true", "android.permission.WAKE_LOCK: granted=true", ].into_iter().map(|s| s.parse().unwrap()).collect(), runtime_permissions: [ "android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_COARSE_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_MEDIA_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", ].into_iter().map(|s| s.parse().unwrap()).collect(), }); } #[test] fn parse_package_gpstest() { let data = include_bytes!("../../test_data/dumpsys/package/gpstest.txt"); let pkginfo = parse_output(data, "com.android.gpstest.osmdroid").unwrap(); assert_eq!(pkginfo, PackageInfo { id: "com.android.gpstest.osmdroid".to_string(), fields: btreemap![ "userId".to_string() => "10153".to_string(), "pkg".to_string() => "Package{746e2fd com.android.gpstest.osmdroid}".to_string(), "codePath".to_string() => "/data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ==".to_string(), "resourcePath".to_string() => "/data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ==".to_string(), "legacyNativeLibraryDir".to_string() => "/data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ==/lib".to_string(), "primaryCpuAbi".to_string() => "null".to_string(), "secondaryCpuAbi".to_string() => "null".to_string(), "versionCode".to_string() => "18093 minSdk=18 targetSdk=29".to_string(), "versionName".to_string() => "3.9.16".to_string(), "splits".to_string() => "[base]".to_string(), "apkSigningVersion".to_string() => "3".to_string(), "applicationInfo".to_string() => "ApplicationInfo{746e2fd com.android.gpstest.osmdroid}".to_string(), "flags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "privateFlags".to_string() => "[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ]".to_string(), "forceQueryable".to_string() => "false".to_string(), "queriesPackages".to_string() => "[]".to_string(), "dataDir".to_string() => "/data/user/0/com.android.gpstest.osmdroid".to_string(), "supportsScreens".to_string() => "[small, medium, large, xlarge, resizeable, anyDensity]".to_string(), "timeStamp".to_string() => "2021-11-07 23:55:08".to_string(), "firstInstallTime".to_string() => "2021-11-07 23:55:09".to_string(), "lastUpdateTime".to_string() => "2021-11-07 23:55:09".to_string(), "installerPackageName".to_string() => "com.android.packageinstaller".to_string(), "signatures".to_string() => "PackageSignatures{f05ebf2 version:3, signatures:[8f857bc5], past signatures:[]}".to_string(), "installPermissionsFixed".to_string() => "true".to_string(), "pkgFlags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "User 0: ceDataInode".to_string() => "392616 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false".to_string(), "gids".to_string() => "[3003]".to_string(), ], requested_permissions: [ "android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS", "android.permission.INTERNET", "android.permission.WRITE_EXTERNAL_STORAGE: restricted=true", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.READ_EXTERNAL_STORAGE: restricted=true", ].into_iter().map(|s| s.parse().unwrap()).collect(), install_permissions: [ "android.permission.INTERNET: granted=true", "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS: granted=true", "android.permission.ACCESS_NETWORK_STATE: granted=true", ].into_iter().map(|s| s.parse().unwrap()).collect(), runtime_permissions: [ "android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", "android.permission.ACCESS_COARSE_LOCATION: granted=true, flags=[ USER_SET|REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]", ].into_iter().map(|s| s.parse().unwrap()).collect(), }); } #[test] fn parse_package_jitsi() { let data = include_bytes!("../../test_data/dumpsys/package/jitsi.txt"); let pkginfo = parse_output(data, "org.jitsi.meet").unwrap(); assert_eq!(pkginfo, PackageInfo { id: "org.jitsi.meet".to_string(), fields: btreemap![ "userId".to_string() => "10152".to_string(), "pkg".to_string() => "Package{1a7e7ac org.jitsi.meet}".to_string(), "codePath".to_string() => "/data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA==".to_string(), "resourcePath".to_string() => "/data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA==".to_string(), "legacyNativeLibraryDir".to_string() => "/data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA==/lib".to_string(), "primaryCpuAbi".to_string() => "arm64-v8a".to_string(), "secondaryCpuAbi".to_string() => "null".to_string(), "versionCode".to_string() => "214010 minSdk=23 targetSdk=30".to_string(), "versionName".to_string() => "21.4.1".to_string(), "splits".to_string() => "[base]".to_string(), "apkSigningVersion".to_string() => "3".to_string(), "applicationInfo".to_string() => "ApplicationInfo{1a7e7ac org.jitsi.meet}".to_string(), "flags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "privateFlags".to_string() => "[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE HAS_DOMAIN_URLS PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ]".to_string(), "forceQueryable".to_string() => "false".to_string(), "queriesPackages".to_string() => "[]".to_string(), "dataDir".to_string() => "/data/user/0/org.jitsi.meet".to_string(), "supportsScreens".to_string() => "[small, medium, large, xlarge, resizeable, anyDensity]".to_string(), "timeStamp".to_string() => "2021-11-04 20:13:57".to_string(), "firstInstallTime".to_string() => "2021-11-04 20:13:55".to_string(), "lastUpdateTime".to_string() => "2021-11-04 20:13:58".to_string(), "installerPackageName".to_string() => "com.android.packageinstaller".to_string(), "signatures".to_string() => "PackageSignatures{81c3c75 version:3, signatures:[224eb835], past signatures:[]}".to_string(), "installPermissionsFixed".to_string() => "true".to_string(), "pkgFlags".to_string() => "[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]".to_string(), "User 0: ceDataInode".to_string() => "392898 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false".to_string(), "gids".to_string() => "[3002, 3003]".to_string(), ], requested_permissions: [ "android.permission.ACCESS_NETWORK_STATE", "android.permission.BLUETOOTH", "android.permission.CAMERA", "android.permission.INTERNET", "android.permission.MANAGE_OWN_CALLS", "android.permission.MODIFY_AUDIO_SETTINGS", "android.permission.RECORD_AUDIO", "android.permission.SYSTEM_ALERT_WINDOW", "android.permission.WAKE_LOCK", "android.permission.ACCESS_WIFI_STATE", "android.permission.FOREGROUND_SERVICE", "android.permission.WRITE_CALENDAR", "android.permission.READ_CALENDAR", ].into_iter().map(|s| s.parse().unwrap()).collect(), install_permissions: [ "android.permission.MODIFY_AUDIO_SETTINGS: granted=true", "android.permission.FOREGROUND_SERVICE: granted=true", "android.permission.BLUETOOTH: granted=true", "android.permission.INTERNET: granted=true", "android.permission.ACCESS_NETWORK_STATE: granted=true", "android.permission.MANAGE_OWN_CALLS: granted=true", "android.permission.ACCESS_WIFI_STATE: granted=true", "android.permission.WAKE_LOCK: granted=true", ].into_iter().map(|s| s.parse().unwrap()).collect(), runtime_permissions: [ "android.permission.READ_CALENDAR: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.CAMERA: granted=false, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.WRITE_CALENDAR: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", "android.permission.RECORD_AUDIO: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]", ].into_iter().map(|s| s.parse().unwrap()).collect(), }); } #[test] fn test_parse_permission_plain() { let line = "android.permission.ACCESS_FINE_LOCATION"; let p = Permission::from_str(line).unwrap(); assert_eq!( p, Permission { name: "android.permission.ACCESS_FINE_LOCATION".to_string(), fields: BTreeMap::new(), } ); } #[test] fn test_parse_permission_fields1() { let line = "android.permission.INTERNET: granted=true"; let p = Permission::from_str(line).unwrap(); assert_eq!( p, Permission { name: "android.permission.INTERNET".to_string(), fields: btreemap![ "granted".to_string() => "true".to_string(), ], } ); } #[test] fn test_parse_permission_fields2() { let line = "android.permission.ACCESS_BACKGROUND_LOCATION: restricted=true"; let p = Permission::from_str(line).unwrap(); assert_eq!( p, Permission { name: "android.permission.ACCESS_BACKGROUND_LOCATION".to_string(), fields: btreemap![ "restricted".to_string() => "true".to_string(), ], } ); } #[test] fn test_parse_permission_flags() { let line = "android.permission.READ_CALL_LOG: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]"; let p = Permission::from_str(line).unwrap(); assert_eq!( p, Permission { name: "android.permission.READ_CALL_LOG".to_string(), fields: btreemap![ "granted".to_string() => "false".to_string(), "flags".to_string() => "[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT]".to_string(), ], } ); } } spytrap-adb-0.3.2/src/pm.rs000064400000000000000000000043171046102023000136320ustar 00000000000000use crate::errors::*; use bstr::ByteSlice; use forensic_adb::Device; use std::str; const CMD: &str = "pm list packages"; #[derive(Debug, PartialEq)] pub struct Apk { pub id: String, } pub async fn list_packages(device: &Device) -> Result> { let output = device .execute_host_exec_out_command(CMD) .await .with_context(|| anyhow!("Failed to run: {:?}", CMD))?; parse_output(&output) } fn parse_output(output: &[u8]) -> Result> { let mut pkgs = Vec::new(); for line in output.lines() { if line.is_empty() { continue; } let line = String::from_utf8_lossy(line); if let Some(package) = line.strip_prefix("package:") { debug!("discovered package={:?}", package); pkgs.push(Apk { id: package.to_string(), }); } } Ok(pkgs) } #[cfg(test)] mod tests { use super::*; #[test] pub fn test_parse_output() { let data = b"package:org.jitsi.meet package:org.lineageos.overlay.accent.black package:com.android.cts.priv.ctsshim package:org.lineageos.overlay.accent.brown package:org.lineageos.overlay.accent.green package:com.android.internal.display.cutout.emulation.corner package:org.lineageos.overlay.customization.blacktheme "; let pkgs = parse_output(data).unwrap(); assert_eq!( &pkgs, &[ Apk { id: "org.jitsi.meet".to_string() }, Apk { id: "org.lineageos.overlay.accent.black".to_string() }, Apk { id: "com.android.cts.priv.ctsshim".to_string() }, Apk { id: "org.lineageos.overlay.accent.brown".to_string() }, Apk { id: "org.lineageos.overlay.accent.green".to_string() }, Apk { id: "com.android.internal.display.cutout.emulation.corner".to_string() }, Apk { id: "org.lineageos.overlay.customization.blacktheme".to_string() } ] ); } } spytrap-adb-0.3.2/src/remote_clock.rs000064400000000000000000000023761046102023000156670ustar 00000000000000use crate::errors::*; use chrono::{DateTime, Duration, NaiveDateTime, Utc}; use forensic_adb::Device; const DATE_COMMAND: &str = "date -u '+%Y-%m-%d %T %N'"; pub async fn determine(device: &Device) -> Result<(DateTime, DateTime, Duration)> { let output = device .execute_host_exec_out_command(DATE_COMMAND) .await .with_context(|| anyhow!("Failed to run date command: {:?}", DATE_COMMAND))?; let local_time = Utc::now(); let output = String::from_utf8_lossy(&output); let remote_time = parse(output.trim()).context("Failed to parse remote time")?; let drift = remote_time.signed_duration_since(local_time); Ok((local_time, remote_time, drift)) } fn parse(s: &str) -> Result> { let dt = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %T %f")?; Ok(DateTime::::from_naive_utc_and_offset(dt, Utc)) } #[cfg(test)] mod tests { use super::*; use chrono::{TimeZone, Timelike}; #[test] fn test_parse_date() { let dt = parse("2021-10-21 22:37:56 716729236").unwrap(); assert_eq!( dt, Utc.with_ymd_and_hms(2021, 10, 21, 22, 37, 56) .unwrap() .with_nanosecond(716729236) .unwrap() ); } } spytrap-adb-0.3.2/src/rules.rs000064400000000000000000000103151046102023000143430ustar 00000000000000use crate::errors::*; use crate::utils; use std::collections::HashMap; use std::fmt; #[derive(Debug, Clone, PartialEq, Default)] pub struct Rules { map: HashMap, } impl Rules { pub fn load_yaml(&mut self, name: F, buf: &[u8]) -> Result { let sha256 = utils::sha256(buf); let list = stalkerware_indicators::parse_from_buf(buf) .context("Failed to load stalkerware-indicators yaml")?; let num_of_rules = list.len(); for rule in list { for package in rule.packages { self.map.insert(package, rule.name.to_string()); } } info!("Loaded {num_of_rules} rules from {name:?} (sha256={sha256})",); Ok(sha256) } pub fn get(&self, pkg_id: &str) -> Option<&String> { self.map.get(pkg_id) } } #[cfg(test)] mod tests { use super::*; use maplit::hashmap; #[test] fn test_parse_ioc_yaml() { let mut rules = Rules::default(); rules .load_yaml( "unit-test", b" - name: Reptilicus names: - Reptilicus - CyberNanny - Vkur type: stalkerware packages: - com.brot.storage.work - com.cycle.start.mess - com.thecybernanny.andapp - net.androidcoreapp.androidbackup - net.delphiboardlayer.androidcoreapp - net.reptilicus.clientapp - net.system_updater_abs341 - net.vkurhandler - se.vkur.clientapp - yc.sysupd.client certificates: - 230E35A26E471352DF5DBDBCF9834E0711500CB0 - 2C08279BCC8EB16B2B31ACFBD7E1D4BB28E49A87 - 2FD8BEF4081F126D4DA655B40E9FC63F116DD857 - 9256E291823DA741B64CB23F7E371D0940E5272E - 9BD494107EFED96F630D29D6E18AE4DCC47149E2 - 6D0FF787BF4534F1077D1E4BF2E18BA381D97061 - D3A7E0E542A3E1112741806AC31F341C4200FBA1 - B61326887306E5A65726AE6BFD1D720D2760CEFF websites: - reptilicus.net - thecybernanny.com c2: ips: - 176.9.42.16 domains: - cabinet.ecohouse-eg.com - cabinet.gps-monitor.uz - cabinet.kfnm.ru - cabinet.vegosm.ru - cabinet.vkur.se - cabinet.vkur1.se - data.reptilicus.net - e2c64.firebaseio.com - mob.eurotrans.kz - phonecontrolapp-e2c64.firebaseio.com - proxy.reptilicus.net - reptilicus.net - rp.apollospy.com - vkur1.se - www.reptilicus.net - name: Snoopza names: - Snoopza type: stalkerware packages: - com.android.core.mngi - com.android.core.mngj - com.android.core.mngk - com.android.core.mngl - com.android.core.mngn - com.android.core.mngo - com.android.core.mngp certificates: [] websites: - snoopza.com c2: ips: - 217.182.250.165 - 46.105.57.148 domains: - snoopza.com - my.snoopza.com - my2.snoopza.com - api.snoopza.com ", ) .unwrap(); assert_eq!( rules.map, hashmap![ "com.brot.storage.work".to_string() => "Reptilicus".to_string(), "com.cycle.start.mess".to_string() => "Reptilicus".to_string(), "com.thecybernanny.andapp".to_string() => "Reptilicus".to_string(), "net.androidcoreapp.androidbackup".to_string() => "Reptilicus".to_string(), "net.delphiboardlayer.androidcoreapp".to_string() => "Reptilicus".to_string(), "net.reptilicus.clientapp".to_string() => "Reptilicus".to_string(), "net.system_updater_abs341".to_string() => "Reptilicus".to_string(), "net.vkurhandler".to_string() => "Reptilicus".to_string(), "se.vkur.clientapp".to_string() => "Reptilicus".to_string(), "yc.sysupd.client".to_string() => "Reptilicus".to_string(), "com.android.core.mngi".to_string() => "Snoopza".to_string(), "com.android.core.mngj".to_string() => "Snoopza".to_string(), "com.android.core.mngk".to_string() => "Snoopza".to_string(), "com.android.core.mngl".to_string() => "Snoopza".to_string(), "com.android.core.mngn".to_string() => "Snoopza".to_string(), "com.android.core.mngo".to_string() => "Snoopza".to_string(), "com.android.core.mngp".to_string() => "Snoopza".to_string(), ] ); } } spytrap-adb-0.3.2/src/scan.rs000064400000000000000000000066331046102023000141450ustar 00000000000000use crate::accessibility; use crate::args; use crate::dumpsys; use crate::errors::*; use crate::ioc::{Suspicion, SuspicionLevel}; use crate::package; use crate::pm; use crate::remote_clock; use crate::rules::Rules; use crate::settings; use crate::tui::Message; use forensic_adb::Device; use tokio::sync::mpsc; pub enum ScanNotifier { Null, Channel(mpsc::Sender), } impl ScanNotifier { pub async fn sus(&mut self, sus: Suspicion) -> Result<()> { if let ScanNotifier::Channel(tx) = self { tx.send(Message::Suspicion(sus)).await?; } Ok(()) } pub async fn app(&mut self, name: String, sus: Suspicion) -> Result<()> { if let ScanNotifier::Channel(tx) = self { tx.send(Message::App { name, sus }).await?; } Ok(()) } } pub struct Settings { pub skip_apps: bool, } impl From<&args::Scan> for Settings { fn from(args: &args::Scan) -> Settings { Settings { skip_apps: args.skip_apps, } } } pub async fn run( device: &Device, rules: &Rules, scan: &Settings, report: &mut ScanNotifier, ) -> Result<()> { debug!("Using device: {:?}", device); info!("Fetching remote clock"); let (local_time, remote_time, drift) = remote_clock::determine(device).await?; info!( "Local time is {}, remote time is {}, drift={:#}", local_time, remote_time, drift ); info!("Enumerating android settings"); for (_namespace, settings) in settings::dump(device).await? { for sus in settings.audit() { warn!("Suspicious {:?}: {}", sus.level, sus.description); report.sus(sus).await?; } } info!("Enumerating service list"); let services = dumpsys::list_services(device).await?; if services.contains("accessibility") { info!("Reading accessibility settings"); let accessibility = accessibility::dump(device).await?; for sus in accessibility.audit() { warn!("Suspicious {:?}: {}", sus.level, sus.description); report.sus(sus).await?; } } if !scan.skip_apps { // TODO: maybe `cmd package list packages -f` info!("Comparing list of installed apps with known stalkerware ids"); let installed_apps = pm::list_packages(device).await?; let mut progress = 0; for apps in installed_apps.chunks(100) { info!( "Scanning installed apps ({}/{})", progress, installed_apps.len() ); for pkg in apps { progress += 1; // TODO: maybe fetch apk and inspect eg. cert if let Some(name) = rules.get(&pkg.id) { let alert = format!( "Found known stalkerware with rule: {:?} ({:?})", pkg.id, name ); warn!("Suspicious {:?}: {}", SuspicionLevel::High, alert); } // fetch infos about package let info = package::dump(device, &pkg.id).await?; trace!("package infos {:?}: {:#?}", pkg.id, info); for sus in info.audit() { warn!("Suspicious {:?}: {}", sus.level, sus.description); report.app(pkg.id.clone(), sus).await?; } } } } info!("Scan finished"); Ok(()) } spytrap-adb-0.3.2/src/settings.rs000064400000000000000000000067331046102023000150620ustar 00000000000000use crate::errors::*; use crate::ioc::{Suspicion, SuspicionLevel}; use forensic_adb::Device; use std::collections::HashMap; const SETTINGS: &[(&str, &[&str])] = &[( "global", &[ "package_verifier_enable", "package_verifier_user_consent", "upload_apk_enable", ], )]; pub async fn dump(device: &Device) -> Result> { let mut out = HashMap::<_, Settings>::new(); for (namespace, keys) in SETTINGS { let settings = out.entry(namespace.to_string()).or_default(); for key in *keys { let cmd = format!("settings get {namespace} {key}"); debug!("Executing {:?}", cmd); let output = device .execute_host_exec_out_command(&cmd) .await .with_context(|| anyhow!("Failed to run: {:?}", cmd))?; let mut output = String::from_utf8_lossy(&output).into_owned(); if output.ends_with('\n') { output.pop(); } debug!( "Received setting for namespace={namespace:?} key={key:?} from device: {output:?}" ); if output != "null" { settings.insert(key.to_string(), output); } } } Ok(out) } #[derive(Debug, PartialEq, Default)] pub struct Settings { pub values: HashMap, } impl Settings { pub fn insert(&mut self, key: String, value: String) { self.values.insert(key, value); } pub fn audit(&self) -> Vec { let mut sus = Vec::new(); for (key, value) in &self.values { match key.as_str() { "package_verifier_enable" => { if value != "1" { warn!("Google Play Protect is turned off"); sus.push(Suspicion { level: SuspicionLevel::High, description: "Google Play Protect is turned off".to_string(), }); } } "package_verifier_user_consent" => { if value == "1" { info!("Scanning apps with Google Play Protect is enabled"); sus.push(Suspicion { level: SuspicionLevel::Good, description: "Scanning apps with Google Play Protect is enabled" .to_string(), }); } else { warn!("Scanning apps with Google Play Protect is disabled"); sus.push(Suspicion { level: SuspicionLevel::High, description: "Scanning apps with Google Play Protect is disabled" .to_string(), }); } } "upload_apk_enable" => { if value != "1" { warn!( "Automatic upload of suspicious apps to Google Play has been disabled" ); sus.push(Suspicion { level: SuspicionLevel::High, description: "Automatic upload of suspicious apps to Google Play has been disabled".to_string(), }); } } _ => (), } } sus } } spytrap-adb-0.3.2/src/tui.rs000064400000000000000000000755011046102023000140220ustar 00000000000000use crate::errors::*; use crate::ioc::{Repository, RepositoryContent, Suspicion, SuspicionLevel}; use crate::scan; use crate::utils; use crossterm::event::EventStream; use crossterm::event::{KeyEvent, KeyModifiers}; use crossterm::{ event::{Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use forensic_adb::{AndroidStorageInput, DeviceInfo, Host}; use indexmap::IndexMap; use ratatui::{ backend::{Backend, CrosstermBackend}, layout::{Alignment, Constraint, Direction, Layout}, style::{Color, Modifier, Style}, text::{Line, Span, Text}, widgets::{Block, Borders, List, ListItem, Paragraph}, Frame, Terminal, }; use std::cmp::Ordering; use std::collections::BTreeSet; use std::convert::Infallible; use std::io; use std::io::Stdout; use std::iter::Chain; use std::slice::Iter; use std::time::Duration; use tokio::sync::mpsc; use tokio::task::JoinSet; use tokio::time; use tokio_stream::StreamExt; const DARK_GREY: Color = Color::Rgb(0x3b, 0x3b, 0x3b); /// Number of items to navigate with page up/down keys const PAGE_MODIFIER: usize = 10; /// The number of lines used by the spytrap-adb UI around the scroll view const SCROLL_CHROME_HEIGHT: usize = 6; const DEVICE_REFRESH_INTERVAL: Duration = Duration::from_secs(1); const ACTIVITY_TICK_INTERVAL: Duration = Duration::from_millis(100); const ACTIVITY: &[&str] = &[".", "o", "O", "°", " ", " ", "°", "O", "o", ".", " ", " "]; /// Check for updates if this many seconds elapsed since last successful update check const DATABASE_UPDATE_CHECK_INTERVAL: i64 = 60 * 60 * 3; #[derive(Debug)] pub enum Message { Suspicion(Suspicion), App { name: String, sus: Suspicion }, StartDownload, ScanTick, ScanEnded, DownloadTick, DownloadEnded(Option), DeviceRefreshTick, DatabaseUpdateAvailable(bool), } #[derive(Debug)] pub enum TimerCmd { Start(Duration), Stop, } impl TimerCmd { pub async fn recv(rx: &mut mpsc::Receiver) -> Result { let cmd = rx.recv().await.context("Channel has closed")?; Ok(cmd) } } #[derive(Debug, PartialEq, Default)] pub struct SavedCursor { offset: usize, cursor: usize, interval: Option, } pub struct App { adb_host: Host, repository: Repository, events_tx: mpsc::Sender, events_rx: mpsc::Receiver, timer_tx: mpsc::Sender, timer_rx: Option>, current_timer: Option, devices: Vec, offset: usize, cursor: usize, /// the previous cursor positions before switching into a different scroll-view cursor_backtrace: Vec, scan: Option, download: Option, } impl App { pub fn new(adb_host: Host, repository: Repository) -> Self { let (events_tx, events_rx) = mpsc::channel(5); let (timer_tx, timer_rx) = mpsc::channel(5); Self { adb_host, repository, events_tx, events_rx, timer_tx, timer_rx: Some(timer_rx), current_timer: None, devices: Vec::new(), offset: 0, cursor: 0, cursor_backtrace: vec![], scan: None, download: None, } } pub async fn init(&mut self) -> Result<()> { let devices = self .adb_host .devices::>() .await .map_err(|e| anyhow!("Failed to list devices from adb: {}", e))?; self.devices = devices; self.start_timer(DEVICE_REFRESH_INTERVAL).await?; if let Some(content) = &self.repository.content { if !content.update_available && utils::now() > content.last_update_check + DATABASE_UPDATE_CHECK_INTERVAL { debug!("Haven't checked for database updates in a while, checking now..."); let tx = self.events_tx.clone(); let repo = self.repository.clone(); let content = content.clone(); tokio::spawn(async move { if let Err(err) = Self::run_update_availability_check(&repo, &content, tx).await { warn!("Failed to check for updates: {err:#}"); } }); } } else { debug!("No existing database present, starting download..."); self.events_tx.send(Message::StartDownload).await.ok(); } Ok(()) } async fn run_update_availability_check( repo: &Repository, content: &RepositoryContent, tx: mpsc::Sender, ) -> Result<()> { let branch = repo.query_latest_branch().await?; let update_available = content.git_commit != branch.sha; tx.send(Message::DatabaseUpdateAvailable(update_available)) .await .ok(); Ok(()) } async fn start_timer(&mut self, interval: Duration) -> Result<()> { self.timer_tx.send(TimerCmd::Start(interval)).await?; self.current_timer = Some(interval); Ok(()) } async fn stop_timer(&mut self) -> Result<()> { self.timer_tx.send(TimerCmd::Stop).await?; self.current_timer = None; Ok(()) } /// The number of visible lines in the current active view pub fn view_length(&self) -> usize { if let Some(scan) = &self.scan { scan.findings.len() + scan.apps.len() + scan .apps .iter() .map(|(name, values)| { if scan.expanded.contains(name) { values.len() } else { 0 } }) .sum::() } else { self.devices.len() } } pub async fn save_cursor(&mut self) -> Result<()> { self.cursor_backtrace.push(SavedCursor { offset: self.offset, cursor: self.cursor, interval: self.current_timer, }); self.offset = 0; self.cursor = 0; self.stop_timer().await?; Ok(()) } pub fn key_up(&mut self) { self.cursor = self.cursor.saturating_sub(1); if self.cursor < self.offset { self.offset = self.cursor; } } pub fn key_down(&mut self, terminal: &Terminal) -> Result<()> { let max = self.view_length().saturating_sub(1); if self.cursor < max { self.cursor += 1; self.recalculate_scroll_offset(terminal)?; } Ok(()) } pub fn recalculate_scroll_offset(&mut self, terminal: &Terminal) -> Result<()> { let scroll_height = terminal.size()?.height as usize - SCROLL_CHROME_HEIGHT; if self.cursor - self.offset > scroll_height { self.offset = self.cursor - scroll_height; } Ok(()) } pub async fn refresh_devices(&mut self) -> Result<()> { let devices = self .adb_host .devices::>() .await .map_err(|e| anyhow!("Failed to list devices from adb: {}", e))?; self.devices = devices; if self.devices.get(self.cursor).is_none() { self.cursor = match self.devices.len() { 0 => 0, n => n - 1, }; } Ok(()) } } #[derive(Debug, Default)] pub struct Spinner { idx: usize, } impl Spinner { pub fn activity_tick(&mut self) { self.idx += 1; self.idx %= ACTIVITY.len(); } pub fn render(&self) -> Span { Span::styled( ACTIVITY[self.idx], Style::default() .fg(Color::LightGreen) .add_modifier(Modifier::BOLD), ) } } #[derive(Debug, Default)] pub struct Download { spinner: Spinner, cancel: Option>, } #[derive(Debug, Default)] pub struct Scan { findings: Vec, apps: IndexMap, expanded: BTreeSet, spinner: Spinner, cancel: Option>, } #[derive(Debug, PartialEq, Eq, Default)] pub struct AppInfos { high: Vec, medium: Vec, low: Vec, } impl AppInfos { pub fn push(&mut self, item: Suspicion) { match item.level { SuspicionLevel::High => self.high.push(item), SuspicionLevel::Medium => self.medium.push(item), SuspicionLevel::Low => self.low.push(item), SuspicionLevel::Good => (), } } pub fn is_empty(&self) -> bool { self.high.is_empty() && self.medium.is_empty() && self.low.is_empty() } pub fn len(&self) -> usize { self.high.len() + self.medium.len() + self.low.len() } pub fn iter( &self, ) -> Chain, Iter<'_, Suspicion>>, Iter<'_, Suspicion>> { self.high .iter() .chain(self.medium.iter()) .chain(self.low.iter()) } } impl Ord for AppInfos { fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal .then(self.high.len().cmp(&other.high.len())) .then(self.medium.len().cmp(&other.medium.len())) .then(self.low.len().cmp(&other.low.len())) } } impl PartialOrd for AppInfos { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } pub enum Action { Shutdown, Clear, } pub async fn run_scan( adb_host: Host, repo: Repository, device: DeviceInfo, events_tx: mpsc::Sender, ) -> Result<()> { let device = adb_host .clone() .device_or_default(Some(&device.serial), AndroidStorageInput::Auto) .await .with_context(|| anyhow!("Failed to access device: {:?}", device.serial))?; let rules = repo.parse_rules().context("Failed to load rules")?; scan::run( &device, &rules, &scan::Settings { skip_apps: false }, &mut scan::ScanNotifier::Channel(events_tx), ) .await?; Ok(()) } pub async fn run_download(mut repo: Repository) -> Result { repo.download_ioc_db() .await .context("Failed to download stalkerware-indicators yaml files")?; Ok(repo) } pub async fn handle_key( terminal: &Terminal, app: &mut App, event: Event, ) -> Result> { match event { Event::Key(KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::NONE, .. }) | Event::Key(KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. }) | Event::Key(KeyEvent { code: KeyCode::Char('q'), modifiers: KeyModifiers::NONE, .. }) => { if let Some(tx) = app.download.take() { drop(tx); } else if let Some(tx) = app.scan.as_mut().and_then(|s| s.cancel.take()) { drop(tx); } else if app.scan.take().is_none() { println!("Exiting..."); return Ok(Some(Action::Shutdown)); } else { let saved = app.cursor_backtrace.pop().unwrap_or_default(); app.offset = saved.offset; app.cursor = saved.cursor; if let Some(interval) = saved.interval { app.start_timer(interval).await?; } else { app.stop_timer().await?; } } } Event::Key(KeyEvent { code: KeyCode::Char('Q'), modifiers: KeyModifiers::SHIFT, .. }) => { println!("Exiting..."); return Ok(Some(Action::Shutdown)); } Event::Key(KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, .. }) => { if let Some(scan) = &mut app.scan { if let Some(mut idx) = app.cursor.checked_sub(scan.findings.len()) { let mut offset = 0; for (i, (name, appinfos)) in scan.apps.iter().enumerate() { let height = if scan.expanded.contains(name) { appinfos.len() + 1 } else { 1 }; if offset + height > idx { idx = i; app.cursor = offset + scan.findings.len(); break; } else { offset += height; } } // if there is an item under the cursor if let Some((name, _appinfos)) = scan.apps.get_index(idx) { // toggle the app on the `expanded` list if !scan.expanded.remove(name) { scan.expanded.insert(name.clone()); } } } } else if let Some(device) = app.devices.get(app.cursor) { let adb_host = app.adb_host.clone(); let repo = app.repository.clone(); let device = device.clone(); let events_tx = app.events_tx.clone(); let (cancel_tx, mut cancel_rx) = mpsc::channel(1); tokio::spawn(async move { let mut interval = time::interval(ACTIVITY_TICK_INTERVAL); let scan = run_scan(adb_host, repo, device, events_tx.clone()); tokio::pin!(scan); loop { tokio::select! { _ = cancel_rx.recv() => { debug!("Scan has been canceled"); events_tx.send(Message::ScanEnded).await.ok(); break; } ret = &mut scan => { debug!("Scan has completed: {:?}", ret); // TODO print errors in UI events_tx.send(Message::ScanEnded).await.ok(); break; } _ = interval.tick() => { events_tx.send(Message::ScanTick).await.ok(); } } } }); app.scan = Some(Scan { cancel: Some(cancel_tx), ..Default::default() }); app.save_cursor().await?; } } Event::Key(KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::NONE, .. }) => { app.key_up(); } Event::Key(KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::NONE, .. }) => { app.key_down(terminal)?; } Event::Key(KeyEvent { code: KeyCode::PageUp, modifiers: KeyModifiers::NONE, .. }) => { for _ in 0..PAGE_MODIFIER { app.key_up(); } } Event::Key(KeyEvent { code: KeyCode::PageDown, modifiers: KeyModifiers::NONE, .. }) => { for _ in 0..PAGE_MODIFIER { app.key_down(terminal)?; } } Event::Key(KeyEvent { code: KeyCode::Home, modifiers: KeyModifiers::NONE, .. }) => { app.offset = 0; app.cursor = 0; } Event::Key(KeyEvent { code: KeyCode::End, modifiers: KeyModifiers::NONE, .. }) => { let max = app.view_length().saturating_sub(1); app.cursor = max; app.recalculate_scroll_offset(terminal)?; } Event::Key(KeyEvent { code: KeyCode::Char('r'), modifiers: KeyModifiers::CONTROL, .. }) => { app.events_tx.send(Message::StartDownload).await.ok(); } Event::Key(KeyEvent { code: KeyCode::Char('l'), modifiers: KeyModifiers::CONTROL, .. }) => { return Ok(Some(Action::Clear)); } Event::Resize(_columns, _rows) => { app.recalculate_scroll_offset(terminal)?; } _ => (), } Ok(None) } async fn run_timer(mut rx: mpsc::Receiver, tx: mpsc::Sender) -> Result<()> { let mut next_interval = None; loop { let interval = if let Some(interval) = next_interval.take() { interval } else { match TimerCmd::recv(&mut rx).await? { TimerCmd::Start(time) => time, TimerCmd::Stop => continue, } }; let mut timer = time::interval(interval); loop { tokio::select! { cmd = TimerCmd::recv(&mut rx) => { match cmd? { TimerCmd::Start(time) => { next_interval = Some(time); break; } TimerCmd::Stop => break, } } _ = timer.tick() => { tx.send(Message::DeviceRefreshTick).await?; } } } } } pub async fn run(terminal: &mut Terminal, app: &mut App) -> Result<()> { let mut stream = EventStream::new(); let mut tasks = JoinSet::new(); let timer_rx = app.timer_rx.take().context("Timer already started")?; tasks.spawn(run_timer(timer_rx, app.events_tx.clone())); loop { terminal.draw(|f| ui(f, app))?; tokio::select! { event = stream.next() => { let Some(event) = event else { break }; let event = event.context("Failed to read terminal input")?; match handle_key(terminal, app, event).await? { Some(Action::Shutdown) => break, Some(Action::Clear) => { terminal.clear()?; }, None => (), } } event = app.events_rx.recv() => { let Some(event) = event else { break }; debug!("Received message from channel: event={event:?}"); match event { Message::Suspicion(sus) => { if let Some(scan) = &mut app.scan { scan.findings.push(sus); scan.findings.sort_by(|a, b| { a.level.cmp(&b.level) .reverse() .then(a.description.cmp(&b.description)) }); } } Message::App { name, sus } => { if let Some(scan) = &mut app.scan { scan.apps.entry(name).or_default().push(sus); scan.apps.sort_by(|k1, v1, k2, v2| { v1.cmp(v2) .reverse() .then(k1.cmp(k2)) }); } } Message::StartDownload => { let events_tx = app.events_tx.clone(); let repo = app.repository.clone(); let (cancel_tx, mut cancel_rx) = mpsc::channel(1); tokio::spawn(async move { let mut interval = time::interval(ACTIVITY_TICK_INTERVAL); let download = run_download(repo); tokio::pin!(download); loop { tokio::select! { _ = cancel_rx.recv() => { debug!("Download has been canceled"); events_tx.send(Message::DownloadEnded(None)).await.ok(); break; } ret = &mut download => { let repo = match ret { Ok(repo) => { debug!("Download has completed"); Some(repo) } Err(err) => { error!("Download has failed: {err:?}"); // TODO print errors in UI None } }; events_tx.send(Message::DownloadEnded(repo)).await.ok(); break; } _ = interval.tick() => { events_tx.send(Message::DownloadTick).await.ok(); } } } }); app.download = Some(Download { cancel: Some(cancel_tx), ..Default::default() }); } Message::ScanTick => { if let Some(scan) = &mut app.scan { scan.spinner.activity_tick(); } } Message::ScanEnded => { if let Some(scan) = &mut app.scan { scan.cancel.take(); } } Message::DownloadTick => { if let Some(download) = &mut app.download { download.spinner.activity_tick(); } } Message::DownloadEnded(repo) => { app.download.take(); if let Some(repo) = repo { app.repository = repo; } } Message::DeviceRefreshTick => { app.refresh_devices().await?; } Message::DatabaseUpdateAvailable(update_available) => { if let Some(content) = &mut app.repository.content { content.last_update_check = utils::now(); content.update_available = update_available; app.repository.write_database_file() .await .context("Failed to write database file")?; } } } } res = tasks.join_next() => { bail!("Task has crashed: {res:?}"); } } } Ok(()) } fn cursor<'a, T: IntoIterator>>(msg: T, selected: bool) -> (Line<'a>, Style) { let mut style = Style::default(); if selected { style = style.bg(DARK_GREY); } let mut row = vec![Span::styled( if selected { " > " } else { " " }, Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), )]; row.extend(msg); (Line::from(row), style) } pub fn ui(f: &mut Frame<'_>, app: &App) { let white = Style::default().fg(Color::White).bg(Color::Black); let chunks = Layout::default() .direction(Direction::Vertical) .constraints( [ Constraint::Length(1), Constraint::Length(1), Constraint::Min(1), Constraint::Length(1), ] .as_ref(), ) .split(f.size()); f.render_widget(render_help_widget(app), chunks[0]); f.render_widget(Block::default().style(white), chunks[1]); f.render_widget(render_app_widget(app), chunks[2]); f.render_widget(render_statusline_widget(app), chunks[3]); } fn render_help_widget(app: &App) -> Paragraph { let white = Style::default().fg(Color::White).bg(Color::Black); let mut text = Vec::new(); if let Some(scan) = &app.scan { if scan.cancel.is_some() { text.push(scan.spinner.render()); text.push(Span::raw(" scanning - ")); } } if let Some(download) = &app.download { if download.cancel.is_some() { text.push(download.spinner.render()); text.push(Span::raw(" downloading - ")); } } if text.is_empty() { text.push(Span::raw("idle - ")); } text.extend([ Span::raw("Press "), Span::styled("ESC", Style::default().add_modifier(Modifier::BOLD)), Span::raw(" to exit - "), Span::raw(concat!( env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION") )), ]); let text = Text::from(Line::from(text)); Paragraph::new(text) .style(white) .alignment(Alignment::Right) } fn render_app_widget(app: &App) -> List { let white = Style::default().fg(Color::White).bg(Color::Black); if let Some(scan) = &app.scan { let mut list = Vec::new(); let mut i = 0; for sus in &scan.findings { let selected = i == app.cursor; let row = sus.to_terminal(); let (content, style) = cursor(row, selected); list.push(ListItem::new(content).style(style)); i += 1; } for (name, findings) in &scan.apps { let selected = i == app.cursor; let is_expanded = scan.expanded.contains(name); let mut row = Vec::new(); row.push(Span::styled( if is_expanded { "[-]" } else { "[+]" }, Style::default().add_modifier(Modifier::BOLD), )); row.push(Span::raw(format!(" App {name:?} ("))); let mut details = Vec::new(); if !findings.high.is_empty() { details.push(Span::styled( format!("{} high", findings.high.len()), SuspicionLevel::High.terminal_color(), )); } if !findings.medium.is_empty() { details.push(Span::styled( format!("{} medium", findings.medium.len()), SuspicionLevel::Medium.terminal_color(), )); } if !findings.low.is_empty() { details.push(Span::styled( format!("{} low", findings.low.len()), SuspicionLevel::Low.terminal_color(), )); } for (i, value) in details.into_iter().enumerate() { if i > 0 { row.push(Span::raw(", ")); } row.push(value); } row.push(Span::raw(")")); let (content, style) = cursor(row, selected); list.push(ListItem::new(content).style(style)); i += 1; // show app details if expanded if is_expanded { for sus in findings.iter() { let selected = i == app.cursor; let mut row = vec![Span::raw(" ")]; row.extend(sus.to_terminal()); let (content, style) = cursor(row, selected); list.push(ListItem::new(content).style(style)); i += 1; } } } // scrolling let list = list.into_iter().skip(app.offset); let title = Span::styled("Findings", white.add_modifier(Modifier::BOLD)); List::new(list).block( Block::default() .borders(Borders::ALL) .style(white) .border_style(Style::default().fg(Color::Green)) .title(title), ) } else { let devices: Vec = app .devices .iter() .enumerate() .map(|(i, device)| { let selected = i == app.cursor; let msg = format!( "{:30} device={:?}, model={:?}, product={:?}", device.serial, utils::human_option_str(device.info.get("device")), utils::human_option_str(device.info.get("model")), utils::human_option_str(device.info.get("product")), ); let (content, style) = cursor([Span::raw(msg)], selected); ListItem::new(content).style(style) }) .collect(); let title = Span::styled("Connected devices", white.add_modifier(Modifier::BOLD)); List::new(devices).block( Block::default() .borders(Borders::ALL) .style(white) .border_style(Style::default().fg(Color::Green)) .title(title), ) } } fn render_statusline_widget(app: &App) -> Paragraph { let white = Style::default().fg(Color::White).bg(Color::Black); let green = Style::default().fg(Color::Green).bg(Color::Black); let yellow = Style::default().fg(Color::Yellow).bg(Color::Black); let mut text = Vec::new(); if let Some(content) = &app.repository.content { text.push(Span::raw("ioc-git:")); text.push(Span::styled(&content.git_commit, green)); text.push(Span::raw(" released:")); text.push(Span::styled( utils::format_datetime(content.released), green, )); if content.update_available { text.push(Span::styled( " (database update available, press ctrl+R)", yellow, )); } } let text = Text::from(Line::from(text)); Paragraph::new(text) .style(white) .alignment(Alignment::Right) } pub fn setup() -> Result>> { enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen)?; let backend = CrosstermBackend::new(stdout); let terminal = Terminal::new(backend)?; Ok(terminal) } pub fn cleanup(terminal: &mut Terminal>) -> Result<()> { disable_raw_mode()?; execute!(terminal.backend_mut(), LeaveAlternateScreen,)?; terminal.show_cursor()?; Ok(()) } spytrap-adb-0.3.2/src/utils.rs000064400000000000000000000011031046102023000143440ustar 00000000000000use chrono::{offset::Utc, TimeZone}; use sha2::{Digest, Sha256}; pub fn human_option_str(x: Option<&String>) -> &str { if let Some(x) = x { x.as_str() } else { "-" } } pub fn now() -> i64 { let now = chrono::offset::Utc::now(); now.timestamp() } pub fn format_datetime(timestamp: i64) -> String { let utc = Utc.timestamp_opt(timestamp, 0).unwrap(); utc.format("%Y-%m-%d %H:%M UTC").to_string() } pub fn sha256(buf: &[u8]) -> String { let mut sha256 = Sha256::new(); sha256.update(buf); hex::encode(sha256.finalize()) } spytrap-adb-0.3.2/test_data/dumpsys/accessibility/plain.txt000064400000000000000000000010151046102023000222200ustar 00000000000000ACCESSIBILITY MANAGER (dumpsys accessibility) currentUserId=0 User state[ attributes:{id=0, touchExplorationEnabled=false, serviceHandlesDoubleTap=false, requestMultiFingerGestures=false, requestTwoFingerPassthrough=false, displayMagnificationEnabled=false, autoclickEnabled=false, nonInteractiveUiTimeout=0, interactiveUiTimeout=0, installedServiceCount=0} shortcut key:{} button:{} button target:{null} Bound services:{} Enabled services:{} Binding services:{} Crashed services:{}] spytrap-adb-0.3.2/test_data/dumpsys/accessibility/plain2.txt000064400000000000000000000005611046102023000223070ustar 00000000000000ACCESSIBILITY MANAGER (dumpsys accessibility) User state[attributes:{id=0, currentUser=true, touchExplorationEnabled=false, displayMagnificationEnabled=false, navBarMagnificationEnabled=false, autoclickEnabled=false, nonInteractiveUiTimeout=0, interactiveUiTimeout=0, installedServiceCount=4} Bound services:{} Enabled services:{} Binding services:{}] spytrap-adb-0.3.2/test_data/dumpsys/accessibility/spylive360.txt000064400000000000000000000024731046102023000230520ustar 00000000000000ACCESSIBILITY MANAGER (dumpsys accessibility) currentUserId=0 User state[ attributes:{id=0, touchExplorationEnabled=false, serviceHandlesDoubleTap=false, requestMultiFingerGestures=false, requestTwoFingerPassthrough=false, displayMagnificationEnabled=false, autoclickEnabled=false, nonInteractiveUiTimeout=0, interactiveUiTimeout=0, installedServiceCount=1} shortcut key:{} button:{} button target:{null} Bound services:{Service[label=WiFi, feedbackType[FEEDBACK_SPOKEN, FEEDBACK_HAPTIC, FEEDBACK_AUDIBLE, FEEDBACK_VISUAL, FEEDBACK_GENERIC, FEEDBACK_BRAILLE], capabilities=1, eventTypes=TYPES_ALL_MASK, notificationTimeout=1000, requestA11yBtn=false]} Enabled services:{{com.wifi0/com.wifi0.AccessibilityReceiver4}} Binding services:{} Crashed services:{}] Display[0] : Window[AccessibilityWindowInfo[title=null, displayId=0, id=8, type=TYPE_SYSTEM, layer=1, region=SkRegion((0,0,1080,72)), bounds=Rect(0, 0 - 1080, 72), focused=false, active=false, pictureInPicture=false, hasParent=false, isAnchored=false, hasChildren=false]], Window[AccessibilityWindowInfo[title=WiFi, displayId=0, id=15, type=TYPE_APPLICATION, layer=0, region=SkRegion((0,0,1080,1920)), bounds=Rect(0, 0 - 1080, 1920), focused=true, active=true, pictureInPicture=false, hasParent=false, isAnchored=false, hasChildren=false]] spytrap-adb-0.3.2/test_data/dumpsys/package/contacts.txt000064400000000000000000001603571046102023000215160ustar 00000000000000Activity Resolver Table: Full MIME Types: vnd.android.cursor.dir/raw_contact: e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false vnd.android.cursor.item/person: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter 4af3d22 Action: "com.android.contacts.action.QUICK_CONTACT" Action: "android.provider.action.QUICK_CONTACT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" vnd.android.cursor.dir/postal-address: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" vnd.android.cursor.dir/calls: 3c5e734 com.android.contacts/.NonPhoneActivity filter c1ecfa3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" StaticType: "vnd.android.cursor.dir/calls" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false vnd.android.cursor.dir/group: 2516c0e com.android.contacts/.activities.PeopleActivity filter 3c433c3 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" vnd.android.cursor.dir/phone: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" vnd.android.cursor.item/postal-address: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" vnd.android.cursor.item/postal-address_v2: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" vnd.android.cursor.dir/email_v2: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" vnd.android.cursor.dir/person: 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false vnd.android.cursor.item/contact: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter 4af3d22 Action: "com.android.contacts.action.QUICK_CONTACT" Action: "android.provider.action.QUICK_CONTACT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" vnd.android.cursor.dir/postal-address_v2: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" text/x-vcard: 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" 873d846 com.android.contacts/.vcard.NfcImportVCardActivity filter 15c9a07 Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" StaticType: "text/vcard" StaticType: "text/x-vcard" text/directory: 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" vnd.android.cursor.item/phone_v2: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" text/vcard: 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" 873d846 com.android.contacts/.vcard.NfcImportVCardActivity filter 15c9a07 Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" StaticType: "text/vcard" StaticType: "text/x-vcard" vnd.android.cursor.item/group: 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" 2516c0e com.android.contacts/.activities.PeopleActivity filter 64f1372 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/group" vnd.android.cursor.item/phone: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" vnd.android.cursor.dir/contact: 2516c0e com.android.contacts/.activities.PeopleActivity filter a7dfc27 Action: "android.intent.action.SEARCH" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false vnd.android.cursor.item/raw_contact: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" vnd.android.cursor.dir/phone_v2: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" image/*: 24f4e7a com.android.contacts/.activities.AttachPhotoActivity filter 209c62b Action: "android.intent.action.ATTACH_DATA" Category: "android.intent.category.DEFAULT" StaticType: "image" mPriority=0, mOrder=0, mHasStaticPartialTypes=true, mHasDynamicPartialTypes=false Base MIME Types: vnd.android.cursor.dir: 2516c0e com.android.contacts/.activities.PeopleActivity filter a7dfc27 Action: "android.intent.action.SEARCH" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" 2516c0e com.android.contacts/.activities.PeopleActivity filter 3c433c3 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false 3c5e734 com.android.contacts/.NonPhoneActivity filter c1ecfa3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" StaticType: "vnd.android.cursor.dir/calls" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false vnd.android.cursor.item: 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" 2516c0e com.android.contacts/.activities.PeopleActivity filter 64f1372 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/group" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter 4af3d22 Action: "com.android.contacts.action.QUICK_CONTACT" Action: "android.provider.action.QUICK_CONTACT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter 4af3d22 Action: "com.android.contacts.action.QUICK_CONTACT" Action: "android.provider.action.QUICK_CONTACT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" text: 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" 873d846 com.android.contacts/.vcard.NfcImportVCardActivity filter 15c9a07 Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" StaticType: "text/vcard" StaticType: "text/x-vcard" 873d846 com.android.contacts/.vcard.NfcImportVCardActivity filter 15c9a07 Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" StaticType: "text/vcard" StaticType: "text/x-vcard" Wild MIME Types: image: 24f4e7a com.android.contacts/.activities.AttachPhotoActivity filter 209c62b Action: "android.intent.action.ATTACH_DATA" Category: "android.intent.category.DEFAULT" StaticType: "image" mPriority=0, mOrder=0, mHasStaticPartialTypes=true, mHasDynamicPartialTypes=false Schemes: mailto: 3c83d17 com.android.contacts/.activities.ShowOrCreateActivity filter 5a93504 Action: "com.android.contacts.action.SHOW_OR_CREATE_CONTACT" Category: "android.intent.category.DEFAULT" Scheme: "mailto" Scheme: "tel" tel: 3c83d17 com.android.contacts/.activities.ShowOrCreateActivity filter 5a93504 Action: "com.android.contacts.action.SHOW_OR_CREATE_CONTACT" Category: "android.intent.category.DEFAULT" Scheme: "mailto" Scheme: "tel" 3c5e734 com.android.contacts/.NonPhoneActivity filter 40532d2 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "tel" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false Non-Data Actions: com.android.contacts.action.LIST_CONTACTS_WITH_PHONES: 2516c0e com.android.contacts/.activities.PeopleActivity filter 43464b Action: "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" com.android.contacts.action.LIST_STREQUENT: 2516c0e com.android.contacts/.activities.PeopleActivity filter d2eee6 Action: "com.android.contacts.action.LIST_STREQUENT" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" android.intent.action.MAIN: 2516c0e com.android.contacts/.activities.PeopleActivity filter c6b762f Action: "android.intent.action.MAIN" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.LAUNCHER" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.APP_CONTACTS" 3c5e734 com.android.contacts/.NonPhoneActivity filter ded795d Action: "android.intent.action.MAIN" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false android.intent.action.VIEW: 77442a0 com.android.contacts/.dialog.CallSubjectDialog filter 8d4e59 Action: "android.intent.action.VIEW" com.android.contacts.action.LIST_ALL_CONTACTS: 2516c0e com.android.contacts/.activities.PeopleActivity filter ebc3b1a Action: "com.android.contacts.action.LIST_ALL_CONTACTS" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" com.android.contacts.action.LIST_DEFAULT: 2516c0e com.android.contacts/.activities.PeopleActivity filter f1dd43c Action: "com.android.contacts.action.LIST_DEFAULT" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" com.android.contacts.action.LIST_STARRED: 2516c0e com.android.contacts/.activities.PeopleActivity filter 511f828 Action: "com.android.contacts.action.LIST_STARRED" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" com.android.contacts.action.LIST_FREQUENT: 2516c0e com.android.contacts/.activities.PeopleActivity filter 9330941 Action: "com.android.contacts.action.LIST_FREQUENT" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" com.android.contacts.action.JOIN_CONTACT: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter 9cc46c Action: "com.android.contacts.action.JOIN_CONTACT" Category: "android.intent.category.DEFAULT" com.android.contacts.action.LIST_CONTACTS: 2516c0e com.android.contacts/.activities.PeopleActivity filter 8c5d5c5 Action: "com.android.contacts.action.LIST_CONTACTS" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.TAB" android.intent.action.CREATE_SHORTCUT: e20ba35 com.android.contacts/alias.MessageShortcut filter 6a99eca Action: "android.intent.action.CREATE_SHORTCUT" Category: "android.intent.category.DEFAULT" ed6583b com.android.contacts/alias.DialShortcut filter 8c7bb58 Action: "android.intent.action.CREATE_SHORTCUT" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.CAR_MODE" 62fc4b1 com.android.contacts/.ContactShortcut filter 22add96 Action: "android.intent.action.CREATE_SHORTCUT" Category: "android.intent.category.DEFAULT" android.intent.action.SEARCH: 2516c0e com.android.contacts/.activities.PeopleActivity filter 7a26ed4 Action: "android.intent.action.SEARCH" Category: "android.intent.category.DEFAULT" MIME Typed Actions: com.android.contacts.action.QUICK_CONTACT: c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter 4af3d22 Action: "com.android.contacts.action.QUICK_CONTACT" Action: "android.provider.action.QUICK_CONTACT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" android.intent.action.EDIT: 2516c0e com.android.contacts/.activities.PeopleActivity filter 64f1372 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/group" d90209c com.android.contacts/.activities.ContactEditorSpringBoardActivity filter 4f59aa5 Action: "android.intent.action.EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" android.intent.action.PICK: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cf6f4be Action: "android.intent.action.PICK" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/phone_v2" StaticType: "vnd.android.cursor.dir/phone" StaticType: "vnd.android.cursor.dir/postal-address_v2" StaticType: "vnd.android.cursor.dir/postal-address" StaticType: "vnd.android.cursor.dir/email_v2" StaticType: "vnd.android.cursor.dir/group" android.intent.action.VIEW: 2516c0e com.android.contacts/.activities.PeopleActivity filter 9e5f87d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.item/group" c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter d8993b3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" 8ccaa88 com.android.contacts/.vcard.ImportVCardActivity filter 84a3c21 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" StaticType: "text/directory" StaticType: "text/vcard" StaticType: "text/x-vcard" 3c5e734 com.android.contacts/.NonPhoneActivity filter c1ecfa3 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" StaticType: "vnd.android.cursor.dir/calls" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false android.provider.action.QUICK_CONTACT: c4afaed com.android.contacts/.quickcontact.QuickContactActivity filter 4af3d22 Action: "com.android.contacts.action.QUICK_CONTACT" Action: "android.provider.action.QUICK_CONTACT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" android.intent.action.GET_CONTENT: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter cac491f Action: "android.intent.action.GET_CONTENT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/phone_v2" StaticType: "vnd.android.cursor.item/phone" StaticType: "vnd.android.cursor.item/postal-address_v2" StaticType: "vnd.android.cursor.item/postal-address" android.intent.action.INSERT_OR_EDIT: e21a440 com.android.contacts/.activities.ContactSelectionActivity filter dde9f79 Action: "android.intent.action.INSERT_OR_EDIT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.item/person" StaticType: "vnd.android.cursor.item/contact" StaticType: "vnd.android.cursor.item/raw_contact" android.intent.action.INSERT: 2516c0e com.android.contacts/.activities.PeopleActivity filter 3c433c3 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/group" e069d70 com.android.contacts/.activities.ContactEditorActivity filter 16958e9 Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" 28096e com.android.contacts/.activities.CompactContactEditorActivity filter cffb80f Action: "android.intent.action.INSERT" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/person" StaticType: "vnd.android.cursor.dir/contact" StaticType: "vnd.android.cursor.dir/raw_contact" mPriority=-1, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false android.nfc.action.NDEF_DISCOVERED: 873d846 com.android.contacts/.vcard.NfcImportVCardActivity filter 15c9a07 Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" StaticType: "text/vcard" StaticType: "text/x-vcard" android.intent.action.ATTACH_DATA: 24f4e7a com.android.contacts/.activities.AttachPhotoActivity filter 209c62b Action: "android.intent.action.ATTACH_DATA" Category: "android.intent.category.DEFAULT" StaticType: "image" mPriority=0, mOrder=0, mHasStaticPartialTypes=true, mHasDynamicPartialTypes=false android.intent.action.SEARCH: 2516c0e com.android.contacts/.activities.PeopleActivity filter a7dfc27 Action: "android.intent.action.SEARCH" Category: "android.intent.category.DEFAULT" StaticType: "vnd.android.cursor.dir/contact" Receiver Resolver Table: Non-Data Actions: android.intent.action.BOOT_COMPLETED: bdfaa1e com.android.contacts/.interactions.OnBootOrUpgradeReceiver filter 544c2ff Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.MY_PACKAGE_REPLACED" android.intent.action.MY_PACKAGE_REPLACED: bdfaa1e com.android.contacts/.interactions.OnBootOrUpgradeReceiver filter 544c2ff Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.MY_PACKAGE_REPLACED" Service Resolver Table: Full MIME Types: vnd.android.cursor.item/contact: 1eae8cc com.android.contacts/.ViewNotificationService filter dbb7715 permission android.permission.WRITE_CONTACTS Action: "com.android.contacts.VIEW_NOTIFICATION" StaticType: "vnd.android.cursor.item/contact" Base MIME Types: vnd.android.cursor.item: 1eae8cc com.android.contacts/.ViewNotificationService filter dbb7715 permission android.permission.WRITE_CONTACTS Action: "com.android.contacts.VIEW_NOTIFICATION" StaticType: "vnd.android.cursor.item/contact" MIME Typed Actions: com.android.contacts.VIEW_NOTIFICATION: 1eae8cc com.android.contacts/.ViewNotificationService filter dbb7715 permission android.permission.WRITE_CONTACTS Action: "com.android.contacts.VIEW_NOTIFICATION" StaticType: "vnd.android.cursor.item/contact" Registered ContentProviders: com.android.contacts/androidx.core.content.FileProvider: Provider{df5c7f3 com.android.contacts/androidx.core.content.FileProvider} ContentProvider Authorities: [com.android.contacts.files]: Provider{df5c7f3 com.android.contacts/androidx.core.content.FileProvider} applicationInfo=ApplicationInfo{d845cb0 com.android.contacts} Key Set Manager: [com.android.contacts] Signing KeySets: 4 Packages: Package [com.android.contacts] (5b94729): userId=10118 pkg=Package{647faae com.android.contacts} codePath=/system/product/priv-app/Contacts resourcePath=/system/product/priv-app/Contacts legacyNativeLibraryDir=/system/product/priv-app/Contacts/lib primaryCpuAbi=null secondaryCpuAbi=null versionCode=10731 minSdk=26 targetSdk=29 versionName=1.7.31 splits=[base] apkSigningVersion=3 applicationInfo=ApplicationInfo{647faae com.android.contacts} flags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE PRIVILEGED PRODUCT PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ] forceQueryable=false queriesPackages=[] dataDir=/data/user/0/com.android.contacts supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity] usesLibraries: android.test.base usesLibraryFiles: /system/framework/android.test.base.jar timeStamp=2009-01-01 01:00:00 firstInstallTime=2009-01-01 01:00:00 lastUpdateTime=2009-01-01 01:00:00 signatures=PackageSignatures{b9af04f version:3, signatures:[ed46f865], past signatures:[]} installPermissionsFixed=true pkgFlags=[ SYSTEM HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] requested permissions: android.permission.READ_CONTACTS android.permission.WRITE_CONTACTS android.permission.GET_ACCOUNTS android.permission.GET_ACCOUNTS_PRIVILEGED android.permission.MANAGE_ACCOUNTS android.permission.ACCESS_NETWORK_STATE android.permission.CALL_PHONE android.permission.READ_PROFILE android.permission.WRITE_PROFILE android.permission.INTERNET android.permission.NFC android.permission.READ_PHONE_STATE android.permission.WAKE_LOCK android.permission.WRITE_SETTINGS android.permission.USE_CREDENTIALS android.permission.VIBRATE android.permission.READ_SYNC_SETTINGS android.permission.READ_EXTERNAL_STORAGE: restricted=true com.android.launcher.permission.INSTALL_SHORTCUT android.permission.WRITE_SYNC_SETTINGS android.permission.READ_SYNC_STATS android.permission.RECEIVE_BOOT_COMPLETED android.permission.FOREGROUND_SERVICE android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS install permissions: android.permission.WRITE_SETTINGS: granted=true android.permission.USE_CREDENTIALS: granted=true android.permission.MANAGE_ACCOUNTS: granted=true android.permission.NFC: granted=true android.permission.FOREGROUND_SERVICE: granted=true android.permission.WRITE_SYNC_SETTINGS: granted=true android.permission.RECEIVE_BOOT_COMPLETED: granted=true android.permission.READ_PROFILE: granted=true android.permission.INTERNET: granted=true android.permission.WRITE_PROFILE: granted=true android.permission.GET_ACCOUNTS_PRIVILEGED: granted=true android.permission.ACCESS_NETWORK_STATE: granted=true android.permission.READ_SYNC_STATS: granted=true android.permission.READ_SYNC_SETTINGS: granted=true android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS: granted=true android.permission.VIBRATE: granted=true com.android.launcher.permission.INSTALL_SHORTCUT: granted=true android.permission.WAKE_LOCK: granted=true User 0: ceDataInode=261398 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false gids=[3003] runtime permissions: android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_UPGRADE_EXEMPT] android.permission.READ_PHONE_STATE: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.CALL_PHONE: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.WRITE_CONTACTS: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.GET_ACCOUNTS: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.READ_CONTACTS: granted=true, flags=[ GRANTED_BY_DEFAULT|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] Queries: system apps queryable: false queries via package name: queries via intent: queryable via interaction: User 0: [lineageos.platform,org.lineageos.flipflap,com.android.location.fused,com.android.providers.settings,com.android.server.telecom,org.lineageos.settings.device,org.lineageos.lineageparts,org.lineageos.setupwizard,com.android.localtransport,android,org.lineageos.settings.doze,com.android.settings,com.android.keychain,com.android.wallpaperbackup,com.android.inputdevices,com.android.dynsystem,org.lineageos.lineagesettings]: com.android.contacts [com.android.providers.contacts,com.android.calllogbackup,com.android.providers.blockednumber,com.android.providers.userdictionary]: com.android.contacts Package Changes: Sequence number=6 User 0: seq=0, package=com.android.traceur seq=3, package=org.fdroid.fdroid seq=5, package=org.jitsi.meet Dexopt state: [com.android.contacts] path: /system/product/priv-app/Contacts/Contacts.apk arm64: [status=speed-profile] [reason=bg-dexopt] Compiler stats: [com.android.contacts] Contacts.apk - 1049 spytrap-adb-0.3.2/test_data/dumpsys/package/fdroid.txt000064400000000000000000000763501046102023000211460ustar 00000000000000Activity Resolver Table: Schemes: FDROIDREPOS: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 6f56c05 Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "fdroidrepo" Scheme: "FDROIDREPO" Scheme: "fdroidrepos" Scheme: "FDROIDREPOS" market: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter c0cf3e4 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "market" Authority: "details": -1 cc4209b org.fdroid.fdroid/.views.main.MainActivity filter fbcd449 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "market" Authority: "search": -1 fdroid.search: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter f976a50 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "fdroid.search" HTTP: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter b10db7c Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "http" Scheme: "HTTP" Scheme: "https" Scheme: "HTTPS" Authority: "": -1 WILD Path: "PatternMatcher{LITERAL: /fdroid/repo}" Path: "PatternMatcher{GLOB: /fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{LITERAL: /fdroid/archive}" Path: "PatternMatcher{GLOB: /fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{LITERAL: /FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/.*/FDROID/REPO}" amzn: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter e2b4702 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "amzn" Authority: "apps": -1 Path: "PatternMatcher{LITERAL: /android}" http: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 93a2b77 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Authority: "f-droid.org": -1 Authority: "www.f-droid.org": -1 Authority: "staging.f-droid.org": -1 Path: "PatternMatcher{PREFIX: /app/}" Path: "PatternMatcher{PREFIX: /packages/}" Path: "PatternMatcher{PREFIX: /repository/browse}" Path: "PatternMatcher{GLOB: /.*/packages/.*}" Path: "PatternMatcher{GLOB: /.*/packages/.*/}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter a2b804d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Scheme: "https" Authority: "play.google.com": -1 Path: "PatternMatcher{LITERAL: /store/apps/details}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 59c0813 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Scheme: "https" Authority: "amazon.com": -1 Authority: "www.amazon.com": -1 Path: "PatternMatcher{LITERAL: /gp/mas/dl/android}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 58ad14e Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Scheme: "https" Authority: "play.google.com": -1 Path: "PatternMatcher{LITERAL: /store/search}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter b10db7c Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "http" Scheme: "HTTP" Scheme: "https" Scheme: "HTTPS" Authority: "": -1 WILD Path: "PatternMatcher{LITERAL: /fdroid/repo}" Path: "PatternMatcher{GLOB: /fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{LITERAL: /fdroid/archive}" Path: "PatternMatcher{GLOB: /fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{LITERAL: /FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/.*/FDROID/REPO}" HTTPS: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter b10db7c Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "http" Scheme: "HTTP" Scheme: "https" Scheme: "HTTPS" Authority: "": -1 WILD Path: "PatternMatcher{LITERAL: /fdroid/repo}" Path: "PatternMatcher{GLOB: /fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{LITERAL: /fdroid/archive}" Path: "PatternMatcher{GLOB: /fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{LITERAL: /FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/.*/FDROID/REPO}" https: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 1500976 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "https" Authority: "f-droid.org": -1 Authority: "www.f-droid.org": -1 Authority: "staging.f-droid.org": -1 Path: "PatternMatcher{PREFIX: /app/}" Path: "PatternMatcher{PREFIX: /packages/}" Path: "PatternMatcher{PREFIX: /repository/browse}" Path: "PatternMatcher{GLOB: /.*/packages/.*}" Path: "PatternMatcher{GLOB: /.*/packages/.*/}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter a2b804d Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Scheme: "https" Authority: "play.google.com": -1 Path: "PatternMatcher{LITERAL: /store/apps/details}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 59c0813 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Scheme: "https" Authority: "amazon.com": -1 Authority: "www.amazon.com": -1 Path: "PatternMatcher{LITERAL: /gp/mas/dl/android}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 58ad14e Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "http" Scheme: "https" Authority: "play.google.com": -1 Path: "PatternMatcher{LITERAL: /store/search}" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter b10db7c Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "http" Scheme: "HTTP" Scheme: "https" Scheme: "HTTPS" Authority: "": -1 WILD Path: "PatternMatcher{LITERAL: /fdroid/repo}" Path: "PatternMatcher{GLOB: /fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/.*/.*/fdroid/repo/*}" Path: "PatternMatcher{LITERAL: /fdroid/archive}" Path: "PatternMatcher{GLOB: /fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive}" Path: "PatternMatcher{GLOB: /.*/.*/.*/.*/fdroid/archive/*}" Path: "PatternMatcher{LITERAL: /FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/FDROID/REPO}" Path: "PatternMatcher{GLOB: /.*/.*/.*/FDROID/REPO}" fdroid.app: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 400b411 Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Category: "android.intent.category.BROWSABLE" Scheme: "fdroid.app" fdroidrepo: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 6f56c05 Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "fdroidrepo" Scheme: "FDROIDREPO" Scheme: "fdroidrepos" Scheme: "FDROIDREPOS" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter e46b45a Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" Scheme: "fdroidrepo" Scheme: "fdroidrepos" fdroidrepos: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 6f56c05 Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "fdroidrepo" Scheme: "FDROIDREPO" Scheme: "fdroidrepos" Scheme: "FDROIDREPOS" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter e46b45a Action: "android.nfc.action.NDEF_DISCOVERED" Category: "android.intent.category.DEFAULT" Scheme: "fdroidrepo" Scheme: "fdroidrepos" FDROIDREPO: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 6f56c05 Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "fdroidrepo" Scheme: "FDROIDREPO" Scheme: "fdroidrepos" Scheme: "FDROIDREPOS" Non-Data Actions: info.guardianproject.panic.action.CONNECT: 24282d9 org.fdroid.fdroid/.panic.PanicPreferencesActivity filter 706049e Action: "info.guardianproject.panic.action.CONNECT" Action: "info.guardianproject.panic.action.DISCONNECT" Category: "android.intent.category.DEFAULT" android.intent.action.MAIN: acf7395 org.fdroid.fdroid/.panic.CalculatorActivity filter 9c9ccaa Action: "android.intent.action.MAIN" Category: "android.intent.category.LAUNCHER" cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 4534c38 Action: "android.intent.action.MAIN" Category: "android.intent.category.LAUNCHER" info.guardianproject.panic.action.DISCONNECT: 24282d9 org.fdroid.fdroid/.panic.PanicPreferencesActivity filter 706049e Action: "info.guardianproject.panic.action.CONNECT" Action: "info.guardianproject.panic.action.DISCONNECT" Category: "android.intent.category.DEFAULT" info.guardianproject.panic.action.TRIGGER: 32c4b7f org.fdroid.fdroid/.panic.PanicResponderActivity filter c17074c Action: "info.guardianproject.panic.action.TRIGGER" Category: "android.intent.category.DEFAULT" android.intent.action.SEARCH: cc4209b org.fdroid.fdroid/.views.main.MainActivity filter 835126f Action: "android.intent.action.SEARCH" Receiver Resolver Table: Schemes: package: 935755f org.fdroid.fdroid/.receiver.PackageManagerReceiver filter c001bac Action: "android.intent.action.PACKAGE_ADDED" Action: "android.intent.action.PACKAGE_CHANGED" Action: "android.intent.action.PACKAGE_REMOVED" Scheme: "package" file: 3671c03 org.fdroid.fdroid/.nearby.UsbDeviceMediaMountedReceiver filter ac20780 Action: "android.intent.action.MEDIA_EJECT" Action: "android.intent.action.MEDIA_REMOVED" Action: "android.intent.action.MEDIA_MOUNTED" Action: "android.intent.action.MEDIA_BAD_REMOVAL" Scheme: "content" Scheme: "file" content: 3671c03 org.fdroid.fdroid/.nearby.UsbDeviceMediaMountedReceiver filter ac20780 Action: "android.intent.action.MEDIA_EJECT" Action: "android.intent.action.MEDIA_REMOVED" Action: "android.intent.action.MEDIA_MOUNTED" Action: "android.intent.action.MEDIA_BAD_REMOVAL" Scheme: "content" Scheme: "file" Non-Data Actions: android.hardware.usb.action.USB_DEVICE_ATTACHED: 1d6e067 org.fdroid.fdroid/.nearby.UsbDeviceAttachedReceiver filter 7871e14 Action: "android.hardware.usb.action.USB_DEVICE_ATTACHED" android.intent.action.BATTERY_OKAY: a66087b org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy filter e294698 Action: "android.intent.action.BATTERY_OKAY" Action: "android.intent.action.BATTERY_LOW" android.intent.action.ACTION_POWER_DISCONNECTED: 7826075 org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy filter b64e80a Action: "android.intent.action.ACTION_POWER_CONNECTED" Action: "android.intent.action.ACTION_POWER_DISCONNECTED" android.hardware.usb.action.USB_DEVICE_DETACHED: 72a16bd org.fdroid.fdroid/.nearby.UsbDeviceDetachedReceiver filter 30774b2 Action: "android.hardware.usb.action.USB_DEVICE_DETACHED" android.intent.action.DEVICE_STORAGE_LOW: b65c381 org.fdroid.fdroid/.receiver.DeviceStorageReceiver filter 2abbc26 Action: "android.intent.action.DEVICE_STORAGE_LOW" ca58ef1 org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy filter 3717ad6 Action: "android.intent.action.DEVICE_STORAGE_LOW" Action: "android.intent.action.DEVICE_STORAGE_OK" android.net.conn.CONNECTIVITY_CHANGE: 242b157 org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy filter 8b83444 Action: "android.net.conn.CONNECTIVITY_CHANGE" android.intent.action.DEVICE_STORAGE_OK: ca58ef1 org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy filter 3717ad6 Action: "android.intent.action.DEVICE_STORAGE_LOW" Action: "android.intent.action.DEVICE_STORAGE_OK" android.net.wifi.STATE_CHANGE: 6aa668b org.fdroid.fdroid/.nearby.WifiStateChangeReceiver filter c4e3368 Action: "android.net.wifi.STATE_CHANGE" android.intent.action.BATTERY_LOW: a66087b org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy filter e294698 Action: "android.intent.action.BATTERY_OKAY" Action: "android.intent.action.BATTERY_LOW" android.intent.action.TIMEZONE_CHANGED: 6f3292d org.fdroid.fdroid/androidx.work.impl.background.systemalarm.RescheduleReceiver filter 1e26e62 Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.TIME_SET" Action: "android.intent.action.TIMEZONE_CHANGED" android.intent.action.TIME_SET: 6f3292d org.fdroid.fdroid/androidx.work.impl.background.systemalarm.RescheduleReceiver filter 1e26e62 Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.TIME_SET" Action: "android.intent.action.TIMEZONE_CHANGED" android.intent.action.BOOT_COMPLETED: fe661b9 org.fdroid.fdroid/.receiver.StartupReceiver filter 4f429fe Action: "android.intent.action.BOOT_COMPLETED" Category: "android.intent.category.HOME" 6f3292d org.fdroid.fdroid/androidx.work.impl.background.systemalarm.RescheduleReceiver filter 1e26e62 Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.TIME_SET" Action: "android.intent.action.TIMEZONE_CHANGED" android.intent.action.ACTION_POWER_CONNECTED: 7826075 org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy filter b64e80a Action: "android.intent.action.ACTION_POWER_CONNECTED" Action: "android.intent.action.ACTION_POWER_DISCONNECTED" androidx.work.diagnostics.REQUEST_DIAGNOSTICS: b062b29 org.fdroid.fdroid/androidx.work.impl.diagnostics.DiagnosticsReceiver filter d7d0eae Action: "androidx.work.diagnostics.REQUEST_DIAGNOSTICS" androidx.work.impl.background.systemalarm.UpdateProxies: cd70bf3 org.fdroid.fdroid/androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver filter e2650b0 Action: "androidx.work.impl.background.systemalarm.UpdateProxies" Registered ContentProviders: org.fdroid.fdroid/.data.TempApkProvider: Provider{133a3eb org.fdroid.fdroid/.data.TempApkProvider} org.fdroid.fdroid/.data.AppProvider: Provider{1b09548 org.fdroid.fdroid/.data.AppProvider} org.fdroid.fdroid/.data.ApkProvider: Provider{35bafe1 org.fdroid.fdroid/.data.ApkProvider} org.fdroid.fdroid/.installer.ApkFileProvider: Provider{6a6a106 org.fdroid.fdroid/.installer.ApkFileProvider} org.fdroid.fdroid/.data.RepoProvider: Provider{aa693c7 org.fdroid.fdroid/.data.RepoProvider} org.fdroid.fdroid/androidx.core.content.FileProvider: Provider{cddbdf4 org.fdroid.fdroid/androidx.core.content.FileProvider} org.fdroid.fdroid/.data.PackageIdProvider: Provider{902691d org.fdroid.fdroid/.data.PackageIdProvider} org.fdroid.fdroid/.data.AppPrefsProvider: Provider{b7bc792 org.fdroid.fdroid/.data.AppPrefsProvider} org.fdroid.fdroid/.data.CategoryProvider: Provider{76ea563 org.fdroid.fdroid/.data.CategoryProvider} org.fdroid.fdroid/.data.InstalledAppProvider: Provider{e12c560 org.fdroid.fdroid/.data.InstalledAppProvider} org.fdroid.fdroid/.data.TempAppProvider: Provider{e617a19 org.fdroid.fdroid/.data.TempAppProvider} ContentProvider Authorities: [org.fdroid.fdroid.data.TempApkProvider]: Provider{133a3eb org.fdroid.fdroid/.data.TempApkProvider} applicationInfo=ApplicationInfo{da0cf24 org.fdroid.fdroid} [org.fdroid.fdroid.data.AppProvider]: Provider{1b09548 org.fdroid.fdroid/.data.AppProvider} applicationInfo=ApplicationInfo{66d5a8d org.fdroid.fdroid} [org.fdroid.fdroid.data.ApkProvider]: Provider{35bafe1 org.fdroid.fdroid/.data.ApkProvider} applicationInfo=ApplicationInfo{434b442 org.fdroid.fdroid} [org.fdroid.fdroid.installer.ApkFileProvider]: Provider{6a6a106 org.fdroid.fdroid/.installer.ApkFileProvider} applicationInfo=ApplicationInfo{6690c53 org.fdroid.fdroid} [org.fdroid.fdroid.data.RepoProvider]: Provider{aa693c7 org.fdroid.fdroid/.data.RepoProvider} applicationInfo=ApplicationInfo{7adb990 org.fdroid.fdroid} [org.fdroid.fdroid.installer]: Provider{cddbdf4 org.fdroid.fdroid/androidx.core.content.FileProvider} applicationInfo=ApplicationInfo{8e21289 org.fdroid.fdroid} [org.fdroid.fdroid.data.PackageIdProvider]: Provider{902691d org.fdroid.fdroid/.data.PackageIdProvider} applicationInfo=ApplicationInfo{8ddd28e org.fdroid.fdroid} [org.fdroid.fdroid.data.AppPrefsProvider]: Provider{b7bc792 org.fdroid.fdroid/.data.AppPrefsProvider} applicationInfo=ApplicationInfo{46e1aaf org.fdroid.fdroid} [org.fdroid.fdroid.data.CategoryProvider]: Provider{76ea563 org.fdroid.fdroid/.data.CategoryProvider} applicationInfo=ApplicationInfo{15cdebc org.fdroid.fdroid} [org.fdroid.fdroid.data.InstalledAppProvider]: Provider{e12c560 org.fdroid.fdroid/.data.InstalledAppProvider} applicationInfo=ApplicationInfo{1d04e45 org.fdroid.fdroid} [org.fdroid.fdroid.data.TempAppProvider]: Provider{e617a19 org.fdroid.fdroid/.data.TempAppProvider} applicationInfo=ApplicationInfo{be8899a org.fdroid.fdroid} Key Set Manager: [org.fdroid.fdroid] Signing KeySets: 9 Packages: Package [org.fdroid.fdroid] (c34d38d): userId=10151 pkg=Package{7013942 org.fdroid.fdroid} codePath=/data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A== resourcePath=/data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A== legacyNativeLibraryDir=/data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A==/lib primaryCpuAbi=null secondaryCpuAbi=null versionCode=1013051 minSdk=22 targetSdk=25 versionName=1.13.1 splits=[base] apkSigningVersion=3 applicationInfo=ApplicationInfo{7013942 org.fdroid.fdroid} flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE HAS_DOMAIN_URLS PARTIALLY_DIRECT_BOOT_AWARE PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ] forceQueryable=false queriesPackages=[] dataDir=/data/user/0/org.fdroid.fdroid supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity] usesLibraries: android.test.base org.apache.http.legacy usesLibraryFiles: /system/framework/android.test.base.jar /system/framework/org.apache.http.legacy.jar timeStamp=2021-11-04 20:12:50 firstInstallTime=2021-11-04 20:12:50 lastUpdateTime=2021-11-04 20:12:50 installerPackageName=com.android.packageinstaller signatures=PackageSignatures{603ad53 version:3, signatures:[8b8a3ff5], past signatures:[]} installPermissionsFixed=true pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] requested permissions: android.permission.INTERNET android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_WIFI_STATE android.permission.CHANGE_WIFI_MULTICAST_STATE android.permission.CHANGE_NETWORK_STATE android.permission.CHANGE_WIFI_STATE android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN android.permission.RECEIVE_BOOT_COMPLETED android.permission.READ_EXTERNAL_STORAGE: restricted=true android.permission.WRITE_EXTERNAL_STORAGE: restricted=true android.permission.WRITE_SETTINGS android.permission.NFC android.permission.WAKE_LOCK android.permission.ACCESS_COARSE_LOCATION android.permission.FOREGROUND_SERVICE android.permission.ACCESS_BACKGROUND_LOCATION: restricted=true android.permission.ACCESS_MEDIA_LOCATION install permissions: android.permission.NFC: granted=true android.permission.CHANGE_NETWORK_STATE: granted=true android.permission.FOREGROUND_SERVICE: granted=true android.permission.RECEIVE_BOOT_COMPLETED: granted=true android.permission.BLUETOOTH: granted=true android.permission.CHANGE_WIFI_MULTICAST_STATE: granted=true android.permission.INTERNET: granted=true android.permission.BLUETOOTH_ADMIN: granted=true android.permission.CHANGE_WIFI_STATE: granted=true android.permission.ACCESS_NETWORK_STATE: granted=true android.permission.ACCESS_WIFI_STATE: granted=true android.permission.WAKE_LOCK: granted=true User 0: ceDataInode=392899 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false lastDisabledCaller: com.android.packageinstaller gids=[3002, 3003, 3001] runtime permissions: android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_COARSE_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_MEDIA_LOCATION: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] enabledComponents: androidx.work.impl.background.systemalarm.RescheduleReceiver androidx.work.impl.background.systemjob.SystemJobService Queries: system apps queryable: false queries via package name: queries via intent: org.lineageos.etar: org.fdroid.fdroid queryable via interaction: User 0: [lineageos.platform,org.lineageos.flipflap,com.android.location.fused,com.android.providers.settings,com.android.server.telecom,org.lineageos.settings.device,org.lineageos.lineageparts,org.lineageos.setupwizard,com.android.localtransport,android,org.lineageos.settings.doze,com.android.settings,com.android.keychain,com.android.wallpaperbackup,com.android.inputdevices,com.android.dynsystem,org.lineageos.lineagesettings]: org.fdroid.fdroid com.android.bluetooth: org.fdroid.fdroid com.android.packageinstaller: org.fdroid.fdroid com.android.inputmethod.latin: org.fdroid.fdroid org.fdroid.fdroid: [lineageos.platform,org.lineageos.flipflap,com.android.location.fused,com.android.providers.settings,com.android.server.telecom,org.lineageos.settings.device,org.lineageos.lineageparts,org.lineageos.setupwizard,com.android.localtransport,android,org.lineageos.settings.doze,com.android.settings,com.android.keychain,com.android.wallpaperbackup,com.android.inputdevices,com.android.dynsystem,org.lineageos.lineagesettings] com.android.packageinstaller Package Changes: Sequence number=6 User 0: seq=0, package=com.android.traceur seq=3, package=org.fdroid.fdroid seq=5, package=org.jitsi.meet Dexopt state: [org.fdroid.fdroid] path: /data/app/~~STVfdipYRYJs6KMukOgQrg==/org.fdroid.fdroid-v49NvZi77MbfUKeimvYw9A==/base.apk arm64: [status=speed-profile] [reason=bg-dexopt] Compiler stats: [org.fdroid.fdroid] base.apk - 5956 spytrap-adb-0.3.2/test_data/dumpsys/package/gpstest.txt000064400000000000000000000144241046102023000213620ustar 00000000000000Activity Resolver Table: Schemes: geo: eb4b26e com.android.gpstest.osmdroid/com.android.gpstest.GpsTestActivity filter 4eb719c Action: "android.intent.action.VIEW" Category: "android.intent.category.DEFAULT" Scheme: "geo" Non-Data Actions: android.intent.action.MAIN: eb4b26e com.android.gpstest.osmdroid/com.android.gpstest.GpsTestActivity filter 61ad0f Action: "android.intent.action.MAIN" Action: "com.google.android.radar.SHOW_RADAR" Category: "android.intent.category.LAUNCHER" Category: "android.intent.category.DEFAULT" f8517a5 com.android.gpstest.osmdroid/com.android.gpstest.Preferences filter 77d877a Action: "android.intent.action.MAIN" Category: "android.intent.category.DEFAULT" com.google.android.radar.SHOW_RADAR: eb4b26e com.android.gpstest.osmdroid/com.android.gpstest.GpsTestActivity filter 61ad0f Action: "android.intent.action.MAIN" Action: "com.google.android.radar.SHOW_RADAR" Category: "android.intent.category.LAUNCHER" Category: "android.intent.category.DEFAULT" Registered ContentProviders: com.android.gpstest.osmdroid/androidx.lifecycle.ProcessLifecycleOwnerInitializer: Provider{a82b4a8 com.android.gpstest.osmdroid/androidx.lifecycle.ProcessLifecycleOwnerInitializer} com.android.gpstest.osmdroid/androidx.core.content.FileProvider: Provider{ea24bc1 com.android.gpstest.osmdroid/androidx.core.content.FileProvider} ContentProvider Authorities: [com.android.gpstest.osmdroid.lifecycle-process]: Provider{a82b4a8 com.android.gpstest.osmdroid/androidx.lifecycle.ProcessLifecycleOwnerInitializer} applicationInfo=ApplicationInfo{2f8ef1e com.android.gpstest.osmdroid} [com.android.gpstest.osmdroid.provider]: Provider{ea24bc1 com.android.gpstest.osmdroid/androidx.core.content.FileProvider} applicationInfo=ApplicationInfo{c0d23ff com.android.gpstest.osmdroid} Key Set Manager: [com.android.gpstest.osmdroid] Signing KeySets: 11 Packages: Package [com.android.gpstest.osmdroid] (ca27354): userId=10153 pkg=Package{746e2fd com.android.gpstest.osmdroid} codePath=/data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ== resourcePath=/data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ== legacyNativeLibraryDir=/data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ==/lib primaryCpuAbi=null secondaryCpuAbi=null versionCode=18093 minSdk=18 targetSdk=29 versionName=3.9.16 splits=[base] apkSigningVersion=3 applicationInfo=ApplicationInfo{746e2fd com.android.gpstest.osmdroid} flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ] forceQueryable=false queriesPackages=[] dataDir=/data/user/0/com.android.gpstest.osmdroid supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity] usesLibraries: android.test.base usesLibraryFiles: /system/framework/android.test.base.jar timeStamp=2021-11-07 23:55:08 firstInstallTime=2021-11-07 23:55:09 lastUpdateTime=2021-11-07 23:55:09 installerPackageName=com.android.packageinstaller signatures=PackageSignatures{f05ebf2 version:3, signatures:[8f857bc5], past signatures:[]} installPermissionsFixed=true pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] requested permissions: android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_LOCATION_EXTRA_COMMANDS android.permission.INTERNET android.permission.WRITE_EXTERNAL_STORAGE: restricted=true android.permission.ACCESS_COARSE_LOCATION android.permission.READ_EXTERNAL_STORAGE: restricted=true install permissions: android.permission.INTERNET: granted=true android.permission.ACCESS_LOCATION_EXTRA_COMMANDS: granted=true android.permission.ACCESS_NETWORK_STATE: granted=true User 0: ceDataInode=392616 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false lastDisabledCaller: com.android.packageinstaller gids=[3003] runtime permissions: android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_COARSE_LOCATION: granted=true, flags=[ USER_SET|REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] Queries: system apps queryable: false queries via package name: queries via intent: queryable via interaction: User 0: [lineageos.platform,org.lineageos.flipflap,com.android.location.fused,com.android.providers.settings,com.android.server.telecom,org.lineageos.settings.device,org.lineageos.lineageparts,org.lineageos.setupwizard,com.android.localtransport,android,org.lineageos.settings.doze,com.android.settings,com.android.keychain,com.android.wallpaperbackup,com.android.inputdevices,com.android.dynsystem,org.lineageos.lineagesettings]: com.android.gpstest.osmdroid com.android.inputmethod.latin: com.android.gpstest.osmdroid com.android.permissioncontroller: com.android.gpstest.osmdroid Package Changes: Sequence number=7 User 0: seq=0, package=com.android.traceur seq=3, package=org.fdroid.fdroid seq=5, package=org.jitsi.meet seq=6, package=com.android.gpstest.osmdroid Dexopt state: [com.android.gpstest.osmdroid] path: /data/app/~~DtCl3GSWjjDeCZQY6-JYjQ==/com.android.gpstest.osmdroid-R1hWv_dQbz--sDQlVH8myQ==/base.apk arm64: [status=speed-profile] [reason=install] Compiler stats: [com.android.gpstest.osmdroid] base.apk - 883 spytrap-adb-0.3.2/test_data/dumpsys/package/jitsi.txt000064400000000000000000000143011046102023000210050ustar 00000000000000Activity Resolver Table: Schemes: org.jitsi.meet: 83deac1 org.jitsi.meet/.MainActivity filter 36dde54 Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "org.jitsi.meet" https: 83deac1 org.jitsi.meet/.MainActivity filter 49b79a7 Action: "android.intent.action.VIEW" Category: "android.intent.category.BROWSABLE" Category: "android.intent.category.DEFAULT" Scheme: "https" Authority: "alpha.jitsi.net": -1 Authority: "beta.meet.jit.si": -1 Authority: "meet.jit.si": -1 Non-Data Actions: android.intent.action.MAIN: 83deac1 org.jitsi.meet/.MainActivity filter 229f266 Action: "android.intent.action.MAIN" Category: "android.intent.category.LAUNCHER" Service Resolver Table: Non-Data Actions: android.telecom.ConnectionService: 87d71fd org.jitsi.meet/.sdk.ConnectionService filter cd2cef2 permission android.permission.BIND_TELECOM_CONNECTION_SERVICE Action: "android.telecom.ConnectionService" Registered ContentProviders: org.jitsi.meet/com.oblador.performance.StartTimeProvider: Provider{91ed803 org.jitsi.meet/com.oblador.performance.StartTimeProvider} org.jitsi.meet/com.reactnativecommunity.webview.RNCWebViewFileProvider: Provider{d451380 org.jitsi.meet/com.reactnativecommunity.webview.RNCWebViewFileProvider} ContentProvider Authorities: [org.jitsi.meet.start.time.provider]: Provider{91ed803 org.jitsi.meet/com.oblador.performance.StartTimeProvider} applicationInfo=ApplicationInfo{75a7db9 org.jitsi.meet} [org.jitsi.meet.fileprovider]: Provider{d451380 org.jitsi.meet/com.reactnativecommunity.webview.RNCWebViewFileProvider} applicationInfo=ApplicationInfo{9ec15fe org.jitsi.meet} Key Set Manager: [org.jitsi.meet] Signing KeySets: 10 Packages: Package [org.jitsi.meet] (8acf15f): userId=10152 pkg=Package{1a7e7ac org.jitsi.meet} codePath=/data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA== resourcePath=/data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA== legacyNativeLibraryDir=/data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA==/lib primaryCpuAbi=arm64-v8a secondaryCpuAbi=null versionCode=214010 minSdk=23 targetSdk=30 versionName=21.4.1 splits=[base] apkSigningVersion=3 applicationInfo=ApplicationInfo{1a7e7ac org.jitsi.meet} flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE HAS_DOMAIN_URLS PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ] forceQueryable=false queriesPackages=[] dataDir=/data/user/0/org.jitsi.meet supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity] timeStamp=2021-11-04 20:13:57 firstInstallTime=2021-11-04 20:13:55 lastUpdateTime=2021-11-04 20:13:58 installerPackageName=com.android.packageinstaller signatures=PackageSignatures{81c3c75 version:3, signatures:[224eb835], past signatures:[]} installPermissionsFixed=true pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ] requested permissions: android.permission.ACCESS_NETWORK_STATE android.permission.BLUETOOTH android.permission.CAMERA android.permission.INTERNET android.permission.MANAGE_OWN_CALLS android.permission.MODIFY_AUDIO_SETTINGS android.permission.RECORD_AUDIO android.permission.SYSTEM_ALERT_WINDOW android.permission.WAKE_LOCK android.permission.ACCESS_WIFI_STATE android.permission.FOREGROUND_SERVICE android.permission.WRITE_CALENDAR android.permission.READ_CALENDAR install permissions: android.permission.MODIFY_AUDIO_SETTINGS: granted=true android.permission.FOREGROUND_SERVICE: granted=true android.permission.BLUETOOTH: granted=true android.permission.INTERNET: granted=true android.permission.ACCESS_NETWORK_STATE: granted=true android.permission.MANAGE_OWN_CALLS: granted=true android.permission.ACCESS_WIFI_STATE: granted=true android.permission.WAKE_LOCK: granted=true User 0: ceDataInode=392898 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false lastDisabledCaller: com.android.packageinstaller gids=[3002, 3003] runtime permissions: android.permission.READ_CALENDAR: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.CAMERA: granted=false, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.WRITE_CALENDAR: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.RECORD_AUDIO: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] Queries: system apps queryable: false queries via package name: queries via intent: queryable via interaction: User 0: [lineageos.platform,org.lineageos.flipflap,com.android.location.fused,com.android.providers.settings,com.android.server.telecom,org.lineageos.settings.device,org.lineageos.lineageparts,org.lineageos.setupwizard,com.android.localtransport,android,org.lineageos.settings.doze,com.android.settings,com.android.keychain,com.android.wallpaperbackup,com.android.inputdevices,com.android.dynsystem,org.lineageos.lineagesettings]: org.jitsi.meet com.android.webview: org.jitsi.meet com.android.inputmethod.latin: org.jitsi.meet com.android.permissioncontroller: org.jitsi.meet org.jitsi.meet: com.android.webview Package Changes: Sequence number=6 User 0: seq=0, package=com.android.traceur seq=3, package=org.fdroid.fdroid seq=5, package=org.jitsi.meet Dexopt state: [org.jitsi.meet] path: /data/app/~~0aX7BWdP29TqZaPPXkHNIA==/org.jitsi.meet-hcYO-DrfCgXIZKdu4pcUeA==/base.apk arm64: [status=speed-profile] [reason=bg-dexopt] Compiler stats: [org.jitsi.meet] base.apk - 2802 spytrap-adb-0.3.2/test_data/dumpsys/package/spylive360.txt000064400000000000000000000350061046102023000216140ustar 00000000000000Activity Resolver Table: Non-Data Actions: android.intent.action.MAIN: 6e6b31 com.wifi0/.activities.WelcomeActivity filter c92c216 Action: "android.intent.action.MAIN" Category: "android.intent.category.LAUNCHER" Receiver Resolver Table: Non-Data Actions: android.intent.action.BATTERY_OKAY: 5ab7925 com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy filter 8a0afa Action: "android.intent.action.BATTERY_OKAY" Action: "android.intent.action.BATTERY_LOW" android.intent.action.ACTION_POWER_DISCONNECTED: fa9d28f com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy filter 310691c Action: "android.intent.action.ACTION_POWER_CONNECTED" Action: "android.intent.action.ACTION_POWER_DISCONNECTED" android.intent.action.QUICKBOOT_POWERON: 4a38797 com.wifi0/.BootComplete filter 2baed84 Action: "android.intent.action.BOOT_COMPLETED" Action: "com.htc.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.MY_PACKAGE_REPLACED" Category: "android.intent.category.DEFAULT" com.htc.intent.action.QUICKBOOT_POWERON: 4a38797 com.wifi0/.BootComplete filter 2baed84 Action: "android.intent.action.BOOT_COMPLETED" Action: "com.htc.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.MY_PACKAGE_REPLACED" Category: "android.intent.category.DEFAULT" android.intent.action.DEVICE_STORAGE_LOW: 17b08ab com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy filter d48fb08 Action: "android.intent.action.DEVICE_STORAGE_LOW" Action: "android.intent.action.DEVICE_STORAGE_OK" android.net.conn.CONNECTIVITY_CHANGE: 60d896d com.wifi0/.NetworkWatcher filter af0e9a2 Action: "android.net.conn.CONNECTIVITY_CHANGE" 4bf82a1 com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy filter 8dddcc6 Action: "android.net.conn.CONNECTIVITY_CHANGE" android.intent.action.DEVICE_STORAGE_OK: 17b08ab com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy filter d48fb08 Action: "android.intent.action.DEVICE_STORAGE_LOW" Action: "android.intent.action.DEVICE_STORAGE_OK" android.app.action.DEVICE_ADMIN_ENABLED: 50f0633 com.wifi0/.receivers.DeviceAdmin filter 9bb5df0 Action: "android.app.action.DEVICE_ADMIN_ENABLED" com.google.android.c2dm.intent.RECEIVE: 3064f69 com.wifi0/com.google.firebase.iid.FirebaseInstanceIdReceiver filter a17fdee Action: "com.google.android.c2dm.intent.RECEIVE" android.intent.action.BATTERY_LOW: 5ab7925 com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy filter 8a0afa Action: "android.intent.action.BATTERY_OKAY" Action: "android.intent.action.BATTERY_LOW" android.intent.action.TIMEZONE_CHANGED: 6df8487 com.wifi0/androidx.work.impl.background.systemalarm.RescheduleReceiver filter ef6bfb4 Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.TIME_SET" Action: "android.intent.action.TIMEZONE_CHANGED" android.intent.action.TIME_SET: 6df8487 com.wifi0/androidx.work.impl.background.systemalarm.RescheduleReceiver filter ef6bfb4 Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.TIME_SET" Action: "android.intent.action.TIMEZONE_CHANGED" android.intent.action.BOOT_COMPLETED: 4a38797 com.wifi0/.BootComplete filter 2baed84 Action: "android.intent.action.BOOT_COMPLETED" Action: "com.htc.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.MY_PACKAGE_REPLACED" Category: "android.intent.category.DEFAULT" 6df8487 com.wifi0/androidx.work.impl.background.systemalarm.RescheduleReceiver filter ef6bfb4 Action: "android.intent.action.BOOT_COMPLETED" Action: "android.intent.action.TIME_SET" Action: "android.intent.action.TIMEZONE_CHANGED" android.intent.action.ACTION_POWER_CONNECTED: fa9d28f com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy filter 310691c Action: "android.intent.action.ACTION_POWER_CONNECTED" Action: "android.intent.action.ACTION_POWER_DISCONNECTED" androidx.work.diagnostics.REQUEST_DIAGNOSTICS: b25e223 com.wifi0/androidx.work.impl.diagnostics.DiagnosticsReceiver filter 41a2320 Action: "androidx.work.diagnostics.REQUEST_DIAGNOSTICS" android.intent.action.MY_PACKAGE_REPLACED: 4a38797 com.wifi0/.BootComplete filter 2baed84 Action: "android.intent.action.BOOT_COMPLETED" Action: "com.htc.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.QUICKBOOT_POWERON" Action: "android.intent.action.MY_PACKAGE_REPLACED" Category: "android.intent.category.DEFAULT" androidx.work.impl.background.systemalarm.UpdateProxies: 6b0a7dd com.wifi0/androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver filter e4aff52 Action: "androidx.work.impl.background.systemalarm.UpdateProxies" Service Resolver Table: Non-Data Actions: com.google.firebase.MESSAGING_EVENT: 5ea7d7f com.wifi0/.services.FbMessagingService filter 64e514c Action: "com.google.firebase.MESSAGING_EVENT" 233729b com.wifi0/com.google.firebase.messaging.FirebaseMessagingService filter bb53638 Action: "com.google.firebase.MESSAGING_EVENT" mPriority=-500, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false android.service.notification.NotificationListenerService: 8e5f595 com.wifi0/.listeners.NotificationListener filter fb26aa permission android.permission.BIND_NOTIFICATION_LISTENER_SERVICE Action: "android.service.notification.NotificationListenerService" android.accessibilityservice.AccessibilityService: 94e4d9 com.wifi0/.AccessibilityReceiver4 filter 5f5be9e permission android.permission.BIND_ACCESSIBILITY_SERVICE Action: "android.accessibilityservice.AccessibilityService" Registered ContentProviders: com.wifi0/com.google.firebase.provider.FirebaseInitProvider: Provider{2226303 com.wifi0/com.google.firebase.provider.FirebaseInitProvider} com.wifi0/androidx.work.impl.WorkManagerInitializer: Provider{a214280 com.wifi0/androidx.work.impl.WorkManagerInitializer} ContentProvider Authorities: [com.wifi0.firebaseinitprovider]: Provider{2226303 com.wifi0/com.google.firebase.provider.FirebaseInitProvider} applicationInfo=ApplicationInfo{5ee0270 com.wifi0} [com.wifi0.workmanager-init]: Provider{a214280 com.wifi0/androidx.work.impl.WorkManagerInitializer} applicationInfo=ApplicationInfo{db559e9 com.wifi0} Key Set Manager: [com.wifi0] Signing KeySets: 13 Packages: Package [com.wifi0] (5de2c5f): userId=10155 pkg=Package{19806ac com.wifi0} codePath=/data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA== resourcePath=/data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA== legacyNativeLibraryDir=/data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA==/lib primaryCpuAbi=null secondaryCpuAbi=null versionCode=140 minSdk=19 targetSdk=30 versionName=1.4.0 splits=[base] apkSigningVersion=2 applicationInfo=ApplicationInfo{19806ac com.wifi0} flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ] privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ALLOW_AUDIO_PLAYBACK_CAPTURE PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE PARTIALLY_DIRECT_BOOT_AWARE PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING ] forceQueryable=false queriesPackages=[] dataDir=/data/user/0/com.wifi0 supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity] timeStamp=2021-12-15 17:52:55 firstInstallTime=2021-12-15 17:51:52 lastUpdateTime=2021-12-15 17:52:55 installerPackageName=com.android.packageinstaller signatures=PackageSignatures{4cb6f75 version:2, signatures:[74831dfd], past signatures:[]} installPermissionsFixed=true pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ] requested permissions: android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_BACKGROUND_LOCATION: restricted=true android.permission.INTERNET ACTION_NOTIFICATION_LISTENER_SETTINGS android.permission.READ_SMS: restricted=true android.permission.READ_CONTACTS android.permission.READ_CALL_LOG: restricted=true android.permission.READ_PHONE_STATE android.permission.WRITE_EXTERNAL_STORAGE: restricted=true android.permission.CAMERA android.permission.SYSTEM_ALERT_WINDOW android.permission.ACTION_MANAGE_OVERLAY_PERMISSION android.permission.ACCESS_WIFI_STATE android.permission.CHANGE_WIFI_STATE android.permission.RECEIVE_BOOT_COMPLETED android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS com.android.browser.permission.READ_HISTORY_BOOKMARKS android.permission.READ_EXTERNAL_STORAGE: restricted=true android.permission.RECEIVE_SMS: restricted=true android.permission.RECORD_AUDIO android.permission.BIND_ACCESSIBILITY_SERVICE com.huawei.systemmanager.permission.ACCESS_INTERFACE android.permission.QUERY_ALL_PACKAGES android.permission.ACCESS_MEDIA_LOCATION android.permission.WRITE_SETTINGS android.permission.MANAGE_EXTERNAL_STORAGE android.permission.WAKE_LOCK com.google.android.c2dm.permission.RECEIVE com.google.android.providers.gsf.permission.READ_GSERVICES com.google.android.gms.permission.ACTIVITY_RECOGNITION android.permission.FOREGROUND_SERVICE com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE install permissions: android.permission.FOREGROUND_SERVICE: granted=true android.permission.RECEIVE_BOOT_COMPLETED: granted=true android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS: granted=true android.permission.INTERNET: granted=true com.android.browser.permission.READ_HISTORY_BOOKMARKS: granted=true android.permission.CHANGE_WIFI_STATE: granted=true android.permission.ACCESS_NETWORK_STATE: granted=true android.permission.ACCESS_WIFI_STATE: granted=true android.permission.QUERY_ALL_PACKAGES: granted=true android.permission.WAKE_LOCK: granted=true User 0: ceDataInode=261852 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false gids=[3003] runtime permissions: android.permission.READ_SMS: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.READ_CALL_LOG: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.RECEIVE_SMS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_COARSE_LOCATION: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.READ_PHONE_STATE: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.CAMERA: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.WRITE_EXTERNAL_STORAGE: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.RECORD_AUDIO: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.READ_CONTACTS: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.ACCESS_BACKGROUND_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_INSTALLER_EXEMPT] android.permission.ACCESS_MEDIA_LOCATION: granted=false, flags=[ USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] enabledComponents: androidx.work.impl.background.systemalarm.RescheduleReceiver androidx.work.impl.background.systemjob.SystemJobService Queries: system apps queryable: false queries via package name: queries via intent: queryable via interaction: User 0: [org.lineageos.lineageparts,com.android.localtransport,com.android.keychain,com.android.inputdevices,android,com.android.settings,org.lineageos.setupwizard,org.lineageos.settings.device,org.lineageos.lineagesettings,com.android.providers.settings,lineageos.platform,com.android.wallpaperbackup,com.android.location.fused,com.android.dynsystem,org.lineageos.flipflap,com.android.server.telecom,org.lineageos.settings.doze]: com.wifi0 [com.android.mms.service,com.android.ons,com.android.providers.telephony,com.android.phone,com.android.stk]: com.wifi0 com.android.providers.media.module: com.wifi0 com.wifi0: [org.lineageos.lineageparts,com.android.localtransport,com.android.keychain,com.android.inputdevices,android,com.android.settings,org.lineageos.setupwizard,org.lineageos.settings.device,org.lineageos.lineagesettings,com.android.providers.settings,lineageos.platform,com.android.wallpaperbackup,com.android.location.fused,com.android.dynsystem,org.lineageos.flipflap,com.android.server.telecom,org.lineageos.settings.doze] Package Changes: Sequence number=0 Dexopt state: [com.wifi0] path: /data/app/~~V5jud4ex5-s8L3x2B4lvUA==/com.wifi0-q-C29yRZYy55N91XTD2EoA==/base.apk arm64: [status=speed-profile] [reason=bg-dexopt] Compiler stats: [com.wifi0] base.apk - 3741