import warnings
warnings.filterwarnings("ignore")
from flask import Flask, render_template, request, redirect, Response
from werkzeug.utils import secure_filename
import os
import csv
import pandas as pd
from androguard.core.bytecodes import apk
import sys
from androguard.misc import AnalyzeAPK
from collections import defaultdict
import numpy as np
import collections
import pickle
from sklearn.preprocessing import LabelEncoder
from sklearn import svm
from keras.models import load_model
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from keras.models import model_from_json
import shutil
import random as python_random

app = Flask(__name__)

@app.route('/')

def server():
   
   return 'Server is up'

@app.route('/upload', methods = ['POST'])

def upload_file():                              #This method fetches the user uploaded APK file and store to a variable file   

   if request.method == 'POST':
      f = request.files['files']
      f.save(secure_filename(f.filename))
      print("File: ", f.filename ," has been fetched to directory.")
      
   file = f.filename
   sc = StandardScaler()

   np.random.seed(42)
   python_random.seed(42)


#------------------------------------------Feature Extraction--------------------------------------------------------


   def getServices(file):                      #Extracting Services from the user uploaded file
        
      data = pd.read_csv('DefaultServiceList.txt',sep='\n',header=None)
      data = data.set_index(0).T
      data.to_csv(os.path.splitext(file)[0] + '_services.csv', index = False)

      df = pd.read_csv(os.path.splitext(file)[0] + "_services.csv")
      new_row = dict.fromkeys(df, 0)    
      #new_row[df.columns[0]] = file
      df = df.append(new_row, ignore_index = True)              
      try:
          a, d, dx = AnalyzeAPK(file)
          
          for col in df.columns:
              for service in a.get_services():
                  if(col == service):
                      df.loc[0, col] = 1
                 

      except Exception:
          print(Exception)

      df.to_csv(os.path.splitext(file)[0] + '_services.csv', index = False)            #Writing services to the csv file
      print("Services Extracted")
                          
   def getPermissions(file):                 #Extracting Permissions from the user uploaded file

      data = pd.read_csv('DefaultPermissionList.txt',sep='\n',header=None)
      data = data.set_index(0).T
      data.to_csv(os.path.splitext(file)[0] + '_permissions.csv', index = False)

      df = pd.read_csv(os.path.splitext(file)[0] + "_permissions.csv")
      new_row = dict.fromkeys(df, 0)    
      #new_row[df.columns[0]] = file
      df = df.append(new_row, ignore_index = True)           
      perm_list = []
      
      try:
          a, d, dx = AnalyzeAPK(file)
          perm_list = a.get_permissions()
          
          for col in df.columns:
              for index in perm_list:
                  if(col == index):
                      df.loc[0, col] = 1
                 

      except Exception as e:
          print(e)

      df.to_csv(os.path.splitext(file)[0] + '_permissions.csv', index = False)        #Writing Permissions to the csv file
      print("Permissions Extracted")

   def getIntents(file):                                #Extracting Intents from the user uploaded file

      data = pd.read_csv('DefaultIntentList.txt',sep='\n',header=None)
      data = data.set_index(0).T
      data.to_csv(os.path.splitext(file)[0] + '_intents.csv', index = False)

      df = pd.read_csv(os.path.splitext(file)[0] + "_intents.csv")
      new_row = dict.fromkeys(df, 0)    
      #new_row[df.columns[0]] = file
      df = df.append(new_row, ignore_index = True)
      intent_list = []
      
      try:
          a, d, dx = AnalyzeAPK(file)
          
          for receiver in a.get_receivers():
              for action,intent_name in a.get_intent_filters('receiver', receiver).items():
                  for intent in intent_name:
                      intent_list.append(intent)
          
          for col in df.columns:
              for index in intent_list:
                  if(col == index):
                      df.loc[0, col] = 1
                 

      except Exception:
          print(Exception)

      df.to_csv(os.path.splitext(file)[0] + '_intents.csv', index = False)          #Writing Intents to the csv file
      print("Intents Extracted")


   def Ngram(file):                                      #Extracting Onegram and Bigram Opcodes from the user uploaded file

        OpCode = []
        standard_codes = "ops.txt"
        apktool = 'apktool.jar'
        def GramOpcodes(fq, n):
              ngrams_list = []
         
              for num in range(0, len(fq)):
                    ngram = ' '.join(fq[num:num + n])
                    ngrams_list.append(ngram)
              return ngrams_list

        def bigramDictionary(each_apk, bigrams, label):

              bigramfreq= collections.defaultdict(str) 
              bigramfreq["NAME"] = each_apk
              bigramfreq["Bigram"] = bigrams
              bigramfreq["LABEL"] = label
                        
              return bigramfreq

        def onegramDictionary(each_apk, onegram, label):

            onegramfreq= collections.defaultdict(str)    
            onegramfreq["NAME"] = each_apk
            onegramfreq["Onegram"] = onegram
            onegramfreq["LABEL"] = label

            return onegramfreq
                    

        def onegram_csv(onegramfreq):                              #Writing Onegram OpCodes to the csv file
              
            with open('onegram_test.csv', 'a') as f:
                writer = csv.writer(f)
                
                if(os.stat("onegram_test.csv").st_size == 0):
                     writer.writerow(list(onegramfreq.keys()))           
                writer.writerow(list(onegramfreq.values()))

        def bigram_csv(bigramfreq):                               #Writing Bigram OpCodes to the csv file
              
            with open('bigram_test.csv', 'a') as f:
                writer = csv.writer(f)
                
                if(os.stat("bigram_test.csv").st_size == 0):
                     writer.writerow(list(bigramfreq.keys()))
                writer.writerow(list(bigramfreq.values()))


        def match_op_codes(std_codes, smali_content, each_apk):

            onegrams = []
            for each_smali in smali_content:
                for each_line in each_smali:
                    for each_word in each_line:
                        if(each_word==''):
                            continue
                        for each_code in std_codes:
                            if (each_word)==(each_code):
                                onegrams.append(each_code)


        def std_codes_list(standard_codes):

            std_codes = [] #empty list which will later contain all the standard op-codes read from the ops.txt file

            with open(standard_codes,'r') as fp:
                for cnt, line in enumerate(fp): # reading each op-code in the txt file
                    read_lines = fp.read();
                    read_lines = read_lines.split("\n")
                    std_codes = read_lines
                    std_codes.pop() #last element is '' blank, so pop it and return the final list

            return std_codes
            
        std_codes = std_codes_list(standard_codes)


        def parse_smalis(app_dir,each_apk):

            smali_content = [] #Empty list that will later contain all of the content of all of the smali files.
            temp = []
            
            for root, dirs, files in os.walk(app_dir+ "/" + each_apk +"/smali"): #Scanning through each file in each sub-directory
                for file in files:
                    if file.endswith(".smali"):
                        file_dest= os.path.join(root, file)
                        with open(file_dest,'r' , encoding="utf8") as fp:
                            temp = fp.readlines()
                            temp = [line.rstrip('\n').split(" ") for line in temp] #store the contents of a file
                            smali_content.append(temp)
                    
            return smali_content 

                          

        def match_op_codes(std_codes, smali_content, each_apk):

            onegrams = []
            for each_smali in smali_content:
                for each_line in each_smali:
                    for each_word in each_line:
                        if(each_word==''):
                            continue
                        for each_code in std_codes:
                            if (each_word)==(each_code):
                                onegrams.append(each_code)

                                

            unique = onegrams
            unique = list(dict.fromkeys(unique))
            return unique
                
        try:

              base=os.path.basename(file)
              new = os.path.splitext(base)[0]
              os.system("java -jar " + apktool + " d " + file)
              #print(os.path.dirname(os.path.abspath(file)))
              smali_content = parse_smalis(os.path.dirname(os.path.abspath(file)),new)
              fq = match_op_codes(std_codes,smali_content,new)
              label = "APK"
              onegramfreq= onegramDictionary(file, fq, label)
              onegramcsv= onegram_csv(onegramfreq)
                         
              bigrams= GramOpcodes(fq, 2)
              bigramfreq= bigramDictionary(file, bigrams, label)
              bigramcsv= bigram_csv(bigramfreq)
              shutil.rmtree(new)
              
                
              #print(op)
              print('Ngrams Extracted')
              
        except Exception as e:
              print(e)
                
     
   #------------------------------------------Feature Extraction End--------------------------------------------------------  
     
     
     
   #----------------------------------------------------Classification--------------------------------------------------------  
        
        
   def load_model_p():                                      #Loading Permissions Model
        filename = 'PermissionsClassifier.sav'
        loaded_model = pickle.load(open(filename, 'rb'))
        return loaded_model
   loaded_model_p = load_model_p()

   def load_model_s():                                      #Loading Services Model
      # load JSON and create model
        filename = 'ServicesClassifier.sav'
        loaded_model = pickle.load(open(filename, 'rb'))
        return loaded_model
   loaded_model_s = load_model_s()

   def load_model_i():                                      #Loading Intents Model
      # load JSON and create model
        filename = 'IntentsClassifier.sav'
        loaded_model = pickle.load(open(filename, 'rb'))
        return loaded_model
   loaded_model_i = load_model_i()


   def load_model_onegram():                               #Loading Onegram Model
      # load json and create model
      json_file = open('model_onegram_lstm.json', 'r')
      loaded_model_json = json_file.read()
      json_file.close()
      loaded_model = model_from_json(loaded_model_json)
      # load weights into new model
      loaded_model.load_weights("model_onegram_lstm.h5")
      return loaded_model
   load_model_onegram = load_model_onegram()
      

   def load_model_bigram():                               #Loading Bigram Model
      # load json and create model
      json_file = open('model_bigram_lstm.json', 'r')
      loaded_model_json = json_file.read()
      json_file.close()
      loaded_model = model_from_json(loaded_model_json)
      # load weights into new model
      loaded_model.load_weights("model_bigram_lstm.h5")
      return loaded_model
   load_model_bigram = load_model_bigram()

   def onegram_prediction(labelencoder_y):                      #Getting Prediction from Onegram Model
      onegram_data = pd.read_csv("onegram_test.csv")
      X4= onegram_data['Onegram']
      vectorizer = TfidfVectorizer(ngram_range = (1,1),max_features=1000)
      X4 = vectorizer.fit_transform(X4)
      X4 = sc.fit_transform(X4.toarray())
      X4= X4.reshape(-1, 1, X4.shape[1])
      pred=load_model_onegram.predict(X4)
      pred = np.argmax(pred,axis=1)
      pred = labelencoder_y.inverse_transform(pred)
      return pred


   def bigram_prediction(labelencoder_y):                      #Getting Prediction from Bigram Model
      bigram_data = pd.read_csv("bigram_test.csv")
      X5= bigram_data['Bigram']
      vectorizer = TfidfVectorizer(ngram_range = (2,2),max_features=200)
      X5 = vectorizer.fit_transform(X5)
      X5 = sc.fit_transform(X5.toarray())
      pred = np.argmax(load_model_bigram.predict(X5.reshape(-1, 1, X5.shape[1])) ,axis=1)
      pred = labelencoder_y.inverse_transform(pred)
      return pred

        
        

   getServices(file)
   getPermissions(file)
   getIntents(file)
   Ngram(file)


   X1 = pd.read_csv(os.path.splitext(file)[0] + "_services.csv")
   X2 = pd.read_csv(os.path.splitext(file)[0] + "_permissions.csv") 
   X3 = pd.read_csv(os.path.splitext(file)[0] + "_intents.csv")


   labels = ["Benign", "Adware", "Trojan", "Ransomware"]    

   labelencoder_y = LabelEncoder()
   labelencoder_y.fit_transform(labels)


   #Getting Predictions from Services, Permissions and Intents Models
   prediction_from_services = loaded_model_s.predict(X1)           
   prediction_from_permissions = loaded_model_p.predict(X2)
   prediction_from_intents = loaded_model_i.predict(X3)

   #Inverse transforming the labels for getting results in string form
   permissions_result = labelencoder_y.inverse_transform(prediction_from_permissions)
   services_result = labelencoder_y.inverse_transform(prediction_from_services)
   intents_result = labelencoder_y.inverse_transform(prediction_from_intents)
   onegram_result = onegram_prediction(labelencoder_y)
   bigram_result =  bigram_prediction(labelencoder_y)


   #Removing APK file + csv files after getting predictions
   os.remove(os.path.splitext(file)[0] + "_permissions.csv")
   os.remove(os.path.splitext(file)[0] + "_services.csv")
   os.remove(os.path.splitext(file)[0] + "_intents.csv")
   #os.remove(os.path.splitext(file)[0] + "_onegram.csv")
   #os.remove(os.path.splitext(file)[0] + "_bigram.csv")
   os.remove(f.filename)

   print(services_result[0])
   print(permissions_result[0])
   print(intents_result[0])
   print(onegram_result[-1])
   print(bigram_result[-1])


   #Storing resullts from all models in a list
   countList = []
   for sval in permissions_result:
      for pval in services_result:
          for ival in intents_result:
                countList= [sval, pval, ival]

   countList.append(onegram_result[-1])
   countList.append(bigram_result[-1])

   benign_count = 0
   adware_count = 0
   ransomware_count = 0
   trojan_count = 0

   malwares=["Adware", "Trojan", "Ransomware"]

   #Getting number of Benign, Adware, Ransomware and Trojan
   for val in countList:
      if(val=="Benign"):
          benign_count= benign_count + 1
      if(val=="Adware"):
          adware_count= adware_count + 1
      if(val=="Trojan"):
          trojan_count= trojan_count + 1
      if(val=="Ransomware"):
          ransomware_count= ransomware_count + 1

   #Voting Process. Designed in a way that it prevents mislabeling of Malwares as Benign and vice versa

   #Priortizing preductions from onegram and bigram models since they have higher accuracies.
   #If results from both onegram and bigram models are same, then that result would be finalized.
   if(onegram_result[-1]==bigram_result[-1]):
        if(onegram_result[-1] in malwares):
              print("Your application is Malware effected! System Detected Malware Type: "+ onegram_result[-1])
              return "Your application is Malware effected! System Detected Malware Type: "+ onegram_result[-1]
        else:
              print("Congratulations! Your application is malware free")
              return "Congratulations! Your application is malware free"
      
   #If results from both onegram and bigram models are not same but both belong to Malware cateogory, then:
   #checking for each malware category's overall votes and if none gets the majority, then going with Bigram Model's prediction  
   elif(onegram_result[-1] in malwares and bigram_result[-1] in malwares):
        if(adware_count > ransomware_count and adware_count > trojan_count):
              print("Your application is Malware effected! System Detected Malware Type: Adware!")
              return "Your application is Malware effected! System Detected Malware Type: Adware!"
        elif(ransomware_count > adware_count and ransomware_count > trojan_count):
              print("Your application is Malware effected! System Detected Malware Type: Ransomware!")
              return "Your application is Malware effected! System Detected Malware Type: Ransomware!"
        elif(trojan_count > adware_count and trojan_count > ransomware_count):
              print("Your application is Malware effected! System Detected Malware Type: Trojan!")
              return "Your application is Malware effected! System Detected Malware Type: Trojan!"
        else:
              print("Your application is Malware effected! System Detected Malware Type: "+ onegram_result[-1])
              return "Your application is Malware effected! System Detected Malware Type: "+ onegram_result[-1]

   #If first two conditions are not met, then checking for Benign's majority
   elif(benign_count > adware_count and benign_count > ransomware_count and benign_count > trojan_count):
        print("Congratulations! Your application is malware free")
        return "Congratulations! Your application is malware free"

   elif(adware_count > ransomware_count and adware_count > trojan_count):
        print("Your application is Malware effected! System Detected Malware Type: Adware!")
        return "Your application is Malware effected! System Detected Malware Type: Adware!"

   elif(ransomware_count > adware_count and ransomware_count > trojan_count):
       print("Your application is Malware effected! System Detected Malware Type: Ransomware!")
       return "Your application is Malware effected! System Detected Malware Type: Ransomware!"

   elif(trojan_count > adware_count and trojan_count > ransomware_count):
       print("Your application is Malware effected! System Detected Malware Type: Trojan!")
       return "Your application is Malware effected! System Detected Malware Type: Trojan!"

   #Lastly, if none of the above conditions are met, then going with our most accurate model i.e, Onegram Model's prediction
   else:
        if(onegram_result[-1] in malwares):
              print("Your application is Malware effected! System Detected Malware Type: "+ onegram_result[-1])
              return "Your application is Malware effected! System Detected Malware Type: "+ onegram_result[-1]
        else:
              print("Congratulations! Your application is malware free")
              return "Congratulations! Your application is malware free"


app.run(host="0.0.0.0", port=5000, debug=False)

