└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # unrasp_guard 2 | 3 | Anti Tamper & Anti Frida Bypass For Our Lovely LolGuard 4 | 5 | Why I Am Doing This - To Help Reversing Community . 6 | Even Malware Owners Using Such RASP on malwares to protect itself but RASP companies forcing down the 7 | guys who want to analyse such malware packed with their RASP . 8 | for you companies - Why you selling your products to such guys without verification - oh why should you care- you guys just need thousands of $ for your shitty protection 9 | 10 | Before Start doing any reversing, lets see what things are loaded from linker for our target app 11 | 12 | ```sh 13 | var do_dlopen = null; 14 | var call_ctor = null; 15 | Process.findModuleByName('linker64').enumerateSymbols().forEach(function(sym) { 16 | if (sym.name.indexOf('do_dlopen') >= 0) { 17 | do_dlopen = sym.address; 18 | } else if (sym.name.indexOf('call_constructor') >= 0) { 19 | call_ctor = sym.address; 20 | } 21 | }) 22 | Interceptor.attach(do_dlopen, function() { 23 | var library = this.context['x0'].readUtf8String(); 24 | console.log(library); 25 | }) 26 | ``` 27 | and we got output as 28 | 29 | ```sh 30 | /data/app/~~randomshit==/com.our.target.apk-randomshit==/oat/arm64/base.odex 31 | /data/app/~~94aZRAHXOJ9Z6tyWBj3tfA==/com.our.target.apk-randomshit==/lib/arm64/libdgrt.so 32 | /data/local/tmp/frida-c8xyz......./frida-agent-64.so 33 | ..... 34 | ``` 35 | output truncated because list is long 36 | 37 | so first our classes.dex is loaded in form on .odex and then lolguard's library. 38 | 39 | lets think in how many way it can check for static Anti Tampering 40 | 1. By opening base.apk and extract meta-inf and compare it . hash or crc whatever 41 | 2. by invoking some java api and calculate signature from those and compare it 42 | many other way there but these are 2 common way to check . 43 | path to base.apk can be retrieved from various way and can be checked normally or with syscall . our selected apk not doing any syscall 44 | operation so we sticking with java side hooks 45 | 46 | lets think how can be base.apk is faked so apk think it is not tampered. 47 | 48 | 1. we can copy original base.apk into asset and extract it at first startup to /data/data/package directory and then we can fake it 49 | 2. or copy base.apk into lib directory of apk so apk extract it at install time for us 50 | 51 | if we go for 1st method then we need to add smali codes which we don't do as we Tampering apk , not adding additional codes 52 | and if we go for 2nd method then we got opportunity to hook code from very early without needing to add anything. 53 | 54 | so what we will do - 55 | 56 | 1. redirect base.apk path to our faked path which we decide later. 57 | 2. some of signature method to provide fake signature 58 | 59 | first we need to get original signature of apk so we can put in our script to fake. 60 | 61 | lets check 62 | https://stackoverflow.com/questions/5578871/how-to-get-apk-signing-signature 63 | to get how can we get signatures. if we convert similer java code javascript. it will be 64 | 65 | ```sh 66 | Java.perform(function() { 67 | try { 68 | var SignArray = []; 69 | var Signatures; 70 | var BuildVersion = Java.use("android.os.Build$VERSION"); 71 | var PackageManager = Java.use("android.content.pm.PackageManager"); 72 | var Context = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext(); 73 | Signatures = 28 <= BuildVersion.SDK_INT.value ? Context.getPackageManager().getPackageInfo(Context.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES.value).signingInfo.value.getApkContentsSigners() : Context.getPackageManager().getPackageInfo(Context.getPackageName(), PackageManager.GET_SIGNATURES.value).signatures.value; 74 | for (var iterate = 0; iterate < Signatures.length; iterate += 1) { 75 | SignArray.push(Signatures[iterate].toCharsString()) 76 | } 77 | console.warn("Original Signature : ", SignArray); 78 | } catch (e) { 79 | console.error(e); 80 | } 81 | }) 82 | ``` 83 | and we get our signature as 84 | 85 | 308202cf308201....... truncated to not reveal about app or developer 86 | 87 | now we have to think how app might check for its own signature. 88 | 89 | from 90 | https://gist.github.com/scottyab/b849701972d57cf9562e 91 | 92 | ```sh 93 | import android.content.Context; 94 | import android.content.pm.PackageInfo; 95 | import android.content.pm.PackageManager; 96 | import android.content.pm.PackageManager.NameNotFoundException; 97 | import android.content.pm.Signature; 98 | 99 | public class TamperCheck { 100 | 101 | 102 | private static final String APP_SIGNATURE = "1038C0E34658923C4192E61B16846"; 103 | public boolean validateAppSignature(Context context) throws NameNotFoundException { 104 | 105 | PackageInfo packageInfo = context.getPackageManager().getPackageInfo( 106 | getPackageName(), PackageManager.GET_SIGNATURES); 107 | for (Signature signature : packageInfo.signatures) { 108 | String sha1 = getSHA1(signature.toByteArray()); 109 | return APP_SIGNATURE.equals(sha1); 110 | } 111 | 112 | return false; 113 | } 114 | 115 | public static String getSHA1(byte[] sig) { 116 | MessageDigest digest = MessageDigest.getInstance("SHA1"); 117 | digest.update(sig); 118 | byte[] hashtext = digest.digest(); 119 | return bytesToHex(hashtext); 120 | } 121 | 122 | public static String bytesToHex(byte[] bytes) { 123 | final char[] hexArray = { '0', '1', '2', '3', '4', '5', '6', '7', '8', 124 | '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 125 | char[] hexChars = new char[bytes.length * 2]; 126 | int v; 127 | for (int j = 0; j < bytes.length; j++) { 128 | v = bytes[j] & 0xFF; 129 | hexChars[j * 2] = hexArray[v >>> 4]; 130 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 131 | } 132 | return new String(hexChars); 133 | } 134 | } 135 | ``` 136 | 137 | we see that it making use of these classes - 138 | 139 | ```sh 140 | android.content.Context; 141 | android.content.pm.PackageInfo; 142 | android.content.pm.PackageManager; 143 | android.content.pm.Signature; 144 | ``` 145 | 146 | so we can get a rough idea what we need to hook. but first we need to decide when should we hook 147 | as we first need to get path of base.apk , this is random on every time apk is installed or reinstalled like 148 | 149 | ```sh 150 | /data/app/com.package~~97aZR....==/base.apk 151 | /data/app/~~14aOR...../Package~~randomshit==/base.apk 152 | ``` 153 | 154 | so we need to calculate correct path first . 155 | 156 | as we seen from first linker hook that it loaded base.odex at very first , we can use that path to calculate new path 157 | 158 | if this is odex loader path 159 | /data/app/~~94aZRAHXOJ9Z6tyWBj3tfA==/com.package.apk-random==/oat/arm64/base.odex 160 | then we can make our new path by removing "oat/arm64/base.odex" from it and adding base.apk. 161 | 162 | now . lets start doing work 163 | 164 | 1. make a copy of original apk , lets say we given it name as copy.apk 165 | 2. in lib folder of original.apk - push copy.apk 166 | 167 | like before 168 | original apk have 169 | 170 | ```sh 171 | /lib/arm64-v8a/libdgrt.so 172 | ``` 173 | we make it 174 | 175 | ```sh 176 | /lib/arm64-v8a/libdgrt.so 177 | /lib/arm64-v8a/copy.apk 178 | ``` 179 | 180 | but wait android doesn't count .apk as valid extension to extract at runtime from lib folder 181 | so we renamimg copy.apk to libbaseapk.so , so android extract it easily. 182 | and we got final fix as 183 | 184 | ```sh 185 | /lib/arm64-v8a/libdgrt.so 186 | /lib/arm64-v8a/libbaseapk.so 187 | ``` 188 | 189 | Note - Apk might refuse to install after adding new library if its AndroidManifest.xml have a specific tag set as 190 | ```sh 191 | android:extractNativeLibs="false" 192 | ``` 193 | so it may need to change to true 194 | ```sh 195 | android:extractNativeLibs="true" 196 | ``` 197 | 198 | lets make script to get our new path 199 | 200 | ```sh 201 | var do_dlopen = null; 202 | var call_ctor = null; 203 | var packagename = "com.our.target.apk" 204 | var Check; 205 | Process.findModuleByName('linker64').enumerateSymbols().forEach(function(sym) { 206 | if (sym.name.indexOf('do_dlopen') >= 0) { 207 | do_dlopen = sym.address; 208 | } else if (sym.name.indexOf('call_constructor') >= 0) { 209 | call_ctor = sym.address; 210 | } 211 | }) 212 | Interceptor.attach(do_dlopen, function() { 213 | var library = this.context['x0'].readUtf8String(); 214 | if (library != null) { 215 | if (library.indexOf(packagename) >= 0 && library.indexOf("base.odex") >= 0) { 216 | console.log("[*] Odex Loading : " + library); 217 | Check = library.replace("oat/arm64/base.odex", "lib/arm64/libbaseapk.so"); 218 | } 219 | } 220 | }) 221 | ``` 222 | 223 | what we did - 224 | we got odex path and with a little replacement of path we adjusted it to our libbaseapk.so 225 | so we don't need to care about random installation path anymore. 226 | now our variable "Check" have path to faked original base.apk and we can start redirecting app to it 227 | if it checking for it 228 | 229 | lets start Hook Java api for signature checks 230 | 231 | 1. android.content.Context 232 | 233 | context class is widely used and every apk use it somewhere for proper working but whole class 234 | is not useful for us . we need to filter few method of context class which may be used in anti tamper checks. 235 | 236 | searching on 237 | https://developer.android.com/reference/android/content/Context 238 | some get* give use 2 method 239 | ```sh 240 | getPackageCodePath() 241 | getPackageResourcePath() 242 | ``` 243 | both return a path to base.apk so they should be hooked. 244 | 245 | you guys might go and do like 246 | 247 | ```sh 248 | var Context = Java.use('android.content.Context'); 249 | ``` 250 | 251 | but wait it not gonna work . there exist a different class which implements context 252 | and that it 253 | 254 | ```sh 255 | android.app.ContextImpl 256 | var Context = Java.use('android.app.ContextImpl'); 257 | ``` 258 | 259 | ```sh 260 | var Context = Java.use('android.app.ContextImpl'); 261 | Context.getPackageCodePath.overload().implementation = function() { 262 | return what; // we need to write our faked apk path but how 263 | } 264 | ``` 265 | 266 | from above our variable Check location , we can call a function which have Check as argument. 267 | like this 268 | 269 | ```sh 270 | var do_dlopen = null; 271 | var call_ctor = null; 272 | var packagename = "com.our.target.apk" 273 | var Check; 274 | Process.findModuleByName('linker64').enumerateSymbols().forEach(function(sym) { 275 | if (sym.name.indexOf('do_dlopen') >= 0) { 276 | do_dlopen = sym.address; 277 | } else if (sym.name.indexOf('call_constructor') >= 0) { 278 | call_ctor = sym.address; 279 | } 280 | }) 281 | Interceptor.attach(do_dlopen, function() { 282 | var library = this.context['x0'].readUtf8String(); 283 | if (library != null) { 284 | if (library.indexOf(packagename) >= 0 && library.indexOf("base.odex") >= 0) { 285 | console.log("[*] Odex Loading : " + library); 286 | Check = library.replace("oat/arm64/base.odex", "lib/arm64/libbaseapk.so"); 287 | Hook(Check); 288 | } 289 | } 290 | }) 291 | 292 | function Hook(input) { 293 | // our java hook here , as odex is loaded already , at this moment Java.available won't return null 294 | 295 | } 296 | ``` 297 | inside Hook function we need to choose between 298 | Java.perform or Java.performNow but we prefer Java.performNow for early hook purpose. 299 | 300 | 301 | ```sh 302 | var do_dlopen = null; 303 | var call_ctor = null; 304 | var packagename = "com.our.target.apk" 305 | var Check; 306 | Process.findModuleByName('linker64').enumerateSymbols().forEach(function(sym) { 307 | if (sym.name.indexOf('do_dlopen') >= 0) { 308 | do_dlopen = sym.address; 309 | } else if (sym.name.indexOf('call_constructor') >= 0) { 310 | call_ctor = sym.address; 311 | } 312 | }) 313 | Interceptor.attach(do_dlopen, function() { 314 | var library = this.context['x0'].readUtf8String(); 315 | if (library != null) { 316 | if (library.indexOf(packagename) >= 0 && library.indexOf("base.odex") >= 0) { 317 | console.log("[*] Odex Loading : " + library); 318 | Check = library.replace("oat/arm64/base.odex", "lib/arm64/libbaseapk.so"); 319 | Hook(Check); 320 | } 321 | } 322 | }) 323 | 324 | function Hook(Input) { 325 | 326 | Java.performNow(function() { 327 | var Context = Java.use('android.app.ContextImpl'); 328 | Context.getPackageCodePath.overload().implementation = function() { 329 | return Input; 330 | } 331 | Context.getPackageResourcePath.overload().implementation = function() { 332 | return Input; 333 | } 334 | } 335 | ``` 336 | 337 | context class's work is over . lets goto 2nd class 338 | 339 | 2. android.content.pm.Signature 340 | 341 | looking on https://developer.android.com/reference/android/content/pm/Signature 342 | we get few public methods 343 | 1. toByteArray 344 | 2. toChars . 2 method with different overload 345 | 3. toCharsString 346 | 347 | lets hook each of them . 348 | 349 | toByteArray expect retval as byte array . so quick search on google give us 350 | https://stackoverflow.com/questions/6226189/how-to-convert-a-string-to-bytearray 351 | 352 | and we form functions as 353 | 354 | ```sh 355 | function TBA() { 356 | var output = Java.array('byte', HTB(OriginalSign)); 357 | return output; 358 | } 359 | function HTB(hex) { 360 | for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); 361 | return bytes; 362 | } 363 | ``` 364 | 365 | what is OriginalSign here ? its that long signature string starting with 3082.... which we got early. 366 | 367 | ```sh 368 | var OriginalSign = "3082......."; 369 | var ACPSign = Java.use("android.content.pm.Signature"); 370 | ACPSign["toByteArray"].overload().implementation = function() { 371 | console.log("android.content.pm.Signature;->toByteArray called"); 372 | var Fix = TBA(); 373 | return Fix; 374 | }; 375 | ``` 376 | lets hook rest method of android.content.pm.Signature class 377 | toChars - 2 method with different overload 378 | 379 | it expect retval in character array , as our signature is already in string form , its gonna easy. 380 | 381 | ```sh 382 | function TCA() { 383 | var ArraySignChar = Array.from(OriginalSign); 384 | return ArraySignChar; 385 | } 386 | ACPSign["toChars"].overload().implementation = function() { 387 | console.log("android.content.pm.Signature;->toChars called"); 388 | var Fix = TCA(); 389 | return Fix; 390 | } 391 | ACPSign["toChars"].overload("[C", "[I").implementation = function(ch, into) { 392 | console.log("android.content.pm.Signature;->toChars 2nd called"); 393 | var Fix = TCA(); 394 | return Fix; 395 | } 396 | ``` 397 | 398 | lets do again for 3rd method toCharString 399 | it expect retval as string and our input is already in string form so we can directly return it. 400 | 401 | ```sh 402 | ACPSign["toCharsString"].overload().implementation = function() { 403 | console.log("android.content.pm.Signature;->toCharsString called"); 404 | return OriginalSign; 405 | } 406 | ``` 407 | 408 | this class hook is over . lets go for another class 409 | PackageManager 410 | ```sh 411 | android.content.pm.PackageManager 412 | ``` 413 | so we try to hook it but again wait , this class won't trigger because this is base class and some other class implements it 414 | 415 | ```sh 416 | android.app.ApplicationPackageManager 417 | ``` 418 | there are lots of method of PackageManager class but we hooking only few because those are sufficient, adding more and more hook 419 | doesn't guarantees of proper working . they often leads to crash. 420 | 421 | let hook getApplicationInfo of PackageManager class . 422 | But think what we need to hook in that - from past experience we know that 423 | 2 field can grab base.apk location which is sourcDir and publicSourceDir which is available in getApplicationInfo. 424 | 425 | now hooking them 426 | 427 | ```sh 428 | var PackageManager = Java.use("android.app.ApplicationPackageManager"); 429 | PackageManager.getApplicationInfo.implementation = function(pn, flags) { 430 | var ret = this.getApplicationInfo(pn, flags); 431 | if (pn === packagename) { 432 | ret.sourceDir = Input; 433 | ret.publicSourceDir = Input; 434 | console.log("android.app.ApplicationPackageManager;->(sourceDir) Hooked"); 435 | } 436 | return ret; 437 | } 438 | ``` 439 | 440 | but when we run it , we see that it not work . lets search as usually and found that 441 | https://github.com/frida/frida/issues/510 issue . From that we can see that it need .value to work properly. 442 | 443 | a quick modification in script 444 | 445 | ```sh 446 | var PackageManager = Java.use("android.app.ApplicationPackageManager"); 447 | PackageManager.getApplicationInfo.implementation = function(pn, flags) { 448 | var ret = this.getApplicationInfo(pn, flags); 449 | if (pn === packagename) { 450 | ret.sourceDir.value = Input; 451 | ret.publicSourceDir.value = Input; 452 | console.log("android.app.ApplicationPackageManager;->(sourceDir) Hooked"); 453 | } 454 | return ret; 455 | } 456 | ``` 457 | doing same for context class 458 | 459 | ```sh 460 | Context.getApplicationInfo.overload().implementation = function() { 461 | var ret = this.getApplicationInfo(); 462 | console.log("android.app.ContextImpl;->getApplicationInfo called"); 463 | ret.sourceDir.value = Input; 464 | ret.publicSourceDir.value = Input; 465 | return ret; 466 | } 467 | ``` 468 | 469 | we repeat same sourceDir hook on a low level package manager class 470 | android.content.pm.IPackageManager$Stub$Proxy 471 | 472 | ```sh 473 | var Stub = Java.use("android.content.pm.IPackageManager$Stub$Proxy"); 474 | Stub.getApplicationInfo.overload("java.lang.String", "int", "int").implementation = function(pkgname, flag, flag2) { 475 | var ret = this.getApplicationInfo.call(this, pkgname, flag, flag2); 476 | if (pkgname == packagename) { 477 | console.log("android.content.pm.IPackageManager$Stub$Proxy;->getApplicationInfo(sourceDir) called"); 478 | ret.sourceDir.value = Input; 479 | ret.publicSourceDir.value = Input; 480 | } 481 | return ret; 482 | } 483 | ``` 484 | 485 | From same issue from https://github.com/frida/frida/issues/510 486 | we can make hook for android.content.pm.ApplicationInfo 487 | but that depends upon android.app.ActivityThread , we can't put that into Java.performNow else it give 488 | error such as - context not found 489 | 490 | 491 | ```sh 492 | if (Java.available) { 493 | Java.perform(function() { 494 | const ActivityThread = Java.use('android.app.ActivityThread'); 495 | const PackageInfo = Java.use('android.content.pm.PackageInfo'); 496 | const ApplicationInfo = Java.use('android.content.pm.ApplicationInfo'); 497 | var context = ActivityThread.currentApplication().getApplicationContext(); 498 | var packageManager = context.getPackageManager(); 499 | var appsinfo = packageManager.getInstalledPackages(0); 500 | for (var i = 0; i < appsinfo.size(); i++) { 501 | var app = Java.cast(appsinfo.get(i), PackageInfo); 502 | if (app.packageName.value == packagename) { 503 | app.applicationInfo.value.sourceDir.value = Check; 504 | console.log("sourceDir Hooked : ", app.applicationInfo.value.sourceDir.value); 505 | } 506 | } 507 | }); 508 | } 509 | ``` 510 | 511 | 512 | These are enough for Bypassing Certificate Check /Anti-Tamper of LolGuard 513 | 514 | lets combine all parts of script. 515 | 516 | ```sh 517 | var do_dlopen = null; 518 | var call_ctor = null; 519 | var packagename = "com.our.target.apk" 520 | var Check; 521 | Process.findModuleByName('linker64').enumerateSymbols().forEach(function(sym) { 522 | if (sym.name.indexOf('do_dlopen') >= 0) { 523 | do_dlopen = sym.address; 524 | } else if (sym.name.indexOf('call_constructor') >= 0) { 525 | call_ctor = sym.address; 526 | } 527 | }) 528 | Interceptor.attach(do_dlopen, function() { 529 | var library = this.context['x0'].readUtf8String(); 530 | console.log(library); 531 | if (library != null) { 532 | if (library.indexOf(packagename) >= 0 && library.indexOf("base.odex") >= 0) { 533 | console.log("[*] Odex Loading : " + library); 534 | Check = library.replace("oat/arm64/base.odex", "lib/arm64/libbaseapk.so"); 535 | Hook(Check) 536 | } 537 | } 538 | }) 539 | 540 | function Hook(Input) { 541 | var OriginalSign = "3082........"; 542 | Java.performNow(function() { 543 | try { 544 | var Context = Java.use('android.app.ContextImpl'); 545 | Context.getPackageCodePath.overload().implementation = function() { 546 | return Input; 547 | } 548 | Context.getPackageResourcePath.overload().implementation = function() { 549 | return Input; 550 | } 551 | Context.getApplicationInfo.overload().implementation = function() { 552 | var ret = this.getApplicationInfo(); 553 | console.log("android.app.ContextImpl;->getApplicationInfo called"); 554 | ret.sourceDir.value = Input; 555 | ret.publicSourceDir.value = Input; 556 | return ret; 557 | } 558 | 559 | function TBA() { 560 | var output = Java.array('byte', HTB(OriginalSign)); 561 | return output; 562 | } 563 | 564 | function TCA() { 565 | var ArraySignChar = Array.from(OriginalSign); 566 | return ArraySignChar; 567 | } 568 | 569 | function HTB(hex) { 570 | for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); 571 | return bytes; 572 | } 573 | var Verf = Java.use("java.security.Signature"); 574 | Verf.verify.overload("[B").implementation = function(by) { 575 | return true; 576 | } 577 | var Stub = Java.use("android.content.pm.IPackageManager$Stub$Proxy"); 578 | Stub.getApplicationInfo.overload("java.lang.String", "int", "int").implementation = function(pkgname, flag, flag2) { 579 | var ret = this.getApplicationInfo.call(this, pkgname, flag, flag2); 580 | if (pkgname == packagename) { 581 | console.log("android.content.pm.IPackageManager$Stub$Proxy;->getApplicationInfo(sourceDir) called"); 582 | ret.sourceDir.value = Input; 583 | ret.publicSourceDir.value = Input; 584 | } 585 | return ret; 586 | } 587 | var PackageManager = Java.use("android.app.ApplicationPackageManager"); 588 | PackageManager.getApplicationInfo.implementation = function(pn, flags) { 589 | var ret = this.getApplicationInfo(pn, flags); 590 | if (pn === pkg) { 591 | ret.sourceDir.value = Input; 592 | ret.publicSourceDir.value = Input; 593 | console.log("android.app.ApplicationPackageManager;->(sourceDir) Hooked"); 594 | } 595 | return ret; 596 | } 597 | var ACPSign = Java.use("android.content.pm.Signature"); 598 | ACPSign["toByteArray"].overload().implementation = function() { 599 | console.log("android.content.pm.Signature;->toByteArray called"); 600 | var Fix = TBA(); 601 | return Fix; 602 | }; 603 | ACPSign["hashCode"].overload().implementation = function() { 604 | var ret = this["hashCode"](); 605 | console.log("Hash : ", ret); 606 | // return 189889969; This we need to grab from original apk first 607 | return ret 608 | } 609 | ACPSign["toCharsString"].overload().implementation = function() { 610 | console.log("android.content.pm.Signature;->toCharsString called"); 611 | return OriginalSign; 612 | } 613 | ACPSign["toChars"].overload().implementation = function() { 614 | console.log("android.content.pm.Signature;->toChars called"); 615 | var Fix = TCA(); 616 | return Fix; 617 | } 618 | ACPSign["toChars"].overload("[C", "[I").implementation = function(ch, into) { 619 | console.log("android.content.pm.Signature;->toChars 2nd called"); 620 | var Fix = TCA(); 621 | return Fix; 622 | } 623 | } catch (e) { 624 | console.error("Error Trigger : ", e); 625 | } 626 | }) 627 | } 628 | if (Java.available) { 629 | Java.perform(function() { 630 | const ActivityThread = Java.use('android.app.ActivityThread'); 631 | const PackageInfo = Java.use('android.content.pm.PackageInfo'); 632 | const ApplicationInfo = Java.use('android.content.pm.ApplicationInfo'); 633 | var context = ActivityThread.currentApplication().getApplicationContext(); 634 | var packageManager = context.getPackageManager(); 635 | var appsinfo = packageManager.getInstalledPackages(0); 636 | for (var i = 0; i < appsinfo.size(); i++) { 637 | var app = Java.cast(appsinfo.get(i), PackageInfo); 638 | if (app.packageName.value == packagename ) { 639 | app.applicationInfo.value.sourceDir.value = Check; 640 | console.log("sourceDir Hooked : ", app.applicationInfo.value.sourceDir.value); 641 | } 642 | } 643 | }); 644 | } 645 | ``` 646 | 647 | Now if we installed our modified apk which have libbaseapk.so in its library folder and start with above frida script. it will start fine instead of crashing. 648 | there are many more hooks available to share but for this i thought sufficient, may be they are for more better packer 649 | 650 | Note - Anti-Frida part yet to be written because i can't found any LolGuard apk with frida detection yet . if you found such please share apk or hash on telegram. 651 | http://t.me/apkunpacker 652 | 653 | If You Like it. Consider Buying me a ☕ 654 | 655 | https://www.paypal.com/paypalme/apkunpacker 656 | 657 | paypal.me/apkunpacker 658 | --------------------------------------------------------------------------------