【问题标题】:Why my Dart implementation of an asm checksum macro does not work?为什么我的 asm 校验和宏的 Dart 实现不起作用?
【发布时间】:2021-08-12 02:00:11
【问题描述】:

我正在尝试在 Dart 语言中实现一个用 masm32 编写的 32 位校验和宏。 这是我的理解:校验和函数将字符串作为输入,并以 4 字节整数返回校验和。 但我没有得到相同的结果。 请问有人看到我的错误吗?

; ecx : length of String variable
; esi : pointer to String variable
; eax : 'return' value of calculated checksum
CHECKSUM32_MACRO MACRO 
   LOCAL Checksum32Loop, Checksum32Done       
        xor     eax,eax
        cmp     ecx,4
        jb      Checksum32Done
    align 16
    Checksum32Loop:
        mov     ebx,dword ptr [esi]
        add     eax,ebx
        shl     ebx,1
        adc     ebx,1
        xor     eax,ebx
        add     esi,4
        sub     ecx,4
        jz      Checksum32Done
        cmp     ecx,4
        jae     Checksum32Loop       
        mov     edx,4        
        sub     edx,ecx        
        sub     esi,edx
        mov     ecx,4
        jmp     Checksum32Loop
   Checksum32Done:     
ENDM
int checksum(String src){
  int i = src.length-1;
  int res = 0;
  do{
    int c  = src.codeUnitAt(i);
    res += c;
    String cBits = c.toRadixString(2);
    int bitFort = int.parse(cBits[0]);
    
    int transform = c << 1;
    transform = transform + 1 + bitFort;
    res = res ^ transform;   
    i--;
  }while(i>=0);  
  return res;
}

我根据建议修改了代码,假设 ASCII 字符串总是 4 的倍数,是时候理解问题了。 但是还是不行。

String deComp = File(CHEMIN_FICHIER_DECOMP).readAsStringSync();

List<int> encoded = [];
for (int i =0; i<deComp.length; i++){
    List<int> cUtf8 = utf8.encode(deComp[i]);
    encoded.addAll(cUtf8);
}
print(checksum_stack(encoded));

_______

int checksum_stack(List<int> src){
  int i = 0;
  int res = 0;
  do{
    int c  = fusion(src.sublist(i, i+4));
    res += c;
    String cBits = c.toRadixString(2).padLeft(8, '0');
    int bitFort = int.parse(cBits[0]);

    int transform = c << 1;
    transform = transform + 1 +bitFort;
    res = res ^ transform;
    i+=4;
  }while(i < src.length-4);
  return res;
}

int fusion(List<int> str){
  if (str.length != 4) {
    throw "need multiple of 4!";
  }
  String hexStr = "";
  str.forEach((c) {
    hexStr += c.toRadixString(16).padLeft(2, '0');
  });  
  return int.parse(hexStr,radix: 16);
}

【问题讨论】:

  • 你的每个字符是 4 个字节吗?您输入的esi 是否直接指向字符?
  • 我不知道,抱歉在 C++ 中(调用者):char * pData; pData =(char *)malloc(nFilesize); ReadFile(hInputFile, (void *)pData, nFilesize, $nBytesRead, 0); masmfunction((void*)pData, nFilesize) 在 masm(接收者)中:masmfunction proc stdcall pSrc:DWORD, _Length:DWORD mov esi, pSrc mov ecx, _Length
  • 该代码确实看起来确实可以同时处理四个字节dword ptr [esi]add esi,4)。 Dart 字符串不是字节序列,而是 16 位 UTF-16 代码单元。如果字符串仅为 ASCII,您可能会将每个字符视为一个字节,但如果不是,您可能需要先对字符串进行 UTF-8 编码,以获得与被散列的输入相媲美的东西。然后你需要处理这些字节。
  • 我听从了您的建议,例如:List&lt;int&gt; encoded = utf8.encode("aé"); 结果:[97, 195, 169] // a : 97, é : 195, 169 所以,要达到每个字符 4 个字节,我必须用 0 完成?手动:[0, 0, 0, 97, 0, 0, 195, 169]
  • 您不需要达到每个字符四个字节,您需要一次处理四个 字节。无论它们来自哪里(例如,来自四个代码单元、三个代码单元或一个代码单元)。此外,您的代码似乎缺少对长度不是四倍数的字符串的处理。最好重写循环以使用 DWORD(4 个字节)。

标签: dart assembly masm checksum crc32


【解决方案1】:

校验和算法的转录错误。
这是我的做法:

import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';

int checksum(String string, {Encoding encoder = utf8, Endian endian = Endian.little})
{
    final ByteData bytes = ByteData.sublistView(Uint8List.fromList(encoder.encode(string)));
    int checksum = 0;
    
    
    if (bytes.lengthInBytes >= 4)   
  {
        for (int i = 0; i < bytes.lengthInBytes; i += 4)
        {
            int chunk = bytes.getUint32(min(i + 4, bytes.lengthInBytes) - 4, endian);
            checksum = (checksum + chunk) ^ ((chunk << 1) + 1 + (chunk >> 31)); 
        }
  }
  
    return checksum & 0xffffffff;
    
}

你完全错过了:

  • 代码使用 DWORD(32 位整数)。
  • 少于 4 个字节的字符串的校验和为零。
  • 代码通过读取最后四个字节(必然与前一个 DWORD 重叠)来处理长度不是四的倍数的字符串。

这是注释的程序集:

CHECKSUM32_MACRO MACRO 
   LOCAL Checksum32Loop, Checksum32Done       
        xor     eax,eax                   ;Checksum = 0
        cmp     ecx,4
        jb      Checksum32Done            ;If len < 4 Then Return
    align 16
    Checksum32Loop:
        mov     ebx,dword ptr [esi]       ;c = DWORD from string (**FOUR** bytes)
        add     eax,ebx                   ;Checksum += c
        shl     ebx,1                     ;CF = c[31], c = c << 1
        adc     ebx,1                     ;c += (1 + CF)
        xor     eax,ebx                   ;Checksum ^= c
        add     esi,4                     ;Point to next DWORD
        sub     ecx,4                     ;Len -= 4
        jz      Checksum32Done            ;If Len == 0 Then Return
        cmp     ecx,4                     
        jae     Checksum32Loop            ;If Len >= 4 Then Cycle back
        mov     edx,4                     
        sub     edx,ecx                   ;edx = 4 - Len (left, so it's 4 - Len % 4 in absolute terms)
        sub     esi,edx                   ;Point to last DWORD (Len-4 in absolute terms, go back 4-Len in relative terms)
        mov     ecx,4                     ;Set Len=4 to cycle one more time
        jmp     Checksum32Loop
   Checksum32Done:     
ENDM

另外,请注意,将数字转换为字符串以提取数字或位通常是一种不好的做法。请改用 &gt;&gt; 移位运算符,最后使用 AND。

【讨论】:

  • 哇,谢谢你的解释和良好的实践建议真的更清楚了。我用一个 464 字节的长字符串测试了你的代码,结果是正确的。奇怪的是,以一个小字符串为例,校验和是不同的,但是一旦以十六进制传递,就很好,但前面多了一个“8”。 String myString = "bonjour petit foufou, tu vas bien et bien pas moi. Tu es sur, je ne le suis pas du tout ok"; masm:564299864 -> 21A28858,您的代码:34924038232 ->821A28858
  • @Gaiusfr 哦,那是因为 dart 的 ints 是 64 位的,我以为它们是 32 位的。让我解决这个问题。好的,我在返回校验和之前添加了&amp; 0xffffffff。如果我没记错的话,中间结果可以是 64 位,然后可以使用 AND 将最终结果缩减为 32 位。这比每次迭代中的 ANDing 更快。
  • 是的,这很好!与此同时,我找到了一个替代解决方案——但不如你的 ^^'。 return BigInt.from(checksum).toUnsigned(32).toInt();
  • 还有n.toUnsigned(32)直接在int上,它实际上就是n &amp; 0xffffffff。浏览BigInt 太过分了。
猜你喜欢
  • 2018-09-03
  • 1970-01-01
  • 2017-04-20
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 1970-01-01
相关资源
最近更新 更多