大家好,这个论坛真不错,呵呵 我也来写点东西吧 这个礼拜六做了两件事 睡觉+吃饭,我下面说的这件事一般是在睡觉的时候做的,还有一半是吃饭的时候做的,恩。开始了 周末在家很无聊,人太无聊了就会做傻事…… 于是我开始逆向XP SP2的ntdll玩,突然有个很想法,闲的没事的时候把里面的函数都用C重写一遍。 本来想找个软柿子捏,我看strlen很不错嘛,又简单,闭着眼就能搞定吧。 程序的结构很简单,移指针找'\0',找到了把指针一减就OK了。 恩,我带着定势思维继续看下去。 完了晕了 .text:7C922ABB mov eax, [ecx] .text:7C922ABD mov edx, 7EFEFEFFh .text:7C922AC2 add edx, eax .text:7C922AC4 xor eax, 0FFFFFFFFh .text:7C922AC7 xor eax, edx .text:7C922AC9 add ecx, 4 .text:7C922ACC test eax, 81010100h 这罗里巴嗦的忙活什么呢…… 屁大点事,找个'\0'还这么麻烦 头晕中…… 三小时后 微软的代码果然牛X,这么简单的函数,看看人家考虑的就是周到,大公司就是大公司,大公司就是一定要把简单的事情复杂化,这样才能把复杂的事情简单化…… 恩,我们看看他是怎么干的。 //.text:7C922A9D mov ecx, [esp+arg_0] 字符串指针放入EXC //.text:7C922AA1 test ecx, 3 判断地址是否是4字节对齐的 //.text:7C922AA7 jz short loc_7C922ABB //.text:7C922AA9 // 不是4字节对齐的地址,一个字节一个字节的比较 //.text:7C922AA9 loc_7C922AA9: ; CODE XREF: _strlen+19j //.text:7C922AA9 mov al, [ecx] //.text:7C922AAB inc ecx //.text:7C922AAC test al, al 判断al是不是'\0' //.text:7C922AAE jz short loc_7C922AEE 是就跳 //.text:7C922AB0 test ecx, 3 是不是对齐地址? //.text:7C922AB6 jnz short loc_7C922AA9 不是就继续 //.text:7C922AB8 add eax, 0 eax清0 //.text:7C922ABB 已经是4字节对齐的地址了,这里可以4个字节4个字节的比较了 //.text:7C922ABB loc_7C922ABB: ; CODE XREF: _strlen+Aj //.text:7C922ABB ; _strlen+34j ... //.text:7C922ABB mov eax, [ecx] 从字符串里读4个字节 //.text:7C922ABD mov edx, 7EFEFEFFh 神奇数字~~! //.text:7C922AC2 add edx, eax //.text:7C922AC4 xor eax, 0FFFFFFFFh 取反 //.text:7C922AC7 xor eax, edx //.text:7C922AC9 add ecx, 4 //.text:7C922ACC test eax, 81010100h 另一个很神奇的数字 //.text:7C922AD1 jz short loc_7C922ABB 检查4字节里有没有0,没有就跳 //.text:7C922AD3 mov eax, [ecx-4] 有的话找出哪个是0 //.text:7C922AD6 test al, al //.text:7C922AD8 jz short loc_7C922B0C 第一个就是 //.text:7C922ADA test ah, ah //.text:7C922ADC jz short loc_7C922B02 第二个是就-3 //.text:7C922ADE test eax, 0FF0000h 第三个是就-2 //.text:7C922AE3 jz short loc_7C922AF8 //.text:7C922AE5 test eax, 0FF000000h 第四个是就-1 //.text:7C922AEA jz short loc_7C922AEE //.text:7C922AEC jmp short loc_7C922ABB 都不是?有没有搞错……一般来这算法如果没有问题是不会到这里的 它用了一种很神奇的算法,可以找出一个四字节中有没有'\0'。 总之,我仿照着写了一个C函数,感觉不如他们汇编写的那个好。测试了一下是OK的。 就算你知道微软的编译选项也请不要拿编译以后的代码和原来的代码比较,因为我没有那么强悍的逆向水平,呵呵就这么着吧。 我觉得原先的代码就是用汇编手写的。 这么简单的一个函数居然还考虑了字节对齐,很强悍…… [cpp]size_t __cdecl strlen(char* p) { char* temp; temp = p; long fourbyte = 0; while (((long)p)&3 != 0) {//如果地址没有对齐 if (*p != '\0' ) p++; else goto exit1; } do { fourbyte = *(long*)(p); p+=4; } while((((fourbyte^0xffffffff)^(fourbyte+0x7EFEFEFF))&0x81010100) == 0); //四字节读入一个long型变量的时候是倒着放的,不要忘了 //即char a[4] = {77,78,79,80},放到一个long 里就是80797877 if (((char*)(&fourbyte))[0] == '\0') { goto exit4; } if (((char*)(&fourbyte))[1] == '\0') { goto exit3; } if (((char*)(&fourbyte))[2] == '\0') { goto exit2; } if (((char*)(&fourbyte))[3] == '\0') { goto exit1; } exit1: return p-temp-1; exit2: return p-temp-2; exit3: return p-temp-3; exit4: return p-temp-4; }[/cpp] 关于那个算法,大家来讨论一下原理吧,呵呵就当思考题了 pk8995@163.com Live in the code |
转载自:http://www.0ginr.com/bbs/viewthread.php?tid=1163&highlight=strlen