├── .idea ├── .name ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── compiler.xml ├── kotlinc.xml ├── vcs.xml ├── AndroidProjectSystem.xml ├── migrations.xml ├── inspectionProfiles │ └── Project_Default.xml ├── deviceManager.xml ├── misc.xml ├── gradle.xml ├── modules.xml └── runConfigurations.xml ├── app ├── src │ ├── main │ │ ├── assets │ │ │ └── .gitignore │ │ ├── cpp │ │ │ ├── .gitignore │ │ │ ├── do_nothing.cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── route-linux.c.patch │ │ │ └── make_nmap.sh │ │ ├── ic_launcher-playstore.png │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── themes.xml │ │ │ │ └── strings.xml │ │ │ ├── values-night │ │ │ │ ├── colors.xml │ │ │ │ └── themes.xml │ │ │ ├── menu │ │ │ │ └── main_menu.xml │ │ │ ├── layout │ │ │ │ ├── settings_activity.xml │ │ │ │ ├── hostname_list_entry.xml │ │ │ │ ├── activity_parser.xml │ │ │ │ ├── parsed_service_view.xml │ │ │ │ ├── activity_main.xml │ │ │ │ └── parsed_host_view.xml │ │ │ ├── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ ├── root_preferences.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── drawable │ │ │ │ ├── ic_launcher_foreground.xml │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── werebug │ │ │ │ └── anmapwrapper │ │ │ │ ├── parser │ │ │ │ ├── Host.kt │ │ │ │ ├── Service.kt │ │ │ │ ├── HostnameAdapter.kt │ │ │ │ ├── ParserActivity.kt │ │ │ │ ├── ServiceAdapter.kt │ │ │ │ ├── HostAdapter.kt │ │ │ │ └── XmlOutputParser.kt │ │ │ │ ├── SettingsActivity.kt │ │ │ │ ├── NmapScan.kt │ │ │ │ ├── ImportNmapAssets.kt │ │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── werebug │ │ │ └── anmapwrapper │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── werebug │ │ └── anmapwrapper │ │ └── ExampleInstrumentedTest.java ├── .gitignore ├── proguard-rules.pro └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── gradle.properties ├── README.md ├── gradlew.bat ├── gradlew └── COPYING /.idea/.name: -------------------------------------------------------------------------------- 1 | ANmap Wrapper -------------------------------------------------------------------------------- /app/src/main/assets/.gitignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release 3 | /debug 4 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /app/src/main/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | libs 2 | nmap-7.*/ 3 | nmap-7.*.tgz 4 | openssl-3.*/ 5 | openssl-3.*.tar.gz -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/cpp/do_nothing.cpp: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | 3 | int main(int argc, char *argv[]) { 4 | printf("This program does nothing.\n"); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruvolof/anmap-wrapper/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #121212 4 | #1E1E1E 5 | -------------------------------------------------------------------------------- /.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/parser/Host.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper.parser 2 | 3 | data class Host( 4 | val hostnames: Set, 5 | val ipAddress: String, 6 | val macAddress: String?, 7 | val services: List 8 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/parser/Service.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper.parser 2 | 3 | data class Service( 4 | val port: Int, 5 | val state: String, 6 | val name: String, 7 | val product: String, 8 | val version: String 9 | ) -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 17 16:00:57 CEST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | /.idea/deploymentTargetSelector.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "ANmap Wrapper" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /.idea/deviceManager.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings_activity.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/werebug/anmapwrapper/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | #FFFFFF 12 | #F0F0F0 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/root_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/hostname_list_entry.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | cmake_minimum_required(VERSION 3.18.1) 5 | 6 | project("anmapwrapper") 7 | 8 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/nmap-7.95/) 9 | 10 | add_executable( 11 | do_nothing.so 12 | do_nothing.cpp) 13 | 14 | add_library( 15 | nmap 16 | SHARED 17 | IMPORTED) 18 | 19 | set_target_properties( 20 | nmap 21 | PROPERTIES IMPORTED_LOCATION 22 | ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libnmap.so) 23 | 24 | target_link_libraries( 25 | do_nothing.so 26 | nmap 27 | -Wl,--allow-shlib-undefined) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_parser.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/route-linux.c.patch: -------------------------------------------------------------------------------- 1 | --- nmap-7.96/libdnet-stripped/src/route-linux.c.orig 2025-05-11 11:41:25.012545026 +0200 2 | +++ nmap-7.96/libdnet-stripped/src/route-linux.c 2025-05-11 11:41:55.132147145 +0200 3 | @@ -124,6 +124,12 @@ 4 | return ret; 5 | } 6 | 7 | +static inline int ip6_is_unspecified(const ip6_addr_t *a) { 8 | + const uint32_t *p = (const uint32_t *)a->data; 9 | + return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0; 10 | +} 11 | + 12 | + 13 | int 14 | route6_add(route_t *r, const struct route_entry *entry, int intf_index) 15 | { 16 | @@ -146,7 +152,7 @@ 17 | 18 | memcpy(&rt.rtmsg_dst, &dst.addr_ip6, sizeof(rt.rtmsg_dst)); 19 | 20 | - if (!IN6_IS_ADDR_UNSPECIFIED(&entry->route_gw.addr_ip6)) { 21 | + if (!ip6_is_unspecified(&entry->route_gw.addr_ip6)) { 22 | rt.rtmsg_flags |= RTF_GATEWAY; 23 | memcpy(&rt.rtmsg_gateway, &entry->route_gw.addr_ip6, 24 | sizeof(rt.rtmsg_gateway)); 25 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/werebug/anmapwrapper/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.example.anmapwrapper", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/parser/HostnameAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper.parser 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.werebug.anmapwrapper.R 9 | 10 | class HostnameAdapter(private val data: List) : 11 | RecyclerView.Adapter() { 12 | 13 | class StringViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 14 | val textView: TextView = itemView.findViewById(R.id.hostname_entry_text_view) 15 | } 16 | 17 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StringViewHolder { 18 | val view = LayoutInflater.from(parent.context) 19 | .inflate(R.layout.hostname_list_entry, parent, false) 20 | return StringViewHolder(view) 21 | } 22 | 23 | override fun onBindViewHolder(holder: StringViewHolder, position: Int) { 24 | holder.textView.text = data[position] 25 | } 26 | 27 | override fun getItemCount(): Int = data.size 28 | } -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true 22 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.preference.PreferenceFragmentCompat 7 | 8 | class SettingsActivity : AppCompatActivity() { 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.settings_activity) 13 | if (savedInstanceState == null) { 14 | supportFragmentManager 15 | .beginTransaction() 16 | .replace(R.id.settings_frame, SettingsFragment()) 17 | .commit() 18 | } 19 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 20 | } 21 | 22 | class SettingsFragment : PreferenceFragmentCompat() { 23 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 24 | setPreferencesFromResource(R.xml.root_preferences, rootKey) 25 | } 26 | } 27 | 28 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 29 | return when (item.itemId) { 30 | android.R.id.home -> { 31 | onBackPressedDispatcher.onBackPressed() 32 | true 33 | } 34 | 35 | else -> super.onOptionsItemSelected(item) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 18 | 22 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/parser/ParserActivity.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper.parser 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.recyclerview.widget.LinearLayoutManager 7 | import com.werebug.anmapwrapper.MainActivity 8 | import com.werebug.anmapwrapper.databinding.ActivityParserBinding 9 | import java.io.File 10 | import java.io.FileInputStream 11 | 12 | class ParserActivity : AppCompatActivity() { 13 | 14 | private lateinit var binding: ActivityParserBinding 15 | private lateinit var hostAdapter: HostAdapter 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | binding = ActivityParserBinding.inflate(layoutInflater) 20 | setContentView(binding.root) 21 | 22 | binding.hostListRecyclerView.layoutManager = LinearLayoutManager(this) 23 | val hosts = FileInputStream(File(filesDir, MainActivity.XML_OUTPUT_FILE)).use { 24 | XMLOutputParser().parse(it) 25 | } 26 | hostAdapter = HostAdapter(hosts) 27 | binding.hostListRecyclerView.adapter = hostAdapter 28 | 29 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 30 | } 31 | 32 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 33 | return when (item.itemId) { 34 | android.R.id.home -> { 35 | onBackPressedDispatcher.onBackPressed() 36 | true 37 | } 38 | 39 | else -> super.onOptionsItemSelected(item) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/parser/ServiceAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper.parser 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.werebug.anmapwrapper.R 10 | 11 | class ServiceAdapter(private val serviceList: List) : 12 | RecyclerView.Adapter() { 13 | 14 | class ServiceViewHolder(view: View) : RecyclerView.ViewHolder(view) { 15 | val portIdTextView: TextView = view.findViewById(R.id.port_id_text_view) 16 | val serviceNameTextView: TextView = view.findViewById(R.id.service_name_text_view) 17 | val productVersionTextView: TextView = 18 | view.findViewById(R.id.service_product_version_text_view) 19 | val portStateTextView: TextView = view.findViewById(R.id.port_state_text_view) 20 | } 21 | 22 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ServiceViewHolder { 23 | val view = LayoutInflater.from(parent.context) 24 | .inflate(R.layout.parsed_service_view, parent, false) 25 | return ServiceViewHolder(view) 26 | } 27 | 28 | @SuppressLint("SetTextI18n") 29 | override fun onBindViewHolder(holder: ServiceViewHolder, position: Int) { 30 | val service = serviceList[position] 31 | holder.portIdTextView.text = service.port.toString() 32 | holder.portStateTextView.text = service.state 33 | holder.serviceNameTextView.text = service.name 34 | holder.productVersionTextView.text = "${service.product} ${service.version}" 35 | } 36 | 37 | override fun getItemCount(): Int = serviceList.size 38 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/parsed_service_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 23 | 24 | 31 | 32 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Nmap Wrapper 3 | Ex: nmap --help 4 | Nmap Wrapper for Android\nCreated by Werebug (https://werebug.com)\n\nThis is a wrapper around the Nmap network scanner cross-compiled for Android.\nThis is not an official Nmap application. To know more about Nmap and its features, you can visit the official website at https://nmap.org.\n\nExamples:\n\n- Scan TCP port 443 on example.com\n\tnmap -p 443 example.com\n- Scan top 50 ports and grab banners in your LAN:\n\tnmap --top-ports 50 -sV 192.168.1.1/24\n\nSome Nmap features require root privileges. If you have a rooted phone, prepend your commands with sudo to run as root:\n\tsudo nmap -O example.com\n\nSource code, cross-compilation instructions and configuration are available at https://github.com/ruvolof/anmap-wrapper. Feel free to contribute or report bugs on Github. 5 | Invalid command syntax. Example:\nnmap 192.168.1.1 6 | Invalid sudo syntax. Example:\nsudo nmap 192.168.1.1 7 | The flag %1$s is reserved for correct functioning of Nmap Wrapper for Android. 8 | Hostname(s): 9 | IPv4: 10 | MAC: 11 | Port 12 | State 13 | Name 14 | Product/Version 15 | Parse 16 | Clear 17 | Settings 18 | 19 | 20 | Experimental Features 21 | XML Parser 22 | Shows a button to parse the xml output when the scan finishes. 23 | 24 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | apply plugin: 'kotlin-android' 6 | 7 | android { 8 | compileSdk = 36 9 | 10 | defaultConfig { 11 | applicationId "com.werebug.anmapwrapper" 12 | minSdk 21 13 | targetSdk 36 14 | versionCode 16 15 | versionName "1.4.5" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | externalNativeBuild { 19 | cmake { 20 | cppFlags '' 21 | } 22 | } 23 | ndk { 24 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_17 37 | targetCompatibility JavaVersion.VERSION_17 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = "17" 42 | } 43 | 44 | packagingOptions { 45 | jniLibs { 46 | useLegacyPackaging true 47 | } 48 | } 49 | 50 | externalNativeBuild { 51 | cmake { 52 | path file('src/main/cpp/CMakeLists.txt') 53 | version '3.22.1' 54 | } 55 | } 56 | 57 | buildFeatures { 58 | viewBinding true 59 | } 60 | namespace 'com.werebug.anmapwrapper' 61 | } 62 | 63 | dependencies { 64 | implementation 'androidx.appcompat:appcompat:1.7.1' 65 | implementation 'com.google.android.material:material:1.12.0' 66 | implementation 'androidx.constraintlayout:constraintlayout:2.2.1' 67 | implementation 'androidx.navigation:navigation-fragment-ktx:2.9.3' 68 | implementation 'androidx.navigation:navigation-ui-ktx:2.9.3' 69 | implementation 'androidx.activity:activity-ktx:1.10.1' 70 | implementation 'androidx.preference:preference-ktx:1.2.1' 71 | testImplementation 'junit:junit:4.13.2' 72 | androidTestImplementation 'androidx.test.ext:junit:1.3.0' 73 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' 74 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ANmap Wrapper 2 | 3 | Nmap wrapper for Android 4 | 5 | This is not an official Nmap application. To know more about Nmap and its features visit the 6 | official homepage of the project: [https://nmap.org](https://nmap.org) 7 | 8 | ## History 9 | 10 | In 2016 I applied for a GSoC at Nmap with a bid for an Android port of Nmap. I built an ugly 11 | prototype with a cross-compiled Nmap shared library, a C wrapper and a simple Android activity to 12 | interact with it. The bid was not successful, and probably for good reasons. 13 | 14 | In 2022, I thought it would still be nice to be able to run Nmap from time to time from my 15 | smartphone. I took the original prototype, polished it a bit and packaged it. It's still ugly, but 16 | it does its job. And maybe angry user feedback will motivate me to spend some time on it. 17 | 18 | ## How to install it 19 | 20 | The application is available on 21 | [Google Play](https://play.google.com/store/apps/details?id=com.werebug.anmapwrapper). 22 | 23 | You can also install an APK from 24 | the [release page](https://github.com/ruvolof/anmap-wrapper/releases) here on GitHub. 25 | However, releases here on GitHub will generally lag behind those on Google Play. 26 | 27 | ## How to contribute 28 | 29 | Contributions are welcome. 30 | 31 | ### How to cross-compile Nmap 32 | 33 | Be aware that the script to compile Nmap is based on my development environment. You might need to 34 | fix some paths for it to be working on your system. 35 | 36 | ``` 37 | cd app/src/main/cpp 38 | ./make_nmap.sh 39 | ``` 40 | 41 | The script will do the following: 42 | 43 | 1) Download latest stable Nmap source and openssl source. 44 | 2) Configure and compile openssl and Nmap for `armeabi-v7a` and `arm64-v8a`, `x86` and `x86_64`. 45 | 3) Import Nmap resource files (like `nmap-services`) to Android assets directory. 46 | 4) Import NSE scripts included with Nmap source to Android assets directory. 47 | 48 | ### How to build the APK if you can't cross-compile Nmap 49 | 50 | 1) Download an apk from the [release page](https://github.com/ruvolof/anmap-wrapper/releases). 51 | 2) Extract the content of the APK. 52 | 3) Copy the `lib` folder inside the apk to `app/src/main/cpp/libs`. 53 | 4) Copy the `assets` folder inside the apk to `app/src/main/assets`. 54 | 5) Build the APK. 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/NmapScan.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper 2 | 3 | import android.os.Handler 4 | import android.util.Log 5 | import android.widget.Toast 6 | import java.io.IOException 7 | import java.lang.ref.WeakReference 8 | import java.nio.charset.StandardCharsets 9 | 10 | class NmapScan internal constructor( 11 | private val mainActivityRef: WeakReference, 12 | private val command: List, 13 | private val mainThreadHandler: Handler, 14 | private val libDir: String 15 | ) : Runnable { 16 | private var stopped = false 17 | 18 | override fun run() { 19 | val processBuilder = ProcessBuilder(command) 20 | processBuilder.redirectErrorStream(true) 21 | try { 22 | val process = processBuilder.start() 23 | mainThreadHandler.post { mainActivityRef.get()!!.initScanView() } 24 | val processStdout = process.inputStream 25 | var exited = false 26 | while (!exited && !stopped) { 27 | val outputByteCount = processStdout.available() 28 | if (outputByteCount > 0) { 29 | val bytes = ByteArray(outputByteCount) 30 | processStdout.read(bytes) 31 | mainThreadHandler.post { 32 | mainActivityRef.get()?.updateOutputView( 33 | String(bytes, StandardCharsets.UTF_8), false 34 | ) 35 | } 36 | } 37 | if (stopped) { 38 | process.destroy() 39 | } 40 | try { 41 | process.exitValue() 42 | exited = true 43 | } catch (ignored: IllegalThreadStateException) { 44 | } 45 | } 46 | if (stopped) { 47 | mainThreadHandler.post { 48 | Toast.makeText( 49 | mainActivityRef.get(), "Stopped.", Toast.LENGTH_SHORT 50 | ).show() 51 | } 52 | } 53 | mainThreadHandler.post { 54 | mainActivityRef.get()?.updateOutputView("", true) 55 | } 56 | } catch (e: IOException) { 57 | Log.e(MainActivity.LOG_TAG, e.message!!) 58 | mainThreadHandler.post { 59 | Toast.makeText(mainActivityRef.get(), e.message, Toast.LENGTH_LONG).show() 60 | } 61 | } 62 | } 63 | 64 | fun stopScan() { 65 | stopped = true 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/werebug/anmapwrapper/parser/HostAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.werebug.anmapwrapper.parser 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import androidx.constraintlayout.widget.ConstraintLayout 8 | import androidx.recyclerview.widget.LinearLayoutManager 9 | import androidx.recyclerview.widget.RecyclerView 10 | import com.werebug.anmapwrapper.R 11 | 12 | class HostAdapter(private val hostList: List) : 13 | RecyclerView.Adapter() { 14 | 15 | class HostViewHolder(view: View) : RecyclerView.ViewHolder(view) { 16 | val containerConstraintLayout: ConstraintLayout = 17 | view.findViewById(R.id.host_adapter_layout_container) 18 | val hostnamesRecyclerView: RecyclerView = view.findViewById(R.id.hostname_values_list_view) 19 | val ipAddressTextView: TextView = view.findViewById(R.id.ip_address_value_text_view) 20 | val macAddressLabelTextView: TextView = view.findViewById(R.id.mac_address_label_text_view) 21 | val macAddressTextView: TextView = view.findViewById(R.id.mac_address_value_text_view) 22 | val servicesRecyclerView: RecyclerView = view.findViewById(R.id.open_services_list_view) 23 | } 24 | 25 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HostViewHolder { 26 | val view = LayoutInflater.from(parent.context) 27 | .inflate(R.layout.parsed_host_view, parent, false) 28 | return HostViewHolder(view) 29 | } 30 | 31 | override fun onBindViewHolder(holder: HostViewHolder, position: Int) { 32 | val host = hostList[position] 33 | holder.ipAddressTextView.text = host.ipAddress 34 | if (host.macAddress != null) { 35 | holder.macAddressLabelTextView.visibility = View.VISIBLE 36 | holder.macAddressTextView.visibility = View.VISIBLE 37 | holder.macAddressTextView.text = host.macAddress 38 | } else { 39 | holder.macAddressLabelTextView.visibility = View.GONE 40 | holder.macAddressTextView.visibility = View.GONE 41 | } 42 | holder.hostnamesRecyclerView.layoutManager = LinearLayoutManager(holder.itemView.context) 43 | holder.hostnamesRecyclerView.adapter = HostnameAdapter(host.hostnames.toList()) 44 | holder.servicesRecyclerView.layoutManager = LinearLayoutManager(holder.itemView.context) 45 | holder.servicesRecyclerView.adapter = ServiceAdapter(host.services) 46 | val backgroundColorRes = if (position % 2 == 0) { 47 | R.color.row_background_even 48 | } else { 49 | R.color.row_background_odd 50 | } 51 | holder.containerConstraintLayout.setBackgroundResource(backgroundColorRes) 52 | } 53 | 54 | override fun getItemCount(): Int = hostList.size 55 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |