Pregunta

Bueno, estoy estudiando un montaje en línea en Delphi y la rutina Crypto de la Asamblea está muy bien, hasta que intento analizar el acretorado en el cuadro de texto.

La violación que obtengo es la siguiente: ERROR

El código completo está aquí:

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 coloco un punto de interrupción en la línea "Edit2.Text:= tcaption (clave);"También puedo ver que la "clave" de campocoramiento se ha cifrado correctamente, pero también con muchos personajes extraños.

Los primeros 16 caracteres son el cifrado real.

cifrado http://img831.imageshack.us/img831/365/29944312.PNG

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

¡Gracias!

¿Fue útil?

Solución

Qué hace el código

Para aquellos de ustedes que no hablan ensamblador, esto es lo que se supone que el código debe hacer, en Pascal. "Probablemente" porque el original contiene algunos errores:

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;

Con las optimizaciones encendidas, el código del ensamblador generado por esto es en realidad más corto en comparación con el código propuesto, no contiene ningún código redundante y estoy dispuesto a apostar es más rápido. Aquí hay algunas optimizaciones que noté en el código generado por Delphi ( en comparación con el código del ensamblador propuesto por la OP ):

  • Delphi invirtió el bucle (abajo a 0). Esto ahorra una instrucción de una "CMP" porque el compilador puede simplemente "Dec ESI" y el bucle en la bandera cero.
  • usado "XOR EDX" y "DIV EBX" para la segunda división, ahorrando una pequeña cantidad de ciclos.

¿Por qué el código del ensamblador proporcionado falla?

Aquí está el código del ensamblador original, con comentarios. El error al final de la rutina, en la instrucción "CMP", está comparando ESI a la longitud de la llave, no a la longitud del nombre. Si la clave es más larga, el nombre, el "cifrado" pasa por encima del nombre, sobrescribir cosas (entre las cosas que se sobrescribir es sobrescribir es el terminador nulo para la cadena, lo que hace que el depurador muestre caracteres divertidos después de los caracteres correctos).

Mientras que la sobrescritura EBX y ESI no están permitidos, esto no es lo que está causando el código en AV, probablemente porque el código de Delphi circundante no usó EBX o ESI (acaba de probar esto).

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.

Mis 2 centavos de consejo

El ensamblador debe usarse para las optimizaciones de velocidad y nada más . Me parece que la OP intentó usar el ensamblador para ofuscar lo que está haciendo el código. No ayudó, solo me tomó unos minutos averiguar exactamente lo que está haciendo el código y soy no un experto en ensamblador.

Otros consejos

primero, debe preservar EDI y ESI.Solo se puede usar EAX, EDX y ECX, sin preservación (excepto cuando lo carga y necesita preservarlo).

Intente agregar un poco de EDI EDI, PUSH ESI y POP ESI, POP EDI alrededor de su código.

No puede simplemente cooptar los registros para sus propios fines en línea ASM sin preservar (guardar y restaurar) el contenido del registro.

En su código, está pisoteando sobre EAX (que se mantiene "auto") y EDX (que, con el Convenio de llamadas predeterminado , lo más probable es que tenga "remitente").

y, como entiendo, otros registros también pueden usarse para las variables locales.

Sugerencia: ¿Qué pasa si ESI o EAX o lo que se mantiene?Tu ensamblador lo basó.Siguiente línea que está tratando de usar Edit2, que requiere acceso a sí mismo, que ... bueno, ya no con nosotros.

Tanto el compilador como usan registros.Necesita jugar bien y cooperar con el compilador, lo que significa salvar / restaurar los valores de los registros.

De lo contrario, creo que necesita descargar el código del ensamblador para separar la rutina, por lo que no habrá un código Pascal, que también puede usar registros.Tenga en cuenta que aún necesitará cumplir con el protocolo de la Convención de Llamadas: No todos los registros se pueden usar libremente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top