首页
论坛
课程
招聘

[调试逆向] [原创]软件的注册码浮点算法的分析!

2010-10-16 23:34 8895

[调试逆向] [原创]软件的注册码浮点算法的分析!

2010-10-16 23:34
8895
今天是九九重阳节,杭州的钱塘江准备放三十万的烟花,到时一定很漂亮,本来一个朋友叫我去看的,只是公司说要一起去郊游,所以就没去了,遗憾~~没办法,有时候鱼与熊掌不可兼得,有得必有失吧,失去了去看烟花的时间,却可以带领大家一起分析一下汇编的浮点运算的一些知识,自己也学到不少,因为自己以前也没有接触过浮点运算,只是听说过,这还是第一次跟踪,文章很简单,但对于没有接触过浮点运算的朋友,希望会有一些帮助!

[软件名称]不知不觉背单词,网上有很多下载地址,大家可以自己搜索下载!(网上好像已经有了破解版!)注明:此文仅用于技术交流,尊重原作者版权!其实别人花那么多精力开发个软件也不容易啦!

分析这款软件我花了半天的时间,从爆破----->到分析出注册码------->到算法的分析!完全使用OD进行分析跟踪,没有使用IDA,主要的原因是因为这款软件比较简单,没有必要,杀鸡何必用牛刀!呵呵~~

这款软件很简单,也没有加壳,所以直接OD载入分析就得了,前面的爆破和注册码的分析太简单了,我想在这里讲也不太合适,如果大家有什么不懂的,多去初学者版块去学习学习,自然就会了,我这里主要是想分析一下这款软件的使用的浮点算法部分,可以去浮点运算比较熟悉了,飘过~~好了,说了这么多进入正题吧!

我先科普一下,汇编中的浮点运算规则吧,以免在后面的分析中大家找不到东南西北!
网上也有很多讲解浮点运算的资料,我这里给大家收集整理对我们有帮助的一些,如果要学习详解的资料,请大家自己搜索学习,《Inter汇编语言程序设计》第五版也有讲的很详细!

浮点执行环境的寄存器主要是8个通用数据寄存器和几个专用寄存器,它们是状态寄存器、控制寄存器、标记寄存器等(这些大家可以在用OD调试的时候,看寄存器窗口就明白了!)

8个浮点数据寄存器(FPU Data Register),编号FPR0~FPR7。每个浮点寄存器都是80位,以扩展精度格式存储数据,当其他类型数据压入数据寄存器时,FPU自动转换成扩展精度,相反,数据寄存器的数据取出时,系统也会自动转换成要求的数据类型。

8个浮点数据寄存器组成首尾相接的堆栈,当前栈顶ST0指向的FPUX由状态寄存器中TOP字段指明。数据寄存器不采用随机存取,而是按照“后进先出”的堆栈原则工作,并且首尾循环。向数据寄存器传送(Load)数据时是入栈,堆栈指针TOP先减1,再将数据压入栈顶寄存器;从数据寄存器取出数据时就是出栈,先将栈顶寄存器数据弹出,再修改堆栈指针使TOP加1。浮点寄存器还有首尾循环相连的特点。例如,若当前栈顶TOP = 0(即ST0 = FPR0),那么,入栈操作后就使TOP= 7(即使ST0=FPR7),数据被压入FPR7。所以,浮点数据寄存据常常被称为浮点数据堆栈!

浮点运算指令有哪些?
浮点传送指令
        浮点数据传送指令完成主存与堆栈顶ST0、数据寄存器STX与堆栈顶之间的浮点格式数据的传送。浮点数据寄存器是一个首尾相接的堆栈,所以它的数据传送实际上是对堆栈的操作,有些要改变堆栈指针TOP,即修改当前栈顶。
算术运算类指令
        这类浮点指令实现浮点数、16/32位整数的加、减、乘、除运算,它们支持的寻址方式相同。这组指令还包括有关算术运算的指令,倒如求绝对值、取整等。
超越函数类指令
        浮点指令集中包含有进行三角函数、指数和对数运算的指令。
浮点比较类指令
        浮点比较指令比较栈顶数据与指定的源操作数,比较结果通过浮点状态寄存器反映。
FPU控制类指令
        FPU控制类指令用于控制和检测浮点处理单元FPU的状态及操作方式。
定义浮点数据
        数据定义指令DWORD,QWORD,TBYTE依次说明32/64/80位数据;它们可以用于定义单精度、双精度和扩展精度浮点数,但不能出现纯整数(其实,整数后面补个小数就可以了)。相应的数据属性依次是DWORD,QWORD,TBYTE。另外,实常数可以用E表示10的幂。
        每当执行一个新的浮点程序时,第一条指令都应该是初始化FPU的指令finit。该指令清除浮点数据寄存器堆栈和异常,为程序提供一个“干净”的初始化状态。否则,遗留在浮点寄存器堆栈中的数据可能会产生堆栈溢出。另一方面,浮点指令程序段结束,也最好清空浮点数据寄存器。

汇编浮点运算指令集
对下面的指令先做一些说明:
ST(i):代表浮点寄存器,所说的出栈,入栈操作都是对ST(i)的影响。
SRC,DST,DEST,OP等指令是指指令的操作数,SRC表示源操作数,DST/DEST表示目的操作数。
MEM8,MEM16,MEM64,MEM80等表示是内存操作数,后面的数值表示该操作数的内存位数(8位为一字节)。
X<----Y表示将Y的值放入X,例ST0<-----ST0-ST1,表示将ST0-ST1的值放入浮点寄存器ST0

一些常用的指令:
指令格式       指令含义            执行的操作
FLD src     装入实数到st(0)    st(0) <- src (mem32/mem64/mem80)  
FILD src    装入整数到st(0)    st(0) <- src (mem16/mem32/mem64)
FBLD src    装入BCD数到st(0)   st(0) <- src (mem80)
FLDZ        将0.0装入st(0)     st(0) <- 0.0  
FLD1        将1.0装入st(0)     st(0) <- 1.0  
FLDPI       将pi装入st(0)      st(0) <- ?(ie, pi)
FLDL2T     将log2(10)装入st(0) st(0) <- log2(10)
FLDL2E     将log2(e)装入st(0)  st(0) <- log2(e)
FLDLG2     将log10(2)装入st(0) st(0) <- log10(2)
FLDLN2     将loge(2)装入st(0)  st(0) <- loge(2)
FST dest  保存实数st(0)到dest  dest <- st(0) (mem32/mem64)
FSTP  dest dest <- st(0)   (mem32/mem64/mem80);然后再执行一次出栈操作
FIST dest 将st(0)以整数保存到dest dest <- st(0) (mem32/mem64)  
FISTP dest   dest <- st(0) (mem16/mem32/mem64);然后再执行一次出栈操作
FBST dest 将st(0)以BCD保存到dest dest <- st(0) (mem80)  
FBSTP dest dest<- st(0) (mem80);然后再执行一次出栈操作
2.  比较指令
指令格式         指令含义           执行的操作
FCOM             实数比较    将标志位设置为 st(0) - st(1) 的结果标志位
FCOM op          实数比较  将标志位设置为 st(0) - op (mem32/mem64)的结果标志位
FICOM op       和整数比较  将Flags值设置为st(0)-op 的结果op (mem16/mem32)
FICOMP op     和整数比较 将st(0)和op比较 op(mem16/mem32)后;再执行一次出栈操作
FTST             零检测   将st(0)和0.0比较
FUCOM st(i)   比较st(0) 和st(i)              
FUCOMP st(i)  比较st(0) 和st(i),并且执行一次出栈操作
FUCOMPP st(i) 比较st(0) 和st(i),并且执行两次出栈操作
FXAM  Examine: Eyeball st(0) (set condition codes)
3.  运算指令
指令格式          指令含义                执行的操作
加法
FADD              加实数           st(0) <-st(0) + st(1)
FADD src   st(0) <-st(0) + src (mem32/mem64)
FADD st(i),st   st(i) <- st(i) + st(0)
FADDP st(i),st  st(i) <- st(i) + st(0);然后执行一次出栈操作
FIADD src   加上一个整数   st(0) <-st(0) + src (mem16/mem32)
减法
FSUB        减去一个实数   st(0) <- st(0) - st(1)
FSUB src    st(0) <-st(0) - src (reg/mem)
FSUB st(i),st   st(i) <-st(i) - st(0)
FSUBP st(i),st  st(i) <-st(i) - st(0),然后执行一次出栈操作
FSUBR st(i),st  用一个实数来减   st(0) <- st(i) - st(0)
FSUBRP st(i),st   st(0) <- st(i) - st(0),然后执行一次出栈操作
FISUB src 减去一个整数 st(0) <- st(0) - src (mem16/mem32)
FISUBR src 用一个整数来减 st(0) <- src - st(0) (mem16/mem32)
乘法
FMUL 乘上一个实数 st(0) <- st(0) * st(1)
FMUL st(i) st(0) <- st(0) * st(i)
FMUL st(i),st  st(i) <- st(0) * st(i)
FMULP st(i),st  st(i) <- st(0) * st(i),然后执行一次出栈操作
FIMUL src 乘上一个整数 st(0) <- st(0) * src (mem16/mem32)
除法
FDIV 除以一个实数  st(0) <-st(0) /st(1)
FDIV st(i)  st(0) <- st(0) /t(i)
FDIV st(i),st st(i) <-st(0) /st(i)
FDIVP st(i),st  st(i) <-st(0) /st(i),然后执行一次出栈操作
FIDIV src 除以一个整数 st(0) <- st(0) /src (mem16/mem32)
FDIVR st(i),st 用实数除 st(0) <- st(i) /st(0)
FDIVRP st(i),st  
FDIVRP st(i),st
FIDIVR src  用整数除 st(0) <- src /st(0) (mem16/mem32)
FSQRT   平方根    st(0) <- sqrt st(0)
FSCALE  2的st(0)次方 st(0) <- 2 ^ st(0)
FXTRACT Extract exponent: st(0) <-exponent of st(0); and gets pushed
st(0) <-significand of st(0)
FPREM  取余数 st(0) <-st(0) MOD st(1)
FPREM1 取余数(IEEE),同FPREM,但是使用IEEE标准[486]
FRNDINT 取整(四舍五入) st(0) <- INT( st(0) ); depends on RC flag
FABS  求绝对值  st(0) <- ABS( st(0) ); removes sign
FCHS  改变符号位(求负数) st(0) <-st(0)
F2XM1 计算(2 ^ x)-1  st(0) <- (2 ^ st(0)) - 1
FYL2X 计算Y * log2(X) st(0)为Y;st(1)为X;将st(0)和st(1)变为st(0) * log2( st(1) )的值
FCOS    余弦函数Cos  st(0) <- COS( st(0) )
FPTAN   正切函数tan st(0) <- TAN( st(0) )
FPATAN  反正切函数arctan  st(0) <- ATAN( st(0) )
FSIN    正弦函数sin       st(0) <- SIN( st(0) )
FSINCOS  sincos函数     st(0) <-SIN( st(0) ),并且压入st(1)
st(0) <- COS( st(0) )
FYL2XP1 计算Y * log2(X+1)  st(0)为Y; st(1)为X;将st(0)和st(1)变为st(0) * log2( st(1)+1 )的值
处理器控制指令
FINIT  初始化FPU
FSTSW AX   保存状态字的值到AX   AX<- MSW
FSTSW dest 保存状态字的值到dest dest<-MSW (mem16)
FLDCW src  从src装入FPU的控制字 FPU CW <-src (mem16)
FSTCW dest 将FPU的控制字保存到dest  dest<- FPU CW
FCLEX  清除异常
FSTENV dest  保存环境到内存地址dest处保存状态字、控制字、标志字和异常指针的值
FLDENV src   从内存地址src处装入保存的环境
FSAVE dest   保存FPU的状态到dest处 94字节
FRSTOR src   从src处装入由FSAVE保存的FPU状态
FINCSTP 增加FPU的栈指针值  st(6) <-st(5); st(5) <-st(4),...,st(0) <-?
FDECSTP 减少FPU的栈指针值  st(0) <-st(1); st(1) <-st(2),...,st(7) <-?
FFREE st(i)  标志寄存器st(i)未被使用
FNOP 空操作,等同CPU的nop st(0) <-st(0)
WAIT/FWAIT   同步FPU与CPU:停止CPU的运行,直到FPU完成当前操作码
FXCH  交换指令,交换st(0)和st(1)的值   st(0) <-st(1)  st(1) <- st(0)

一些常用的指令也就这些,大家如果记忆不好,也不要紧,用的时候去查就行了,其实也很好记的,无非一些英文单词的缩写吧了~~

前面的找注册码为600341115我就不多讲了,很容易找到的,如果有不懂就请先去初学者版块学习学习~~~
我们找到注册码计算比较那段的段首地址为004141EC,我们在那里下断点,单步跟踪就可以找到注册码算法了!
004141EC处的代码如下:
004141EC              55                    push ebp
004141ED              8BEC                  mov ebp,esp
004141EF              81C4 70FFFFFF         add esp,-90
004141F5              8955 C0               mov dword ptr ss:[ebp-40],edx
004141F8              8945 C4               mov dword ptr ss:[ebp-3C],eax
.............................................

前面一堆代码基本也没用,只用于将注册对话框中输入的注册码,取出然后转化为字符串,很简单就不讲了!然后我们向下看一直到00414234这里,代码如下:
00414234              8945 BC               mov dword ptr ss:[ebp-44],eax
00414237              33D2                  xor edx,edx
00414239              8955 B8               mov dword ptr ss:[ebp-48],edx
0041423C              33C9                  xor ecx,ecx
0041423E              894D B4               mov dword ptr ss:[ebp-4C],ecx
00414241              EB 10                 jmp short Recite.00414253
00414243              8B45 BC               mov eax,dword ptr ss:[ebp-44]
00414246              8B55 B4               mov edx,dword ptr ss:[ebp-4C]
00414249              0FBE0C10              movsx ecx,byte ptr ds:[eax+edx]
0041424D              014D B8               add dword ptr ss:[ebp-48],ecx
00414250              FF45 B4               inc dword ptr ss:[ebp-4C]
00414253              8D45 FC               lea eax,dword ptr ss:[ebp-4]
00414256              E8 69990800           call Recite.0049DBC4
0041425B              48                    dec eax
0041425C              3B45 B4               cmp eax,dword ptr ss:[ebp-4C]
0041425F            ^ 7F E2                 jg short Recite.00414243

经常跟踪软件人,一看会知道这段代码的功能了,其实就是将字符中的字符的各个ASCII码进行累加,累加和存入一个局部变量中。
申请三个局部变量:
[ebp-44]用来存放机器码:2141215019417
[ebp-48]用来存放ASCII码之和
[ebp-4c]用来存放取的是上这面这个字符串的第几个字符
注册码为600341115
代码很简单,就是一利用一个循环,然后将机器码的各位的ASCII码进行累加!然后将结果存到ss:[ebp-48]中
也就是32+31+34+31+32+31+35+30+31+39+34+31+37=25F(十六进制)=607(十进制)

得到了这个值,下面就要开始计算注册码了,OD有一个很好的功能就是注解功能,我经常用这个功能,因为有时软件比较长,也不是一次就可以分析的出来的,要反反复复不停的跟踪,所以做注释是一个很好的习惯,我想学逆向的朋友们都会有这个好习惯的!

下面我就不多说了,大家看我在OD中的注释就会很清楚了!
下面就一堆浮点计算指令,与很多CALL,究竟那个CALL对自己有用呢?这就要靠经验了,我也说不清楚!

其实我用IDA分析过一段代码,很简单,因为IDA有一个很强大的功能就是代码的自动识别功能,这里我不能不配服IDA的作者!
我用IDA分析了下面的一小段代码,得到如下结果:
.text:00414261                 fild    [ebp+var_48]
.text:00414264                 add     esp, 0FFFFFFF8h ; x
.text:00414267                 fstp    [esp+98h+var_98]
.text:0041426A                 call    _cos

其实0041426A处就是计算浮点的_COS的值
下面我完全用OD去分析注册码的算法,不借助IDA!分析如下:

上面的IDA 对应的OD代码为
00414261          DB45 B8        fild dword ptr ss:[ebp-48]         ; 将EBP-48中的实整数607装入到ST(0)
00414264          83C4 F8        add esp,-8
00414267          DD1C24         fstp qword ptr ss:[esp]            ; 将ST(0)的数值装入到ESP中
0041426A          E8 1D2B0800    call Recite.00496D8C

00496D8C处的代码如下:

00496D8C          55             push ebp
00496D8D          8BEC           mov ebp,esp
00496D8F          53             push ebx
00496D90          DD45 08        fld qword ptr ss:[ebp+8]       ; 加载实数到ST(0)
00496D93          66:B8 F07F     mov ax,7FF0                    ; 将7FF0值传给AX
00496D97          66:2345 0E     and ax,word ptr ss:[ebp+E]     ; 将EBP+E中的4082与AX的7FF0进行与操作
00496D9B          66:3D 4043     cmp ax,4340                    ; 将与的结果与4340进行比较
00496D9F          73 08          jnb short Recite.00496DA9      ; 跳转没有实现
00496DA1          E8 E6000000    call Recite.00496E8C

我们在来看看00496E8C处的代码如下:

00496E8C          B1 02          mov cl,2                       ; 将2传给Cl
00496E8E          EB 02          jmp short Recite.00496E92      ; 短跳转
00496E90          B1 04          mov cl,4
00496E92          D9E5           fxam                           ; 进行浮点检查
00496E94          83EC 04        sub esp,4
00496E97          9B             wait
00496E98          DD3C24         fstsw word ptr ss:[esp]        ; 浮点检查保存状态器ESP值为43C8
00496E9B          9B             wait
00496E9C          8A6424 01      mov ah,byte ptr ss:[esp+1]     ; 将ESP+1中保存的'<'传给ah
00496EA0          9E             sahf                           ; 恢复标志低八,AH=3C,S0,Z0,A1,P1,C0,FL=06
00496EA1          72 0D          jb short Recite.00496EB0       ; CF=1跳转,所以没跳
00496EA3          75 2A          jnz short Recite.00496ECF      ; ZF=0跳转,所以跳转了
00496EA5          80F9 02        cmp cl,2
00496EA8          75 04          jnz short Recite.00496EAE
00496EAA          DDD8           fstp st
00496EAC          D9E8           fld1
00496EAE          EB 1A          jmp short Recite.00496ECA
00496EB0          74 0C          je short Recite.00496EBE
00496EB2          7B 0A          jpo short Recite.00496EBE
00496EB4          DDD8           fstp st
00496EB6          D905 98BF4A00  fld dword ptr ds:[4ABF98]
00496EBC          D9E4           ftst
00496EBE          EB 0A          jmp short Recite.00496ECA
00496EC0          DED9           fcompp
00496EC2          D905 98BF4A00  fld dword ptr ds:[4ABF98]
00496EC8          D9E4           ftst
00496ECA          E9 97000000    jmp Recite.00496F66
00496ECF          D9E1           fabs                           ; 求ST(0)绝对值
00496ED1          DB2D 84BF4A00  fld tbyte ptr ds:[4ABF84]      ; 装载实数到ST(0)值为0.7853981633974482944
00496ED7          D9E5           fxam                           ; 浮点检查
00496ED9          D9C9           fxch st(1)                     ; 交换ST(0)与ST(1)的值ST(1)为607.00000000000000000
00496EDB          D9F8           fprem                          ; ST(0)MODST(1)取余数0.6726178571699049472
00496EDD          B5 02          mov ch,2                       ; CH赋值为2
00496EDF          22EC           and ch,ah                      ; 用2与上面AH的值('<')进行与操作,3CAND2
00496EE1          D0ED           shr ch,1                       ; 与的结果为0,然后右移一位
00496EE3          9B             wait
00496EE4          DD3C24         fstsw word ptr ss:[esp]        ; 保存状态字到ESP
00496EE7          9B             wait
00496EE8          8A6424 01      mov ah,byte ptr ss:[esp+1]     ; 取ESP中的第二位数1
00496EEC          9E             sahf                           ; 恢复标志低八位AH=31(S0,Z0,A1,P0,C1),FL=46
00496EED          7A D1          jpe short Recite.00496EC0      ; PF=1时跳转,显然这里不跳
00496EEF          B0 03          mov al,3                       ; AL赋值3
00496EF1          22C4           and al,ah                      ; AH('1') AND AL=03,结果为1
00496EF3          D0E4           shl ah,1                       ; AH左移一位,31*2=62('b')
00496EF5          D0E4           shl ah,1                       ; AH再左移一位
00496EF7          D0D0           rcl al,1                       ; AL循环进位左移,01*2=02
00496EF9          04 FC          add al,0FC                     ; AL与0FC与操作
00496EFB          D0D0           rcl al,1                       ; 将上面得到的结果AL=FE,循环进位左移
00496EFD          80F9 02        cmp cl,2                       ; CL与2进行比较
00496F00          75 04          jnz short Recite.00496F06      ; 不跳
00496F02          02C1           add al,cl                      ; CL=02 AND AL=FE
00496F04          B5 00          mov ch,0                       ; CH=0
00496F06          24 07          and al,7                       ; AL=FE AND 7
00496F08          A8 01          test al,1                      ; AL=06检查是否为1
00496F0A          74 04          je short Recite.00496F10       ; 跳转
00496F0C          DEE9           fsubp st(1),st
00496F0E          EB 02          jmp short Recite.00496F12
00496F10          DDD9           fstp st(1)                     ; 将ST(0)--->ST(1)
00496F12          D9F2           fptan                          ; 正切函数0.6726178571699049472
00496F14          80F9 04        cmp cl,4                       ; CL=02与4进行比较
00496F17          74 20          je short Recite.00496F39       ; 不跳
00496F19          A8 03          test al,3                      ; AL=06检查是为3
00496F1B          7A 02          jpe short Recite.00496F1F      ; 不跳P位为0
00496F1D          D9C9           fxch st(1)                     ; 将ST(0)值1.0000000000000000000与ST(1)值0.7965240390979069952进行交换
00496F1F          D9C1           fld st(1)                      ; 将ST(1)-->ST(0)
00496F21          D8C8           fmul st,st                     ; ST(0)*ST(0)
00496F23          D9C9           fxch st(1)                     ; 交换ST(0)值1与ST(1)的值0.7965240390979069952
00496F25          D8C8           fmul st,st                     ; ST(0)值0.7965240390979069952乘以ST(0)
00496F27          DEC1           faddp st(1),st                 ; 得到ST(0)值0.6344505448608441344加上ST(1)的值1
00496F29          D9FA           fsqrt                          ; 求ST(0)的值1.6344505448608441344的平方根得到1.2784563132390735360
00496F2B          D0E8           shr al,1                       ; AL的值右移1
00496F2D          D0E8           shr al,1                       ; AL的值右移1
00496F2F          32C5           xor al,ch                      ; 得到AL=01与CH=00进行异或
00496F31          74 02          je short Recite.00496F35       ; 不跳
00496F33          D9E0           fchs                           ; 改变ST(0)的符号位求负数得到-1.2784563132390735360
00496F35          DEF9           fdivp st(1),st                 ; ST(1)除以ST(0)得到-0.7821933292866443264
00496F37          EB 2D          jmp short Recite.00496F66      ; 跳出这个CALL

最后得到ST(0)的值为-0.7821933292866443264

我后返回到00496DA6处,接下来往下看
00496DA6          5B             pop ebx
00496DA7          5D             pop ebp
00496DA8          C3             retn

又一次返回,到0041426F处,代码如下所示:
0041426F          83C4 08        add esp,8
00414272          D805 34444100  fadd dword ptr ds:[414434]        ; 将得到的ST(0)的值-0.7821933292866443264加上1得到0.2178066707133556224
00414278          D83D 34444100  fdivr dword ptr ds:[414434]       ; 用DS:[00414434]的值1去除ST(0)得到4.5912276089837926400
0041427E          DD9D 70FFFFFF  fstp qword ptr ss:[ebp-90]        ; 将ST(0)的值4.5912276089837926400传给SS:[EBP-90]
00414284          68 00804C40    push 404C8000
00414289          6A 00          push 0
0041428B          E8 7C2F0800    call Recite.0049720C

然后我们再进入0049720C处看看此处的代码,如下所示:
0049720C          55             push ebp
0049720D          8BEC           mov ebp,esp
0049720F          8D65 F8        lea esp,dword ptr ss:[ebp-8]
00497212          53             push ebx
00497213          DD45 08        fld qword ptr ss:[ebp+8]          ; 将EBP+8值57.0000000000000传给ST(0)
00497216          66:8B45 0E     mov ax,word ptr ss:[ebp+E]        ; 将EBP+E值404C传给AL
0049721A          66:D1E0        shl ax,1                          ; AX左移一位
0049721D          74 12          je short Recite.00497231          ; 没跳
0049721F          72 31          jb short Recite.00497252          ; 没跳
00497221          66:3D E0FF     cmp ax,0FFE0                      ; 将上面左移的AX的值勤8098与0FFE0进行比较
00497225          74 1F          je short Recite.00497246          ; 没跳
00497227          E8 18FCFFFF    call Recite.00496E44

再进入00496E44处,如下所示:
00496E4A          D9C9           fxch st(1)                        ; 将ST(0)与ST(1)的值57进行交换
00496E4C          83EC 0C        sub esp,0C
00496E4F          D9E5           fxam                              ; 浮点检查
00496E51          9B             wait
00496E52          DD7C24 0A      fstsw word ptr ss:[esp+A]         ; 保存状态字
00496E56          9B             wait
00496E57          8A6424 0B      mov ah,byte ptr ss:[esp+B]        ; 将ESP+B中的4传入到AH
00496E5B          9E             sahf                              ; 恢复标志低八位AH=34(S0,Z0,A1,P1,C0),FL=16
00496E5C          72 0B          jb short Recite.00496E69          ; 没跳
00496E5E          74 05          je short Recite.00496E65          ; 没跳
00496E60          F6C4 02        test ah,2                         ; 检查AH是否为2
00496E63          74 1D          je short Recite.00496E82          ; 跳转实现
...................................................
00496E82          D9F1           fyl2x                             ; 求st(0) * log2( st(1) )的值,ST(0)值57.000000000000000000与ST(1)0.3010299956639812096
00496E84          83C4 0C        add esp,0C
00496E87          C3             retn                              ; 得到ST(0)的值为1.7558748556724915200

返回到00497227C处
0049722C          5B             pop ebx                           ; 恢复堆栈,返回
0049722D          8BE5           mov esp,ebp
0049722F          5D             pop ebp
00497230          C3             retn

又一次返回到00414290处,代码如下:
00414293          DCBD 70FFFFFF  fdivr qword ptr ss:[ebp-90]     ; 前面得到的[EBP-90]的值4.5912276089837926400除以ST(0)的值得到ST(0)的值为

1.7558748556724915200
00414299          D95D B0        fstp dword ptr ss:[ebp-50]      ; 将上面得到的ST(0)的值2.6147806571473310720传给[EBP-50]
0041429C          66:C745 D8 080>mov word ptr ss:[ebp-28],8      ; 赋[EBP-28]的值为8
004142A2          D945 B0        fld dword ptr ss:[ebp-50]       ; 将[EBP-50]的值--->ST(0)
004142A5          E8 062D0800    call Recite.00496FB0

这里又有一个CALL,再次进入00496FB0,代码所下:
00496FB0          55             push ebp
00496FB1          8BEC           mov ebp,esp
00496FB3          8D65 F4        lea esp,dword ptr ss:[ebp-C]
00496FB6          9B             wait
00496FB7          D97D FC        fstcw word ptr ss:[ebp-4]       ; 浮点检查保存控制器
00496FBA          9B             wait
00496FBB          8A45 FD        mov al,byte ptr ss:[ebp-3]      ; 将EBP-3的值13传给AL
00496FBE          804D FD 0C     or byte ptr ss:[ebp-3],0C       ; [EBP-3]的值13与0C或操作
00496FC2          D96D FC        fldcw word ptr ss:[ebp-4]       ; 浮点加载控制器
00496FC5          DF7D F4        fistp qword ptr ss:[ebp-C]      ; ST(0)的值2.6147806571473310720传给[EBP-C]
00496FC8          8845 FD        mov byte ptr ss:[ebp-3],al      ; 将AL=13传给[EBP-3]
00496FCB          D96D FC        fldcw word ptr ss:[ebp-4]       ; 从[EBP-4]值装入FCW
00496FCE          8B45 F4        mov eax,dword ptr ss:[ebp-C]    ; 将[EBP-C]的值赋值给EAX
00496FD1          8B55 F8        mov edx,dword ptr ss:[ebp-8]    ; 将[EBP-8]的值零赋值给EDX
00496FD4          8BE5           mov esp,ebp
00496FD6          5D             pop ebp
00496FD7          C3             retn                            ; 返回

返回到004142AA处,代码如下:
004142AA          8985 74FFFFFF  mov dword ptr ss:[ebp-8C],eax
004142B0          DB85 74FFFFFF  fild dword ptr ss:[ebp-8C]      ; 将[EBP-8C]的值2传给ST(0)
004142B6          D86D B0        fsubr dword ptr ss:[ebp-50]     ; 用前面得到的[EBP-50]的值2.6147806571473310720减去ST(0)的值2
004142B9          DC0D 38444100  fmul qword ptr ds:[414438]      ; 将得到的ST(0)的值0.6147806644439697408乘以[414438]处的值1000000000000.000
004142BF          E8 EC2C0800    call Recite.00496FB0            ;利用这个CALL将6.1478066444396974080e+11转化为注册码,中间会有误差,但返回值是我们所关心的

我们在进入00496FB0处,代码如下:
其实这段代码我们以前跟踪过只是数值有上些改变吧了!
00496FB0          55             push ebp
00496FB1          8BEC           mov ebp,esp
00496FB3          8D65 F4        lea esp,dword ptr ss:[ebp-C]
00496FB6          9B             wait
00496FB7          D97D FC        fstcw word ptr ss:[ebp-4]       ; 浮点检查保存控制器
00496FBA          9B             wait
00496FBB          8A45 FD        mov al,byte ptr ss:[ebp-3]      ; 将EBP-3的值13传给AL
00496FBE          804D FD 0C     or byte ptr ss:[ebp-3],0C       ; [EBP-3]的值13与0C或操作
00496FC2          D96D FC        fldcw word ptr ss:[ebp-4]       ; 浮点加载控制器
00496FC5          DF7D F4        fistp qword ptr ss:[ebp-C]      ; ST(0)的值6.1478066444396974080e+11传给[EBP-C]
00496FC8          8845 FD        mov byte ptr ss:[ebp-3],al      ; 将AL=13传给[EBP-3]
00496FCB          D96D FC        fldcw word ptr ss:[ebp-4]       ; 从[EBP-4]值装入FCW
00496FCE          8B45 F4        mov eax,dword ptr ss:[ebp-C]    ; 将[EBP-C]的值eax=23C87A7B赋值给EAX
00496FD1          8B55 F8        mov edx,dword ptr ss:[ebp-8]    ; 将[EBP-8]的值零赋值给EDX
00496FD4          8BE5           mov esp,ebp
00496FD6          5D             pop ebp
00496FD7          C3             retn                            ; 返回

返回到004142C4处,代码如下:
004142C4          8985 70FFFFFF  mov dword ptr ss:[ebp-90],eax   ; 将eax=23C87A7B传给ebp-90
004142CA          DB85 70FFFFFF  fild dword ptr ss:[ebp-90]      ; 将十六进制23C87A7B传给ST(0)
004142D0          83C4 F8        add esp,-8                      ; 堆栈平衡
004142D3          DD1C24         fstp qword ptr ss:[esp]         ; ST0的值(即为注册码)传给ESP中
004142D6          E8 912C0800    call Recite.00496F6C

后面还有一些CALL,没有作实际的处理,基本上就是浮点数转换为整数的操作,这时太不分析了,这样我们的注册码就得到了600341115

算法总结如下:这个算法其实很简单,中间只有两个很简单的函数,我这里就不用这两个函数,而是去慢慢分析这两个函数是如何来计算的,主要是为了加强分析能力!

607.00000000000000000与0.7853981633974482944取余数得到
0.6726178571699049472

然后对上面得到的值求正切函数
得到ST0的值为1.0000000000000000000
ST1的值为0.7965240390979069952
交换上面的两个值
ST0值变为0.7965240390979069952
ST1值变为1.0000000000000000000

又将ST1--->ST0,然后ST0*ST0得到ST0值还是1
然后再交换ST0与ST1的值
ST0值还是0.7965240390979069952
ST1值还是1.0000000000000000000
然后ST0*ST0得到0.6344505448608441344,再将ST0的值与ST1的值相加,得到
1.6344505448608440320,在求平方根,得到ST0的值为1.2784563132390735360

将ST0的值求负得-1.2784563132390735360
用ST1的值1.0000000000000000000除以ST0的值-1.2784563132390735360得到-0.7821933292866443264

将ST0的值-0.7821933292866443264加上1得到0.2178066707133556224
再用存储在数据段中常数1去除ST0的值0.2178066707133556224得到4.5912276089837926400

再将浮点数57.00000000000000传给ST0,然后计算log10(2)的值0.3010299956639812096,并将值也压入ST0中,此时
ST0的值为0.3010299956639812096,ST1的值57.00000000000000,交换得
ST0的值为57.00000000000000,ST1的值为0.3010299956639812096
然后再求ST0*Log2(ST1)的值得到1.7558748556724915200--->ST0

然后在将前面得到的ST0的值4.5912276089837926400除以1.7558748556724915200得到2.6147806571473310720
然后用2.6147806571473310720-2得到0.6147806571473310720的值然后乘以1000000000000.000得到

6.147806571473310720e+11
到这里我们的注册码就出来了!

总体来说软件逆向=精力+经验+热爱,逆向一个软件是很累的活,但我相对于写软件的人来说还是比较年轻的,呵呵,希望大家想学逆向工程式的朋友们都有能有这三种东西,我想你将来会在这个领域小有成就,一切只是时间的问题!

这最后说明一点,浮点数到最后转换成注册码的时候,会有一点误差,这是不可避免的,大家只要单步跟踪到注册码就行了,至于中间的结果,在浮点运算转换成其它数值的时候会有误差,也不用去管,这是不可避免的!

好了,文章就写到这了,说点题外话,最近比较忙,QQ基本不上线了,看雪上可能有时会看到我写的一些文章,最近在潜心去修练逆向工程与反汇编知识,争取能更上一层楼,我相信这一切只是时间的问题,只要自己肯努力去做!因为不想被太多人打扰QQ就不上了,请各位看雪上的朋友们原谅,不过只要熊猫正正有时间,一定会尽量能大家带来一些好文章!(只能尽力啦,因为毕竟熊猫正正现在还只是菜鸟一只,呵呵,还需要努力才行),我一直很崇拜几位科学家,钱学森,陈景润,王选等(其实还有好多无私奉献的科学家,我就不一一列举了),他们的技术那不用说了,中国没人能比啦,但我想说主要是他们的精神,真的值得我们去好好学习一下!只为自己喜欢的事业而努力奋斗,不在乎得到了多少荣誉,在他们喜欢的领域,他们总是默默无闻,不为名利,只为自己所热爱的事业!!(注:五笔打的比较快,难免会有一些笔误!请原谅!)

[推荐]看雪企服平台,提供项目众包、渗透测试、安全分析、定制项目开发、APP等级保护等安全服务!

最新回复 (9)
kagayaki 2010-10-17 02:01
2
0
支持一下,我上一次就是分析到一个浮点算法,看不明, 就放弃去分析了.......
sjclch 2010-10-17 10:16
3
0
这是一个苦力活,,谢谢楼主
因为我没有理解浮点,所以觉浮点计算有点诡异
denglifeng 1 2010-10-17 15:23
4
0
很强,浮点的算法分析起来确实挺费劲。
顶一个
hbfp 2010-10-17 15:40
5
0
收藏一份慢慢学习!
ycmint 5 2010-10-24 12:19
6
0
正正老乡,写的不错,学习了,思路很清晰哦 。。。上面那个运算指令 是参考看雪里面的吧,有点小问题,希望能订正,前段时间 搞这 也被看雪 忽悠了。。。运算指令中 加减乘除 不带操作数的 公式 应该是 比如说  fadd: (st)+(st1)->st(1)  ,在弹出 st 。。。呵呵 。。。我丢了帖子 没人屌。。
http://bbs.pediy.com/showthread.php?t=123076
熊猫正正 9 2010-10-24 19:44
7
0
首先我想说没人理很正常,其实我想说你理解错了~~对你的疑问我只能作如下解释,如果还不清楚,我也无能为力了~~
在浮点运算中,将目标操作数与源操作数相加,和存储到目标位置。目标操作数总是 FPU 寄存器,源操作数可以是寄存器或内存位置。内存中的源操作数可以是单精度实数、双精度实数、字整数或短整数格式。
无操作数版的指令将ST0+ST1--->ST0
单操作数版将内存位置内容+ST0--->ST0
双操作数版将ST0+ST1--->ST0

FADD             ST0+ST1----->ST0
FADD  src            src (mem32/mem64) +ST0---->ST0
FADD  st(i),st      ST0+ST(i)---->ST(i)
FADDP                ST0+ST1---->ST1,并弹出寄存器堆栈
FADDP st(i),st     ST0+ST(i)---->ST(i),并弹出寄存器堆栈
FADDP指令将会执行一次额外的操作-->弹出FPU寄存器堆栈
(注:有些汇编器中,有时将FADD即为FADDP,其实这些都不重要,在调试的时候观察堆栈窗口就行了!)

最后只给这样对你说:要想别人理你,先把自己变的STRONG!不然永远没有理你!
ycmint 5 2010-10-24 20:37
8
0
恩。。。谢了。。。我要变强壮 呵呵。。。可能是我在 写 win32 汇编的时候 fadd 和faddp 是一个功能,所以我就认为手册上对了,看雪那一篇 错了,谢谢你告诉我原因。。。
supercolin 1 2010-10-25 11:11
9
0
体力活+耐心啊
不错
hack一生 1 2010-12-23 20:56
10
0
刚好,分析个浮点的..3Q
游客
登录 | 注册 方可回帖
返回