欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]【老刘谈算法006】直接从内存中找答案——字符串转双字函数分析(3)

[原创]【老刘谈算法006】直接从内存中找答案——字符串转双字函数分析(3)

a2dw.asm

; #########################################################################

    ; --------------------------------------

    ; This procedure was written by Iczelion

    ; --------------------------------------

      .386

      .model flat, stdcall  ; 32 bit memory model

      option casemap :none  ; case sensitive

      include \MASM32\INCLUDE\kernel32.inc

    .code

; #########################################################################

a2dw proc uses ecx edi edx esi String:DWORD

      ;----------------------------------------

      ; Convert decimal string into dword value

      ; return value in eax

      ;----------------------------------------

      xor ecx, ecx

      mov edi, String

      invoke lstrlen, String    ;在前面清空ecx是没用的,WinApi可能改变ecx、edx的值

      .while eax != 0

        xor edx, edx

        mov dl, byte ptr [edi]

        sub dl, "0" ; subtrack each digit with "0" to convert it to hex value

        mov esi, eax

        dec esi

        push eax

        mov eax, edx    ;ascii对应的byte

        push ebx

        mov ebx, 10

          .while esi > 0    ;num*10^esi

            mul ebx    ;结果到eax(低位),edx(高位)中

            dec esi

          .endw

        pop ebx

        add ecx, eax        ;ecx储存结果

        pop eax

        inc edi

        dec eax

      .endw

        mov eax, ecx

        ret

a2dw endp

; #########################################################################

end

以前听罗老师说不建议用masm32自带的宏、lib,可能有各种奇葩错误,我还不大相信,直到我看到了这个函数。
这个函数看着工工整整,算法的可行性也在上一篇分析过了,但其实完全无法工作。
为什么呢?请看第24、26行,
为了使ecx储存结果,函数在调用winAPI之前将ecx清零,
但这么做其实是徒劳的,WinAPI执行后,ebx、edi、esi和ebp的值总是不会被改变的,但 ecx 和 edx 的值有极大几率被改变。(同时这也是win对api调用的callback的要求)
这就导致ecx有了一个未知的初始值,导致累加发生在一个未知值的基础上,最终得到了没有任何意义的返回值。

atodw.asm

; #########################################################################

  ; ---------------------------------------------------------------

  ;      本程序最初由 Tim Roberts 编写

  ;

  ;     Alexander Yackubtchik 优化了部分代码

  ; ---------------------------------------------------------------

    .486

    .model flat, stdcall  ; 32 bit memory model

    option casemap :none  ; case sensitive

    .code

; #########################################################################

atodw proc String:DWORD

  ; ----------------------------------------

  ; 十进制转dword

  ; eax储存返回值

  ; ----------------------------------------

    push esi

    push edi

    xor eax, eax

    mov esi, [String]

    xor ecx, ecx

    xor edx, edx

    mov al, [esi]

    inc esi

    cmp al, 2D    ;检测负号

    jne proceed    ;不是负号就跳转

    mov al, byte ptr [esi]

    not edx    ;FFFFFFFF

    inc esi

    jmp proceed

  @@: 

    sub al, 30h    ;ascii->byte

    lea ecx, dword ptr [ecx+4*ecx]    ;ecx*=5

    lea ecx, dword ptr [eax+2*ecx]    ;ecx=ecx*2+eax

    mov al, byte ptr [esi]

    inc esi

  proceed:

    or al, al

    jne @B    ;非0(没处理完)上跳

    lea eax, dword ptr [edx+ecx]

    xor eax, edx

    pop edi

    pop esi

    ret

atodw endp

; #########################################################################

end

这个函数支持有符号数,好评。

补码

补码是当今广泛使用的有符号数编码规则,
负数表示为其绝对值表示的正数按位取反再+1(或-1后按位取反),
补码的优点是用无符号数规则进行有符号数加减计算,结果仍满足补码规则,且符合数学运算规定。

代码亮点:灵活的使用xor和lea

这个函数对有符号数的处理可谓颇为巧妙,
若字符串的第一个符号不是“-”,函数会将其按照正数处理,令edx为0,
在执行到49行时,ecx中就储存了正确的结果,而由于edx为0,不会改变ecx的值,就相当于执行了eax=ecx+0,eax中储存正确的结果。
由于0 xor any=any,下一行的xor不会改变eax的值。
若字符串的第一个符号是“-”,edx将会=0xFFFFFFFF,
这样,49行会使eax-=1,
由于edx的所有二进制位均为1,所以any xor edx=not any,50行实际上将eax进行了按位取反操作,
这样,eax正好满足补码规则,被顺利的转换为负数。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功