11

I'm trying to create an exe using pyinstaller for a school project but, windows defender seems to report a virus threat and blocks the file. I want to send this exe to some other people but i wouldn't be able to do that unless I fix this. So these are my queries- Why does the exe file get reported as a virus? A quick scan on virus total says that 16 engines detect this file as a Trojan. Also, is there any way to prevent windows defender or any other antivirus from alerting users of a virus threat , I mean, is there any way to make my file look safe to antiviruses in case it was just a false threat? And in case that is not possible, what are the other safe alternatives to pyinstaller? I'm just a beginner so any tips would be really appreciated. Thanks.

EDIT: as requested by @Pro Chess, ive included my script.

import socket 
import threading
import pickle

class Server :
    def __init__(self) :
        self.HEADER = 64
        self.PORT = 5050
        self.SERVER =  socket.gethostbyname(socket.gethostname())
        self.ADDR = (self.SERVER, self.PORT)
        self.FORMAT = 'utf-8'
        self.DISCONNECT_MESSAGE = "!DISCONNECT"

        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind(self.ADDR)
        self.save_dict = {}

    def file_access(self) :
        with open("project_data\\savedata.dat","rb") as save_file :
            save_dict = pickle.load(save_file)
            return save_dict

    def file_dump(self) :
        with open("project_data\\savedata.dat","wb") as save_file :
            pickle.dump(self.save_dict,save_file)

    def recieve(self,conn) :
        msg_length = conn.recv(self.HEADER).decode(self.FORMAT)
        if msg_length:
            msg_length = int(msg_length)
            msg = conn.recv(msg_length).decode(self.FORMAT)
            return msg

    def handle_client(self,conn, addr):
        print(f"[NEW CONNECTION] {addr} connected.")

        connected = True
        while connected:
            try :
                self.save_dict = self.file_access()
                msg = self.recieve(conn)
                if msg == self.DISCONNECT_MESSAGE:
                    connected = False
                elif msg == "Save Data" :
                    player_id = conn.recv(5000)
                    try :
                        name,code = pickle.loads(player_id)
                    except EOFError :
                        pass
                    if (name,code) not in self.save_dict :
                        conn.send("Available".encode(self.FORMAT))
                        msg1 = self.recieve(conn)
                        if msg1 == "Game Data" :
                            game_data = conn.recv(5000)
                            #msg = pickle.loads(msg_data)
                            self.save_dict[(name,code)] = game_data
                            print(self.save_dict)
                            conn.send("Success".encode(self.FORMAT))
                    else :
                        conn.send("Exists".encode(self.FORMAT))
                        msg1 = self.recieve(conn)
                        if msg1 == "Game Data" :
                            game_data = conn.recv(5000)
                            self.save_dict[(name,code)] = game_data
                            conn.send("Success".encode(self.FORMAT))
                elif msg == "Wipe" :
                    self.save_dict.pop((name,code))
                    print(f"new dict is ",self.save_dict)
                elif msg == "Load" :
                    player_id = conn.recv(5000)
                    try :
                        name,code = pickle.loads(player_id)
                    except EOFError :
                        pass
                    if (name,code) in self.save_dict :
                        conn.send("Present".encode(self.FORMAT))
                        conn.send(self.save_dict[(name,code)])
                    else :
                        conn.send("Absent".encode(self.FORMAT))
                elif msg == "Check Data" :
                    player_id = conn.recv(5000)
                    try :
                        name,code = pickle.loads(player_id)
                    except EOFError :
                        pass
                    if (name,code) in self.save_dict :
                        conn.send("Exists".encode(self.FORMAT))
                    else :
                        conn.send("New".encode(self.FORMAT))
                self.file_dump()
            except ConnectionResetError :
                connected = False

        conn.close()
        print(f"[Terminated] connection terminated for {addr}")
            

    def start(self):
        self.server.listen()
        print(f"[LISTENING] Server is listening on {self.SERVER}")
        while True:
            conn, addr = self.server.accept()
            thread = threading.Thread(target=self.handle_client, args=(conn, addr))
            thread.start()
            print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")


print("[STARTING] server is starting...")
server = Server()
server.start()

Ive used the socket package to run a server on my local network.

JGZ
  • 307
  • 1
  • 3
  • 13
  • 1
    Windows Defender alerts your `exe` as a `trojan` because it identifies that your script is trying to do an outbound connection. Please include the python script that you made. – Pro Chess Nov 11 '20 at 15:16
  • @Pro Chess Thanks for the tip. I've included my script. Like you said, it involves an outbound connection. – JGZ Nov 11 '20 at 15:46

6 Answers6

13

METHOD 1

A possible solution for this would be to encrypt your code. There are several ways of encrypting your code. But the easiest one is to use base64 or basically converting text-to-binary encoding. and you need to make sure that there is no special character because base64 only have this charachter set. You can check here the base64 table https://en.wikipedia.org/wiki/Base64

import base64

your_code = base64.b64encode(b"""

# All your code goes in here.  

import socket 
import threading
import pickle

class Server :
    def __init__(self) :
        self.HEADER = 64
        self.PORT = 5050
        self.SERVER =  socket.gethostbyname(socket.gethostname())
        self.ADDR = (self.SERVER, self.PORT)
        self.FORMAT = 'utf-8'
        self.DISCONNECT_MESSAGE = "!DISCONNECT"
# Continue your code...
""")

exec(base64.b64decode(your_code))

This technique is used for hacking and other malicious purposes to avoid anti-virus software detecting it as a malware. This might work for you. Try recompiling it. Let us know if it works.

METHOD 2

If the above method doesn't work, try out this method. This method uses fernet cryptography. This means that the code is more tightly encrypted makes it even difficult for the anti-virus software to recognize this as a malware than the first method. For this, you need a python module called cryptography https://pypi.org/project/cryptography/

from cryptography.fernet import Fernet
import base64

code = b"""

import socket 
import threading
import pickle

class Server :
    def __init__(self) :
        self.HEADER = 64
        self.PORT = 5050
        self.SERVER =  socket.gethostbyname(socket.gethostname())
        self.ADDR = (self.SERVER, self.PORT)
        self.FORMAT = 'utf-8'
        self.DISCONNECT_MESSAGE = "!DISCONNECT"

        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind(self.ADDR)
        self.save_dict = {}

    def file_access(self) :
        with open("project_data\\savedata.dat","rb") as save_file :
            save_dict = pickle.load(save_file)
            return save_dict

    def file_dump(self) :
        with open("project_data\\savedata.dat","wb") as save_file :
            pickle.dump(self.save_dict,save_file)

    def recieve(self,conn) :
        msg_length = conn.recv(self.HEADER).decode(self.FORMAT)
        if msg_length:
            msg_length = int(msg_length)
            msg = conn.recv(msg_length).decode(self.FORMAT)
            return msg

    def handle_client(self,conn, addr):
        print(f"[NEW CONNECTION] {addr} connected.")

        connected = True
        while connected:
            try :
                self.save_dict = self.file_access()
                msg = self.recieve(conn)
                if msg == self.DISCONNECT_MESSAGE:
                    connected = False
                elif msg == "Save Data" :
                    player_id = conn.recv(5000)
                    try :
                        name,code = pickle.loads(player_id)
                    except EOFError :
                        pass
                    if (name,code) not in self.save_dict :
                        conn.send("Available".encode(self.FORMAT))
                        msg1 = self.recieve(conn)
                        if msg1 == "Game Data" :
                            game_data = conn.recv(5000)
                            #msg = pickle.loads(msg_data)
                            self.save_dict[(name,code)] = game_data
                            print(self.save_dict)
                            conn.send("Success".encode(self.FORMAT))
                    else :
                        conn.send("Exists".encode(self.FORMAT))
                        msg1 = self.recieve(conn)
                        if msg1 == "Game Data" :
                            game_data = conn.recv(5000)
                            self.save_dict[(name,code)] = game_data
                            conn.send("Success".encode(self.FORMAT))
                elif msg == "Wipe" :
                    self.save_dict.pop((name,code))
                    print(f"new dict is ",self.save_dict)
                elif msg == "Load" :
                    player_id = conn.recv(5000)
                    try :
                        name,code = pickle.loads(player_id)
                    except EOFError :
                        pass
                    if (name,code) in self.save_dict :
                        conn.send("Present".encode(self.FORMAT))
                        conn.send(self.save_dict[(name,code)])
                    else :
                        conn.send("Absent".encode(self.FORMAT))
                elif msg == "Check Data" :
                    player_id = conn.recv(5000)
                    try :
                        name,code = pickle.loads(player_id)
                    except EOFError :
                        pass
                    if (name,code) in self.save_dict :
                        conn.send("Exists".encode(self.FORMAT))
                    else :
                        conn.send("New".encode(self.FORMAT))
                self.file_dump()
            except ConnectionResetError :
                connected = False

        conn.close()
        print(f"[Terminated] connection terminated for {addr}")
            

    def start(self):
        self.server.listen()
        print(f"[LISTENING] Server is listening on {self.SERVER}")
        while True:
            conn, addr = self.server.accept()
            thread = threading.Thread(target=self.handle_client, args=(conn, addr))
            thread.start()
            print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")


print("[STARTING] server is starting...")
server = Server()
server.start()

"""

key = Fernet.generate_key()
encryption_type = Fernet(key)
encrypted_message = encryption_type.encrypt(code)

decrypted_message = encryption_type.decrypt(encrypted_message)

exec(decrypted_message)

This time the compiled exe was uploaded to https://www.virustotal.com/gui/ and the results were better

METHOD 3 - Use another method to freeze your code

There are many methods to convert your code to an exe. Another most popular way to freeze your code is to use py2exe. Install the module from the pypi website.

  1. Create a new python file called setup.py in the same directory as your main code file. Then paste following in your setup.py file.
from distutils.core import setup
import py2exe

setup(console=['main.py'])
  1. Open cmd and type python setup.py py2exe

  2. After a while, a folder named dist will be created. It will contain all dependencies for your exe.

  3. Now you can compress this file by zipping it and send it to another person. Another possible solution is to use a compiler like InnoSetup to compile all your exe and the dependencies into a single msi file.

Community
  • 1
  • 1
Pro Chess
  • 831
  • 1
  • 8
  • 23
  • Almost exactly the kind of answer i was looking for. This time, windows defender didnt report anything but when i ran it through virus total, [I still got 16 out of 72 hits](https://www.virustotal.com/gui/file/afbc8c26b3e0db0a0e23c0f521ca707b3826ca75415f2bd705dd5dd910b37117/detection). So though it did not work like i wanted it to, i still think it was a good answer. Do you have any other methods that may work? – JGZ Nov 12 '20 at 10:27
  • Do you have any other anti-virus software with real-time protection on? If so, does it give any sort of alert that the `exe` is a malware. – Pro Chess Nov 12 '20 at 13:05
  • Another possible solution is to sign your `exe`. After you have signed an `exe` it will not show `Unknown Publisher`. This might lower the chance of an anti-virus software detecting it as a malware – Pro Chess Nov 12 '20 at 13:09
  • Check the following links to get an idea on how to sign an `exe`. https://stackoverflow.com/questions/252226/signing-a-windows-exe-file https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool?redirectedfrom=MSDN – Pro Chess Nov 12 '20 at 13:12
  • Upload your `exe` to https://nodistribute.com/ and check how many AVs detect it as a malware. – Pro Chess Nov 12 '20 at 13:30
  • wouldnt i need a code sign certificate to sign my ```exe```? also, wouldnt that cost a bit? i was looking for a free solution since im making this just for a small school project. – JGZ Nov 12 '20 at 13:35
  • I tried uploading my ```exe``` to nodistribute but apparently they only scan files upto 8mb for free. It costs to upload larger files. – JGZ Nov 12 '20 at 13:39
  • 1
    I compiled your code using pyinstaller without even encrypting it in linux. https://www.virustotal.com/gui/file/17802ff54c98cbfbdb9db314b6daac7ec55e42074139c5f09a45e7c9109a7853/detection It seems that only one engine detected it. – Pro Chess Nov 12 '20 at 13:43
  • could you explain why that happened? Also, making one on linux wont help me as I specifically want an exe file. Still, thanks for trying. also, as you were asking for how many avs detect my file on nodistribute, i uploaded it to metadefender and got [3 hits](https://metadefender.opswat.com/results/file/bzIwMTExMmJsNmhBUUs0bk9EOExBVnFHZEM/regular/overview?lang=en) – JGZ Nov 12 '20 at 14:25
  • 1
    I edited my answer with another method. Please try that out. Most reputable AV software like `AVG, McAffee, Avast, Kaspersky, Avira, MalwareBytes` did not detect it as a malware. You can now send your `exe` to others. Because mostly people use the above mentioned AV software. – Pro Chess Nov 12 '20 at 16:02
  • Thanks. I'll check it out. Could u provide a google drive link or something to the ```exe``` you compiled too? – JGZ Nov 12 '20 at 18:45
  • @Jovan https://drive.google.com/file/d/1SCZXUTjhnGbax3a5Cm12XjjC0BrHybZf/view?usp=sharing That is the link to the `exe` that I compiled. – Pro Chess Nov 13 '20 at 14:12
3

Check if any .exe files being created in the temp folder. If yes, then this will be one of the reason for your anti-virus to detect malware.
Usually, if a program accesses temp folder, it will be considered as a potentially harmful application and flagged. It's the behaviour of malwares, it gains access to temp, and starts to control the pc from temp.
And that's why if you've observed, anti-virus clears the temp folder if you run it as temp folder is the one that contains most of the bulshits in it.
Make sure you that you place no executables in the temp folder.

theycallmepix
  • 474
  • 4
  • 13
2

UTF-8 Problem SOLVED

If you have any UTF-8 characters in your code converting to bytes not work.

SyntaxError: bytes can only contain ASCII literal characters.

to SOLVE this

import base64

def encode(data):
    try:
        # Standard Base64 Encoding
        encodedBytes = base64.b64encode(data.encode("utf-8"))
        return str(encodedBytes, "utf-8")
    except:
        return ""
    
def decode(data):
    try:
        message_bytes = base64.b64decode(data)
        return message_bytes.decode('utf-8')
    except:
        return ""

your_code = encode("""

# coding: utf-8

# In[2]:


import os
import requests
import time
import pickle



Your code here.........
""")

exec(decode(your_code))
Kamran Gasimov
  • 1,445
  • 1
  • 14
  • 11
2

This problem occurs because pyinstaller compiling is similar a trojan and windows defender knows the exe file as a malicious file. If you use nuitka for compiling of your code, this problem is solved. I suggest you read this. https://github.com/Nuitka/Nuitka Have a nice day.

2

I had the same problem, and then I figured out, it was just because I've used docstrings for multiple lines comments,
if you have it get ride of it and you will be fine

""" code here......... """

  • This seemed like a silly solution when I first read it, but it really works! I reduced all my docstrings to single lines, and my EXE is no longer seen as a virus. – Opus4210 Mar 24 '23 at 00:24
1

I had the same issue when compiling with pyinstaller -F option (to have everything in one exe file instead of a folder with a lot of files).

With this option, virustotal score was 12/70.

I did it again without the -F option : the result is bigger (from 9MB to 20MB) but the virustotal score is now 2/70.

Laurent C.
  • 196
  • 1
  • 9