Struggling with multiple programming languages? No worries. Our Code Converter has got you covered. Give it a go!
In this tutorial, you'll learn how to remove persistent malware using Python. Persistent malware is malicious software designed to maintain a continuous presence on an infected system, even after system reboots, software reinstallations, or user logouts.
When really experienced hackers write malware, they often include persistence so that if or when the target realizes that their computer has been compromised and deletes the suspected file, or even unwittingly just restart their computer, their malware will still be running on the system — they'll still have access to the system.
You may or may not know, but every time you log in or start your computer, some applications start running automatically, without user interaction.
This is the feature that hackers exploit. They program their malware in such a way that it copies itself to a somewhat discrete location - like AppData
(on Windows) and /root/.config
(on Linux), including the copy to the section (in the OS) where those applications get executed automatically. This way, every time you start your computer, their malware will get executed.
By the way, If you want to persist your Python program, then check this tutorial.
Table of contents:
On Windows, those files that are executed automatically at startup are stored in the registry. To access the said registry, on your search bar, type in regedit. You should see:
From here, open up HKEY_CURRENT_USER > SOFTWARE > Microsoft > Windows > CurrentVersion > Run
You should see a display similar to:
All these applications at the right part of the screen are executed at system startup. So ideally, a hacker would make a copy of their malware (placing it in a discrete location), and refer it here so that every time your system comes on, the malware gets executed. Mind you, achieving this is very simple, we cover it in this tutorial.
Let's say you notice a suspicious file that wasn't there, you google it and you don't see any relevant indications that it's an expected file. The next step would be to remove it. You can just right-click and delete it, then trace the file it's referencing for execution and also delete it. But we're going to save the hassle of always having to navigate to this directory and delete it manually. We'll automate the process using Python.
For a typical scenario, imagine you just checked the registry, and you noticed a new file you did not install and after googling, found out that it's not part of the default Windows file. Something like:
No hacker that wants to be discrete would name it "Tracker". They could give it harmless names like "Microsoft Edge" or even "Anti-Virus". So don't just trust a file because it has a familiar name. Trace the file to where it's being referenced from — the paths under Data
and see if it's legit.
In this scenario, what you have to do to stop the persistence is delete that entry and the file it is executing - in this case, C:\Users\muham\AppData\Roaming\tracker.exe
.
On Linux, one way an attacker might make his malware persistent is by adding it to the cron table. In Linux, crontab
is a utility that allows you to schedule commands or scripts to run automatically at specified times or intervals. It is part of the cron service, which is a time-based job scheduler in Unix-like operating systems.
So an attacker may place the malware in the cron table. All we need to do is access the cron table and delete the suspicious entries.
To access the cron table, run:
$ crontab -e
Result:
These are some malicious files I put there for demo purposes. We already covered how to do this here.
So, if you see any entries in your cron table that are suspicious, what you want to do is remove them. We'll achieve that using Python.
Now that we've seen how hackers make malware persistent, we'll write a program to remove persistent malware. We will make the program cross-platform so it runs on Windows and Linux.
Open up a Python file, and name it meaningfully like remove_persistent_malware.py
. Let's start by importing the necessary libraries:
import os
import platform
import subprocess
import tempfile
# Windows-specific imports
if platform.system() == "Windows":
import winreg
We imported various Python modules, including os
for operating system interactions, platform
for determining the system type, subprocess
for running external commands, and tempfile
for creating temporary files; it also conditionally imports the winreg
module for accessing the Windows registry if the operating system is detected as Windows.
Moving on (starting with Windows):
# Get Windows start-up entries and display
def list_windows_startup_entries():
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Run")
entries = []
try:
i = 0
while True:
entry_name, entry_value, entry_type = winreg.EnumValue(key, i)
entries.append((i + 1, entry_name, entry_value))
i += 1
except OSError:
pass
winreg.CloseKey(key)
return entries
# Remove Windows start-up entries
def remove_windows_startup_entry(index, entries):
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Run", 0, winreg.KEY_SET_VALUE)
try:
entry_name, entry_value = entries[index - 1][1], entries[index - 1][2]
winreg.DeleteValue(key, entry_name)
print(f"[+] Entry {entry_name} has been removed successfully.")
if os.path.isfile(entry_value):
os.remove(entry_value)
print(f"[+] File '{entry_value}' has been deleted successfully.")
else:
print(f"[-] File '{entry_value}' not found or unable to delete.")
except IndexError:
print("[-] Invalid entry index.")
except OSError as e:
print(f"[-] Error removing entry: {e}")
finally:
winreg.CloseKey(key)
The first function list_windows_startup_entries()
retrieves and displays the current startup entries in the Windows registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
. It opens this registry key, enumerates through its values, and appends each entry's index
, name
, and value
to a list. Finally, it returns this list of startup entries.
The second function, remove_windows_startup_entry(index, entries)
takes an index and the list of startup entries as arguments. It opens the same registry key with write access and attempts to remove the specified entry using the provided index. It first retrieves the entry name and value from the entries
list, and then deletes the corresponding registry value. If the entry value corresponds to a file path, it also attempts to delete that file. The function prints success or error messages based on the outcome of the removal process. Finally, it closes the registry key.
Please be aware that the code also attempts to delete the file, not just the Windows registry entry. Therefore, do not attempt to delete a system program that you may have added for demonstration purposes.
Next, we handle the cron table on Linux:
# Get the cron tab entries
def list_linux_crontab_entries():
try:
output = subprocess.check_output(["crontab", "-l"], stderr=subprocess.STDOUT).decode('utf-8').strip()
if output:
entries = output.split("\n")
return [(i + 1, entry) for i, entry in enumerate(entries)]
else:
return []
except subprocess.CalledProcessError as e:
if "no crontab" in e.output.decode('utf-8'):
return []
else:
raise
def remove_linux_crontab_entry(index, entries):
try:
entry = entries[index - 1][1]
all_entries = [e[1] for e in entries if e[1] != entry]
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
tmp_file.write("\n".join(all_entries).encode('utf-8'))
tmp_file.write(b"\n")
tmp_file_path = tmp_file.name
subprocess.check_output(["crontab", tmp_file_path], stderr=subprocess.STDOUT)
os.unlink(tmp_file_path)
print(f"[+] Entry '{entry}' has been removed successfully.")
except IndexError:
print("[-] Invalid entry index.")
except Exception as e:
print(f"[-] Error removing crontab entry: {e}")
The function list_linux_crontab_entries()
retrieves and lists the current cron job entries in the user's crontab
file on Linux systems. It runs the crontab -l
command to list the crontab entries and capture the output. If there are entries, it splits the output by newlines and returns a list of tuples, where each tuple contains the entry index and the entry text. If there are no entries or the user has no crontab
file, it returns an empty list.
The function remove_linux_crontab_entry(index, entries)
takes an index and the list of crontab entries as arguments. It first retrieves the entry text corresponding to the provided index from the entries
list. Then, it creates a new list all_entries
containing all entries except the one to be removed. Next, it creates a temporary file and writes the all_entries
list, separated by newlines. It then runs the crontab
command with the temporary file path to replace the user's crontab
file with the updated entries. Finally, it prints a success message and deletes the temporary file. If an invalid index is provided or an exception occurs during the process, it prints an error message.
Finally, the main()
function:
def main():
os_name = platform.system()
if os_name == "Windows":
entries = list_windows_startup_entries()
if not entries:
print("[-] No startup entries found.")
else:
print("[+] Startup entries:")
for index, name, value in entries:
print(f"{index}. {name}: {value}")
print("\n")
choice = int(input("[!] Enter the number of the entry you want to remove (0 to exit): "))
if choice == 0:
return
elif 0 < choice <= len(entries):
remove_windows_startup_entry(choice, entries)
else:
print("[-] Invalid choice.")
elif os_name == "Linux":
entries = list_linux_crontab_entries()
if not entries:
print("[-] No crontab entries found.")
else:
print("[+] Crontab entries:")
for index, entry in entries:
print(f"{index}. {entry}")
print("\n")
choice = int(input("[!] Enter the number of the entry you want to remove (0 to exit): "))
if choice == 0:
return
elif 0 < choice <= len(entries):
remove_linux_crontab_entry(choice, entries)
else:
print("[-] Invalid choice.")
else:
print(f"[-] Unsupported operating system: {os_name}")
if __name__ == "__main__":
main()
The main()
function is the entry point of the script. It first determines the operating system using the platform.system()
function.
For Windows:
list_windows_startup_entries()
to retrieve the list of startup entries from the registry.entries
, it prints a message indicating so.entries
with their index
, name
, and value
.entry
they want to remove (0 to exit).0
, it exits the script.index
, it calls remove_windows_startup_entry()
with the chosen index
and the list of entries
.index
, it prints an error message.For Linux:
list_linux_crontab_entries()
to retrieve the list of crontab entries
.entries
, it prints a message indicating so.entries
with their index
and the entry
text.entry
they want to remove (0
to exit).0
, it exits the script.index
, it calls remove_linux_crontab_entry()
with the chosen index
and the list of entries
.index
, it prints an error message.If the operating system is neither Windows nor Linux, it prints a message indicating that it is unsupported.
Now, we'll run our script starting with the Windows OS. Navigate to the directory where your code is saved and run:
Result:
$ python remove_peristent_malware.py
We can see that there's a suspicious entry Tracker
. To remove that, we simply specify its number (which is 7):
Pretty simple. Now when you run the program again, you'll see only 6 entries:
Running our script is also very straightforward. Navigate to your working directory (where you have your script) and run:
$ python3 remove_persistent_malware.py
Result:
Assuming we want to remove the first entry, we simply specify 1:
It's really that simple. When you run the crontab -e
command again, you'll notice that you now have just one entry to be executed at start-up:
Please note that the crontab
you view is for a particular user. For example, if you switch to an admin user and check the cron tables, the entries may be different. Also, a good challenge for you (Linux users) is to extend the code so that after removing the unwanted file from the entry, it also deletes it from the source - similar to what I did on Windows. It's pretty simple.
Also, for Linux users, in addition to crontab
, other ways to run applications or scripts at startup on Linux include the .bashrc
file, systemd services, autostart entries for desktop environments, init scripts, bash scripts in /etc/profile.d/
, desktop entries, and the XDG Autostart specification. So feel free to experiment with removing files using the other methods. Please be careful so you do not delete essential files.
If you want to learn how to recover forgotten passwords on Windows - perfect for digital forensics, check out this tutorial.
Check the complete code here.
Happy coding ♥
Let our Code Converter simplify your multi-language projects. It's like having a coding translator at your fingertips. Don't miss out!
View Full Code Improve My Code
Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!