Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tderick committed Oct 24, 2024
0 parents commit 38daa86
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
venv
25 changes: 25 additions & 0 deletions Dockerfile
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"]
14 changes: 14 additions & 0 deletions README.md
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
```
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__/
Empty file added app/__init__.py
Empty file.
280 changes: 280 additions & 0 deletions app/feature_extractor.py
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
36 changes: 36 additions & 0 deletions app/main.py
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")


Loading

0 comments on commit 38daa86

Please sign in to comment.