此算法为hash中比较简单的线性探测法,代码优化选项OD(无)
//线性探测
#include
#include
#include
#include
#define MAX_SIZE 13
#define b 13
char *ht[MAX_SIZE];
int stringtoint(char *key)
{
int number;
for(number=0;*key;key++)
number+=*key;
return number;
}
int f(int k)
{
return k%b;
}
char* search(char* key)
{
int current,homebucket;
homebucket=f(stringtoint(key));
for(current=homebucket;ht[current]&&strcmp(ht[current],key);)
{
current=(current+1)%b;
if(current==homebucket)
return NULL;
}
if(strcmp(ht[current],key)==0)
return ht[current];
return NULL;
}
void insert(char *key)
{
int homebucket,current;
homebucket=f(stringtoint(key));
for(current=homebucket;ht[current];)
{
current=(current+1)%b;
if(current==homebucket)
return;
}
ht[current]=(char*)malloc(sizeof(*key));
strcpy(ht[current],key);
}
int main()
{
int i;
char word[MAX_SIZE];
for(i=0;i
惯例,OD跟踪,往下翻,有个jmp main指令,很明显就是它啦,F7跟进去,显示如下代码
01381170 >/$ 83EC 18 sub esp, 18 ; 首先分配临时存储空间
01381173 |. A1 00303801 mov eax, dword ptr [__security_cookie]
01381178 |. 33C4 xor eax, esp
0138117A |. 894424 14 mov dword ptr [esp+14], eax
0138117E |. 33C0 xor eax, eax
01381180 |. 53 push ebx
01381181 |. 8B1D A8203801 mov ebx, dword ptr [<&MSVCR90.fflush>] ; MSVCR90.fflush
01381187 |. 55 push ebp
01381188 |. 8B2D AC203801 mov ebp, dword ptr [<&MSVCR90.__iob_func>] ; MSVCR90.__p__iob
0138118E |. 56 push esi
0138118F |. 8B35 B0203801 mov esi, dword ptr [<&MSVCR90.scanf>] ; MSVCR90.scanf
01381195 |. A3 6C333801 mov dword ptr [ht], eax ; 初始化全局变量的指针数组
.....................
......................
013811DE |. 57 push edi
013811DF |. 90 nop
013811E0 |> 8D4424 14 /lea eax, dword ptr [esp+14] ; eax指向分配存储空间中最后一个变量
013811E4 |. 50 |push eax
013811E5 |. 68 04213801 |push 01382104 ; ASCII "%s"
013811EA |. FFD6 |call esi ; scanf("%s",esp+14)
013811EC |. 8D7C24 1C |lea edi, dword ptr [esp+1C] ; 其实这个esp+1c就是上面那个esp+14,详细可以查查C的调用约定
013811F0 |. E8 FBFEFFFF |call insert ; 额。。这个我们先不管,先把主函数整体给反汇编出来
013811F5 |. FFD5 |call ebp ; fflush
013811F7 |. 50 |push eax
013811F8 |. FFD3 |call ebx
013811FA |. 83C4 0C |add esp, 0C ; 堆栈平衡跑这来了,服了
013811FD |. 836C24 10 01 |sub dword ptr [esp+10], 1 ; 其实就是上面那个mov dword ptr [esp+c],3,看到没
01381202 |.^ 75 DC \jnz short 013811E0 ; if [esp+10]==1 break;
01381204 |. 8B1D A0203801 mov ebx, dword ptr [<&MSVCR90.printf>] ; MSVCR90.printf
0138120A |. 68 08213801 push 01382108 ; /format = "search:"
0138120F |. FFD3 call ebx ; \printf
01381211 |. 8BCF mov ecx, edi
01381213 |. 51 push ecx
01381214 |. 68 04213801 push 01382104 ; ASCII "%s"
01381219 |. FFD6 call esi ; scanf调用
0138121B |. E8 E0FDFFFF call search
01381220 |. 50 push eax ; 从这里可以看出eax的返回值是个字符串指针
01381221 |. 68 10213801 push 01382110 ; ASCII "%s",LF
01381226 |. FFD3 call ebx
01381228 |. 83C4 14 add esp, 14 ; 还是平衡堆栈
0138122B |. BE 6C333801 mov esi, offset ht ; ht是个全局变量
01381230 |. 5F pop edi
01381231 |> 8B16 /mov edx, dword ptr [esi]
01381233 |. 52 |push edx
01381234 |. 68 10213801 |push 01382110 ; ASCII "%s",LF
01381239 |. FFD3 |call ebx
0138123B |. 83C6 04 |add esi, 4 ; 由此可见全局变量是每个元素4字节的数组
0138123E |. 83C4 08 |add esp, 8
01381241 |. 81FE A0333801 |cmp esi, offset _adjust_fdiv
01381247 |.^ 7C E8 \jl short 01381231
01381249 |. 68 14213801 push 01382114 ; /command = "pause"
0138124E |. FF15 A4203801 call dword ptr [<&MSVCR90.system>] ; \system
01381254 |. 8B4C24 24 mov ecx, dword ptr [esp+24]
01381258 |. 83C4 04 add esp, 4
0138125B |. 5E pop esi
0138125C |. 5D pop ebp
0138125D |. 5B pop ebx
0138125E |. 33CC xor ecx, esp
01381260 |. 33C0 xor eax, eax
01381262 |. E8 04000000 call __security_check_cookie
01381267 |. 83C4 18 add esp, 18
0138126A \. C3 retn
下面是insert函数,简单的将字符串每个字节相加,得到的数再取余数,然后看能不能插入hash表,不过有个地方代码很古怪,作用肯定是求余啦,只是。。。
012810F0 >/$ 8A07 mov al, byte ptr [edi] ; 这里edi就是指向输入的字符啦
012810F2 |. 33C9 xor ecx, ecx
012810F4 |. 8BD7 mov edx, edi ; 随时关注edi的动向哦~~~
012810F6 |. 84C0 test al, al
012810F8 |. 74 12 je short 0128110C
012810FA |. 8D9B 00000000 lea ebx, dword ptr [ebx] ; 前面用到的寄存器在这都有效。。。
01281100 |> 0FBEC0 /movsx eax, al ; movsx:此乃带符号扩展
01281103 |. 42 |inc edx
01281104 |. 03C8 |add ecx, eax
01281106 |. 8A02 |mov al, byte ptr [edx]
01281108 |. 84C0 |test al, al
0128110A |.^ 75 F4 \jnz short 01281100 ; 仔细分析这一段代码就是把每个字符的Ascii数值相加放入ecx
0128110C |> B8 4FECC44E mov eax, 4EC4EC4F ; 除法,取模运算的优化,定点表示法(即4EC4EC4F为除数的倒数小数位的1倍或2的n次方倍的16进制表示)
01281111 |. F7E9 imul ecx ;指令执行后eax为商的小数部分,edx为商的整数部分(可能是2的n次方倍)
01281113 |. C1FA 02 sar edx, 2 ; 带符号右移4位,由此可以看出商为真正的商的4倍
01281116 |. 8BC2 mov eax, edx
01281118 |. C1E8 1F shr eax, 1F ;取符号位(适应负数情况)
0128111B |. 03C2 add eax, edx ;加上符号位
0128111D |. 6BC0 0D imul eax, eax, 0D ; 三操作数乘法指令,以前还真没见过 eax=eax*0D 如是
01281120 |. 2BC8 sub ecx, eax ; 执行后ecx即为余数,(x%y=x-(x/y)*y) ,还不明白可以去看百度文库的代码逆向文章其中有除法,取模的优化详解~~
01281122 |. 833C8D 6C3328>cmp dword ptr [ecx*4+ht], 0
0128112A |. 56 push esi
0128112B |. 8BF1 mov esi, ecx
0128112D |. 74 1C je short 0128114B ; 如果为NULL
0128112F |. 90 nop
01281130 |> 8D46 01 /lea eax, dword ptr [esi+1] ; ht数组的下一个下标
01281133 |. 99 |cdq
01281134 |. BE 0D000000 |mov esi, 0D
01281139 |. F7FE |idiv esi
0128113B |. 8BF2 |mov esi, edx
0128113D |. 3BF1 |cmp esi, ecx
0128113F |. 74 2A |je short 0128116B ; 插入失败
01281141 |. 833CB5 6C3328>|cmp dword ptr [esi*4+ht], 0
01281149 |.^ 75 E5 \jnz short 01281130 ; 嗯,这里还是没有空位,继续找吧
0128114B |> 6A 01 push 1 ; /size = 1
0128114D |. FF15 B4202801 call dword ptr [<&MSVCR90.malloc>] ; \malloc
01281153 |. 83C4 04 add esp, 4
01281156 |. 8904B5 6C3328>mov dword ptr [esi*4+ht], eax
0128115D |. 8BCF mov ecx, edi
0128115F |. 8BD0 mov edx, eax
01281161 |> 8A01 /mov al, byte ptr [ecx] ; 简单地将字符拷贝到新分配的内存中
01281163 |. 8802 |mov byte ptr [edx], al
01281165 |. 41 |inc ecx
01281166 |. 42 |inc edx
01281167 |. 84C0 |test al, al
01281169 |.^ 75 F6 \jnz short 01281161
0128116B |> 5E pop esi
0128116C \. C3 retn
这是search函数
00241000 /$ 8A07 mov al, byte ptr [edi] ; 此处的edi就是那个存字符的缓冲区也是要搜索的
00241002 |. 33C9 xor ecx, ecx
00241004 |. 8BD7 mov edx, edi
00241006 |. 84C0 test al, al
00241008 |. 74 12 je short 0024101C ; 如果字符串结尾
0024100A |. 8D9B 00000000 lea ebx, dword ptr [ebx] ; 此处是printf函数地址,可以在主函数找到。。顺便感叹下这寄存器窜的
00241010 |> 0FBEC0 /movsx eax, al ; 下面这个循环稍微看下就知道是字符串数值相加啦
00241013 |. 42 |inc edx
00241014 |. 03C8 |add ecx, eax
00241016 |. 8A02 |mov al, byte ptr [edx]
00241018 |. 84C0 |test al, al
0024101A |.^ 75 F4 \jnz short 00241010
0024101C |> B8 4FECC44E mov eax, 4EC4EC4F ;依然是个取余操作,详情参考前面注释
00241021 |. F7E9 imul ecx
00241023 |. C1FA 02 sar edx, 2
00241026 |. 8BC2 mov eax, edx
00241028 |. C1E8 1F shr eax, 1F
0024102B |. 03C2 add eax, edx
0024102D |. 6BC0 0D imul eax, eax, 0D
00241030 |. 2BC8 sub ecx, eax ; 这里得到了余数
00241032 |. 53 push ebx
00241033 |. 56 push esi
00241034 |. 8BF1 mov esi, ecx
00241036 |. 8D04B5 000000>lea eax, dword ptr [esi*4]
0024103D |. 83B8 6C332400>cmp dword ptr [eax+24336C], 0 ; 看全局数组某个位置是否为空
00241044 |. 8BD6 mov edx, esi
00241046 |. 74 58 je short 002410A0 ; 下面循环貌似有点复杂,先看发现空位后的操作
00241048 |. EB 06 jmp short 00241050
0024104A | 8D9B 00000000 lea ebx, dword ptr [ebx]
00241050 |> 8B80 6C332400 /mov eax, dword ptr [eax+24336C] ; 搜索到的字符串地址
00241056 |. 8BCF |mov ecx, edi ; 字符串寄存器又换成ecx了
00241058 |> 8A18 |/mov bl, byte ptr [eax]
0024105A |. 3A19 ||cmp bl, byte ptr [ecx]
0024105C |. 75 1A ||jnz short 00241078
0024105E |. 84DB ||test bl, bl
00241060 |. 74 12 ||je short 00241074 ; 发现相等并等于0字符后跳出并检查是否为所求字符串
00241062 |. 8A58 01 ||mov bl, byte ptr [eax+1]
00241065 |. 3A59 01 ||cmp bl, byte ptr [ecx+1]
00241068 |. 75 0E ||jnz short 00241078
0024106A |. 83C0 02 ||add eax, 2
0024106D |. 83C1 02 ||add ecx, 2
00241070 |. 84DB ||test bl, bl
00241072 |.^ 75 E4 |\jnz short 00241058
00241074 |> 33C0 |xor eax, eax
00241076 |. EB 05 |jmp short 0024107D
00241078 |> 1BC0 |sbb eax, eax
0024107A |. 83D8 FF |sbb eax, -1
0024107D |> 85C0 |test eax, eax
0024107F |. 74 1F |je short 002410A0
00241081 |. 8D42 01 |lea eax, dword ptr [edx+1] ; 下标后移一个位置后取余
00241084 |. 99 |cdq
00241085 |. B9 0D000000 |mov ecx, 0D
0024108A |. F7F9 |idiv ecx
0024108C |. 3BD6 |cmp edx, esi
0024108E |. 74 49 |je short 002410D9 ; 意思就是取余后仍然等于原来那个位置,那么查找失败返回0
00241090 |. 8D0495 000000>|lea eax, dword ptr [edx*4]
00241097 |. 83B8 6C332400>|cmp dword ptr [eax+24336C], 0
0024109E |.^ 75 B0 \jnz short 00241050 ; 发现下一个非空位,继续搜索
002410A0 |> 8B3495 6C3324>mov esi, dword ptr [edx*4+24336C]
002410A7 |. 8BCF mov ecx, edi ; 之后ecx指向搜索的字符串
002410A9 |. 8BC6 mov eax, esi
002410AB |. EB 03 jmp short 002410B0 ; 跳转之后的基本都是strcmp函数的代码,看来这个库函数是汇编实现还是内联的。。效率确实没得说
002410AD | 8D49 00 lea ecx, dword ptr [ecx]
002410B0 |> 8A10 /mov dl, byte ptr [eax]
002410B2 |. 3A11 |cmp dl, byte ptr [ecx]
002410B4 |. 75 28 |jnz short 002410DE
002410B6 |. 84D2 |test dl, dl
002410B8 |. 74 12 |je short 002410CC
002410BA |. 8A50 01 |mov dl, byte ptr [eax+1]
002410BD |. 3A51 01 |cmp dl, byte ptr [ecx+1]
002410C0 |. 75 1C |jnz short 002410DE
002410C2 |. 83C0 02 |add eax, 2
002410C5 |. 83C1 02 |add ecx, 2
002410C8 |. 84D2 |test dl, dl
002410CA |.^ 75 E4 \jnz short 002410B0
002410CC |> 33C0 xor eax, eax ; 看着这么多指令,其实就是返回找到的那个元素的地址
002410CE |. F7D8 neg eax
002410D0 |. 1BC0 sbb eax, eax
002410D2 |. F7D0 not eax
002410D4 |. 23C6 and eax, esi
002410D6 |. 5E pop esi
002410D7 |. 5B pop ebx
002410D8 |. C3 retn
002410D9 |> 5E pop esi
002410DA |. 33C0 xor eax, eax
002410DC |. 5B pop ebx
002410DD |. C3 retn
002410DE |> 1BC0 sbb eax, eax ; 返回0
002410E0 |. 83D8 FF sbb eax, -1
002410E3 |. F7D8 neg eax
002410E5 |. 1BC0 sbb eax, eax ; 这里算出eax==FFFFFFFF
002410E7 |. F7D0 not eax
002410E9 |. 23C6 and eax, esi
002410EB |. 5E pop esi
002410EC |. 5B pop ebx
002410ED \. C3 retn