去掉 Rainlendar Pro for Linux 的“未注册”字样

很久没有更新 Blog 了。但这并不代表生活索然无味,最近的一段时间,

  • MSRA 总算办夏令营了!于是有幸到在北京玩了六天,见到了许多新老朋友,有许多事情可以说。但如果篇幅有限,只能用一个字来总结这次夏令营,那只能是“热” :p
  • 实现了 Linux 下的一个通用的 Sandbox,新版本 ZOJ 的后端应该就是它了。同时,发现了现有 ZOJ 几个非常严重的 Bug,真不知道在开源的情况下 ZOJ 是怎么活到现在的,现在想想还是冷汗直流啊 :sigh:
  • 与学校免试研究生相关事情总算告于段落了,结果是和朋友们一起还要在学校呆至少两年多。可以向后来的学弟学妹们传达的信息是,如果不想被直博,那么就趁早准备出国吧… o.o
  • 做了一些简单的对各种 Web Sever 的性能测试,发现 Thin 表现出色,之前对用 ROR 性能的担忧就减少许多了。新版本的 ZOJ 的前端实现应该就是用它了 (:
  • 跟踪了东方永夜抄并实现了有一些功能的外挂,虽然即便这样,尝试了几次也只能让最高分的最高位达到 4 ,即将到 5,相比之下最高的未作弊记录是 6 … O:
  • 用 LaTeX 和一段 Ruby 脚本自动实现集训队的代码模版的排版,相比之前手工用 Word 来排版,就不会出现少一个右括号而导致的悲剧情况了 ._.

这些事情有的是属于另外一个大一点的事情的一部分,完成度还没有到一定程度;有的是通过 Google around 再自己调一调就能完成的事情;有的是不适合公开的事情,所以就没有写 .~.

那么回到正题,Rainlendar 是一个很好的桌面日历软件,官方非常厚道,未注册的话只会在网络日历事件的工具提示中显示“[UNREGISTERED]”,没有使用时间限制和其他功能限制。而且,只要为 Rainlendar 的翻译做出一点贡献就能免费获得一份授权。

可惜目前中文版的翻译进度是 100%,而授权费用要 100 人名币多,翻一倍再凭借学生证就能买到 Windows 7 正版了,太贵了 :sigh: 那么来自己和谐掉这个“[UNREGISTERED]”吧。

显然,下面的途径都能达到目的:

  • 和谐掉授权检测部分的代码,让未授权始终通过;进一步地查明授权文件验证方式,算出授权文件
  • 找出并和谐加入“[UNREGISTERED]”字样部分的代码
  • 找到“[UNREGISTERED]”所在的数据段(如果存在),将这个字符串修改成空串
  • 换掉和谐掉显示文字的函数,发现待显示字串中有“[UNREGISTERED]”字样就把他删除了再显示

前两种途径难度都很大。那么祈祷一下 “[UNREGISTERED]” 是存在数据段或者是其他文件中的,不过显然作者不会这么弱的…… 于是就换掉显示文字的函数,用 file 命令发现 rainlendar 是动态链接的,那么这个成功的希望比较大。

Rainlendar 链接到了 libpango,那么几乎可以断定它是使用 Pango 绘制文字的,容易知道 Pango 绘制文字的函数是 pango_layout_set_text_orig,它的签名是

void pango_layout_set_text (PangoLayout *layout, const char *text, int length);

到这里就很接近成功啦 (:  由于 Linux 支持 LD_PRELOAD,那么换掉一个库函数很简单:

#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
 
#define UNREGISTERED "[UNREGISTERED] "
 
static void *libpango;
static void (*pango_layout_set_text_orig)(void *, const char *, int);
 
__attribute__((__constructor__)) void pango_hook_init() {
	libpango = dlopen("libpango-1.0.so", RTLD_LOCAL  | RTLD_LAZY);
	pango_layout_set_text_orig = dlsym(libpango, "pango_layout_set_text");
}
 
void pango_layout_set_text(void *layout, const char *text, int length) {
	char *p = text ? strstr(text, UNREGISTERED) : 0;
	if (__builtin_expect((long)p, 0)) {
		int len = length >= 0 ? length : strlen(text);
		memmove(p, p + sizeof(UNREGISTERED) - 1,
			(len -= sizeof(UNREGISTERED) - 1) - (p - text)
			);
		if (length >= 0) length = len;
	}
	pango_layout_set_text_orig(layout, text, length);
}

使用 gcc $^ -fPIC -shared -o libpango_hook.so -ldl 将上面的代码编译成动态链接库文件 libpango_hook.so,接着修改 /usr/bin/rainlendar,在运行 Rainlendar 之前,正确设置好 LD_PRELOAD,大概是这个样子:

#!/bin/dash
RAINLENDAR_PATH='/opt/rainlendar2'
export LD_PRELOAD="$RAINLENDAR_PATH/libpango_hook.so"
exec $RAINLENDAR_PATH/rainlendar2

接着祈祷一下 Rainlendar 不是通过 dlopen 再 dlsym 的方式定位 pango_layout_set_text 这个函数的,那么事实证明它确实不是这样的 ^_^

虽然中文字体不好看,但是“[UNREGISTERED]”不见了。

14 thoughts on “去掉 Rainlendar Pro for Linux 的“未注册”字样

    • @roy : @mars :
      Windows 下没有 LD_PRELOAD。如果办到类似的事情有其他违法的途径会相对容易,这样的话不会公开写在这里…. -,- 见谅

    • @鸿剑青 : Rainlender 会截断过长的文字,也就是会出现 [UNREG… 或者是 …ISTER] 这样的情况,对他们分别作处理之后就很好了。

      #include &lt;string.h&gt;
      #include &lt;stdio.h&gt;
      #include &lt;stdlib.h&gt;
      #include &lt;dlfcn.h&gt;
       
      #define UNREGISTERED "[UNREGISTERED] "
       
      static void *libpango;
      static void (*pango_layout_set_text_orig)(void *, const char *, int);
       
      __attribute__((__constructor__)) void pango_hook_init() {
      	libpango = dlopen("libpango-1.0.so", RTLD_LOCAL  | RTLD_LAZY);
      	pango_layout_set_text_orig = dlsym(libpango, "pango_layout_set_text");
      }
       
      void pango_layout_set_text(void *layout, char *text, int length) {
      	if (text) {
      		// printf("TEXT: '%s' LEN: %d\n", text, length);
      		char *p = text ? strstr(text, UNREGISTERED) : 0;
      		int len = length &gt;= 0 ? length : strlen(text);
      		if (__builtin_expect((long)p, 0)) {
      			memmove((char *)p, p + sizeof(UNREGISTERED) - 1, 
      				(len -= sizeof(UNREGISTERED) - 1) - (p - text)
      				);
      			if (length &gt;= 0) length = len;
      		} else {
      			// handle "[UNREGI$", "^STERED]" ...
      			for (p = text + len - 1; p &gt;= text &amp;&amp; *p != '['; p--);
      			if (*p == '[' &amp;&amp; strncmp(p, UNREGISTERED, text + len - p) == 0) {
      				if (length &gt;= 0) length -= (text + len - p);
      				*p = 0;
      			}
      			for (p = text; p &lt; text + len &amp;&amp; *p != ']'; p++); 
      			if (*p == ']' &amp;&amp; 
      				strncmp(
      					text, UNREGISTERED + sizeof(UNREGISTERED) - 3 - (p - text), 
      					p - text) == 0
      			   ) {
      				p += 2;
      				memmove(text, p, len - (p - text));
      				if (length &gt;= 0) length -= p - text;
      			}
      		}
      	}
      	pango_layout_set_text_orig(layout, text, length);
      }
  1. 嗯,之前出现的状况砥确是你所描述的情况,应该正如你所分析的,启用了新版本,用一下,看看效果。非常感谢!

  2. 楼主高人! 看了你的文章,能看明白是什么,但是动手能力太差,可否提供win7下修改好的文件? 拜谢!

Leave a Reply to 鸿剑青 Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>