程式扎記: [ Python 常見問題 ] How to monitor global keyboard event using python?

標籤

2015年6月9日 星期二

[ Python 常見問題 ] How to monitor global keyboard event using python?

Source From Here
Question
I want to write a program that can monitor for a global keyboard event. However, I have no idea how to implement this on a Linux-based system. Anyone know how to get started?

How-To
PyKeylogger is a proof of concept of a pure-python keylogger for linux. It uses Xlib (so you must have an X connection!) to monitor the state of the keyboard. Here's how you use it:
- test.py
  1. #!/usr/bin/env python  
  2. import keylogger  
  3. import time  
  4.   
  5. now = time.time()  
  6. done = lambda: time.time() > now + 60  
  7. def print_keys(t, modifiers, keys): print "%.2f   %r   %r" % (t, keys, modifiers)  
  8.   
  9. keylogger.log(done, print_keys)  
This will print key events to stdout for 60 seconds. If you wanted to be evil, instead of passing in a print callback, you could pass in a remote logging precedure.

Sample output:
1314238675.42 'o' {'left shift': False, 'right alt': False, 'right shift': False, 'left alt': False, 'left ctrl': False, 'right ctrl': False}
1314238675.51 'm' {'left shift': False, 'right alt': False, 'right shift': False, 'left alt': False, 'left ctrl': False, 'right ctrl': False}
1314238675.65 'g' {'left shift': False, 'right alt': False, 'right shift': False, 'left alt': False, 'left ctrl': False, 'right ctrl': False}

Below is the modified version of keylogger.py which allow you to register callback for interested keyboard event:
keylogger.py
  1. # Copyright (c) 2011, Andrew Moffat  
  2. # All rights reserved.  
  3.   
  4. # Redistribution and use in source and binary forms, with or without  
  5. # modification, are permitted provided that the following conditions are met:  
  6. #     * Redistributions of source code must retain the above copyright  
  7. #       notice, this list of conditions and the following disclaimer.  
  8. #     * Redistributions in binary form must reproduce the above copyright  
  9. #       notice, this list of conditions and the following disclaimer in the  
  10. #       documentation and/or other materials provided with the distribution.  
  11. #     * Neither the name of the nor the  
  12. #       names of its contributors may be used to endorse or promote products  
  13. #       derived from this software without specific prior written permission.  
  14.   
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND  
  16. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
  17. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  
  18. # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY  
  19. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  
  20. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  
  21. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  
  22. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  
  23. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  
  24. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  25.   
  26.   
  27. import sys  
  28. from time import sleep, time  
  29. import ctypes as ct  
  30. from ctypes.util import find_library  
  31.   
  32.   
  33. # linux only!  
  34. assert("linux" in sys.platform)  
  35.   
  36.   
  37. x11 = ct.cdll.LoadLibrary(find_library("X11"))  
  38. display = x11.XOpenDisplay(None)  
  39.   
  40.   
  41. this will hold the keyboard state.  32 bytes, with each  
  42. # bit representing the state for a single key.  
  43. keyboard = (ct.c_char * 32)()  
  44.   
  45. # these are the locations (bytebyte value) of special  
  46. # keys to watch  
  47. shift_keys = ((6,4), (7,64))  
  48. modifiers = {  
  49.     "left shift": (6,4),  
  50.     "right shift": (7,64),  
  51.     "left ctrl": (4,32),  
  52.     "right ctrl": (13,2),  
  53.     "left alt": (8,1),  
  54.     "right alt": (13,16)  
  55. }  
  56. last_pressed = set()  
  57. last_pressed_adjusted = set()  
  58. last_modifier_state = {}  
  59. caps_lock_state = 0  
  60.   
  61. # key is byte number, value is a dictionary whose  
  62. # keys are values for that byte, and values are the  
  63. # keys corresponding to those byte values  
  64. key_mapping = {  
  65.     1: {  
  66.         0b00000010: "",  
  67.         0b00000100: ("1""!"),  
  68.         0b00001000: ("2""@"),  
  69.         0b00010000: ("3""#"),  
  70.         0b00100000: ("4""$"),  
  71.         0b01000000: ("5""%"),  
  72.         0b10000000: ("6""^"),  
  73.     },  
  74.     2: {  
  75.         0b00000001: ("7""&"),  
  76.         0b00000010: ("8""*"),  
  77.         0b00000100: ("9""("),  
  78.         0b00001000: ("0"")"),  
  79.         0b00010000: ("-""_"),  
  80.         0b00100000: ("=""+"),  
  81.         0b01000000: "",  
  82.         0b10000000: "",  
  83.     },  
  84.     3: {  
  85.         0b00000001: ("q""Q"),  
  86.         0b00000010: ("w""W"),  
  87.         0b00000100: ("e""E"),  
  88.         0b00001000: ("r""R"),  
  89.         0b00010000: ("t""T"),  
  90.         0b00100000: ("y""Y"),  
  91.         0b01000000: ("u""U"),  
  92.         0b10000000: ("i""I"),  
  93.     },  
  94.     4: {  
  95.         0b00000001: ("o""O"),  
  96.         0b00000010: ("p""P"),  
  97.         0b00000100: ("[""{"),  
  98.         0b00001000: ("]""}"),  
  99.         0b00010000: "",  
  100.         #0b00100000: "",  
  101.         0b01000000: ("a""A"),  
  102.         0b10000000: ("s""S"),  
  103.     },  
  104.     5: {  
  105.         0b00000001: ("d""D"),  
  106.         0b00000010: ("f""F"),  
  107.         0b00000100: ("g""G"),  
  108.         0b00001000: ("h""H"),  
  109.         0b00010000: ("j""J"),  
  110.         0b00100000: ("k""K"),  
  111.         0b01000000: ("l""L"),  
  112.         0b10000000: (";"":"),  
  113.     },  
  114.     6: {  
  115.         0b00000001: ("'""\""),  
  116.         0b00000010: ("`""~"),  
  117.         #0b00000100: "",  
  118.         0b00001000: ("\\", "|"),  
  119.         0b00010000: ("z""Z"),  
  120.         0b00100000: ("x""X"),  
  121.         0b01000000: ("c""C"),  
  122.         0b10000000: ("v""V"),  
  123.     },  
  124.     7: {  
  125.         0b00000001: ("b""B"),  
  126.         0b00000010: ("n""N"),  
  127.         0b00000100: ("m""M"),  
  128.         0b00001000: (",""<"),  
  129.         0b00010000: ("."">"),  
  130.         0b00100000: ("/""?"),  
  131.         #0b01000000: "",  
  132.     },  
  133.     8: {  
  134.         #0b00000001: "",  
  135.         0b00000010: " ",  
  136.         0b00000100: "",  
  137.     },  
  138.     13: {  
  139.         #0b00000010: "",  
  140.         #0b00010000: "",  
  141.     },  
  142. }  
  143.   
  144.   
  145.   
  146.   
  147. def fetch_keys_raw():  
  148.     x11.XQueryKeymap(display, keyboard)  
  149.     return keyboard  
  150.   
  151.   
  152.   
  153. def fetch_keys():  
  154.     global caps_lock_state, last_pressed, last_pressed_adjusted, last_modifier_state  
  155.     keypresses_raw = fetch_keys_raw()  
  156.   
  157.   
  158.     # check modifier states (ctrl, alt, shift keys)  
  159.     modifier_state = {}  
  160.     for mod, (i, byte) in modifiers.iteritems():  
  161.         modifier_state[mod] = bool(ord(keypresses_raw[i]) & byte)  
  162.       
  163.     # shift pressed?  
  164.     shift = 0  
  165.     for i, byte in shift_keys:  
  166.         if ord(keypresses_raw[i]) & byte:  
  167.             shift = 1  
  168.             break  
  169.   
  170.     # caps lock state  
  171.     if ord(keypresses_raw[8]) & 4: caps_lock_state = int(not caps_lock_state)  
  172.   
  173.   
  174.     # aggregate the pressed keys  
  175.     pressed = []  
  176.     for i, k in enumerate(keypresses_raw):  
  177.         o = ord(k)  
  178.         if o:  
  179.             for byte,key in key_mapping.get(i, {}).iteritems():  
  180.                 if byte & o:  
  181.                     if isinstance(key, tuple): key = key[shift or caps_lock_state]  
  182.                     pressed.append(key)  
  183.   
  184.       
  185.     tmp = pressed  
  186.     pressed = list(set(pressed).difference(last_pressed))  
  187.     state_changed = tmp != last_pressed and (pressed or last_pressed_adjusted)  
  188.     last_pressed = tmp  
  189.     last_pressed_adjusted = pressed  
  190.   
  191.     if pressed: pressed = pressed[0]  
  192.     else: pressed = None  
  193.   
  194.   
  195.     state_changed = last_modifier_state and (state_changed or modifier_state != last_modifier_state)  
  196.     last_modifier_state = modifier_state  
  197.   
  198.     return state_changed, modifier_state, pressed  
  199.   
  200.   
  201.   
  202.   
  203. def log(done, callback, filterfun=None, sleep_interval=.005):  
  204.     while not done():  
  205.         sleep(sleep_interval)  
  206.         changed, modifiers, keys = fetch_keys()  
  207.         if changed and (filterfun==None or filterfun(keys, modifiers)):  
  208.             callback(time(), modifiers, keys)  
  209.   
  210. if __name__ == "__main__":  
  211.     now = time()  
  212.     done = lambda: time() > now + 60  
  213.     def print_keys(t, modifiers, keys): print "%.2f   %r   %r" % (t, keys, modifiers)  
  214.   
  215.     log(done, print_keys)  
Then you can use below sample code to trigger callback by ^U (Ctrl+U):
  1. #!/usr/bin/env python  
  2. import keylogger  
  3. import time  
  4.   
  5. now = time.time()  
  6. done = lambda: time.time() > now + 60  
  7. def print_keys(t, modifiers, keys):  
  8.     print "\n%.2f   %r   %r" % (t, keys, modifiers)  
  9.   
  10.   
  11. def filter(key, modifiers):  
  12.     if key!=None and key in ['u''U'] and (modifiers['left ctrl'] or modifiers['right ctrl']):  
  13.         return True  
  14.     return False  
  15.   
  16. keylogger.log(done, print_keys, filter)  


沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!