24 Commits

Author SHA1 Message Date
SoulKindred
1a2849d698 Add files via upload
Contains Stap, python-script and whitelist.txt
Does not fully work. Requires return-value in start_stap function. If that works, it works fully. Then begin with tidy up work.
2023-06-08 19:32:47 +02:00
Sebastian Lenzlinger
da25db0825 Merge branch 'main' of github.com:sebaschi/keylogger-detector 2023-06-08 13:27:01 +02:00
Sebastian Lenzlinger
1635c68ade Add platform check (must be Linux). 2023-06-08 13:26:49 +02:00
Sebastian Lenzlinger
d84a0717bc Merge pull request #4 from sebaschi/stap-scripts
Stap scripts
2023-06-08 01:02:38 +02:00
Sebastian Lenzlinger
4a9af2f04c Merge branch 'main' into stap-scripts 2023-06-08 01:02:06 +02:00
Sebastian Lenzlinger
1ea740dfd3 Merge pull request #3 from sebaschi/SoulKindred-patch-2
Corrected journal
2023-06-08 00:58:59 +02:00
Sebastian Lenzlinger
e9d283f94e Merge pull request #2 from sebaschi/SoulKindred-patch-1
Update dev_journal.md
2023-06-08 00:58:25 +02:00
SoulKindred
0bc3704846 Update dev_journal.md
updated journal and dodged copyright infringement
2023-06-08 00:14:31 +02:00
SoulKindred
00c2b25a27 Update dev_journal.md
avoided copyright infringement
2023-06-08 00:10:25 +02:00
SoulKindred
c0893c31e6 Add files via upload
Uploading funcall_trace scripts. Both scripts do essentially the same. Script 1 is simplified for the use with python. Script 2 gives more information, but is more difficult for use with python. The script 2 might be useful for further tracing.
2023-06-07 22:05:15 +02:00
Sebastian Lenzlinger
94cb159a49 Cleanup unused code.
Remove unused code from repo.
2023-06-07 21:00:09 +02:00
Sebastian Lenzlinger
2150441d48 Add Makefile entry that also gets rid of __pycache__ 2023-06-07 20:57:17 +02:00
SoulKindred
1d5fc7375c Update dev_journal.md
added diary entry to stap-scripts
2023-06-07 20:40:13 +02:00
Sebastian Lenzlinger
63b92c967a Logic ajust 2023-06-07 20:33:15 +02:00
Sebastian Lenzlinger
7c9a6101a0 Condition logic ajustments 2023-06-07 20:27:06 +02:00
Sebastian Lenzlinger
6f56f0bd30 For VM, sorry 2023-06-07 20:24:33 +02:00
Sebastian Lenzlinger
0cdb0cd846 To get it in to VM. As are most commits. 2023-06-07 20:21:10 +02:00
Sebastian Lenzlinger
2b2546b70e Beauty fix 2023-06-07 20:19:12 +02:00
Sebastian Lenzlinger
2c9ba696c8 DEBUG 2023-06-07 20:15:31 +02:00
Sebastian Lenzlinger
9b3151452d DEBUG 2023-06-07 20:12:43 +02:00
Sebastian Lenzlinger
538c6368c5 FIX 2023-06-07 20:06:33 +02:00
Sebastian Lenzlinger
316ff1c90e Add new auto blacklist featue 2023-06-07 20:04:19 +02:00
Sebastian Lenzlinger
a4c5d94006 Update dev_journal.md
Fix ''' to ```
2023-06-07 19:45:43 +02:00
Sebastian Lenzlinger
d434029e56 Merge pull request #1 from sebaschi/userland
The userland programm core functionality is completed. 
keylogger-detector.py detects all processes reading from a keyboard file in /dev/input/eventX.
The keylogger asks the user if they would like to kill any process, and does so.
Many PIDs with the same programm are handled.
There is a whitelist and autokill list configurable(only partially from withing the running program) aswell as what names keyboards might have, like 'kbd'.
2023-06-07 19:44:33 +02:00
18 changed files with 340 additions and 728 deletions

View File

@@ -131,7 +131,7 @@ Killing a process still doesn't work:
## Wednesday, 7. June 2023, night ## Wednesday, 7. June 2023, night
### Sebastian ### Sebastian
This is the latest output aftert a test run where actually 3 processes has keyloggers runnig. This is the latest output aftert a test run where actually 3 processes has keyloggers runnig.
''' ```
[kldetect@fedora src]$ sudo ./keylogger_detector.py [kldetect@fedora src]$ sudo ./keylogger_detector.py
[sudo] password for kldetect: [sudo] password for kldetect:
/usr/sbin/fuser /usr/sbin/fuser
@@ -159,7 +159,7 @@ cat: config.: No such file or directory
[Verbose] Suspicious processes not killed: [] [Verbose] Suspicious processes not killed: []
[Verbose] Suspicious processes killed: [] [Verbose] Suspicious processes killed: []
[+] No suspicious processes found [+] No suspicious processes found
''' ```
This is after extensivly refactoring because I was starting to loose oversight over the code. So I split it up into utils, config and keylogger_detector. This is after extensivly refactoring because I was starting to loose oversight over the code. So I split it up into utils, config and keylogger_detector.
#### TODO: #### TODO:
1. Ivestigate and bug fix 1. Ivestigate and bug fix
@@ -173,3 +173,10 @@ All in all, the main functionality works as intended. Basically now would be the
#### TODO #### TODO
1. Write report 1. Write report
2. Add functionality to userspace detector 2. Add functionality to userspace detector
## Wednesday, 7, June 2023
### Michel
I have written 1 systemtap scripts, that can detect, whenever a module registers at the Keyboard-notifier. The Script can currently detect whenever a module registers. My script cant detect which kernel module registered. Here comes Sebastians idea of writing a python script, that can unload all un-known modules and loads them back in, whilst the stap-script is running. Whenever a module is loaded in, and it triggers the stap-script, we know it is tracking key-strokes. Those modules will be shown to the user and the user then has to decide whether to unload and remove them, or keep them. My script is based on a redhat-script. The redhat-script is called funcall_tracer2.stp . The idea behind both scripts is the same. My script is simplyfied for the use with python.

View File

@@ -1,2 +1,5 @@
clean: clean:
rm *.o *.txt *.out *.exe rm *.o *.txt *.out
sclean:
sudo rm -rf *.o *.txt *.out __pycache__

8
src/funcall_trace1.stp Normal file
View File

@@ -0,0 +1,8 @@
probe kernel.function("register_keyboard_notifier").call
{
printf("%s (%d)\n", execname(),tid())
}
probe end{
printf("end\n")
}

58
src/funcall_trace2.stp Normal file
View File

@@ -0,0 +1,58 @@
#! /usr/bin/env stap
#
# Copyright (C) 2010-2015 Red Hat, Inc.
# Written by William Cohen <wcohen@redhat.com>
#
# The linetimes.stp script takes two arguments: where to find the function
# and the function name. linetimes.stp will instrument each line in the
# function. It will print out the number of times that the function is
# called, a table with the average and maximum time each line takes,
# and control flow information when the script exits.
#
# For example all the lines of the do_unlinkat function:
#
# stap linetimes.stp kernel do_unlinkat
global calls, times, last_pp, region, cfg
probe $1.function(@2).call { calls <<< 1 }
probe $1.function(@2).return {
t = gettimeofday_us()
s = times[tid()]
if (s) {
e = t - s
region[last_pp[tid()]] <<< e
cfg[last_pp[tid()], pp()] <<< 1
}
delete times[tid()]
delete last_pp[tid()]
}
probe $1.statement(@2 "@*:*") {
t = gettimeofday_us()
s = times[tid()]
if (s) {
e = t - s
region[last_pp[tid()]] <<< e
cfg[last_pp[tid()], pp()] <<< 1
}
times[tid()] = t
last_pp[tid()] = pp()
}
probe end {
printf("\n%s %s call count: %d\n", @1, @2, @count(calls));
printf("\n%-58s %10s %10s\n", "region", "avg(us)", "max(us)");
foreach (p+ in region) {
printf("%-58s %10d %10d\n", p, @avg(region[p]), @max(region[p]));
}
printf("\n\ncontrol flow graph information\n")
printf("from\n\tto\n=======================\n")
foreach ([src+] in region) {
printf("%-s\n", src)
foreach ([s,dest+] in cfg[src,*]) { # slice for all dest's
printf("\t%-s %d\n", dest, @count(cfg[src,dest]));
}
}
}

View File

@@ -3,6 +3,7 @@
import sys import sys
from config import CONFIG_FILE, load_config, save_config from config import CONFIG_FILE, load_config, save_config
from utils import ( from utils import (
check_platform,
check_root, check_root,
check_packages, check_packages,
get_keyboard_device_files, get_keyboard_device_files,
@@ -13,12 +14,13 @@ from utils import (
kill_process kill_process
) )
# Global variables # Global variables/CLI options
auto_kill_option = False auto_kill_option = False
verbose_option = False verbose_option = False
safe_option = False safe_option = False
add_white_list_option = False add_white_list_option = False
add_black_list_option = False
debug_option = False debug_option = False
# Functions # Functions
@@ -33,7 +35,8 @@ def print_help():
print(' -v, --verbose\t\t\tVerbose mode. Informative information will be displayed duting execution') print(' -v, --verbose\t\t\tVerbose mode. Informative information will be displayed duting execution')
print(' -a, --auto-kill\t\tAutomatically kill blacklisted processes') print(' -a, --auto-kill\t\tAutomatically kill blacklisted processes')
print(' -s, --safe\t\t\tSafe mode. Asked to confirm before killing a process') print(' -s, --safe\t\t\tSafe mode. Asked to confirm before killing a process')
print(' -w, --add-white-list\t\t\tActivate prompt to add program names to the whitelist') #For some reason this line gets messed up in display print(' -w, --add-white-list\t\t\tActivate prompt to add program names to the whitelist') #For some reason this line gets messed up in display
print(' -b, --add-black-list\t\t\tAutomatically add program names chosen to kill to the blacklist')
print(' -d, --debug\t\t\tDebug mode. Print debug statements') print(' -d, --debug\t\t\tDebug mode. Print debug statements')
def set_input_options(): def set_input_options():
@@ -47,7 +50,7 @@ def set_input_options():
""" """
global auto_kill_option, verbose_option, safe_option, add_white_list_option global auto_kill_option, verbose_option, safe_option, add_white_list_option
global debug_option global debug_option, add_black_list_option
if len(sys.argv) > 1: if len(sys.argv) > 1:
for arg in sys.argv[1:]: for arg in sys.argv[1:]:
print(arg) print(arg)
@@ -62,6 +65,8 @@ def set_input_options():
safe_option = True safe_option = True
elif arg == '-w' or arg == '--add-white-list' : elif arg == '-w' or arg == '--add-white-list' :
add_white_list_option = True add_white_list_option = True
elif arg == '-b' or arg == '--add-black-list':
add_black_list_option = True
elif arg == '-d' or arg == '--debug': elif arg == '-d' or arg == '--debug':
debug_option = True debug_option = True
@@ -109,8 +114,9 @@ def detect_keyloggers():
# 1. Setup and initialization # 1. Setup and initialization
############################ ############################
debug(True, str(sys.argv)) # Set manually to debug if args are being read debug(True, str(sys.argv)) # Set manually to debug if args are being read
check_platform()
global auto_kill_option, verbose_option, safe_option global auto_kill_option, verbose_option, safe_option
global CONFIG_FILE
set_input_options() set_input_options()
if verbose_option: if verbose_option:
print('[Verbose] Input options set') print('[Verbose] Input options set')
@@ -175,16 +181,18 @@ def detect_keyloggers():
print('[Verbose] Auto-killable process found:', name) print('[Verbose] Auto-killable process found:', name)
if safe_option: if safe_option:
if confirm_kill_procces(name): if confirm_kill_procces(name):
kill_process(name_pid_dict[name]) kill_processes(name_pid_dict[name])
else: else:
kill_process(name_pid_dict[name]) kill_processes(name_pid_dict[name])
if verbose_option:
print('[Verbose] Process auto-killed:', name)
############################ ############################
# 6. Identify suspicious processes, i.e. those not whitelisted # 6. Identify suspicious processes, i.e. those not whitelisted
############################ ############################
suspicious_processes = [] suspicious_processes = []
for name in process_names: for name in process_names:
if name not in white_listed_programs: if (name not in white_listed_programs and name not in auto_kill_programs) or (name in auto_kill_programs and not auto_kill_option):
suspicious_processes.append(name) suspicious_processes.append(name)
if verbose_option: if verbose_option:
print('[Verbose] Suspicious processes found:', suspicious_processes) print('[Verbose] Suspicious processes found:', suspicious_processes)
@@ -241,7 +249,7 @@ def detect_keyloggers():
############################ ############################
# 8. Update whitelist if option set # 8. Update whitelist and/or blacklist if options set
############################ ############################
debug(debug_option, 'Whitelist option:' + str(add_white_list_option)) debug(debug_option, 'Whitelist option:' + str(add_white_list_option))
if add_white_list_option: if add_white_list_option:
@@ -254,12 +262,17 @@ def detect_keyloggers():
if verbose_option: if verbose_option:
print('[Verbose] Newly whitelisted programs: ', to_whitelist) print('[Verbose] Newly whitelisted programs: ', to_whitelist)
to_kill = list(set(to_kill))
if add_black_list_option:
auto_kill_programs.extend(to_kill)
if verbose_option:
print('[Verbose] Newly blacklisted programs: ', to_kill)
########################### ###########################
# 9. Cleanup # 9. Cleanup
########################### ###########################
to_kill = list(set(to_kill))
auto_kill_programs = list(set(auto_kill_programs)) auto_kill_programs = list(set(auto_kill_programs))
auto_kill_programs.extend(to_kill)
config['auto_kill_programs'] = auto_kill_programs config['auto_kill_programs'] = auto_kill_programs
white_listed_programs = list(set(white_listed_programs)) white_listed_programs = list(set(white_listed_programs))
config['white_listed_programs'] = white_listed_programs config['white_listed_programs'] = white_listed_programs

View File

@@ -0,0 +1,6 @@
probe kernel.function("register_keyboard_notifier").call
{
printf("triggered\n")
}

View File

@@ -0,0 +1,150 @@
import subprocess
import time
import multiprocessing
import os
global Smell
#==============================================================================================================
#
#Functions
#
#==============================================================================================================
def list_modules(command):
result = subprocess.run(command, shell = True, capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip().split('\n')
else:
print(f"Failed with error:{result.stderr}")
return[]
def get_whitelist(file_path):
try:
with open(file_path, 'r') as file:
lines = file.read().splitlines()
return lines
except IOError:
print(f'Error: Failed to load whitelist{file_path}')
def compare_mods(A, B):
setA = set(A)
setB = set(B)
result = setB - setA
return list(result)
def tidy_up(entries):
cleaned_entries = []
for entry in entries:
modules = entry.split()
if modules:
first_mod = modules[0]
cleaned_entries.append(first_mod)
return cleaned_entries
def unload_mod(modules):
tmp = []
for module in modules:
result = subprocess.run(['sudo','rmmod', module],capture_output = True, text = True)
if result.returncode == 0:
print(f"Unloaded module: {module}")
else:
print(f"Failed to unloaded module: {module}")
tmp.append(module)
print(result.stderr)
result_out = compare_mods(tmp, modules)
print(result_out)
return result_out
#TODO Get Return-value from start_stap()
def start_stap():
print("Starting Sniffer")
output = subprocess.Popen(['stap','funcall_trace.stp'],universal_newlines=True)
if output.stdout != "":
output.terminate()
print("fishy")
Smell = "fishy"
else:
output.terminate()
print("nothing fishy")
Smell = "not fishy"
print(Smell + " smell")
def load_mod(module):
result = subprocess.run(['sudo','insmod', module],capture_output = True, text = True)
if result.returncode == 0:
print(f"Loaded module: {module}")
time.sleep(5)
else:
print(f"Failed to Loaded module: {module}")
print(result.stderr)
def find_file(filename):
result = []
for root, dirs, files in os.walk("/"):
if filename in files:
file_path = os.path.join(root, filename)
result.append(file_path)
result_out = result[0]
result_out = ''.join(result_out)
return result_out
def getpath(sus_modules):
for i in range(len(sus_modules)):
sus_modules[i] = find_file(sus_modules[i] + ".ko")
return sus_modules
def detect_logger(module):
p1 = multiprocessing.Process(target=start_stap)
p1.start()
p2 = multiprocessing.Process(target=load_mod(module))
p2.start()
p1.join()
p2.join()
#==============================================================================================================
#
#Work
#
#==============================================================================================================
whitelist = get_whitelist("whitelist.txt")
lsmod_output = list_modules("lsmod");
sus_modules = compare_mods(whitelist, lsmod_output)
sus_modules = tidy_up(sus_modules)
print(sus_modules)
sus_modules = unload_mod(sus_modules)
time.sleep(1)
print("waited")
sus_modules = getpath(sus_modules)
print(sus_modules)
if len(sus_modules) == 0:
exit()
suspects = []
for module in range(len(sus_modules)):
suspects.append(detect_logger(sus_modules[module]))
print(suspects)

View File

@@ -0,0 +1,68 @@
Module Size Used by
tls 147456 0
uinput 20480 0
isofs 65536 1
snd_seq_dummy 16384 0
snd_hrtimer 16384 1
vboxvideo 36864 0
drm_vram_helper 24576 1 vboxvideo
nf_conntrack_netbios_ns 16384 1
nf_conntrack_broadcast 16384 1 nf_conntrack_netbios_ns
nft_fib_inet 16384 1
nft_fib_ipv4 16384 1 nft_fib_inet
nft_fib_ipv6 16384 1 nft_fib_inet
nft_fib 16384 3 nft_fib_ipv6,nft_fib_ipv4,nft_fib_inet
nft_reject_inet 16384 6
nf_reject_ipv4 16384 1 nft_reject_inet
nf_reject_ipv6 24576 1 nft_reject_inet
nft_reject 16384 1 nft_reject_inet
nft_ct 24576 16
nft_chain_nat 16384 3
nf_nat 65536 1 nft_chain_nat
nf_conntrack 192512 4 nf_nat,nft_ct,nf_conntrack_netbios_ns,nf_conntrack_broadcast
nf_defrag_ipv6 24576 1 nf_conntrack
nf_defrag_ipv4 16384 1 nf_conntrack
ip_set 65536 0
rfkill 40960 3
nf_tables 352256 237 nft_ct,nft_reject_inet,nft_fib_ipv6,nft_fib_ipv4,nft_chain_nat,nft_reject,nft_fib,nft_fib_inet
nfnetlink 20480 3 nf_tables,ip_set
qrtr 57344 4
snd_intel8x0 57344 2
snd_ac97_codec 200704 1 snd_intel8x0
ac97_bus 16384 1 snd_ac97_codec
snd_seq 106496 7 snd_seq_dummy
intel_rapl_msr 20480 0
snd_seq_device 16384 1 snd_seq
intel_rapl_common 36864 1 intel_rapl_msr
snd_pcm 184320 2 snd_intel8x0,snd_ac97_codec
snd_timer 53248 3 snd_seq,snd_hrtimer,snd_pcm
sunrpc 815104 1
rapl 24576 0
binfmt_misc 28672 1
snd 143360 12 snd_seq,snd_seq_device,snd_intel8x0,snd_timer,snd_ac97_codec,snd_pcm
joydev 28672 0
pcspkr 16384 0
soundcore 16384 1 snd
i2c_piix4 36864 0
vboxguest 53248 6
loop 40960 0
zram 32768 2
crct10dif_pclmul 16384 1
crc32_pclmul 16384 0
crc32c_intel 24576 3
polyval_generic 16384 0
video 73728 0
ghash_clmulni_intel 16384 0
vmwgfx 458752 2
drm_ttm_helper 16384 3 vmwgfx,drm_vram_helper,vboxvideo
wmi 45056 1 video
sha512_ssse3 49152 0
e1000 188416 0
serio_raw 20480 0
ttm 102400 3 vmwgfx,drm_vram_helper,drm_ttm_helper
ata_generic 16384 0
pata_acpi 16384 0
ip6_tables 40960 0
ip_tables 40960 0
fuse 212992 5

View File

@@ -1,67 +0,0 @@
#!/bin/bash
# ===============================
# Step1: Find keyboard file paths
# ===============================
# Output file path
kbd_output_file="kbd_file_paths.txt"
# Function to follow symbolic links recursively
follow_symlinks() {
local filepath=$1
if [[ -L $filepath ]]; then
local resolved_path=$(readlink -f "$filepath")
echo "$resolved_path" >> "$kbd_output_file"
follow_symlinks "$resolved_path"
fi
}
# Traverse files in /dev/input/by-path
echo -n > "$kbd_output_file"
find /dev/input/by-path -type l -name '*kbd*' -print0 | while IFS= read -r -d '' filepath; do
#echo "$filepath" >> "$kbd_output_file"
follow_symlinks "$filepath"
done
echo "Keyboard file paths written to $kbd_output_file"
# ===============================
# Step2: Find pids using keyboard event files
# ===============================
# Use found kbd file paths to find corresponding pids
pids_input_file="$kbd_output_file"
pids_output_file="pids.txt"
echo -n > "$pids_output_file"
declare -a pids_array
# Get pids of processes using the keyboard and put in array
while IFS= read -r pathname; do
pids=$(fuser "$pathname")
# add pids to array
for pid in $pids; do
pids_array+=("$pid")
done
done < "$pids_input_file"
# sort and remove duplicates
sorted_pids=$(printf '%s\n' "${pids_array[@]}" | sort -nu)
# write unique and sorted pids to file, separated by newlines
printf '%s\n' "${sorted_pids[@]}" > "$pids_output_file"
echo "Pids written to $pids_output_file"
# ===============================
# Step3: Find processes/program names using pids
# ===============================
exe_input_file="$pids_output_file"
exe_output_file="suspicous_exes.txt"
# Clear output file
echo -n > "$exe_output_file"

View File

@@ -1,24 +0,0 @@
import os
# Output file path
kbd_output_file = "kbd_file_paths.txt"
# Function to follow symbolic links recursively
def follow_symlinks(filepath):
if os.path.islink(filepath):
resolved_path = os.path.realpath(filepath)
with open(kbd_output_file, "a") as f:
f.write(resolved_path + "\n")
follow_symlinks(resolved_path)
# Traverse files in /dev/input/by-path
with open(kbd_output_file, "w") as f:
f.write("")
for root, dirs, files in os.walk("/dev/input/by-path"):
for filename in files:
if "kbd" in filename:
filepath = os.path.join(root, filename)
follow_symlinks(filepath)
print("Keyboard file paths written to", kbd_output_file)

View File

@@ -1,40 +0,0 @@
#!/bin/bash
# Output file path
output_file="keyboard_info.txt"
# Step 1: Find keyboard device files
keyboard_files=()
while IFS= read -r -d '' file; do
if [[ $file == *"kbd"* || $file == *"keyboard"* ]]; then
keyboard_files+=("$file")
fi
done < <(find /dev/input/by-path -type l -name 'event*')
# Step 2: Check processes with open keyboard files
echo "Keyboard Information" > "$output_file"
echo "=====================" >> "$output_file"
for keyboard_file in "${keyboard_files[@]}"; do
echo "Keyboard device file: $keyboard_file" >> "$output_file"
event_file=$(readlink -f "$keyboard_file")
echo "Event file: $event_file" >> "$output_file"
pids=$(fuser -v "$event_file" 2>/dev/null | awk -F'[: ]+' 'NR>1{print $2}')
echo "PIDs with file open: $pids" >> "$output_file"
# Step 3: Check corresponding programs
echo "Corresponding Programs" >> "$output_file"
echo "---------------------" >> "$output_file"
for pid in $pids; do
program=$(readlink -f "/proc/$pid/exe")
echo "PID $pid corresponds to program: $program" >> "$output_file"
done
echo >> "$output_file"
done
echo "Keyboard information written to $output_file"

View File

@@ -1,251 +0,0 @@
#!/usr/bin/env python3
import os # for going directories
import subprocess # for running commands, in particular fuser
import sys # for exiting
import signal # for killing processes
import json # for handling our configurations
CONFIG_FILE = 'config.json'
auto_kill_option = False
verbose_option = False
safe_option = False
# Check if the user is in sudo mode
def check_sudo():
if os.geteuid() != 0:
print("[-] Please rerun as root")
sys.exit(1)
# Check if the user has the required packages installed
def check_packages():
packages = ['fuser']
missing_packages = []
for package in packages:
try:
subprocess.check_call(['which', package])
except subprocess.CalledProcessError:
missing_packages.append(package)
if len(missing_packages) > 0:
print("[-] Please install the following packages:", str(missing_packages))
sys.exit(1)
# Follow symlinks to find real path
def get_real_path(path):
if os.path.islink(path):
return os.path.realpath(path)
else:
return path
# get keyboard device files
def get_keyboard_device_files(kbd_names):
keyboard_device_files = []
for root, dirs, files in os.walk('/dev/input/by-path'):
for file in files:
if any(kbd_name in file for kbd_name in kbd_names):
keyboard_device_files.append(get_real_path(os.path.join(root, file)))
return keyboard_device_files
# print a list to a file separated by newlines
def print_list_to_file(list, file):
with open(file, 'w') as f:
for item in list:
f.write("%s\n" % item)
# find pids using file using fuser
def get_pids(file):
try:
pids = subprocess.check_output(['fuser', file]).decode('utf-8').split()
except subprocess.CalledProcessError:
if verbose_option:
print("[-] Error: fuser failed to run on", file)
return []
#pids = [int(pid) for pid in pids]
return pids
# clear a file
def clear_file(file):
open(file, 'w').close()
# find programm name using pid
def get_program_name(pid):
status_file = '/proc/' + str(pid) + '/status'
with open(status_file, 'r') as f:
# See cat /proc/[pid]/status | grep Name
for line in f:
if line.startswith('Name:'):
program_name = line.split(":")[1].strip()
return program_name
# proces input arguments and set options
def set_input_arguments():
global auto_kill_option
global verbose_option
global safe_option
if len(sys.argv) > 1:
if '-a' in sys.argv:
auto_kill_option = True
if '-v' in sys.argv:
verbose_option = True
if '-s' in sys.argv:
safe_option = True
# ask user to confirm a list of programs to kill
def confirm_kill_programs(programs, times=0):
print("Confirm to kill the following programs:")
for program in programs:
print(program)
print("y/n?")
answer = input()
if answer == 'y':
return True
elif answer == 'n':
return False
else:
if times > 5:
print("[-] Too many tries. Exiting")
sys.exit(1)
print("[-] Please answer y or n")
return confirm_kill_programs(programs, times+1)
# kill list of processes
def kill_processes(pids):
print(pids) ## DEBUG
print("Killing processes with pids:", pids)
for pid in pids:
os.kill(pid, signal.SIGKILL)
if verbose_option:
print("[-] Killed process with pid", pid)
# the main program starts here
def detect_keyloggers():
###############################
# Step 0: Check minimal requirements/ Set up
###############################
check_sudo()
check_packages()
config = load_config()
# initialize white_listed_programs
if 'white_listed_programs' in config:
white_listed_programs = config['white_listed_programs']
else:
config['white_listed_programs'] = []
white_listed_programs = []
# initialize auto_kill_programs
if 'auto_kill_programs' in config:
auto_kill_programs = config['auto_kill_programs']
else:
config['auto_kill_programs'] = []
auto_kill_programs = []
# initialize kbd_names
if 'kbd_names' in config:
kbd_names = config['kbd_names']
else:
config['kbd_names'] = []
kbd_names = []
# Set options
set_input_arguments()
###############################
# Step 1: Get keyboard device files
###############################
keyboard_device_files = get_keyboard_device_files(kbd_names)
###############################
# Step 2: Get pids using keyboard device files
###############################
pids = []
for file in keyboard_device_files:
pids += get_pids(file)
pids = sorted(list(set(pids)))
###############################
# Step 3: Get program names using pids
###############################
program_names = []
program_pid_dict = {}
# Get program names
for pid in pids:
program_name = get_program_name(pid)
program_pid_dict[program_name] = program_pid_dict[program_name].append(int(pid))
if auto_kill_option and program_name in auto_kill_programs:
os.kill(pid, signal.SIGKILL)
if verbose_option:
print("[-] Auto-Killed process", program_name, "with pid", pid)
else:
program_names.append(program_name)
program_names = sorted(list(set(program_names)))
# Identify suspicious programs
suspicious_programs = []
for program_name in program_names:
if program_name not in white_listed_programs:
suspicious_programs.append(program_name)
if verbose_option:
###############################=
# Intermezzo: Print results
###############################
print("Keyboard device files:")
for file in keyboard_device_files:
print(file)
print("")
print("Pids:")
for pid in pids:
print(pid)
print("")
print("Program names:")
for program_name in program_names:
print(program_name)
print("")
if len(suspicious_programs) == 0:
print("[+] No suspicious programs found")
sys.exit(0)
###############################
# Step 4: Ask user to kill any suspicious programs
###############################
print("Suspicious programs:")
for program_name in suspicious_programs:
print(program_name)
user_input = input("Please enter those programs you want to kill. Use the whitespace(spacebar) to separate values.")
if user_input == '':
print("[-] No programs to kill")
sys.exit(0)
programs_to_kill = user_input.split()
programs_to_kill = [program_name for program_name in programs_to_kill if program_name in suspicious_programs] # Filter out programs that are not suspicious
pids_to_kill = []
for program_name in programs_to_kill:
pids_to_kill.append(program_pid_dict[program_name])
auto_kill_programs.append(program_name)
if safe_option:
if confirm_kill_programs(programs_to_kill):
kill_processes(pids_to_kill)
else:
kill_processes(pids_to_kill)
###############################
# Step 5: Save config
###############################
config['auto_kill_programs'] = list(set(auto_kill_programs))
config['white_listed_programs'] = list(set(white_listed_programs))
config['kbd_names'] = list(set(kbd_names))
save_config(config)
if __name__ == "__main__":
detect_keyloggers()

View File

@@ -1,77 +0,0 @@
import os
import subprocess
# ===============================
# Step1: Find keyboard file paths
# ===============================
# Output file path
kbd_output_file = "kbd_file_paths.txt"
# Function to follow symbolic links recursively
def follow_symlinks(filepath):
if os.path.islink(filepath):
resolved_path = os.path.realpath(filepath)
with open(kbd_output_file, "a") as output_file:
output_file.write(resolved_path + "\n")
follow_symlinks(resolved_path)
# Traverse files in /dev/input/by-path
with open(kbd_output_file, "w") as output_file:
for root, dirs, files in os.walk("/dev/input/by-path"):
for file in files:
if "kbd" in file:
filepath = os.path.join(root, file)
output_file.write(filepath + "\n")
follow_symlinks(filepath)
print("Keyboard file paths written to", kbd_output_file)
# ===============================
# Step2: Find pids using keyboard event files
# ===============================
# Use found kbd file paths to find corresponding pids
pids_input_file = kbd_output_file
pids_output_file = "pids.txt"
pids_array = []
# Get pids of processes using the keyboard and put in array
with open(pids_input_file, "r") as input_file:
for pathname in input_file:
pathname = pathname.strip()
pids = subprocess.check_output(["fuser", pathname]).decode().split()
pids_array.extend(pids)
# Sort and remove duplicates
sorted_pids = sorted(set(pids_array))
print()
print("The following pids where found:" + str(sorted_pids) + "\n")
# Write unique and sorted pids to file, separated by newlines
with open(pids_output_file, "w") as output_file:
output_file.write("\n".join(sorted_pids) + "\n")
print("Pids written to", pids_output_file)
# ===============================
# Step3: Find processes/program names using pids
# ===============================
exe_input_file = pids_output_file
exe_output_file = "suspicious_exes.txt"
# Clear output file
with open(exe_output_file, "w") as output_file:
output_file.write("")
exe_pid_dict = {}
for pid in sorted_pids:
#Get name of executable from PID using process status file
status_file_path = "/proc/" + pid + "/status"
with open(status_file_path, "r") as status_file:
for line in status_file:
# See cat /proc/{pid}/status | grep "Name:"
if line.startswith("Name:"):
exe_name = line.split(":")[1].strip()
exe_pid_dict[exe_name] = pid
print("The following executables where found:" + str(exe_pid_dict) + "\n")

View File

@@ -1,112 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#define DEVICE_DIR "/dev/input"
#define BY_PATH_DIR "/dev/input/by-path"
#define PROC_DIR "/proc"
void get_program_name(long pid) {
char exe_file_path[256];
snprintf(exe_file_path, sizeof(exe_file_path), "/proc/%ld/exe", pid);
if (access(exe_file_path, F_OK) == 0) {
char program_path[256];
ssize_t path_len = readlink(exe_file_path, program_path, sizeof(program_path) - 1);
if (path_len != -1) {
program_path[path_len] = '\0';
printf("Corresponding program: %s\n\n", program_path);
}
}
}
void find_keyboard_files() {
DIR *by_path_dir = opendir(BY_PATH_DIR);
if (by_path_dir == NULL) {
perror("opendir");
exit(EXIT_FAILURE);
}
struct dirent *entry;
while ((entry = readdir(by_path_dir)) != NULL) {
if (strstr(entry->d_name, "kbd") != NULL || strstr(entry->d_name, "keyboard") != NULL) {
char device_file_path[256];
snprintf(device_file_path, sizeof(device_file_path), "%s/%s", BY_PATH_DIR, entry->d_name);
char link_dest[256];
ssize_t link_size = readlink(device_file_path, link_dest, sizeof(link_dest) - 1);
if (link_size == -1) {
perror("readlink");
continue;
}
link_dest[link_size] = '\0';
printf("Keyboard device file: %s\n", link_dest);
char event_file[256];
snprintf(event_file, sizeof(event_file), "%s/%s", DEVICE_DIR, link_dest);
DIR *proc_dir = opendir(PROC_DIR);
if (proc_dir == NULL) {
perror("opendir");
continue;
}
struct dirent *pid_entry;
while ((pid_entry = readdir(proc_dir)) != NULL) {
if (pid_entry->d_type != DT_DIR)
continue;
// Check if the entry name is a numeric value (PID)
char *endptr;
long pid = strtol(pid_entry->d_name, &endptr, 10);
if (*endptr != '\0')
continue;
char fd_dir_path[256];
snprintf(fd_dir_path, sizeof(fd_dir_path), "%s/%s/fd", PROC_DIR, pid_entry->d_name);
DIR *fd_dir = opendir(fd_dir_path);
if (fd_dir == NULL)
continue;
struct dirent *fd_entry;
while ((fd_entry = readdir(fd_dir)) != NULL) {
if (fd_entry->d_type != DT_LNK)
continue;
char fd_file_path[256];
snprintf(fd_file_path, sizeof(fd_file_path), "%s/%s", fd_dir_path, fd_entry->d_name);
char link_dest[256];
ssize_t link_size = readlink(fd_file_path, link_dest, sizeof(link_dest) - 1);
if (link_size == -1)
continue;
link_dest[link_size] = '\0';
if (strcmp(link_dest, event_file) == 0) {
printf("Process with PID %ld is using this file.\n", pid);
get_program_name(pid);
}
}
closedir(fd_dir);
}
closedir(proc_dir);
}
}
closedir(by_path_dir);
}
int main() {
printf("Finding keyboard files...\n\n");
find_keyboard_files();
return 0;
}

View File

@@ -1,77 +0,0 @@
import os
import subprocess
# ===============================
# Step1: Find keyboard file paths
# ===============================
# Output file path
kbd_output_file = "kbd_file_paths.txt"
# Function to follow symbolic links recursively
def follow_symlinks(filepath):
if os.path.islink(filepath):
resolved_path = os.path.realpath(filepath)
with open(kbd_output_file, "a") as output_file:
output_file.write(resolved_path + "\n")
follow_symlinks(resolved_path)
# Traverse files in /dev/input/by-path
with open(kbd_output_file, "w") as output_file:
for root, dirs, files in os.walk("/dev/input/by-path"):
for file in files:
if "kbd" in file:
filepath = os.path.join(root, file)
output_file.write(filepath + "\n")
follow_symlinks(filepath)
print("Keyboard file paths written to", kbd_output_file)
# ===============================
# Step2: Find pids using keyboard event files
# ===============================
# Use found kbd file paths to find corresponding pids
pids_input_file = kbd_output_file
pids_output_file = "pids.txt"
pids_array = []
# Get pids of processes using the keyboard and put in array
with open(pids_input_file, "r") as input_file:
for pathname in input_file:
pathname = pathname.strip()
pids = subprocess.check_output(["fuser", pathname]).decode().split()
pids_array.extend(pids)
# Sort and remove duplicates
sorted_pids = sorted(set(pids_array))
print()
print("The following pids where found:" + str(sorted_pids) + "\n")
# Write unique and sorted pids to file, separated by newlines
with open(pids_output_file, "w") as output_file:
output_file.write("\n".join(sorted_pids) + "\n")
print("Pids written to", pids_output_file)
# ===============================
# Step3: Find processes/program names using pids
# ===============================
exe_input_file = pids_output_file
exe_output_file = "suspicious_exes.txt"
# Clear output file
with open(exe_output_file, "w") as output_file:
output_file.write("")
exe_pid_dict = {}
for pid in sorted_pids:
#Get name of executable from PID using process status file
status_file_path = "/proc/" + pid + "/status"
with open(status_file_path, "r") as status_file:
for line in status_file:
# See cat /proc/{pid}/status | grep "Name:"
if line.startswith("Name:"):
exe_name = line.split(":")[1].strip()
exe_pid_dict[exe_name] = pid
print("The following executables where found:" + str(exe_pid_dict) + "\n")

View File

@@ -1,67 +0,0 @@
#!/bin/bash
# ===============================
# Step1: Find keyboard file paths
# ===============================
# Output file path
kbd_output_file="kbd_file_paths.txt"
# Function to follow symbolic links recursively
follow_symlinks() {
local filepath=$1
if [[ -L $filepath ]]; then
local resolved_path=$(readlink -f "$filepath")
echo "$resolved_path" >> "$kbd_output_file"
follow_symlinks "$resolved_path"
fi
}
# Traverse files in /dev/input/by-path
echo -n > "$kbd_output_file"
find /dev/input/by-path -type l -name '*kbd*' -print0 | while IFS= read -r -d '' filepath; do
#echo "$filepath" >> "$kbd_output_file"
follow_symlinks "$filepath"
done
echo "Keyboard file paths written to $kbd_output_file"
# ===============================
# Step2: Find pids using keyboard event files
# ===============================
# Use found kbd file paths to find corresponding pids
pids_input_file="$kbd_output_file"
pids_output_file="pids.txt"
echo -n > "$pids_output_file"
declare -a pids_array
# Get pids of processes using the keyboard and put in array
while IFS= read -r pathname; do
pids=$(fuser "$pathname")
# add pids to array
for pid in $pids; do
pids_array+=("$pid")
done
done < "$pids_input_file"
# sort and remove duplicates
sorted_pids=$(printf '%s\n' "${pids_array[@]}" | sort -nu)
# write unique and sorted pids to file, separated by newlines
printf '%s\n' "${sorted_pids[@]}" > "$pids_output_file"
echo "Pids written to $pids_output_file"
# ===============================
# Step3: Find processes/program names using pids
# ===============================
exe_input_file="$pids_output_file"
exe_output_file="suspicous_exes.txt"
# Clear output file
echo -n > "$exe_output_file"

View File

@@ -3,6 +3,20 @@ import subprocess # for executing shell commands
import signal # for sending signals to processes import signal # for sending signals to processes
import sys # for exit import sys # for exit
def check_platform():
"""
Check if platform is Linux.
Raises:
SystemExit: If the platform isn not LInux.
"""
if sys.platform != 'linux':
print("[-] This script only works on Linux.")
def check_root(): def check_root():
""" """
Check if script is run as root(sudo). Check if script is run as root(sudo).