-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 38daa86
Showing
8 changed files
with
433 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Use a specific, slim Python image | ||
FROM python:3.11.4-slim | ||
|
||
# Set environment variables for optimal performance | ||
ENV PYTHONDONTWRITEBYTECODE=1 | ||
ENV PYTHONUNBUFFERED=1 | ||
|
||
# Create a non-root user | ||
RUN adduser --disabled-password --gecos "" appuser | ||
|
||
COPY requirements.txt . | ||
RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt | ||
|
||
WORKDIR /code | ||
COPY ./app /code/app | ||
|
||
# Change ownership and switch to non-root user | ||
RUN chown -R appuser:appuser /code | ||
USER appuser | ||
|
||
# Expose port 80 | ||
EXPOSE 8000 | ||
|
||
# Run Gunicorn with Uvicorn worker | ||
CMD ["gunicorn", "app.main:app", "--workers", "3", "--worker-class", "uvicorn.workers.UvicornWorker","--timeout","3000", "--bind", "0.0.0.0:8000"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Build the docker image | ||
``` | ||
docker build -t tderick/android-malware-detection:latest . | ||
``` | ||
|
||
# Run the image | ||
``` | ||
docker run -p 8080:8000 tderick/android-malware-detection:latest | ||
``` | ||
|
||
# Push to docker hub | ||
``` | ||
docker push tderick/android-malware-detection:latest | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__pycache__/ |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
from androguard.misc import AnalyzeAPK | ||
from androguard.core.apk import APK | ||
|
||
features = { | ||
'onServiceConnected': 0, | ||
'bindService': 0, | ||
'attachInterface': 0, | ||
'ServiceConnection': 0, | ||
'android.os.Binder': 0, | ||
'SEND_SMS': 0, | ||
'Ljava.lang.Class.getCanonicalName': 0, | ||
'Ljava.lang.Class.getMethods': 0, | ||
'Ljava.lang.Class.cast': 0, | ||
'Ljava.net.URLDecoder': 0, | ||
'android.content.pm.Signature': 0, | ||
'android.telephony.SmsManager': 0, | ||
'READ_PHONE_STATE': 0, | ||
'getBinder': 0, | ||
'ClassLoader': 0, | ||
'Landroid.content.Context.registerReceiver': 0, | ||
'Ljava.lang.Class.getField': 0, | ||
'Landroid.content.Context.unregisterReceiver': 0, | ||
'GET_ACCOUNTS': 0, | ||
'RECEIVE_SMS': 0, | ||
'Ljava.lang.Class.getDeclaredField': 0, | ||
'READ_SMS': 0, | ||
'getCallingUid': 0, | ||
'Ljavax.crypto.spec.SecretKeySpec': 0, | ||
'android.intent.action.BOOT_COMPLETED': 0, | ||
'USE_CREDENTIALS': 0, | ||
'MANAGE_ACCOUNTS': 0, | ||
'android.content.pm.PackageInfo': 0, | ||
'KeySpec': 0, | ||
'TelephonyManager.getLine1Number': 0, | ||
'DexClassLoader': 0, | ||
'HttpGet.init': 0, | ||
'SecretKey': 0, | ||
'Ljava.lang.Class.getMethod': 0, | ||
'System.loadLibrary': 0, | ||
'android.intent.action.SEND': 0, | ||
'Ljavax.crypto.Cipher': 0, | ||
'WRITE_SMS': 0, | ||
'READ_SYNC_SETTINGS': 0, | ||
'AUTHENTICATE_ACCOUNTS': 0, | ||
'android.telephony.gsm.SmsManager': 0, | ||
'WRITE_HISTORY_BOOKMARKS': 0, | ||
'TelephonyManager.getSubscriberId': 0, | ||
'mount': 0, | ||
'INSTALL_PACKAGES': 0, | ||
'Runtime.getRuntime': 0, | ||
'CAMERA': 0, | ||
'Ljava.lang.Object.getClass': 0, | ||
'WRITE_SYNC_SETTINGS': 0, | ||
'READ_HISTORY_BOOKMARKS': 0, | ||
'Ljava.lang.Class.forName': 0, | ||
'INTERNET': 0, | ||
'android.intent.action.PACKAGE_REPLACED': 0, | ||
'Binder': 0, | ||
'android.intent.action.SEND_MULTIPLE': 0, | ||
'RECORD_AUDIO': 0, | ||
'IBinder': 0, | ||
'android.os.IBinder': 0, | ||
'createSubprocess': 0, | ||
'NFC': 0, | ||
'ACCESS_LOCATION_EXTRA_COMMANDS': 0, | ||
'URLClassLoader': 0, | ||
'WRITE_APN_SETTINGS': 0, | ||
'abortBroadcast': 0, | ||
'BIND_REMOTEVIEWS': 0, | ||
'android.intent.action.TIME_SET': 0, | ||
'READ_PROFILE': 0, | ||
'TelephonyManager.getDeviceId': 0, | ||
'MODIFY_AUDIO_SETTINGS': 0, | ||
'getCallingPid': 0, | ||
'READ_SYNC_STATS': 0, | ||
'BROADCAST_STICKY': 0, | ||
'android.intent.action.PACKAGE_REMOVED': 0, | ||
'android.intent.action.TIMEZONE_CHANGED': 0, | ||
'WAKE_LOCK': 0, 'RECEIVE_BOOT_COMPLETED': 0, | ||
'RESTART_PACKAGES': 0, | ||
'Ljava.lang.Class.getPackage': 0, | ||
'chmod': 0, | ||
'Ljava.lang.Class.getDeclaredClasses': 0, | ||
'android.intent.action.ACTION_POWER_DISCONNECTED': 0, | ||
'android.intent.action.PACKAGE_ADDED': 0, | ||
'PathClassLoader': 0, | ||
'TelephonyManager.getSimSerialNumber': 0, | ||
'Runtime.load': 0, | ||
'TelephonyManager.getCallState': 0, | ||
'BLUETOOTH': 0, | ||
'READ_CALENDAR': 0, | ||
'READ_CALL_LOG': 0, | ||
'SUBSCRIBED_FEEDS_WRITE': 0, | ||
'READ_EXTERNAL_STORAGE': 0, | ||
'TelephonyManager.getSimCountryIso': 0, | ||
'sendMultipartTextMessage': 0, | ||
'PackageInstaller': 0, | ||
'VIBRATE': 0, | ||
'remount': 0, | ||
'android.intent.action.ACTION_SHUTDOWN': 0, | ||
'sendDataMessage': 0, | ||
'ACCESS_NETWORK_STATE': 0, | ||
'chown': 0, | ||
'HttpPost.init': 0, | ||
'Ljava.lang.Class.getClasses': 0, | ||
'SUBSCRIBED_FEEDS_READ': 0, | ||
'TelephonyManager.isNetworkRoaming': 0, | ||
'CHANGE_WIFI_MULTICAST_STATE': 0, | ||
'WRITE_CALENDAR': 0, | ||
'android.intent.action.PACKAGE_DATA_CLEARED': 0, | ||
'MASTER_CLEAR': 0, | ||
'HttpUriRequest': 0, | ||
'UPDATE_DEVICE_STATS': 0, | ||
'WRITE_CALL_LOG': 0, | ||
'DELETE_PACKAGES': 0, | ||
'GET_TASKS': 0, | ||
'GLOBAL_SEARCH': 0, | ||
'DELETE_CACHE_FILES': 0, | ||
'WRITE_USER_DICTIONARY': 0, | ||
'android.intent.action.PACKAGE_CHANGED': 0, | ||
'android.intent.action.NEW_OUTGOING_CALL': 0, | ||
'REORDER_TASKS': 0, | ||
'WRITE_PROFILE': 0, | ||
'SET_WALLPAPER': 0, | ||
'BIND_INPUT_METHOD': 0, | ||
'divideMessage': 0, | ||
'READ_SOCIAL_STREAM': 0, | ||
'READ_USER_DICTIONARY': 0, | ||
'PROCESS_OUTGOING_CALLS': 0, | ||
'CALL_PRIVILEGED': 0, | ||
'Runtime.exec': 0, 'BIND_WALLPAPER': 0, | ||
'RECEIVE_WAP_PUSH': 0, | ||
'DUMP': 0, | ||
'BATTERY_STATS': 0, | ||
'ACCESS_COARSE_LOCATION': 0, | ||
'SET_TIME': 0, | ||
'android.intent.action.SENDTO': 0, | ||
'WRITE_SOCIAL_STREAM': 0, | ||
'WRITE_SETTINGS': 0, | ||
'REBOOT': 0, | ||
'BLUETOOTH_ADMIN': 0, | ||
'TelephonyManager.getNetworkOperator': 0, | ||
'/system/bin': 0, | ||
'MessengerService': 0, | ||
'BIND_DEVICE_ADMIN': 0, | ||
'WRITE_GSERVICES': 0, | ||
'IRemoteService': 0, | ||
'KILL_BACKGROUND_PROCESSES': 0, | ||
'SET_ALARM': 0, | ||
'ACCOUNT_MANAGER': 0, | ||
'/system/app': 0, | ||
'android.intent.action.CALL': 0, | ||
'STATUS_BAR': 0, | ||
'TelephonyManager.getSimOperator': 0, | ||
'PERSISTENT_ACTIVITY': 0, | ||
'CHANGE_NETWORK_STATE': 0, | ||
'onBind': 0, | ||
'Process.start': 0, | ||
'android.intent.action.SCREEN_ON': 0, | ||
'Context.bindService': 0, | ||
'RECEIVE_MMS': 0, | ||
'SET_TIME_ZONE': 0, | ||
'android.intent.action.BATTERY_OKAY': 0, | ||
'CONTROL_LOCATION_UPDATES': 0, | ||
'BROADCAST_WAP_PUSH': 0, | ||
'BIND_ACCESSIBILITY_SERVICE': 0, | ||
'ADD_VOICEMAIL': 0, | ||
'CALL_PHONE': 0, | ||
'ProcessBuilder': 0, | ||
'BIND_APPWIDGET': 0, | ||
'FLASHLIGHT': 0, | ||
'READ_LOGS': 0, | ||
'Ljava.lang.Class.getResource': 0, | ||
'defineClass': 0, | ||
'SET_PROCESS_LIMIT': 0, | ||
'android.intent.action.PACKAGE_RESTARTED': 0, | ||
'MOUNT_UNMOUNT_FILESYSTEMS': 0, | ||
'BIND_TEXT_SERVICE': 0, | ||
'INSTALL_LOCATION_PROVIDER': 0, | ||
'android.intent.action.CALL_BUTTON': 0, | ||
'android.intent.action.SCREEN_OFF': 0, | ||
'findClass': 0, | ||
'SYSTEM_ALERT_WINDOW': 0, | ||
'MOUNT_FORMAT_FILESYSTEMS': 0, | ||
'CHANGE_CONFIGURATION': 0, | ||
'CLEAR_APP_USER_DATA': 0, | ||
'intent.action.RUN': 0, | ||
'android.intent.action.SET_WALLPAPER': 0, | ||
'CHANGE_WIFI_STATE': 0, | ||
'READ_FRAME_BUFFER': 0, | ||
'ACCESS_SURFACE_FLINGER': 0, | ||
'Runtime.loadLibrary': 0, | ||
'BROADCAST_SMS': 0, | ||
'EXPAND_STATUS_BAR': 0, | ||
'INTERNAL_SYSTEM_WINDOW': 0, | ||
'android.intent.action.BATTERY_LOW': 0, | ||
'SET_ACTIVITY_WATCHER': 0, | ||
'WRITE_CONTACTS': 0, | ||
'android.intent.action.ACTION_POWER_CONNECTED': 0, | ||
'BIND_VPN_SERVICE': 0, | ||
'DISABLE_KEYGUARD': 0, | ||
'ACCESS_MOCK_LOCATION': 0, | ||
'GET_PACKAGE_SIZE': 0, | ||
'MODIFY_PHONE_STATE': 0, | ||
'CHANGE_COMPONENT_ENABLED_STATE': 0, | ||
'CLEAR_APP_CACHE': 0, | ||
'SET_ORIENTATION': 0, | ||
'READ_CONTACTS': 0, | ||
'DEVICE_POWER': 0, | ||
'HARDWARE_TEST': 0, | ||
'ACCESS_WIFI_STATE': 0, | ||
'WRITE_EXTERNAL_STORAGE': 0, | ||
'ACCESS_FINE_LOCATION': 0, | ||
'SET_WALLPAPER_HINTS': 0, | ||
'SET_PREFERRED_APPLICATIONS': 0, | ||
'WRITE_SECURE_SETTINGS': 0 | ||
} | ||
|
||
class APKFeatureExtractor: | ||
def __init__(self, apk_path): | ||
self.apk_path = apk_path | ||
self.features = features | ||
self.dx = None | ||
self.a = None | ||
self.d = None | ||
|
||
def extract_features(self): | ||
self.a, self.d, self.dx = AnalyzeAPK(self.apk_path) | ||
self.extract_api_calls() | ||
self.extract_permissions() | ||
self.extract_intents() | ||
self.extract_commands() | ||
return self.features | ||
|
||
def extract_api_calls(self): | ||
for method in self.dx.get_methods(): | ||
for _, call, _ in method.get_xref_to(): | ||
api_call = f'{call.class_name.strip(";").replace("/",".")}.{call.name}' | ||
if api_call in self.features: | ||
self.features[api_call] = 1 | ||
|
||
def extract_permissions(self): | ||
permissions = self.a.get_permissions() | ||
for perm in permissions: | ||
perm = perm.split('.')[-1] | ||
if perm in self.features: | ||
self.features[perm] = 1 | ||
|
||
def extract_intents(self): | ||
intents = self.extract_manifest_intents(self.apk_path) | ||
for intent in intents: | ||
if intent in self.features: | ||
self.features[intent] = 1 | ||
|
||
def extract_commands(self): | ||
for method in self.dx.get_methods(): | ||
command = method.name | ||
if command in self.features: | ||
self.features[command] = 1 | ||
|
||
def extract_manifest_intents(self,apk_path): | ||
# Load the APK | ||
apk = APK(apk_path) | ||
|
||
# Get the AndroidManifest.xml | ||
manifest_xml = apk.get_android_manifest_xml() | ||
|
||
# Parse the XML | ||
root = apk.get_android_manifest_xml() | ||
intents = [] | ||
|
||
# Extract intents from <intent-filter> in <activity>, <service>, <receiver>, and <provider> | ||
for component in ['activity', 'service', 'receiver', 'provider']: | ||
for element in root.findall(f".//{component}"): | ||
for intent_filter in element.findall(".//intent-filter"): | ||
for action in intent_filter.findall(".//action"): | ||
if action is not None and '{http://schemas.android.com/apk/res/android}name' in action.attrib: | ||
intents.append(action.attrib['{http://schemas.android.com/apk/res/android}name']) | ||
|
||
return intents |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import tempfile | ||
import json | ||
from fastapi import FastAPI, File, UploadFile, HTTPException, Response | ||
|
||
from .feature_extractor import APKFeatureExtractor | ||
|
||
app = FastAPI( | ||
title="Android Malware Detection", | ||
summary="Malware Detection API using Machine Learning", | ||
description="This API is used to detect malware in Android applications using Machine Learning. Users have to submit APK file and the API will return the result of the detection (Malware or Benign).", | ||
version="0.0.1", | ||
) | ||
|
||
|
||
|
||
@app.post('/api/v1/android-malware-detection') | ||
async def android_malware_detection(file: UploadFile = File(...)): | ||
|
||
if not file.filename.endswith(".apk"): | ||
raise HTTPException(status_code=400, detail="Only APK files are allowed") | ||
|
||
# Create a temporary file in memory | ||
with tempfile.NamedTemporaryFile(delete=False, suffix=".apk") as temp_file: | ||
# Write the content of the uploaded file to the temporary file | ||
content = await file.read() # Read the file content from UploadFile | ||
temp_file.write(content) # Write the content to the temporary file | ||
|
||
# Get the path of the temporary file | ||
temp_file_path = temp_file.name | ||
|
||
rs = APKFeatureExtractor(temp_file_path).extract_features() | ||
|
||
json_response = json.dumps(rs) | ||
return Response(content=json_response,media_type="application/json") | ||
|
||
|
Oops, something went wrong.