2006年8月19日星期六

一点想法

大多数一般的软件产品在发布其初版的时候就应该考虑到以后更新版本的问题。不应该在初版时就公布过多的不成熟的功能,这将直接导致更新版本时负担过重。用户一般不习惯看到 ChangeLog 中有某功能被移除的记录,即使是免费软件也同样,商业软件更可能让用户觉得花钱升级还不如不升级。当然,如果初版一来就有较强的完整的功能,这也将为软件的前途做出好的铺垫。因此,一切都应该由开发者在着手开发前仔细地揣度的掂量,自己的团队有没有能力完成所有功能,市场有没有对这些功能的兴趣。

2006年7月24日星期一

考研班

这段时间上考研复习班,每天早上7点钟起床,晚上7点钟才能回到宿舍,真是很累。不过好歹也养成了一天三顿饭的正常习惯(原来都是一天一顿……汗)。中午也很难好好的休息一下,教室的环境比较恶劣。还是能充分地感受到当时高考时候的那种氛围,每个人都在为了一个考试努力奋斗,为了考上一个好的研究生院而奋斗,而我在中间就感觉像是一个异族,和高考一样,对这一切都放不开,自己明明不愿做,但对这一切有都不愿真心实意地投入,这种心态和准备TOEFL和GRE确实还是不同的。现在还是存有疑虑,怕两头都抓,到头来两头都抓不住啊。

后天就结束了,可以回家啦!

2006年7月9日星期日

C 与汇编代码结合

花了近一个星期,研究了一个看起来初级得不能再初级的问题,刚才终于成功了。就是用汇编写一个函数,导出到一个 C 程序里面调用它。想得很简单,不外乎就是 .asm 和 .c 分别编译成 .obj,然后链接。谁知问题多多。

汇编我用的是 NASM (http://sourceforge.net/projects/nasm),代码如下,实现一个类似 memcpy 的函数(myMemcpy.asm):

global _myMemcpy

;segment myMemcpy class=code

_myMemcpy:
mov eax, esp
push cx
push ds
push es

mov cx, [ss:eax + 12] ; count
mov ds, [ss:eax + 10] ; src segment
mov si, [ss:eax + 8] ; src offset
mov es, [ss:eax + 6] ; dest segment
mov di, [ss:eax + 4] ; dest offset

xor eax, eax
mov dx, es
mov ax, di ; return value is in dx:ax

rep movsb

pop es
pop ds
pop cx
retf

返回值存于 dx:ax。

C 代码如下,用 Turbo C 2.01 编译(test.c):

#include >stdio.h<
#include >stdlib.h<

extern void* myMemcpy(void* dest, void* src, int count);

main()
{
const int c = 5;
char* a = (char*) malloc(c);
char* b = (char*) malloc(c);

sprintf(a, "Dest");
sprintf(b, "Src");

printf(myMemcpy(a, b, c));

return 0;
}

如果运行正确,则会输出 Src。

NASM 汇编语句为 nasmw myMemcpy.asm -f obj,没什么好说的。

C 编译语句为 tcc -mh -c test.c,此处的 -mh 表示选择 Huge 内存模型,而其他的内存模型都不可行。如果用 Small 或者 Tiny 模型会产生错误:

Fixup overflow in module TEST.C at _TEXT:0036, target = _MYMEMCPY

原因可参见 Coping with 'Fixup Overflow' messages.,而 Large 模型(-ml)会在程序结束前的一个 call 产生错误,错误代码 36。

链接语句为 tlink /x test.obj myMemcpy.obj lib\c0h.obj, , , lib\ch.lib ,c0h.obj 和 ch.lib 对应 Huge 模型的库文件。

如果在 myMemcpy.asm 没写 segment 语句,NASM 会自动把 _myMemcpy 分配到 __NASMDEFSEG 段里去。segment 语句后面的 class=code 会告诉链接器,这个段一个代码段。可以在链接时把 /x 改成 /s,生成详细的 map 文件,里面记载了每个段的类型。当然,这个 segment 语句不是必须的。

关于外部函数的声明,Coping with 'Fixup Overflow' messages. 里面提到可以写成

extern void (far * far myMemcpy)(void* dest, void* src, int count);

这样,产生的代码(可由 tcc -S test.c 产生)为

mov ax,seg _myMemcpy
mov es,ax
call dword ptr es:_myMemcpy

而一般的声明方式产生的代码是

call far ptr _myMemcpy

两种方法我都试过,不过似乎只有一般的 call far ptr _myMemcpy 可以正常运行,也不会产生任何链接错误或者警告。简单才是美嘛。

我这里生成的 exe 文件,程序的实际入口是在相对入口地址偏移 F8 的一个 call,用 W32Dasm 打开可以发现那是 call 0000:0000,也就是在 call 前的代码运行时修改了这个 call 的实际目标地址。而 call 里面的代码就和 tcc -S test.c 生成的 test.asm 类似了。

虽然简单的一个小程序,但遇到问题还是折磨死人。不过也好,幸亏有这些问题,我也学到很多关于系统底层、可执行文件和调试方面的知识。

2006年7月8日星期六

夜思

自习回宿舍,夜阑人静,耳闻贝多芬的钢奏,信步所至,百感交集。忽然想到,这奇妙无比的感觉虽然现在就在我身上,这无可替代的享受虽然现在还属于我,但它决不会永远地持续下去,即使我现在就站在原地依依不舍,夜阑总会被白昼驱赶,人静总会被秩序打破。这享受也只能属于我一个人,不可能同另一个人,另一个女人,共同拥有和品味。想到这里,本欲放声痛哭,奈何无泪可出。抬头望月,回想这三年半独立的生活和种种遭遇,还不如一个释怀的苦笑来得痛快。行至湖边,希望拥有一架钢琴、希望能自己来创造快乐的信念越来越强烈,挥之不去。

虽然音乐绝不是一个人唯一的快乐源泉,虽然肉体一样需要得到满足……

回到宿舍,又是一片乌烟瘴气。

2006年7月1日星期六

《忏悔录》读后

一个多月没更新了,GRE 和期末考试是主要原因。同原来一样,在最忙的时候还是忍不住去找了一本书来读。上次在厦门的书店就看到了这本《忏悔录》,回来就在图书馆借了回来。

今天晚上刚看完了第二部,总的来说有两点感想:

1) 世上没有毫无道理的无理取闹。从书中的注解和安德烈•莫洛亚为1949年法国勃达斯版的《忏悔录》写的序言中看,卢梭在他的作品中所表达的很多疑惑和指控其实都是缺少根据的。而他所不能理解的百科全书派的朋友们变成了他的敌人,他自己的所作所为在其中所占的地位也应该是不可忽略的,如安德烈•莫洛亚所写到的,“他,一个聪明的公民,一个与道德为伍的朋友,一个对不纯洁的享乐的蔑视者,一个文明的敌人,征服了巴黎。接着,这个戏剧的反对者却为宫廷写了一部歌剧。…… 这位发表最著名的教育论文的作者却把自己的五个孩子全送进了育婴堂,或者至少还为此而夸耀。他就这样给自己的敌人提供了致命的武器。” 即使安德烈•莫洛亚的论据中也有言过其实的部分,卢梭所宣扬的和所做的还是反映出了很大的矛盾之处,而这可能正是他的朋友们所不能容忍的。我想,但我自己的朋友对我的态度有所改变而我又有所发觉的时候,我应该首先在自己身上找原因,虽然这往往是一项异常困难的任务。

2) 从这本书里面,我至少认识到,从小学开始就如雷贯耳的这些伟人的名字,他们的主人在真实的生活中却和任何一个有血有肉的普通人一样,有虚荣,有胆怯,有错误,有斗争。卢梭,这个自由的坚定守护者,教育理论的提倡者,竟也是一个多愁善感的、没有亲手抚养过孩子的人;狄德罗,被称为“哲学家”的哲学家,被卢梭认为是搞阴谋的人;而伏尔泰,不仅是卢梭对他怀有无法和解的憎恨,迟到了几十年的莫扎特对他毫无好感,即使他是法国启蒙运动的主要人物。时间就是一个大的筛子,会把(它认为)应该被人记住的人留下来并且美化他,把其他的人都抛进历史的长河中。一个人真正的面目往往和我们能够了解到的是迥异的。

2006年5月10日星期三

介入别人的生活

我一直认为,我希望介入别人的生活,亲近他们,无论他们是男人还是女人。同时,得不到成功的原因也应该是别人拒绝我的介入。问题在于,如果我坚持,进一步采取行动,我多半就能最终成功,而事实上我却没有这么做。看起来我好像喜欢孤独而黑暗,因为孤独和黑暗意味着深邃和独立。然而我内心告诉我,我并不喜欢,我试图避免,却又不知道如何改变。

我开始意识到,并不是别人拒绝我的介入,而是我不愿别人介入我的生活,虽然这两者看起来很相似。一位女生请我吃饭,我习惯地产生一种警惕感,害怕她进一步地介入我的生活,改变我的生活。按理来说,这种感觉一般应该是女生产生的,对于男生,有女性青睐(我不讨论女生的实际意图,假设她是确实青睐于我)一般都是好事,我却本能似地感到畏惧。小时候的痛苦回忆每每在关键的时刻起作用,告诉我,别人可能会使你原本虽不完美但也还平静的生活从此消失,甚至可能会伤害你。归根到底,父亲在这一切里仍然扮演了至关重要的角色。我不知道如何去改变这一切,因为我不愿意去强行改变我自己,所幸我认为每人都有自己的命运,谋事在人成事在天吧。

2006年5月6日星期六

去厦门玩了一转

五一这几天去厦门旅游,今天早上刚回广州。总体感觉厦门确实是一个不可多得的好地方,水好,山虽然不高也不错,最重要的是,人也好。

给我感受最深的有两样:第一件,鼓浪屿的小巷们让我重新找到了小时候成都的熟悉而又陌生的印象。在那里,家家户户都是一排平房,一扇面朝街道的带有门槛的小门,而里屋也因为缺少光照而显得黑乎乎的,除了差了一个天井,活脱脱就是爷爷奶奶老房子的翻版。而那些街道也是一块块石板砌成的,一天一天被本应恬静平淡的人们踩着,因此我也不止一次责备包括我在内的旅游者们打扰了那些居民们。我不知道他们为什么可以容忍外人介入他们的世外桃源,但如果仅仅是为了钱,他们有一万个其他的途径可以致富。那天晚上路过了一所鼓浪屿上的基督教小教堂,便也进去听了听牧师的布道。很难形容当时的感觉,外面是没有路灯的老街,里面是 10 余位虔诚的信徒,牧师操着并不标准的普通话,手中捧着一本老旧的圣经,一切都显得是那么的和谐和安静,似乎时间又回到了 10 余年前的某个晚上,某个记忆深处的晚上。

第二件,厦门人,至少在我所能接触的范围内,非常的好客,非常的让人感到一种温暖。印象最深的几个人有:我们的房东,一个退了役的军人,处于下岗状态的服装店老板。对于我们两个陌生人(我和同去的朋友)的几次麻烦的要求不厌其烦,对我们房租几乎不收押金,而一天晚上的促膝长谈也很大程度上拉近了我们的距离。晓风书店的几位店员,首先晓风书店(厦大外不远)是我见过的品味档次最高的书店之一(在这里可以轻易找到很多大书店找不到的数,而要找所谓的“世界名著”则很难),店员们对读者的要求总是十分热情地帮忙,而当我问及他们为什么不扩大书店的规模时,他们和我想到的共同答案也是,在可能获得更多利润的同时将不可避免地降低书店的档次。云香茶叶店的沏茶小姐,从她熟练的沏茶技巧和沉稳大方的气质,我觉得茶叶店缺少了她将很大程度上降低品茶者在这里享受到的那种艺术气息。厦门国际青年旅社的人们,老房子改造的旅社,和鼓浪屿的老街有着异曲同工之妙,在这里,原本素不相识人们就好像在一个大家庭里一样,很容易感受到弥漫在空气中的和谐。

总之,在厦门,在与人的交往中人们可以很大程度上放松平常的警惕和距离感,只要你愿意,几乎人人都是朋友;而在与自然的接触中,人们可以充分体会到自然与人的和谐共处,当然也少不了自然本身之美。

2006年4月30日星期日

PE Loader 有所突破

想了一个办法,把 Loader 宿主程序的 ImageBase 在链接时设到不是 0x00400000 的地址,那么被运行的程序就可以在 0x00400000 安家了。

今天花了很多时间来修改导入表的程序段,加入了 Forwarder 导出函数(比如 WSock32.dll 中的导出函数 WSAStartup 其实只是 WS2_32.dll 中的 WSAStartup 的一个引用)的导入程序段,现在基本上可以保证所有导入函数能够被正确导入。

现在的问题是,即使被运行程序的 ImageBase 正确了,被导入的 DLL 们也无法保证他们的 ImageBase。一般来说,很多 DLL 的 ImageBase 都处在很高的内存段,而且相互之间很可能会重叠,遇到这些情况,被导入的 DLL 就必须挪位置。不幸的是,很多程序里面给出的访问地址就是以默认 ImageBase 的值为参照的,所以肯定会出错。我还想不到什么办法来解决这个问题。

更奇快的是,我现在一调试程序,整个 Windows (Windows XP SP1)的所有图标都没了,然后陷入半死机状态,所有程序无法运行。我发现是只要我一装载 exe 文件到 0x00400000,就会出现这个问题,完全让人摸不到头脑……

关于调试器,我找不到合适的、运行于 Windows 下的、开源的 exe 的调试器,而且我发现,就算是 OllyDbg,它也是通过 API 来装载好了一个 exe 后再进行调试的,而我需要的恰恰是在不调用任何关键 API 的情况下装载并运行 exe。看来后面的路还很长。

2006年4月25日星期二

PE Loader 遇到难题

开始做 PE Loader。我的思路是,在自己的宿主程序里读入一个 EXE 文件,将之映像(Image)复制到内存(当然是这个 Loader 宿主程序的内存空间里的某处),同时把相关 DLL 以同样的方式载入,最后跳转(JMP)到 EXE 程序的入口地址,将程序的控制权交给它。我还没有考虑到如果目标 EXE 程序结束后,程序运行指针会指向哪里,实在不行,用一个 CALL 来调用。

现在的进度是,能够将 EXE 读入内存(Section 正确对齐 SectionAlignment),能把所有相关 DLL 载入内存,找到导入函数入口地址并修改 FirstThunk 数组。由于 Loader 宿主程序的映像自己占据了 0x00400000(ImageBase 默认值),所以载入的 EXE 的 ImageBase 肯定会在其他地方。当我想 JMP 时,发现程序中很多地址还是以 0x00400000 作为 ImageBase,所以一访问这些地址就会出错。我现在想不到怎么解决这个问题,想不明白 Windows 的 PE Loader 是怎么考虑这个重定位的,而且我手动把一个 EXE 的 ImageBase 改到 0x00500000,程序一样无法运行。

最大嫌疑是在 IAT 表和 Relocation 表。但很多程序根本没有 Relocation 表,IAT 也在修正 FirstThunk 数组时改过了。网上关于 PE Loader 方面的资料真的很少,更多的都是讲 PE 文件结构(想来这几天把 Iczelion 和 LUEVELSMEYER 的 PE 教程看了不知道多少遍)。我想,很可能为这个 EXE 分配内存时不能用常规的 VirtualAlloc,在 Loader 宿主空间里申请,而应该类似虚拟机那样让其有一个独立的虚拟内存空间。

现在的打算是,有时间就研究一下类似 OllyDbg 的这些调试器,因为它们确实能做到 Load and Execute PE file.

2006年4月14日星期五

GRE 作文终于考完了

4月13日上午终于把 GRE 作文考了,题目比较容易。无论如何,解决一心头大患了(想想为了它规避了多少节课)。现在该准备笔试了。