本来我自己计算的时候写了一大堆,不过还是挑重点的讲吧:
这是strlen的反汇编代码
004010F0 >/$ 8B4C24 04 mov ecx,dword ptr ss:[esp+0x4] ; ecx=字符串地址
004010F4 |. F7C1 03000000 test ecx,0x3
004010FA |. 74 14 je short Test_CPP.00401110
004010FC |> 8A01 /mov al,byte ptr ds:[ecx]
004010FE |. 41 |inc ecx
004010FF |. 84C0 |test al,al
00401101 |. 74 40 |je short Test_CPP.00401143
00401103 |. F7C1 03000000 |test ecx,0x3
00401109 |.^ 75 F1 \jnz short Test_CPP.004010FC
0040110B |. 05 00000000 add eax,0x0
00401110 |> 8B01 /mov eax,dword ptr ds:[ecx]
00401112 |. BA FFFEFE7E |mov edx,0x7EFEFEFF
00401117 |. 03D0 |add edx,eax
00401119 |. 83F0 FF |xor eax,0xFFFFFFFF
0040111C |. 33C2 |xor eax,edx
0040111E |. 83C1 04 |add ecx,0x4
00401121 |. A9 00010181 |test eax,0x81010100
00401126 |.^ 74 E8 |je short Test_CPP.00401110
00401128 |. 8B41 FC |mov eax,dword ptr ds:[ecx-0x4]
0040112B |. 84C0 |test al,al
0040112D |. 74 32 |je short Test_CPP.00401161
0040112F |. 84E4 |test ah,ah
00401131 |. 74 24 |je short Test_CPP.00401157
00401133 |. A9 0000FF00 |test eax,0xFF0000
00401138 |. 74 13 |je short Test_CPP.0040114D
0040113A |. A9 000000FF |test eax,0xFF000000
0040113F |. 74 02 |je short Test_CPP.00401143
00401141 |.^ EB CD \jmp short Test_CPP.00401110
00401143 |> 8D41 FF lea eax,dword ptr ds:[ecx-0x1]
00401146 |. 8B4C24 04 mov ecx,dword ptr ss:[esp+0x4]
0040114A |. 2BC1 sub eax,ecx
0040114C |. C3 retn
0040114D |> 8D41 FE lea eax,dword ptr ds:[ecx-0x2]
00401150 |. 8B4C24 04 mov ecx,dword ptr ss:[esp+0x4]
00401154 |. 2BC1 sub eax,ecx
00401156 |. C3 retn
00401157 |> 8D41 FD lea eax,dword ptr ds:[ecx-0x3]
0040115A |. 8B4C24 04 mov ecx,dword ptr ss:[esp+0x4]
0040115E |. 2BC1 sub eax,ecx
00401160 |. C3 retn
00401161 |> 8D41 FC lea eax,dword ptr ds:[ecx-0x4]
00401164 |. 8B4C24 04 mov ecx,dword ptr ss:[esp+0x4]
00401168 |. 2BC1 sub eax,ecx
0040116A \. C3 retn
0040116B CC int3
0040116C CC int3
0040116D CC int3
0040116E CC int3
0040116F CC int3
00401170 >/$ 75 01 jnz short Test_CPP.00401173
00401172 |. C3 retn
看起来有点长得不可理喻啊,不过这种方法对于长字符串来说是要比以下这种方法快四倍的:
repne scasb
not ecx
dec dec
下面来分析下这个函数:
004010F0 >/$ 8B4C24 04 mov ecx,dword ptr ss:[esp+0x4] ; ecx=字符串地址
004010F4 |. F7C1 03000000 test ecx,0x3
004010FA |. 74 14 je short Test_CPP.00401110
004010FC |> 8A01 /mov al,byte ptr ds:[ecx]
004010FE |. 41 |inc ecx
004010FF |. 84C0 |test al,al
00401101 |. 74 40 |je short Test_CPP.00401143
00401103 |. F7C1 03000000 |test ecx,0x3
00401109 |.^ 75 F1 \jnz short Test_CPP.004010FC
这段代码主要是用来测试字符串地址是否为0,4,8,C结尾的,因为:
0011(0x3)
AND
1100(0xC)
1000(0x8)
0100(0x4)
0000(0x0)
EQUAL
0x0
但是如果字符串长度小于4的话这段代码也能直接计算这个字符串的长度
后面的部分代码可以如下表示:(其中four(str)为递增的四个字符,每一次循环都取下个四字符)
eax=【four(str) xor 0xFFFFFFFF】xor【four(str) + 0x7EFEFEFF】
if(and(eax,81010100)!=0) then ok,计算长度
你会发现0x7EFEFEFF的二进制串很有规律:
01111110111111101111111011111111(隔一段就出现一次0,不妨将出现0的地方称为零点吧)
而0x7EFEFEFF+0000006B(包含0x0),就会使得某些零点变为1,然后再进行xor 【four(str) xor 0xFFFFFFFF】也是不会改变这个事实的
这个时候and(eax,81010100)就会不等于0了,因为81010100的二进制串为:10000001000000010000000100000000(原来是零点的地方现在都是1了)