Why my Dart implementation of an asm checksum macro does not work?

Viewed 169

I'm trying to implement a 32bits checksum macro written in masm32 to the Dart language. Here is what I understood: the checksum function takes a String as input and returns the checksum in a 4 bytes integer. But I don't get the same result. Does anyone see my errors please?

; 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;
}

I modified the code according to the advice, with as postulate an ASCII string so always multiple of 4, the time to understand the problem. It still doesn't work though.

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);
}
1 Answers

The transcription of the checksum algorithm is wrong.
Here's how I'd do it:

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;
    
}

You totally missed that:

  • The code is working with DWORDs (32-bit integers).
  • Strings with less than 4 bytes have zero checksum.
  • The code handles strings with length non-multiple of four by reading the last four bytes (that, necessarily, overlap with the previous DWORD).

Here's the commented assembly:

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

Also, note that converting numbers to string to extract digits or bits is generally a bad practice. Use the >> shift operator instead and eventually an AND.

Related