Question

Eh bien, je étudie une certaine assemblée en ligne à Delphes et la routine de crypto de l'assemblage se passe bien, jusqu'à ce que j'essaie d'analyser le shortching dans la zone de texte.

La violation que je reçois est la suivante: Erreur

Le code complet est ici:

procedure TForm2.Button1Click(Sender: TObject);

var
len,keylen:integer;
name, key:ShortString;

begin

name :=  ShortString(Edit1.Text);
key := '_r <()<1-Z2[l5,^';
len := Length(name);
keylen := Length(key);

nameLen := len;
serialLen := keyLen;

asm

  XOR EAX,EAX
  XOR ESI,ESI
 XOR EDX,EDX
 XOR ECX,ECX


  @loopBegin:

        MOV EAX,ESI
        PUSH $019
        CDQ
        IDIV DWORD PTR DS:[serialLen]
        MOV EAX,ESI
        POP EBX
        LEA ECX,DWORD PTR DS:[key+EDX]
        CDQ
        IDIV DWORD PTR DS:[nameLen]
        LEA EAX,DWORD PTR DS:[name]
        MOVZX EAX,BYTE PTR DS:[name+EDX]
        MOVZX EDX,BYTE PTR DS:[ECX]
        XOR EAX,EDX
        CDQ
        IDIV EBX
        ADD DL,$041
        INC ESI
        CMP ESI,DWORD PTR DS:[serialLen]
        MOV BYTE PTR DS:[ECX],DL

        JL @loopBegin


end;

edit2.Text:= TCaption(key);


end;

Si je place un point d'arrêt sur la ligne "Edit2.text:= TCaption (clé);"Je peux voir que la "clé" shorttring a été correctement cryptée, mais avec beaucoup de personnages étranges derrière elle aussi.

Les 16 premiers caractères sont le vrai cryptage.

cryptagehttp://img831.imageshack.us/img831/365/29944312.png

bigger version: http://img831.imageshack.us/img831/365/29944312.png

merci!

Était-ce utile?

La solution

Quel est le code

Pour ceux d'entre vous qui ne parlent pas assemblant, c'est ce que le code est probablement censé faire, à Pascal. "Probablement" parce que l'original contient des bugs:

procedure TForm14.Button1Click(Sender: TObject);
var KeyLen:Integer;
    Name, Key:ShortString;
    i:Integer;
    CurrentKeyByte:Byte;
    CurrentNameByte:Byte;
begin
  Name := ShortString(Edit1.Text);
  Key := '_r <()<1-Z2[l5,^';
  keyLen := Length(key);

  asm int 3 end; // This is here so I can inspect the assembler output in the IDE
                 // for the "Optimised" version of the code

  for i:=1 to Length(Name) do
  begin
    CurrentKeyByte := Byte(Key[i mod KeyLen]);
    CurrentNameByte := Byte(Name[i]);
    CurrentNameByte := ((CurrentKeyByte xor CurrentNameByte) mod $019) + $041;
    Name[i] := AnsiChar(CurrentNameByte);
  end;

  Caption := Name;

end;

Avec les optimisations activées, le code de l'assembleur généré par celui-ci est en réalité plus court par rapport au code proposé, ne contient aucun code redondant et je suis prêt à parier est plus rapide. Voici quelques optimisations que j'ai remarquées dans le code généré par Delphi ( par rapport au code de l'assembleur proposé par l'OP ):

  • Delphi a inversé la boucle (Downto 0). Cela permet d'économiser une instruction "CMP" car le compilateur peut simplement "décéder ESI" et une boucle sur le drapeau zéro.
  • utilisé "XOR EDX" et "DIV EBX" pour la deuxième division, enregistrant une minuscule quantité de cycles.

Pourquoi le code d'assembleur fourni est-il défaillant?

Voici le code d'assembleur d'origine, avec des commentaires. Le bogue est à la fin de la routine, à l'instruction "CMP" - il s'agit de la comparaison de l'ESI à la longueur de la clé, pas à la longueur du nom. Si la clé est plus longue, le nom, "cryptage" passe au-dessus du nom de nom, écrasement des trucs (parmi les trucs qui sont remplacés est le terminateur NULL pour la chaîne, ce qui provoque le débogueur de montrer des caractères amusants après les caractères corrects).

Tout en écrasant EBX et ESI n'est pas autorisé, ce n'est pas ce qui cause le code AV, probablement parce que le code Delphi environnant n'utilisait pas EBX ou ESI (juste essayé).

asm

 XOR EAX,EAX ; Wasteful, the first instruction in Loop overwrites EAX
 XOR ESI,ESI
 XOR EDX,EDX ; Wasteful, the first CDQ instruction in Loop overwrites EDX
 XOR ECX,ECX ; Wasteful, the first LEA instruction overwrites ECX


 @loopBegin:
       ; Etering the loop, ESI holds the index for the next char to be
       ; encrypted.

       MOV EAX,ESI ; Load EAX with the index for the next char, because
                   ; we intend to do some divisions (setting up the call to IDIV)
       PUSH $019   ; ? pushing this here, so we can pop it 3 lines later... obfuscation
       CDQ         ; Sign-extend EAX (required for IDIV)
       IDIV DWORD PTR DS:[serialLen] ; Divide EAX by the length of the key.
       MOV EAX,ESI ; Load the index back to EAX, we're planning on an other IDIV. Why???
       POP EBX     ; Remember the PUSH $019?
       LEA ECX,DWORD PTR DS:[key+EDX] ; EDX is the result of "ESI mod serialLen", this
                                      ; loads the address of the current char in the
                                      ; encryption key into ECX. Dividing by serialLen
                                      ; is supposed to make sure we "wrap around" at the
                                      ; end of the key
        CDQ ; Yet some more obfuscation. We're now extending EAX into EDX in preparation for IDIV.
            ; This is obfuscation becasue the "MOV EAX, ESI" instruction could be written right here
            ; before the CDQ.
        IDIV DWORD PTR DS:[nameLen] ; We divide the current index by the length of the text
                                    ; to be encrypted. Once more the code will only use the reminder,
                                    ; but why would one do this? Isn't ESI (the index) always supposed to
                                    ; be LESS THEN nameLen? This is the first sign of trouble.
        LEA EAX,DWORD PTR DS:[name] ; EAX now holds the address of NAME.
        MOVZX EAX,BYTE PTR DS:[name+EDX] ; EAX holds the current character in name
        MOVZX EDX,BYTE PTR DS:[ECX]      ; EDX holds the current character in Key
        XOR EAX,EDX ; Aha!!!! So this is an obfuscated XOR loop! EAX holds the "name[ESI] xor key[ESI]"
        CDQ         ; We're extending EAX (the XOR result) in preparation for a divide
        IDIV EBX    ; Divde by EAX by EBX (EBX = $019). Why????
        ADD DL,$041 ; EDX now holds the remainder of our previous XOR, after the division by $019;
                    ; This is an number from $000 to $018. Adding $041 turns it into an number from
                    ; $041 to $05A (ASCII chars from "A" to "Z"). Now I get it. This is not encryption,
                    ; this is a HASH function! One can't un-encrypt this (information is thrown away at
                    ; the division).
        INC ESI     ; Prep for the next char


        ; !!! BUG !!!
        ;
        ; This is what's causing the algorithm to generate the AV. At this step the code is
        ; comparing ESI (the current char index) to the length of the KEY and loops back if
        ; "ESI < serialLen". If NAME is shorter then KEY, encryption will encrypt stuff beyond
        ; then end of NAME (up to the length of KEY). If NAME is longer then KEY, only Length(Key)
        ; bytes would be encrypted and the rest of "Name" would be ignored.
        ;
        CMP ESI,DWORD PTR DS:[serialLen]


        MOV BYTE PTR DS:[ECX],DL ; Obfuscation again. This is where the mangled char is written
                                 ; back to "Name".

        JL @loopBegin            ; Repeat the loop.

mon conseil de 2 cents de conseils

Assembleur doit être utilisé pour les optimisations de vitesse et rien d'autre . Cela me semble que si l'OP essayait d'utiliser Assembler pour obscurcir ce que fait le code. N'aide pas, cela ne m'a pas fait quelques minutes pour comprendre exactement ce que le code fait et je suis pas un expert d'assembleur.

Autres conseils

Tout d'abord, vous devez préserver EDI et ESI.Seuls EAX, EDX et EXC, peuvent être utilisés sans préservation (sauf lorsque vous le chargez et devez le préserver).

Essayez d'ajouter un peu d'EDI, appuyez sur ESI et POP ESI, Pop Edi autour de votre code.

Vous ne pouvez pas simplement co-opter les registres de vos propres fins dans l'ASM inine sans préserver (sauvegarder et restaurer) le contenu du registre.

Dans votre code, vous piétinez sur EAX (qui contient «Self») et EDX (qui - avec la Convention par défaut Calling Calling - détient le «Sender»).

Et si je comprends bien, d'autres registres peuvent également être utilisés pour les variables locales.

Astuce: Et si ESI ou EAX ou quoi que ce soit vous-même?Votre assembleur la corbeille.Ligne suivante que vous essayez d'utiliser Edit2, qui nécessite un accès à soi, ce qui est ... Eh bien, plus avec nous.

Le compilateur et vous utilisez des registres.Vous devez jouer à Nice et coopérer avec le compilateur, ce qui signifie économiser / restaurer les valeurs des registres.

Sinon, je pense que vous devez décharger le code de l'assembleur pour séparer la routine. Il n'y aura donc aucun code Pascal, qui peut également utiliser des registres.Notez que vous aurez toujours besoin de conforme du protocole de la convention d'appel: tous les registres ne peuvent pas être utilisés librement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top