Skip to content

Instantly share code, notes, and snippets.

@evilthreads669966
Last active October 3, 2020 00:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evilthreads669966/61607530b32e58a7f430ea13b0f3fe24 to your computer and use it in GitHub Desktop.
Save evilthreads669966/61607530b32e58a7f430ea13b0f3fe24 to your computer and use it in GitHub Desktop.
Heuristic Analysis
Heuristic analysis is a method employed by many computer antivirus programs designed to detect previously unknown computer viruses, as well as new variants of viruses already in the "wild". - wikipedia

Android anti-virus products lack the ability to analyze applications beyond the manifest.xml file's declared permissions and whether it has a process running or not. - Chris Basinger

Heuristic Evasion

Heuristic evasion on Android to me is more of avoiding sandboxing. In its' simplest form a sandbox environment in mobile is nothing more than an emulator or a virtual machine as most people know it. This allows the cyber security analyst or whomever they may be to analyze an application with logging ability, automatic rooting of a device, and access to software programs for debugging such as ADB which allows for utility programs to be ran.

After this, assuming you have successfully canceled out the scenario of ending up in a sandbox, there is several other things to factor into your algorithm of safety. Although you would assume that any knowledgable malware developer would supress all logging calls it is still a possibility that something could end up in the log file. So this leaves you with evading logging. Evading logging capabilities of a user of your software includes numerous checks. However, all of these checks stem from one thing and that is Android Debug Bridge or better known in its' acronymn form as ADB. One thing that is constant in the use of ADB is that it is accessed through a computer.

The most common way to enable ADB on an Android device is to connect it to a computer with a USB cord. By checking whether the device has any devices connected to it before running any malicious code allows us to avoid the user from accessing the log files through this method. Android provides developers with a class named USBManager. This class has a method that allows for you to find out whether there are any devices connected to this device through USB but not what type of device. Another way of preventing ADB through a USB connection is to just check whether the Android device is charging. By checking whether it is charging it allows for you to be even safer.

@RequiresApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private fun Context.hasUsbDevices() = (this.getSystemService(Context.USB_SERVICE) as UsbManager).deviceList.isNotEmpty()
private fun Context.isConnected(): Boolean {
  val intent = this.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
  val plugged = intent!!.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
  if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
      return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS
  return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB
}

Another way of enabling ADB on an Android device is through a WiFi connection on a local area network. The way this works is your first have to connect your Android device to the computer with a USB cord using one of the numerous apps available on the play store for this purpose. Upon doing this you have the ability to connect to your phone over WiFi using your computer as long the ADB WiFi app is running on your phone. The most important thing to note is that this always opens up port 5555 on your Android phone. This one important fact about ADB over WiFi allows us to do something to detect whether a device is using this technology. The way we do this is to cheeck whether port 5555 is open on the wireless network interface card. If this port is open then it is not safe to run any malicious code.

This wasn't working for me. But I wanted to point you in the right direction.

private fun Context.hasAdbOverWifi(): Boolean{
  var isOpen = false
  val mgr = this.getSystemService(Context.WIFI_SERVICE) as WifiManager
  if(!mgr.isWifiEnabled)
      return isOpen
  val job = Thread {
      NetworkInterface.getNetworkInterfaces().asSequence().forEach { networkInterface ->
          networkInterface.inetAddresses.asSequence().forEach { addresses ->
              if (addresses.isLoopbackAddress && addresses.toString().contains(".")) {
                  Log.d("EVADE", addresses.toString())
                  runCatching {
                      SocketFactory.getDefault().createSocket(addresses.hostAddress.toString().split("/")[1], 5555).close()
                      isOpen = true
                  }
              }
          }
      }
  }
  job.run()
  job.join()
  return isOpen
}

Network analysis is also a major topic in heuristic evasion on Android. There are several wways of anaylzing an Android device's network traffic. One way is to use a firewall which logs IP addresses and port numbers for all applications. A second one is any port scanning or networking utility apps that are available on the Google Play Store. In order to accomplish avoid either of these two types of apps you must analyze the installed software on the device. This simply involves querying the package manager on the device and checking whether any apps contain the word firewall in their titles. You would also check for names like networking utlity, port scan, etcetera. In the near future this will be a problem for the malware developer as Google has patched this in their upcoming Android 11 release. They have removed the ability to query the package manager without declaring what you are looking for in your manifest file which means that this type of evasion will soon be deprecated.

private fun Context.hasFirewall(): Boolean {
  lateinit var packages: List<PackageInfo>
  if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
      packages = this.packageManager.getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES)
  else
      packages = this.packageManager.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)
  packages.forEach { app ->
      val name = app.packageName.toLowerCase()
      if (name.contains("firewall") || name.contains("adb")
          || name.contains("port scanner") || name.contains("network scanner")
          || name.contains("network analysis") || name.contains("ip tools")
          || name.contains("net scan") || name.contains("network analyzer"))
          return true
  }
  return false
}

The final way to analyze network traffic on an Android device is through a proxy server using a VPN. Luckily for the malware developer it is possible to detect this. Android's API provides the developer with the ability to check the networking capabilities of all network interfaces. By iterating through the available network interfaces you can simply check for each one whether there is an active VPN connection running through it.

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun Context.hasVPN(): Boolean{
  val mgr = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
  mgr.allNetworks.forEach {network ->
      val capabilities = mgr.getNetworkCapabilities(network)
      if(capabilities!!.hasTransport(NetworkCapabilities.TRANSPORT_VPN))
          return true
  }
  return false
}

Another thing to avoid when developing your malware is a rooted device. Now this is speculative as a rooted device allows you many advantages from the perspective of the malware developer. For example rooted devices allow you to enable an accessibility service for an application without the user's knowledge or permission. Now the reason for evading rooted devices is one of the most debatable ones. How can you trust whether you'll be safe knowing that the device's owner has super user.

private fun Context.isRooted() = RootBeer(this).apply { setLogging(false) }.isRooted

Now last but not least is checking whether the device is an emulator or not. Android has a Build class with many different properties involving the hardware on the device. By checking manufacturer names and other product identifiers regarding the phone you can find out whether it is indeed a real device.

private val isEmulator = (Build.DEVICE.contains("generic")
      || Build.FINGERPRINT.contains("generic")
      || Build.MODEL.contains("google_sdk")
      || Build.MODEL.contains("Emulator")
      || Build.MODEL.contains("Android SDK built for x86")
      || Build.BOARD == "QC_Reference_Phone"
      || Build.MANUFACTURER.contains("Genymotion")
      || Build.HOST.startsWith("Build") //MSI App Player
      || (Build.BRAND.startsWith("generic") || Build.DEVICE.startsWith("generic"))
      || Build.HARDWARE.contains("goldfish")
      || Build.HARDWARE.contains("ranchu")
      || Build.PRODUCT.contains("sdk_google")
      || Build.PRODUCT.contains("google_sdk")
      || Build.PRODUCT.contains("full_x86")
      || Build.PRODUCT.contains("sdk")
      || Build.PRODUCT.contains("sdk_x86")
      || Build.PRODUCT.contains("vbox86p")
      || Build.PRODUCT.contains("emulator")
      || Build.PRODUCT.contains("simulator"))

I have created an Android library that allows you to perform all of these checks before running any code. This will prevent your software from being analyzed and tested. Of course there is always static code analysis tactics through reverse engineering. I cannot protect your code from this type of analysis in code obviously but there is a library called EvadeMe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment