gto250 发表于 2012-12-21 22:39:57

关于P版的汇编高效处理字符串的升级问题【已解决】

本帖最后由 gto250 于 2012-12-22 22:43 编辑

P版的字符串比较汇编代码(点击跳转)

我试着改过代码,但是总是内存错误,因为对汇编不怎么熟悉,所以还是到论坛上来求租。
问题基本相同,有
$str1="1a2b3c4d4d3c2b1a……4d3c2b1a"
$str2="1a2b3c4d1a2b3c4d……1a2b3c4d"
这样的两个长度相同的字符串,当然内容各不相同,由数字和字母组成,字符串很长。
现在打算每8位比较一次,比较厚相同的返回相同的内容,不同的返回“11223344”
如上面的字串返回的就是“1a2b3c4d11223344……11223344”


其实上面的字串是一个二进制的字串,如“0x1a2b3c4d4d3c2b1a……4d3c2b1a”,因为在上面的链接中我问过P版关于那机器码的相关问题,研究过很长时间,所以还是想用字符串的方式进行比较,因为比较好理解一点。
如果哪位觉得用二进制比较容易的话,也可以贴相关的代码,供学习一下

pusofalse 发表于 2012-12-22 18:44:08

004001E1/.55            push    ebp
004001E2|.8BEC          mov   ebp, esp
004001E4|.56            push    esi
004001E5|.57            push    edi
004001E6|.8B75 08       mov   esi, dword ptr
004001E9|.8B7D 0C       mov   edi, dword ptr
004001EC|.8B4D 10       mov   ecx, dword ptr
004001EF|.8B55 14       mov   edx, dword ptr
004001F2|.33C0          xor   eax, eax
004001F4|.C1E9 02       shr   ecx, 2
004001F7|.FC            cld
004001F8|>F3:A7         /repe    cmpsd
004001FA|.75 06         |jnz   short 00400202
004001FC|.5F            |pop   edi
004001FD|.5E            |pop   esi
004001FE|.C9            |leave
004001FF|.C2 1000       |retn    10
00400202|>8956 FC       |mov   dword ptr , edx
00400205|.40            |inc   eax
00400206|.3BC0          |cmp   eax, eax
00400208\.^ EB EE         \jmp   short 004001F8
0040020A      CC            int3
0040020B      CC            int3使用DllCallAddress函数调用这段汇编代码。原型为:
func(dst, src, len, replace)

每4个字节进行比较dst和src中的数据,总共比较Int(len/4)次,如果某处内容不同,将替换为replace所指定的值。
参数分别是:
dst - 内存1的起始地址。
src - 内存2的起始地址。
len - 需要比较的长度,以字节为单位。
replace - 将不同处的内容替换为此值。

函数返回值为替换的次数,替换后的内容,将在dst中返回。

gto250 发表于 2012-12-22 20:12:15

回复 2# pusofalse


    谢谢P版,我先试着自己使用一下

gto250 发表于 2012-12-22 21:27:34

感谢P版的汇编代码,虽然不知道是什么意思,但是还是根据这机器码写出了au3的代码
以下就是应用的FUNC,不知道有没有同样需求的兄弟们需要使用。

最后,如果P版有空的话,能否解释一下汇编代码
我只知道shr 攀挀砀, 2这句是右移2位的意思,就是除以4的意思,因为是按照四个字节比较
如果是三个字节比较的话,应该就不能这么写了吧?,如果我要改成三个字节比较的话,要怎么修改呢?

$Data1 = "ff12345601020304ff12345605060708"

$Data2 = "ff12345605060708ff12345601020304"


$r = cmps($Data1, $Data2, "0x12345678")
MsgBox(0, "替换次数" & $r & "次", $r)


Func cmps($dst, $src, $replace)
        Local $r
        If StringLeft($replace, 2) = '0x' Then
                $replace = StringRegExpReplace(StringTrimLeft($replace, 2), '(\w{2})(\w{2})(\w{2})(\w{2})', '0x$4$3$2$1')
        Else
                $replace = StringRegExpReplace($replace, '(\w{2})(\w{2})(\w{2})(\w{2})', '0x$4$3$2$1')
        EndIf
       
        $Opcode = Binary("0x558BEC56578B75088B7D0C8B4D108B551433C0C1E902FCF3A775065F5EC9C210008956FC403BC0EBEECCCC")
        $CodeBufferPtr_return=DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", BinaryLen($Opcode), "dword", 0x00001000, "dword",0x00000040)
        Local $CodeBufferPtr = $CodeBufferPtr_return
        Local $CodeBuffer = DllStructCreate("byte[" & BinaryLen($Opcode) & "]", $CodeBufferPtr)
        DllStructSetData($CodeBuffer, 1, $Opcode)
       

        If IsBinary($dst) = False Then $dst = Binary("0x" & $dst)
        Local $dst_BufferLen = BinaryLen($dst)
        Local $dst_Buffer = DllStructCreate("byte[" & $dst_BufferLen & "]")
        DllStructSetData($dst_Buffer, 1, $dst)

        If IsBinary($src) = False Then $src = Binary("0x" & $src)
        Local $src_BufferLen = BinaryLen($src)
        Local $src_Buffer = DllStructCreate("byte[" & $src_BufferLen & "]")
        DllStructSetData($src_Buffer, 1, $src)

        Local $Ret = DllCallAddress("uint", $CodeBufferPtr, "ptr", DllStructGetPtr($dst_Buffer), "ptr", DllStructGetPtr($src_Buffer), "uint", $dst_BufferLen, "uint", $replace)

      DllCall("kernel32.dll", "bool", "VirtualFree", "ptr", $CodeBufferPtr, "ulong_ptr", 0, "dword", 0x00008000)
       
        $r = $Ret
        $r = DllStructGetData($dst_Buffer, 1)
        Return $r
EndFunc   ;==>cmps

pusofalse 发表于 2012-12-22 21:43:26

回复 3# gto250


    测试代码如下。函数是以你说的二进制方式比较的,非字符串方式。
#include <Thread.au3>

Local $bBinary = "0x558BEC56578B75088B7D0C8B4D108B551433C0C1E902FCF3A775065F5EC9C210008956FC403BC0EBEECCCC"

Local $pStart = _RTVirtualAlloc(4096)
Local $tStart = DllStructCreate("ubyte", $pStart)
DllStructSetData($tStart, 1, Binary($bBinary))

Local $bBinary1 = Binary("0x1111111133333333EEEEEEEE") ; 字符串1
Local $bBinary2 = Binary("0x1111111133333333FFFFFFFF") ; 字符串2

Local $tDst = DllStructCreate("ubyte [" & BinaryLen($bBinary1) & "]")
Local $tSrc = DllStructCreate("ubyte [" & BinaryLen($bBinary2) & "]")

DllStructSetData($tDst, 1, $bBinary1)
DllStructSetData($tSrc, 1, $bBinary2)

Local $pDst = DllStructGetPtr($tDst)
Local $pSrc = DllStructGetPtr($tSrc)

Local $iResult = DllCallAddress("long", $pStart, "ptr", $pDst, "ptr", $pSrc, "long", BinaryLen($bBinary1), "dword", 0x44444444)

MsgBox(0, StringFormat("Replaced %d DWORD(s).", $iResult), DllStructGetData($tDst, 1))

#CS
004001E1/.55            push    ebp
004001E2|.8BEC          mov   ebp, esp
004001E4|.56            push    esi
004001E5|.57            push    edi
004001E6|.8B75 08       mov   esi, dword ptr
004001E9|.8B7D 0C       mov   edi, dword ptr
004001EC|.8B4D 10       mov   ecx, dword ptr
004001EF|.8B55 14       mov   edx, dword ptr
004001F2|.33C0          xor   eax, eax
004001F4|.C1E9 02       shr   ecx, 2
004001F7|.FC            cld
004001F8|>F3:A7         /repe    cmpsd
004001FA|.75 06         |jnz   short 00400202
004001FC|.5F            |pop   edi
004001FD|.5E            |pop   esi
004001FE|.C9            |leave
004001FF|.C2 1000       |retn    10
00400202|>8956 FC       |mov   dword ptr , edx
00400205|.40            |inc   eax
00400206|.3BC0          |cmp   eax, eax
00400208\.^ EB EE         \jmp   short 004001F8
0040020A      CC            int3
0040020B      CC            int3
#CE

pusofalse 发表于 2012-12-22 21:57:05

回复 4# gto250


    汇编中有3个字符串比较指令,分别是cmpsb, cmpsw,cmpsd,含义分别是按字节比较,按字比较,按双字比较,64位汇编中应该还会有个cmpsq指令,按四字比较(?)。这些个指令中,能一次比较1、2、4、8个字节,唯独没有你说的按3个字节对齐进行比较。如果一次要比较3个、5个或7个,总之,要比较的数量没有现成的CPU指令支持,代码就要全部改动了。

gto250 发表于 2012-12-22 22:35:21

回复 6# pusofalse


    谢谢
页: [1]
查看完整版本: 关于P版的汇编高效处理字符串的升级问题【已解决】