逆向分析练习三(最长公共前缀)
这次选择的这个代码中存在for和while的嵌套,适合提升逆向中对循环的感知。
题目描述
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
debug版本汇编代码
009F3580 push ebp
009F3581 mov ebp,esp
009F3583 sub esp,0F0h
009F3589 push ebx
009F358A push esi
009F358B push edi
009F358C lea edi,[ebp-0F0h]
009F3592 mov ecx,3Ch
009F3597 mov eax,0CCCCCCCCh
009F359C rep stos dword ptr es:[edi] //rep 和 stos 在下面分析节点的[1]中有描述
009F359E mov eax,dword ptr [strs] ////这下面开始是业务逻辑的代码了
009F35A1 mov ecx,dword ptr [eax]
009F35A3 push ecx //上面应该是从一个指针的指针对象中取出地址
009F35A4 call @ILT+495(_strlen) (9F11F4h) //调用strlen
009F35A9 add esp,4
009F35AC mov dword ptr [a],eax //a = strlen的返回值
009F35AF mov eax,dword ptr [strs] //
009F35B2 mov ecx,dword ptr [eax]
009F35B4 mov dword ptr [ss],ecx //ss = strs[0]
009F35B7 mov dword ptr [j],1 //这下面这一块应该是个for(;j<strSize;j++)
009F35BE jmp longestCommonPrefix+49h (9F35C9h)
009F35C0 mov eax,dword ptr [j] //进入for循环
009F35C3 add eax,1
009F35C6 mov dword ptr [j],eax
009F35C9 mov eax,dword ptr [j] //上面这一块是j++,下面开始是for的代码块
009F35CC cmp eax,dword ptr [strsSize]
009F35CF jge longestCommonPrefix+90h (9F3610h) //这里直接跳出 j>=strSize
009F35D1 mov dword ptr [i],0 //i=0
009F35D8 mov eax,dword ptr [i] //进入循环判断
009F35DB cmp eax,dword ptr [a]
009F35DE jge longestCommonPrefix+88h (9F3608h) //if(a<strlen)进入下面代码块
009F35E0 mov eax,dword ptr [j]
009F35E3 mov ecx,dword ptr [strs]
009F35E6 mov edx,dword ptr [ecx+eax*4] //edx = strs[j]
009F35E9 mov eax,dword ptr [i]
009F35EC movsx ecx,byte ptr [edx+eax] //movsx是有符号的mov,ecx=strs[j][i]
009F35F0 mov edx,dword ptr [ss] //上面只移动一个byte作为偏移所以说strs应该是个char**
009F35F3 add edx,dword ptr [i]
009F35F6 movsx eax,byte ptr [edx]
009F35F9 cmp ecx,eax //ss[i]和 strs[j][i]做比较看是否相等
009F35FB jne longestCommonPrefix+88h (9F3608h) //这里是if(strs[j][i]==ss[i])
009F35FD mov eax,dword ptr [i] //相等就i++
009F3600 add eax,1
009F3603 mov dword ptr [i],eax
009F3606 jmp longestCommonPrefix+58h (9F35D8h) //跳回第二层循环判断
009F3608 mov eax,dword ptr [i]
009F360B mov dword ptr [a],eax //a = i
009F360E jmp longestCommonPrefix+40h (9F35C0h) //进入第一层循环
009F3610 mov eax,dword ptr [ss]
009F3613 add eax,dword ptr [a]
009F3616 mov byte ptr [eax],0
009F3619 mov eax,dword ptr [ss] //返回的还是eax是个指针
009F361C pop edi
009F361D pop esi
009F361E pop ebx
009F361F add esp,0F0h
009F3625 cmp ebp,esp
009F3627 call @ILT+335(__RTC_CheckEsp) (9F1154h)
009F362C mov esp,ebp
009F362E pop ebp
009F362F ret
分析
-
stos:作用是把eax里面的数据,赋值给后面的地址,然后edi + 4,或者是-4,加或减主要看方向标志位DF,DF=0 就是+地址 DF=1就是减地址
-
例如:stos dword ptr es:[edi]这条stos指令可以分解后变成下面汇编代码
mov dword ptr es:[edi],eax add edi , 4
-
-
rep指令作用是:重复后面的指令,是个前缀,例如这里 rep stos,就是重复执行stos指令
rep指令每次执行的时候都从ecx寄存器里面读取值,当ecx大于0,就执行后面语句,执行完以后,会让ecx - 1,然后再执行rep后面的指令。
DOWRD func(char** strs,int strSize){
int a = strlen(strs[0]);
char* ss = strs[0]
for(int j=0;j<strSize;j++){
int i = 0;
while(i<a){
if(ss[i] == strs[j][i]){
i++;
}else{
a = i;
break;
}
}
}
ss[a] = 0;
return ss;
}
源代码
char * longestCommonPrefix(char ** strs, int strsSize){
int a=strlen(strs[0]);
char*ss=strs[0];
for(int j=1;j<strsSize;j++)
{
int i=0;
while(i<a&&strs[j][i]==ss[i])
{
i++;
}
a=i;
}
ss[a]='\0';
return ss;
}