How to give a program fake system time so that you can use it forever (Linux)

Nov 30, 2007

Comments

[Disclaim: It’s evil, don’t use it unless you are fighting with some even more evil software.]

Short Intro. [Skip it if you don’t know much about OS or aren’t interested in the technical detail ]

As you might know, every program on Linux system runs on the kernel instead of directly contacting with actual machine. For modern operation systems like Linux, BSD(Mac) and Windows, a mechanism called system call is used to request the system resources via operation system so that operation system has the full control of all programs. In brief, when user program need call a function in library, e.g. print in stdlib, library function forwards (usually library function is a light-weighed wrap of the system call) the request to operation system. Since the system is highly hierarchical and user program is built on the top of libraries and OS kernel, it’s possible to insert some layers in between program and OS to intercept the request. Don’t panic about the nerdy name. Actually this strategy is commonly used on Window platform in anti-virus software as well, because anti-virus software want to monitor every system resource usage for any program and prevent the malicious resource requests.

Here, we simply want to intercept a system call named “TIME” so that every time a program request the current time (so that it can verify whether the licence has expired), we feed the program with a fixed (fake) time. By fooling the program around, you can literally use a program forever. God, doesn’t this mean I can use all software forever? The bad news is for some OSs like Windows, it’s very hard to do system call interception as all the APIs are undocumented and software might have other ways to prevent this. The good news is lots of software on Linux and Mac are simply reading system time. Actually, only top developers and Microsoft partners know how to do system interception. However, for Linux, since the system itself is open source from bottom up, there is no way to prevent such kind of interception (Now you know why some software companies don’t like Linux :).

Approach 

On Linux, tons of methods are around. Here I just introduce three of them briefly under the assumption that you don’t have the source code of that software. [Otherwise you can just modify the source code]

**

Method 1: Intercept library call in linking time.**

Sometimes you have a library (A) that can be used as a part of your program and you want to intercept the library call of that library (A). The best way and the easiest way is to write a fake function and link it in the compile time. This method is totally harmless to your system and very neat. If you can do some modification in makefile, then this procedure is totally transparent to both developer and user.

**

Method 2: Intercept system call in the run time**

If you’ve already got a execute program, then there is no way to intercept the system call in compiling time. To intercept the system call in the run time, there are two ways. The first approach is putting the target program in a designed container. Typically, a container fork/create/call the target program as child process. Since in OS, parent process has accessibility to the child process, it can intercept syscalls easily via ptrace toolset. The second method is to hack the kernel, namely, to tell the Linux kernel to response syscall in a certain way. Since now Linux supports kernel module, a very convenience approach is to compile a program as kernel module and install it on the fly. However, this method is less flexible then the previous method as now all the syscall are intercepted, even for system calls from other programs. [Sure, you can restrict the module only applicable to a certain process via a pid comparison, but then you need to feed the kernel module with PID, it’s awkward ]

In my implementation, I use ptrace/container method. I’ve tried kernel module method but failed as there were not so much well-formed documents on Linux 2.6 kernel.

Here is my code, it’s self-explanatory, have fun with hacking. [Download the C file]

/* Faketime wraps a user program and feed it with user-specified fake system time
   so that it can be used forever without any "licence expired" problem
	 
    Copyright (C) 2007 Eric You XU, Washington University ( youxu [@T] wustl.edu ) 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

*/

#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <linux/user.h>

/*
Register layout defined linux/user.h, but actually in 
 asm-$(arch)/user.h
struct user_regs_struct {
        long ebx, ecx, edx, esi, edi, ebp, eax;
        unsigned short ds, __ds, es, __es;
        unsigned short fs, __fs, gs, __gs;
        long orig_eax, eip;
        unsigned short cs, __cs;
        long eflags, esp;
        unsigned short ss, __ss;
};
*/

/* Note that EAX now is RAX in x86-64
 	we can also find the actural offset for any register
 	from <asm-$(arch)/ptrace-abi.h>
#define RAX 24
*/

#define ORIG_RAX 44
/* ORIG_RAX stores the number of syscall */

#define SYS_TIME 13
/* Machine specific syscall number is defined in 
	unistd.h */

#define back_to_future 1175737392

/* Time is stored as a long interger in C, you can get 
	current time via time(NULL). Thus, it's very easy to 
	get a long integer denoting some time in the past. 
	
	Python/Java can also be helpful in figuring this out 
	
	If you don't know how, just keep in mind that 
	Dec. 1, 2007 is about 1196476452. 
	One day interval = 60*60*24 = 86400 [Time flies fast]
*/

char* host_program = "your program name here";
char* arglist = "your program fake list here";
/* Make modifications for these two lines, then 
	compile it via
		gcc faketime.c -o faketime
	use it via 
		./faketime
*/

int main()
{   pid_t child;
    long orig_rax, eax;

	 struct user_regs_struct regs;
	 int status;
    int insyscall = ;
    child = fork();
    if(child == ) {
        ptrace(PTRACE_TRACEME, , NULL, NULL);
        execl(host_program, arglist, NULL);
          }
    else {
       while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_rax = ptrace(PTRACE_PEEKUSER,
                     child, ORIG_RAX, NULL);

          if(orig_rax == SYS_TIME ) { /* Intercept SYS_TIME syscall */
             if(insyscall == ) { /* Syscall entry */
                insyscall = 1;
             			  }
         	 else { /* Syscall exit */
					ptrace(PTRACE_GETREGS, child, , &regs);
				   	/* We can also use ptrace(PTRACE_SETREGS, child ,RAX, &back_to_future); 
						but it doesn't work. There might be some tricky here */
					regs.eax = back_to_future;
					ptrace(PTRACE_SETREGS, child, , &regs);
            	                 }
          } // End if with SYS_TIME 
       ptrace(PTRACE_SYSCALL, child, NULL, NULL);
        }
    }
    return ;
}


说 Python 的几句坏话

Nov 29, 2007

Comments

我一直很推崇使用 Python, 但是推崇不是迷信, 今天就说说几个 Python 相对于 Java 和其他语言的缺点, 以及初学者要注意什么.

**

  1. 文档不完善, 最少惊奇原则不适用**

很多 C++ 和 Java 开发者都知道, MSDNJava Doc 堪称技术文档的典范. Java 更是充分体现 Knuth 提出的 Literate Programming 的精华, 用一个专门的叫 javadoc 的工具自动提取程序元信息, 生成交叉引用的 JavaDoc 文档. 任何复杂的类, 框架, 在javadoc 的统一管理下, 可以生成非常漂亮的文档. 如果再配合设计模式中的 facade 模式, 一个复杂的框架很简单就可以上手. 这方面, Hibernate, Lucene 都是优秀的典范. 当然, 交叉引用的文档也有明显的缺陷, 就是极其庞大冗长, 因此 JavaDoc 和 JDK 是独立的两个部分. MSDN 更加绝了, 直接出几张光盘, 你爱装不装. 虽然冗长, 好处就是在面向对象的汪洋大海中顺着链接, 永远不会丢失. 而 Python 的文档还远远不够细致. 所有的对象方法, 只是简单介绍. 如果一个对象方法返回另外一个不熟悉的对象, 则必须手工定位新对象的位置, 而不能顺着文档继续前进. 为了搞清楚一个包, 完成一个小任务, 可能要多次搜索文档. 对于黑客级别的用户来说, Python 文档言简意赅, 喜闻乐见, 只要记住了每个对象的每个函数, 写程序如行云流水. 对于初学者和用 Python 写稍大项目的人来说, 这就是最恐怖的噩梦. 因为以介绍单个函数功能为组织方式的文档, 对于刚上手的用户来说, 很难顺着对象调用关系,通过文档指引完成任务. 即使著名的框架比如 django, 也缺少交叉引用的细致的文档. Python Library Reference 只能说是一个完成了 50% 的README. 在这种情况下, 除非对一个框架很熟悉, 否则这个框架对于初学者来说简直是”惊奇的噩梦” — 一般用户只能看例子然后顿悟到: 哦, 原来是这样用的.

除此以外, 因为语言的动态性, 使得 IDE 能给的帮助特别少. 对于 Java 程序员来说, 很多时候都是靠着 Eclipse 的提示去选择正确的方法调用. 而因为 Python 的动态性, 变量的类型到运行时才能确定, 因此能够给的提示相对变少. Python 既是动态的, 又是强类型的, 所以代码的阅读者必须要一步一步手工追踪中间对象的类型, 并且查阅对象方法文档, 才能搞清楚到底一段代码做得什么. 而这一切, 偏偏是没有 IDE 帮助的. 对于开发者来说, 如果不熟悉对象的可用方法, 用 Python 开发也会事倍功半, 因为要常常停下来去 Google.

当然, 文档不够完美, IDE 不支持的现状, 根本是因为 Python 的动态性: 谁让 Python 返回值不规定类型呢? 我的建议是, A. 对于现成已知的一些对象方法上, Python 社区应该注意文档的维护. B. 对于不熟悉对象的可用方法这个问题, 要买一本 Pocket Reference. 或者下载一些 cheet sheet. 在刚接触 Python 或者 刚接触 Python 框架的时候, 一本薄薄的可供快速查阅所有对象的所有方法的小书, 无比重要. 这也间接的说明, 和 shell 脚本, 正则表达式语法等一样, 脚本语言必须要很熟悉才能发挥魔力.

(– 附实例,对Java 不熟悉的可直接跳过: 我曾经做过一个游戏, 这个游戏类似于推箱子, 其中的一个核心是要把多幅带alpha 通道的图像拼成一个图像, 然后显示在窗口中. 打开 API Doc, 简单的搜索一下, 发现有 java.awt.Image 类. 不过这个是抽象类, 没关系, 文档下面就明明白白写着两个可以直接用的类, 一个是 BufferedImage, 另一个是 VolatileImage. 看上去 BufferedImage 更像, 于是点进去, 正是想要的. 看看构造函数, 一个空白的图像就构造好了. 顺着方法向下看, 看到 createGraphics() 方法, 返回 Graphics2D 方法, 说明中写着这个可用做在 Image 上绘图, 正是想要的, 点返回值, 是 Graphics2D, 有方法叫 drawImage. 任务完成, 轻松自然.

回到 Python. 我遇到过一个需求, 是用 Python 提取所有一个 Zip 文件的注释. Google Zip+Python 定位到 zipfile 模块. 找到 ZipFile 对象. 读文档没发现可以往外读注释, 只能碰运气, 开一个解释器尝试. 看上去 getinfo 可以. 因为没有交叉引用, 只能搜索ZipInfo 这个对象. 终于看到一个方法是 comment. 不知道类型, 仍然需要自己在解释器下先 type 测试一下, 才能在程序中写下一行完整的语句.–)

**

  1. 文档上有, 但是你不能用**

我用 Ubuntu/MacOSX系统, Python 是自带的. 以为万事大吉, 实际上, 因为 Python 所有的库都是运行时装载的, 所以有些库在有些系统上是缺失的, 而文档则是所有标准安装的超集. 因此即使文档上有, 你也没法用.

这个问题对于 Java 程序员和 .Net 程序员来说是不能接受的. 对于任何一个良好设计的 SDK, 文档和实际可用的应该具有同等的覆盖面. 在 Java 中, 有一个包就是有, 没有就是没有, 绝对不会出现标准文档中提到了, 实际中却不可用的情况. 对于可选包, 用户也能理解需要下载模块. 而对于 Python, 开发人员很难理解为什么标准文档中提到了, 实际中却缺这个组件缺那个组件. 一个潜在的伤害就是, Python 的跨平台能力没那么强. 试想, 开发的机器上有一个模块, 而用户没有. 而标准文档偏偏又认为这个是一个标准组件, 那么, 在装载的时候,找不到这个组件了, 让用户根本就无从下手. 我的建议是 A. Python 应当有更好的 import 报错机制, 对于不是每台机器都安装的模块, 应当给出平台中立的更加友好的帮助信息, 而不是简单的 ImportError. B. 应当在 Python 语言中支持更加强大的内省机制, 对于系统当前可用的包, 应当有一个全局的缓存. 这样, 用户才能知道哪些可用哪些不可用, 不可用的时候怎么办. C. 对于用户来说, 只能求助于Google 或新闻组, 或者从 Python 源代码中找线索. 一个较好的办法是在 Python 源代码上 执行 make test, Python 会汇报哪些模块已经编译, 哪些模块被跳过了.

(–实例: 前几天想写个程序抓Google Reader. 认证的时候要用到 SSL. 因此找到 Python 文档, 照着例子 import urllib2, 然后直接连 Https 服务器. 出错了, 原因是没有 ssl 支持, 不能访问 https. 所有的 Python 在线文档都说, 这个是标准模块. 对呀, Python Library Reference 里面都有的模块, 怎么能有问题呢? Google 搜索也帮不了什么忙, 因为 Python 是跨平台的, 不同的平台有不同的解决方法. 所有的在线帮助只能提示到 _ssl.o 这一层. 于是, 只能翻看 Python 源代码, 在 Modules/_ssl.c 中看了一圈, 发现需要 openssl/ssl.h 等文件, 这才意识到, 需要安装 openssl 和 libssl-dev. 在 Ubuntu 上, 同样的问题还存在于 sqlite3 模块, tk 模块 和 bz2 模块. 分别需要安装 libsqlite3-dev, tk-dev 和 libbz2-dev, 再重新编译 Python 系统. 使用 make; python setup.py install 来安装 –)

3. Python 是胶水语言, 用是简单的, 模块开发是痛苦的

Python 用到一定程度以后, 一个自然的需求就是把其他语言, 特别是C语言写的库包装成 Python 库. 这个问题说复杂也不复杂, 在 Python/C Reference Manual 中也提到了所有必要的信息. 只是层次太细. Boost.Python 有更加好的实现, 不过需要对 C++ 模板很熟悉才行. 当然, Python 的模块开发的痛苦比起 JNI 复杂的 JNIEnv 对象, Lua 的 基于栈的 Push Pop 已经是大大轻松了. 相比较于 Ruby 的自动生成 makefile, 也算各有千秋. 只是模块开发设计到的技术细节太多, 要对 Python/C API 了然于胸才行. 因此我的建议是: 不要从轮子造起. 如果有这样的模块, 就用. 如果没有, 而又非要用特定功能的模块不可, 那就暂时先不要考虑用 Python. 相比较于其他更加成熟的语言, 比如 Java, Python 的可用模块还是显得少了一点. 因为 Jython 的出现, 这个情况已经大大缓解. 不过工业界历来喜欢用最稳定的东西, 所以可以想象, 多少 Web2.0 公司是为了用 Lucene 才用 Java 服务器的, 而每天又有多少新的公司投入 Java 的怀抱 :)


各位朋友, 请不要向我传福音

Nov 26, 2007

Comments

我知道您是耶稣的使者, 想传福音于我. 我读过使徒行传, 能理解你传福音的动机. 你有信的自由, 我也有不信的自由.

你可以随便怎么解释相对论, 大爆炸. 牛顿时代你说宇宙规则需要人设定, 大爆炸时代你说是上帝点的火, 进化论出现后你说有微观进化和宏观进化. 这些解释您都可以相信. 不管你怎么解释科学, 各位朋友, 请不要把你的解释宣扬给我, 我对科学有我的解释.

我不信, 但是我尊重您的信, 请不要问我为什么不信, 我真的不想和任何人辩论信仰问题.

哦, 对了, 按照圣经, 我一定会下地狱. 我知道, 所以请不要老是提醒我.

谢谢.


语言和思维表达随笔

Nov 21, 2007

Comments

某午夜, 灵感泉涌, 找灯开关, 找笔, 找纸, 找合适的词, 试图再现思维的火花. 提笔, 乱七八糟的中文, 潦草的数学符号、记号, 几行伪代码, 夹杂英文单词. 我常常想, 假如我从来不曾学过数学, 从来不会英语, 我记下的思维状态, 是否会和现在一样; 我在思考的时候, 会不会自觉的使用数学语言, 会不会用英语? 咿呀学语的婴孩, 不会使用人类语言的聋哑人, 又该如何记下他们的思维? 语言符号到底是构建了人类的思维, 还是限制了思维的表达?

谱曲要入五音, 写词要从平仄; 语言要有语法, 文章要有结构; 这些, 包括编程, 计算, 证明, 都是在使用思维而赋之于符号规则系统(语言). 符号系统有其规则和用法. 守其规则, 才能有交流. 如果放弃了语言的交流功能, 则可以解放思维, 自由表达. 如果我们观察乡村里的”土哑巴”(不会正规的手语,手语是自创), 则只有周围的少数人能理解其含义. 从他的思维层到自创的手语层也许是透明的一一对应的, 无碍的. 但出了他的手语层而要求和他人交流时, 其自创的手语又成了交流的障碍. 因此, 规则是用以交流用的语言必须内涵的. “即使狮子会说话,我们也不懂得它”.

语言能力不是与生俱来的. 即使完全通过逻辑, 从原始的事实构建到高层的抽象, 也需要时间. 以数学为例, 从最简单的集合论和代数系统到费马大定理, 完全通透的走过也需要比较长的时间. 而构建这个大厦的砖块只是几条公理, 建造的方法又只有逻辑一种. Wittgenstein (维特根斯坦) 在逻辑哲学论中讲语言是思维逻辑结构的几何投影. 思维逻辑结构建立后, 可以借助于语言, 通过一个人传递到另一个人. 如果语言的几何投影能够被简单还原, 思维就通过语言完成了一次传递. 在A(创造者)脑中, 语言是被构建的. 在B(学习者) 脑中, 语言是被解构并还原为事实的逻辑图像(思想)的. 在学习者那里, 思维是要需要借助语言进行的.

一个人认识到的世界不是完备的, 因此使用的语言集合也只是一个子集. 对语言的理解程度决定于对世界的构建. 仍以数学为例, 研究微分方程的数学家与研究代数数论的数学家如果遇到了, 可能彼此都不懂对方在讲什么. 微分方程世界的语言, 对于代数数论世界的数学家, 是难以解构的, 因为他的世界里缺少语言反向解构的对应物. 回到自然语言, 英语对于很多中国人, 是无法解构的, 因为我们无法分辨这个语素到底对应了什么. 但是假使我有一本字典, 这个问题也就解决了. 抽象的层次, 表达的符号, 描述的世界等所有的一切不同, 都让语言这个逻辑图像呈现出了不同的样子, 而让不熟悉这个语言系统, 或者缺乏逻辑世界对应物的人, 云里雾里.

我们来研究一个设计精巧而简单的语言: 程序设计语言. 程序设计语言是也是表达思维的工具.

程序设计语言有语法, 有规则. 语法和规则是为了让底层的语言理解机制消歧义, 获得一致性的解释. 符号在程序设计语言中发挥了原子的作用. 几乎所有的编程语言都是基于封闭世界假设, 也就是说, 没有声明(被计算机感知)的原子符号, 不能使用, 每个事物都有其本体论含义. 编程语言把真实世界的逻辑关系映射到了计算机世界. 因此受制于计算机世界. 在语言和思维关系当中, 语言的限制造成思维表达能力相对受制, 而相反的, 交流能力却大大提高 (居然能和一台机器打交道了).

图灵完全性从一定程度上解决了语言能力的问题. 一般来说, 编程语言被计算机解构后, 描述计算机世界的能力是相同的. 但是这不代表编程语言的表达真实世界的能力是一样的. 事实上, 不同的编程语言的描述世界的能力是不一样的.

程序设计语言是真实世界的逻辑图像, 这个图像对应了一种逻辑(算法), 一个设计哲学(模式, API)或者一个世界模型(框架). 而这个图像可能位于世界的不同层次. 以Java, C 和 SQL 为例. Java 是对象世界的逻辑图像. C 是过程世界的逻辑抽象. SQL 是关系(数据库)世界的逻辑抽象. 因为世界难以”一言以蔽之”, 因此不同的语言出现是必然的. 三个语言在解题能力上是等价的, 然而, 之所以没有人用 Java 用做关系数据库的语言, 是因为 Java 的逻辑抽象, 不是关系数据库的一个直接几何投影. 因此, 用 Java 这个逻辑图像去重现施加于关系数据库世界的逻辑, 是不够直接简明的, 至少说, 不是最贴切的.

从程序设计语言的使用上, 我们可以清楚的看到, 语言的错误选择限制了思维. 语言的错误选择不在于语言本身, 而在于语言和表达世界的不契合. 以 Java 编程为例, 一开始的 public static void main 封装在一个 public class 中, 不知道吓退了多少从来没有接触过面向对象的语言学习者. 推而广之, 即使一个框架设计再好, 不了解其设计哲学, 仍然觉得难学难用. 不契合来自于四方面, 1. 对世界模型的不熟稔, 造成语言无法使用. 比如, 对MVC的不熟悉造成Struts难学难用. 对IR不熟悉造成 Lucene 难学难用. 2. 太复杂的抽象造成语言难以入门, 比如 Java 模型造成一行Hello, world 都要至少构造一个 class, 一个 main 和一个 system.out (在Java 6.0中已经无须这么复杂). 一个简单的逻辑映射出不属于这个世界的很多抽象. 不必要的复杂性吓退了初学者. 3. 太高层的抽象造成语言难以理解, 比如 LISP, 抽象于 lambda 演算, 至今仍有不少人对于 Y-Combinator 的高层抽象表现出费解; 而对于精通函数式语言的人来说, Google 的 MapReduce 又显得那么自然. 不了解高度抽象的世界模型, 必然是如同看到满屏幕的乱码一样, 无法下手. 4. 太低的抽象造成语言难以使用, 如C, 在没有library 情况下, 链表都要手工构造. 即使想描述一个简单的思维, 都需要映射出很多不必要的底层抽象. 这样让语言的使用者主观上不倾向于用这套语言体系.

[我最近在学习法语, 其名词的性限制了我的表达, 继而限制了沟通. 因为假如我用错了, 别人会不懂. 因此触发了语言和思维的思考. 同时这几天, 我在考虑到底选择什么样的程序设计语言才是好的选择, 因此有抽象层次和语言表达能力的思考. 正巧有一本维特根斯坦的哲学逻辑在手边, 偶翻一下, 触发了思考, 故写下这篇随笔. 在语言选择方面, 我推荐 Python, 我认为它达到了抽象层次和表达能力的契合, 无论你是初学者, 还是高手].


Another two books this week

Nov 18, 2007

Comments

I grabbed another two books today.

freakonomics.jpg

and

i_l.jpg

The first book will make me freaking, and then second book, as usual, will make me an a**hole in the next several weeks :)

Happy reading!!