@@ -1 +1,550 @@
//
Java . perform ( function ( ) {
console . log ( `\n[!] Frida Basic Hooks started
[Info]
[Author] BitTheByte (Ahmed Ezzat)
[GITHUB] https://gist.github.com/BitTheByte/19e5a08fd112275e8d5eeb269c490a09
[Version] v0.1
[Features]
* Enable Debugging Mode for webview(s)
* Root Detection Bypass
* SSL unpinning
[Disclaimer]
* This is NOT an original work by the author. most of the source code is a community based effort
If your work is included in this script and wish to get credited please contact me.
* You're free to copy, edit, redistribute the source code.
[HELP]
* Using \`frida -U -l script.js -f com.example.android --no-pause\` is recommended
* For debugging purposes Set debug = true at the line:23
` )
var debug = false
function rtlog ( ...args ) {
if ( debug ) {
console . log ( args )
}
}
var Webview = Java . use ( "android.webkit.WebView" )
Webview . loadUrl . overload ( "java.lang.String" ) . implementation = function ( url ) {
rtlog ( "Setting the value of setWebContentsDebuggingEnabled() to true" ) ;
this . setWebContentsDebuggingEnabled ( true ) ;
this . loadUrl . overload ( "java.lang.String" ) . call ( this , url ) ;
}
var RootPackages = [
"com.noshufou.android.su" ,
"com.noshufou.android.su.elite" ,
"eu.chainfire.supersu" ,
"com.koushikdutta.superuser" ,
"com.thirdparty.superuser" ,
"com.yellowes.su" ,
"com.koushikdutta.rommanager" ,
"com.koushikdutta.rommanager.license" ,
"com.dimonvideo.luckypatcher" ,
"com.chelpus.lackypatch" ,
"com.ramdroid.appquarantine" ,
"com.ramdroid.appquarantinepro" ,
"com.devadvance.rootcloak" ,
"com.devadvance.rootcloakplus" ,
"de.robv.android.xposed.installer" ,
"com.saurik.substrate" ,
"com.zachspong.temprootremovejb" ,
"com.amphoras.hidemyroot" ,
"com.amphoras.hidemyrootadfree" ,
"com.formyhm.hiderootPremium" ,
"com.formyhm.hideroot" ,
"me.phh.superuser" ,
"eu.chainfire.supersu.pro" ,
"com.kingouser.com"
] ;
var RootBinaries = [ "su" , "busybox" , "supersu" , "Superuser.apk" , "KingoUser.apk" , "SuperSu.apk" ] ;
var RootProperties = { "ro.build.selinux" : "1" , "ro.debuggable" : "0" , "service.adb.root" : "0" , "ro.secure" : "1" } ;
var RootPropertiesKeys = [ ] ;
for ( var k in RootProperties ) RootPropertiesKeys . push ( k ) ;
var PackageManager = Java . use ( "android.app.ApplicationPackageManager" ) ;
var Runtime = Java . use ( 'java.lang.Runtime' ) ;
var NativeFile = Java . use ( 'java.io.File' ) ;
var String = Java . use ( 'java.lang.String' ) ;
var SystemProperties = Java . use ( 'android.os.SystemProperties' ) ;
var BufferedReader = Java . use ( 'java.io.BufferedReader' ) ;
var ProcessBuilder = Java . use ( 'java.lang.ProcessBuilder' ) ;
var loaded_classes = Java . enumerateLoadedClassesSync ( ) ;
rtlog ( "Loaded " + loaded_classes . length + " classes!" ) ;
var useKeyInfo = false ;
var useProcessManager = false ;
rtlog ( "loaded: " + loaded_classes . indexOf ( 'java.lang.ProcessManager' ) ) ;
if ( loaded_classes . indexOf ( 'java.lang.ProcessManager' ) == - 1 ) {
rtlog ( "ProcessManager hook not loaded" ) ;
}
var KeyInfo = null ;
if ( loaded_classes . indexOf ( 'android.security.keystore.KeyInfo' ) == - 1 ) {
rtlog ( "KeyInfo hook not loaded" ) ;
}
PackageManager . getPackageInfo . overload ( 'java.lang.String' , 'int' ) . implementation = function ( pname , flags ) {
var shouldFakePackage = ( RootPackages . indexOf ( pname ) > - 1 ) ;
if ( shouldFakePackage ) {
rtlog ( "Bypass root check for package: " + pname ) ;
pname = "set.package.name.to.a.fake.one.so.we.can.bypass.it" ;
}
return this . getPackageInfo . call ( this , pname , flags ) ;
} ;
NativeFile . exists . implementation = function ( ) {
var name = NativeFile . getName . call ( this ) ;
var shouldFakeReturn = ( RootBinaries . indexOf ( name ) > - 1 ) ;
if ( shouldFakeReturn ) {
rtlog ( "Bypass return value for binary: " + name ) ;
return false ;
} else {
return this . exists . call ( this ) ;
}
} ;
var exec = Runtime . exec . overload ( '[Ljava.lang.String;' ) ;
var exec1 = Runtime . exec . overload ( 'java.lang.String' ) ;
var exec2 = Runtime . exec . overload ( 'java.lang.String' , '[Ljava.lang.String;' ) ;
var exec3 = Runtime . exec . overload ( '[Ljava.lang.String;' , '[Ljava.lang.String;' ) ;
var exec4 = Runtime . exec . overload ( '[Ljava.lang.String;' , '[Ljava.lang.String;' , 'java.io.File' ) ;
var exec5 = Runtime . exec . overload ( 'java.lang.String' , '[Ljava.lang.String;' , 'java.io.File' ) ;
exec5 . implementation = function ( cmd , env , dir ) {
if ( cmd . indexOf ( "getprop" ) != - 1 || cmd == "mount" || cmd . indexOf ( "build.prop" ) != - 1 || cmd == "id" || cmd == "sh" ) {
var fakeCmd = "grep" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
if ( cmd == "su" ) {
var fakeCmd = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
return exec5 . call ( this , cmd , env , dir ) ;
} ;
exec4 . implementation = function ( cmdarr , env , file ) {
for ( var i = 0 ; i < cmdarr . length ; i = i + 1 ) {
var tmp_cmd = cmdarr [ i ] ;
if ( tmp_cmd . indexOf ( "getprop" ) != - 1 || tmp_cmd == "mount" || tmp_cmd . indexOf ( "build.prop" ) != - 1 || tmp_cmd == "id" || tmp_cmd == "sh" ) {
var fakeCmd = "grep" ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
if ( tmp_cmd == "su" ) {
var fakeCmd = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
}
return exec4 . call ( this , cmdarr , env , file ) ;
} ;
exec3 . implementation = function ( cmdarr , envp ) {
for ( var i = 0 ; i < cmdarr . length ; i = i + 1 ) {
var tmp_cmd = cmdarr [ i ] ;
if ( tmp_cmd . indexOf ( "getprop" ) != - 1 || tmp_cmd == "mount" || tmp_cmd . indexOf ( "build.prop" ) != - 1 || tmp_cmd == "id" || tmp_cmd == "sh" ) {
var fakeCmd = "grep" ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
if ( tmp_cmd == "su" ) {
var fakeCmd = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
}
return exec3 . call ( this , cmdarr , envp ) ;
} ;
exec2 . implementation = function ( cmd , env ) {
if ( cmd . indexOf ( "getprop" ) != - 1 || cmd == "mount" || cmd . indexOf ( "build.prop" ) != - 1 || cmd == "id" || cmd == "sh" ) {
var fakeCmd = "grep" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
if ( cmd == "su" ) {
var fakeCmd = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
return exec2 . call ( this , cmd , env ) ;
} ;
exec . implementation = function ( cmd ) {
for ( var i = 0 ; i < cmd . length ; i = i + 1 ) {
var tmp_cmd = cmd [ i ] ;
if ( tmp_cmd . indexOf ( "getprop" ) != - 1 || tmp_cmd == "mount" || tmp_cmd . indexOf ( "build.prop" ) != - 1 || tmp_cmd == "id" || tmp_cmd == "sh" ) {
var fakeCmd = "grep" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
if ( tmp_cmd == "su" ) {
var fakeCmd = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
}
return exec . call ( this , cmd ) ;
} ;
exec1 . implementation = function ( cmd ) {
if ( cmd . indexOf ( "getprop" ) != - 1 || cmd == "mount" || cmd . indexOf ( "build.prop" ) != - 1 || cmd == "id" || cmd == "sh" ) {
var fakeCmd = "grep" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
if ( cmd == "su" ) {
var fakeCmd = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ;
rtlog ( "Bypass " + cmd + " command" ) ;
return exec1 . call ( this , fakeCmd ) ;
}
return exec1 . call ( this , cmd ) ;
} ;
String . contains . implementation = function ( name ) {
if ( name == "test-keys" ) {
rtlog ( "Bypass test-keys check" ) ;
return false ;
}
return this . contains . call ( this , name ) ;
} ;
var get = SystemProperties . get . overload ( 'java.lang.String' ) ;
get . implementation = function ( name ) {
if ( RootPropertiesKeys . indexOf ( name ) != - 1 ) {
rtlog ( "Bypass " + name ) ;
return RootProperties [ name ] ;
}
return this . get . call ( this , name ) ;
} ;
Interceptor . attach ( Module . findExportByName ( "libc.so" , "fopen" ) , {
onEnter : function ( args ) {
var path = Memory . readCString ( args [ 0 ] ) ;
path = path . split ( "/" ) ;
var executable = path [ path . length - 1 ] ;
var shouldFakeReturn = ( RootBinaries . indexOf ( executable ) > - 1 )
if ( shouldFakeReturn ) {
Memory . writeUtf8String ( args [ 0 ] , "/notexists" ) ;
rtlog ( "Bypass native fopen" ) ;
}
} ,
onLeave : function ( retval ) { }
} ) ;
Interceptor . attach ( Module . findExportByName ( "libc.so" , "system" ) , {
onEnter : function ( args ) {
var cmd = Memory . readCString ( args [ 0 ] ) ;
rtlog ( "SYSTEM CMD: " + cmd ) ;
if ( cmd . indexOf ( "getprop" ) != - 1 || cmd == "mount" || cmd . indexOf ( "build.prop" ) != - 1 || cmd == "id" ) {
rtlog ( "Bypass native system: " + cmd ) ;
Memory . writeUtf8String ( args [ 0 ] , "grep" ) ;
}
if ( cmd == "su" ) {
rtlog ( "Bypass native system: " + cmd ) ;
Memory . writeUtf8String ( args [ 0 ] , "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) ;
}
} ,
onLeave : function ( retval ) { }
} ) ;
BufferedReader . readLine . overload ( ) . implementation = function ( ) {
var text = this . readLine . call ( this ) ;
if ( text != null ) {
var shouldFakeRead = ( text . indexOf ( "ro.build.tags=test-keys" ) > - 1 ) ;
if ( shouldFakeRead ) {
rtlog ( "Bypass build.prop file read" ) ;
text = text . replace ( "ro.build.tags=test-keys" , "ro.build.tags=release-keys" ) ;
}
}
return text ;
} ;
ProcessBuilder . start . implementation = function ( ) {
var cmd = this . command . call ( this ) ;
var shouldModifyCommand = false ;
for ( var i = 0 ; i < cmd . size ( ) ; i = i + 1 ) {
var tmp_cmd = cmd . get ( i ) . toString ( ) ;
if ( tmp_cmd . indexOf ( "getprop" ) != - 1 || tmp_cmd . indexOf ( "mount" ) != - 1 || tmp_cmd . indexOf ( "build.prop" ) != - 1 || tmp_cmd . indexOf ( "id" ) != - 1 ) {
shouldModifyCommand = true ;
}
}
if ( shouldModifyCommand ) {
rtlog ( "Bypass ProcessBuilder " + cmd ) ;
this . command . call ( this , [ "grep" ] ) ;
return this . start . call ( this ) ;
}
if ( cmd . indexOf ( "su" ) != - 1 ) {
rtlog ( "Bypass ProcessBuilder " + cmd ) ;
this . command . call ( this , [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ] ) ;
return this . start . call ( this ) ;
}
return this . start . call ( this ) ;
} ;
if ( useProcessManager ) {
var ProcManExec = ProcessManager . exec . overload ( '[Ljava.lang.String;' , '[Ljava.lang.String;' , 'java.io.File' , 'boolean' ) ;
var ProcManExecVariant = ProcessManager . exec . overload ( '[Ljava.lang.String;' , '[Ljava.lang.String;' , 'java.lang.String' , 'java.io.FileDescriptor' , 'java.io.FileDescriptor' , 'java.io.FileDescriptor' , 'boolean' ) ;
ProcManExec . implementation = function ( cmd , env , workdir , redirectstderr ) {
var fake_cmd = cmd ;
for ( var i = 0 ; i < cmd . length ; i = i + 1 ) {
var tmp_cmd = cmd [ i ] ;
if ( tmp_cmd . indexOf ( "getprop" ) != - 1 || tmp_cmd == "mount" || tmp_cmd . indexOf ( "build.prop" ) != - 1 || tmp_cmd == "id" ) {
var fake_cmd = [ "grep" ] ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
}
if ( tmp_cmd == "su" ) {
var fake_cmd = [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ] ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
}
}
return ProcManExec . call ( this , fake_cmd , env , workdir , redirectstderr ) ;
} ;
ProcManExecVariant . implementation = function ( cmd , env , directory , stdin , stdout , stderr , redirect ) {
var fake_cmd = cmd ;
for ( var i = 0 ; i < cmd . length ; i = i + 1 ) {
var tmp_cmd = cmd [ i ] ;
if ( tmp_cmd . indexOf ( "getprop" ) != - 1 || tmp_cmd == "mount" || tmp_cmd . indexOf ( "build.prop" ) != - 1 || tmp_cmd == "id" ) {
var fake_cmd = [ "grep" ] ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
}
if ( tmp_cmd == "su" ) {
var fake_cmd = [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ] ;
rtlog ( "Bypass " + cmdarr + " command" ) ;
}
}
return ProcManExecVariant . call ( this , fake_cmd , env , directory , stdin , stdout , stderr , redirect ) ;
} ;
}
if ( useKeyInfo ) {
KeyInfo . isInsideSecureHardware . implementation = function ( ) {
rtlog ( "Bypass isInsideSecureHardware" ) ;
return true ;
}
}
var X509TrustManager = Java . use ( 'javax.net.ssl.X509TrustManager' ) ;
var HostnameVerifier = Java . use ( 'javax.net.ssl.HostnameVerifier' ) ;
var SSLContext = Java . use ( 'javax.net.ssl.SSLContext' ) ;
var TrustManager ;
try {
TrustManager = Java . registerClass ( {
name : 'org.wooyun.TrustManager' ,
implements : [ X509TrustManager ] ,
methods : {
checkClientTrusted : function ( chain , authType ) { } ,
checkServerTrusted : function ( chain , authType ) { } ,
getAcceptedIssuers : function ( ) {
return [ ] ;
}
}
} ) ;
} catch ( e ) {
rtlog ( "registerClass from X509TrustManager >>>>>>>> " + e . message ) ;
}
var TrustManagers = [ TrustManager . $new ( ) ] ;
try {
var TLS_SSLContext = SSLContext . getInstance ( "TLS" ) ;
TLS_SSLContext . init ( null , TrustManagers , null ) ;
var EmptySSLFactory = TLS_SSLContext . getSocketFactory ( ) ;
} catch ( e ) {
rtlog ( e . message ) ;
}
rtlog ( 'Custom, Empty TrustManager ready' ) ;
var SSLContext_init = SSLContext . init . overload ( '[Ljavax.net.ssl.KeyManager;' , '[Ljavax.net.ssl.TrustManager;' , 'java.security.SecureRandom' ) ;
SSLContext_init . implementation = function ( keyManager , trustManager , secureRandom ) {
rtlog ( 'Overriding SSLContext.init() with the custom TrustManager' ) ;
SSLContext_init . call ( this , null , TrustManagers , null ) ;
} ;
try {
var CertificatePinner = Java . use ( 'okhttp3.CertificatePinner' ) ;
rtlog ( 'OkHTTP 3.x Found' ) ;
CertificatePinner . check . overload ( 'java.lang.String' , 'java.util.List' ) . implementation = function ( ) {
rtlog ( 'OkHTTP 3.x check() called. Not throwing an exception.' ) ;
}
} catch ( err ) {
if ( err . message . indexOf ( 'ClassNotFoundException' ) === 0 ) {
throw new Error ( err ) ;
}
}
try {
var PinningTrustManager = Java . use ( 'appcelerator.https.PinningTrustManager' ) ;
rtlog ( 'Appcelerator Titanium Found' ) ;
PinningTrustManager . checkServerTrusted . implementation = function ( ) {
rtlog ( 'Appcelerator checkServerTrusted() called. Not throwing an exception.' ) ;
}
} catch ( err ) {
if ( err . message . indexOf ( 'ClassNotFoundException' ) === 0 ) {
throw new Error ( err ) ;
}
}
try {
var OkHttpClient = Java . use ( "com.squareup.okhttp.OkHttpClient" ) ;
OkHttpClient . setCertificatePinner . implementation = function ( certificatePinner ) {
rtlog ( "OkHttpClient.setCertificatePinner Called!" ) ;
return this ;
} ;
var CertificatePinner = Java . use ( "com.squareup.okhttp.CertificatePinner" ) ;
CertificatePinner . check . overload ( 'java.lang.String' , '[Ljava.security.cert.Certificate;' ) . implementation = function ( p0 , p1 ) {
rtlog ( "okhttp Called! [Certificate]" ) ;
return ;
} ;
CertificatePinner . check . overload ( 'java.lang.String' , 'java.util.List' ) . implementation = function ( p0 , p1 ) {
rtlog ( "okhttp Called! [List]" ) ;
return ;
} ;
} catch ( e ) {
rtlog ( "com.squareup.okhttp not found" ) ;
}
var WebViewClient = Java . use ( "android.webkit.WebViewClient" ) ;
WebViewClient . onReceivedSslError . implementation = function ( webView , sslErrorHandler , sslError ) {
rtlog ( "WebViewClient onReceivedSslError invoke" ) ;
sslErrorHandler . proceed ( ) ;
return ;
} ;
WebViewClient . onReceivedError . overload ( 'android.webkit.WebView' , 'int' , 'java.lang.String' , 'java.lang.String' ) . implementation = function ( a , b , c , d ) {
rtlog ( "WebViewClient onReceivedError invoked" ) ;
return ;
} ;
WebViewClient . onReceivedError . overload ( 'android.webkit.WebView' , 'android.webkit.WebResourceRequest' , 'android.webkit.WebResourceError' ) . implementation = function ( ) {
rtlog ( "WebViewClient onReceivedError invoked" ) ;
return ;
} ;
var HttpsURLConnection = Java . use ( "javax.net.ssl.HttpsURLConnection" ) ;
HttpsURLConnection . setDefaultHostnameVerifier . implementation = function ( hostnameVerifier ) {
rtlog ( "HttpsURLConnection.setDefaultHostnameVerifier invoked" ) ;
return null ;
} ;
HttpsURLConnection . setSSLSocketFactory . implementation = function ( SSLSocketFactory ) {
rtlog ( "HttpsURLConnection.setSSLSocketFactory invoked" ) ;
return null ;
} ;
HttpsURLConnection . setHostnameVerifier . implementation = function ( hostnameVerifier ) {
rtlog ( "HttpsURLConnection.setHostnameVerifier invoked" ) ;
return null ;
} ;
var TrustHostnameVerifier ;
try {
TrustHostnameVerifier = Java . registerClass ( {
name : 'org.wooyun.TrustHostnameVerifier' ,
implements : [ HostnameVerifier ] ,
method : {
verify : function ( hostname , session ) {
return true ;
}
}
} ) ;
} catch ( e ) {
rtlog ( "registerClass from hostnameVerifier >>>>>>>> " + e . message ) ;
}
try {
var RequestParams = Java . use ( 'org.xutils.http.RequestParams' ) ;
RequestParams . setSslSocketFactory . implementation = function ( sslSocketFactory ) {
sslSocketFactory = EmptySSLFactory ;
return null ;
}
RequestParams . setHostnameVerifier . implementation = function ( hostnameVerifier ) {
hostnameVerifier = TrustHostnameVerifier . $new ( ) ;
return null ;
}
} catch ( e ) {
rtlog ( "Xutils hooks not Found" ) ;
}
try {
var AbstractVerifier = Java . use ( "ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier" ) ;
AbstractVerifier . verify . overload ( 'java.lang.String' , '[Ljava.lang.String' , '[Ljava.lang.String' , 'boolean' ) . implementation = function ( ) {
rtlog ( "httpclientandroidlib Hooks" ) ;
return null ;
}
} catch ( e ) {
rtlog ( "httpclientandroidlib Hooks not found" ) ;
}
var TrustManagerImpl = Java . use ( "com.android.org.conscrypt.TrustManagerImpl" ) ;
try {
TrustManagerImpl . verifyChain . implementation = function ( untrustedChain , trustAnchorChain , host , clientAuth , ocspData , tlsSctData ) {
rtlog ( "TrustManagerImpl verifyChain called" ) ;
return untrustedChain ;
}
} catch ( e ) {
rtlog ( "TrustManagerImpl verifyChain nout found below 7.0" ) ;
}
try {
var OpenSSLSocketImpl = Java . use ( 'com.android.org.conscrypt.OpenSSLSocketImpl' ) ;
OpenSSLSocketImpl . verifyCertificateChain . implementation = function ( certRefs , authMethod ) {
rtlog ( 'OpenSSLSocketImpl.verifyCertificateChain' ) ;
}
rtlog ( 'OpenSSLSocketImpl pinning' )
} catch ( err ) {
rtlog ( 'OpenSSLSocketImpl pinner not found' ) ;
}
try {
var Activity = Java . use ( "com.datatheorem.android.trustkit.pinning.OkHostnameVerifier" ) ;
Activity . verify . overload ( 'java.lang.String' , 'javax.net.ssl.SSLSession' ) . implementation = function ( str ) {
rtlog ( 'Trustkit.verify1: ' + str ) ;
return true ;
} ;
Activity . verify . overload ( 'java.lang.String' , 'java.security.cert.X509Certificate' ) . implementation = function ( str ) {
rtlog ( 'Trustkit.verify2: ' + str ) ;
return true ;
} ;
rtlog ( 'Trustkit pinning' )
} catch ( err ) {
rtlog ( 'Trustkit pinner not found' )
}
} ) ;