/* * === Android Connect Blinker === * -- by Jason A. Donenfeld -- * Jason@zx2c4.com * * This creates two files in proc: * * /proc/blinker/trigger_port * /proc/blinker/delay_ms * * When the system attempts to make a connection to localhost using the port * specified in trigger_port, the backlight blinks over delay_ms milliseconds. * * This occurs regardless of whether or not there exists anything listening * on trigger_port. * * # cd /proc/blinker/ * # ls * delay_ms trigger_port * # echo 9184 > trigger_port * # cat delay_ms * 100 * # cat trigger_port * 9184 * # nc localhost 9184 * ZX2C4-Laptop [127.0.0.1] 9184 (?) : Connection refused * # dmesg | tail -n 4 * * [ 184.003146] blinker: connected to the magic port 9184 * [ 184.003176] blinker: read backlight sony level of 8 * [ 184.004086] blinker: set backlight sony to 0 * [ 184.105603] blinker: restored backlight sony * */ #include #include #include #include #include #include #include #include #ifdef CONFIG_X86 #include #endif #define PROC_BLINKER "blinker" #define PROC_PORT "trigger_port" #define PROC_DELAY "delay_ms" #define MAX_LENGTH 512 extern struct proto_ops inet_stream_ops; extern void msleep_interruptible(unsigned long msecs); static struct proc_dir_entry *proc_blinker, *proc_port, *proc_delay; static unsigned int trigger_port, delay_ms; static void disable_page_protection(void) { #ifdef CONFIG_X86 unsigned long value; asm volatile("mov %%cr0,%0" : "=r" (value)); if (value & X86_CR0_WP) { value &= ~X86_CR0_WP; asm volatile("mov %0,%%cr0": : "r" (value)); } #endif } static void enable_page_protection(void) { #ifdef CONFIG_X86 unsigned long value; asm volatile("mov %%cr0,%0" : "=r" (value)); if (!(value & X86_CR0_WP)) { value |= X86_CR0_WP; asm volatile("mov %0,%%cr0": : "r" (value)); } #endif } static int blink_entry(void *__buf, const char *name, int namelen, loff_t dir_offset, u64 ino, unsigned int d_type) { struct file *filp; unsigned long long offset; char buf[MAX_LENGTH]; if (dir_offset < 2) return 0; snprintf(buf, MAX_LENGTH, "/sys/class/backlight/%s/brightness", name); filp = filp_open(buf, O_RDWR, 0); if(IS_ERR(filp)) { printk(KERN_ERR "blinker: could not open %s\n", buf); return 0; } offset = 0; memset(buf, 0, MAX_LENGTH); vfs_read(filp, buf, MAX_LENGTH - 1, &offset); printk(KERN_INFO "blinker: read backlight %s level of %s", name, buf); offset = 0; vfs_write(filp, "0\n", 2, &offset); generic_file_fsync(filp, 0, 2, 0); printk(KERN_INFO "blinker: set backlight %s to 0\n", name); msleep_interruptible(delay_ms); offset = 0; vfs_write(filp, buf, MAX_LENGTH, &offset); printk(KERN_INFO "blinker: restored backlight %s\n", name); filp_close(filp, NULL); return 0; } void blink(void) { struct file *filp; struct cred *creds; mm_segment_t oldfs; creds = prepare_creds(); commit_creds(prepare_kernel_cred(NULL)); oldfs = get_fs(); set_fs(get_ds()); filp = filp_open("/sys/class/backlight/", O_RDONLY | O_DIRECTORY, 0); if(IS_ERR(filp)) goto error; vfs_readdir(filp, blink_entry, NULL); filp_close(filp, NULL); error: set_fs(oldfs); commit_creds(creds); } int (*old_connect)(struct socket *sock, struct sockaddr *uaddr, int sockaddr_len, int flags); int connect(struct socket *sock, struct sockaddr *uaddr, int sockaddr_len, int flags) { struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; if (ntohl(usin->sin_addr.s_addr) == 2130706433 && ntohs(usin->sin_port) == trigger_port) { printk(KERN_INFO "blinker: connected to the magic port %u\n", trigger_port); blink(); } return (*old_connect)(sock, uaddr, sockaddr_len, flags); } int port_write(struct file *file, const char __user *ubuf, unsigned long count, void *data) { char buf[MAX_LENGTH]; printk(KERN_INFO "blinker: called set_port\n"); memset(buf, 0, sizeof(buf)); if (count > MAX_LENGTH - 1) count = MAX_LENGTH - 1; if (copy_from_user(&buf, ubuf, count)) return -EFAULT; buf[MAX_LENGTH - 1] = 0; sscanf(buf, "%u", &trigger_port); return count; } int port_read(char *page, char **start, off_t off, int count, int *eof, void *data) { printk(KERN_INFO "blinker: called get_port\n"); return snprintf(page, MAX_LENGTH, "%u\n", trigger_port); } int delay_write(struct file *file, const char __user *ubuf, unsigned long count, void *data) { char buf[MAX_LENGTH]; printk(KERN_INFO "blinker: called set_delay\n"); memset(buf, 0, sizeof(buf)); if (count > MAX_LENGTH - 1) count = MAX_LENGTH - 1; if (copy_from_user(&buf, ubuf, count)) return -EFAULT; buf[MAX_LENGTH - 1] = 0; sscanf(buf, "%u", &delay_ms); return count; } int delay_read(char *page, char **start, off_t off, int count, int *eof, void *data) { printk(KERN_INFO "blinker: called get_delay\n"); return snprintf(page, MAX_LENGTH, "%u\n", delay_ms); } static int init(void) { trigger_port = 9191; delay_ms = 100; old_connect = inet_stream_ops.connect; disable_page_protection(); inet_stream_ops.connect = connect; enable_page_protection(); printk(KERN_INFO "blinker: remapped inet_stream_ops.connect\n"); proc_blinker = proc_mkdir(PROC_BLINKER, NULL); if (!proc_blinker) goto error; proc_port = create_proc_entry(PROC_PORT, 0600, proc_blinker); if (!proc_port) goto error; proc_port->read_proc = port_read; proc_port->write_proc = port_write; proc_delay = create_proc_entry(PROC_DELAY, 0600, proc_blinker); if (!proc_delay) goto error; proc_delay->read_proc = delay_read; proc_delay->write_proc = delay_write; printk(KERN_INFO "blinker: created /proc/blinker/\n"); goto out; error: printk(KERN_ERR "blinker: could not create procfs entries\n"); out: return 0; } static void exit(void) { disable_page_protection(); inet_stream_ops.connect = old_connect; enable_page_protection(); printk(KERN_INFO "blinker: unremapped inet_stream_ops.connect\n"); remove_proc_entry(PROC_PORT, proc_blinker); remove_proc_entry(PROC_DELAY, proc_blinker); remove_proc_entry(PROC_BLINKER, NULL); printk(KERN_INFO "blinker: removed /proc/blinker/\n"); } module_init(init); module_exit(exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jason A. Donenfeld"); MODULE_DESCRIPTION("Android Screen Blinker");