Delphi:redboxに文字列を入れるときのアクセス違反?
-
17-09-2020 - |
質問
まあ、私はDelphiでいくつかのインラインアセンブリを勉強しており、ショートストリングをテキストボックスに解析しようとするまで、アセンブリ暗号化ルーチンはすべて素晴らしいです。
フルコードはここにあります:
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;
.
行 "edit2.text:= tcaption(key);;"行にブレークポイントを配置した場合ショートストリング「キー」が確かに適切に暗号化されているが、その背後にあるたくさんの奇妙なキャラクターがあることがわかります。
最初の16文字は実際の暗号化です。
暗号化http://img831.imageshack.us/img831/365/29944312PNG
bigger version: http://img831.imageshack.us/img831/365/29944312.png
.
ありがとう!
解決
コードが何をしているのか
アセンブラを話していないあなたのために、これは、Pascalでは、コードがそうすることになっているものです。オリジナルにいくつかのバグが含まれているため、「おそらく」:
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;
.
最適化が有効になっていると、これにより生成されたアセンブラコードは、提案されたコードと比較して実際には短く、冗長なコードは含まれておらず、私は賭けていることを望んでいます。ここでは、Delphi生成コードで注目されているいくつかの最適化があります(によって提案されたアセンブラコードと比較して):
- delphiはループを逆にしました(downto 0)。これにより、コンパイラは単に「DEC ESI」とゼロフラグをループすることができるため、1つの「CMP」命令が保存されます。
- 2番目の部門には「XOR EDX」と「DIV EBX」を使用し、小さなサイクルを節約します。
提供されたアセンブラコードが失敗したのはなぜですか?
これは元のアセンブラコードです。コメント付き。ルーチンの最後のバグの「CMP」命令では、名前の長さではなく、ESIをキーの長さに比較します。キーが長い場合は名前が長い場合、名前の上に「暗号化」が表示されます。
EBXとESIを上書きすることはできませんが、これはAVへのコードを引き起こしているものではありません。
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.
.
私の2セント相手
アセンブラは、速度の最適化に使用し、他に何もないです。 OPがCOMSTBLERを使用しようとしているかのように、コードがしていることを難読化しようとしたかのように見えます。助けてはいけませんでした、それは私にコードが何をしていることと私はではないアセンブラの専門家である
他のヒント
最初のオフ、EDIとESIを保存する必要があります。EAX、EDX、ECXのみが保存なしで使用できます(ロードして保存する必要がある場合を除く)。
プッシュEDIを追加してみて、ESIとPOP ESIを押して、コードの周囲のPOP EDI。
レジスタの内容を保存しない(保存および復元)
を保存せずにインラインASMで独自の目的で登録することはできません。あなたのコードの中であなたはEAX( "self"を保持しています)とEDX(デフォルトの register 呼び出し条約を持っています - 最も可能性が高い)。
および私が理解しているので、他のレジスタもローカル変数に使用できます。
ヒント:ESIまたはEAX、または自分自身を持っているものは何であるか。あなたのアセンブラはそれを捨てます。次の行edit2を使用しようとしているedit2を使用しようとしています。これは...まあ、もはや私たちと一緒にはもうありません。
コンパイラとレジスタを使用します。あなたはコンパイラと協力して協力する必要があります。これは、レジスタの値を保存/復元することを意味します。
それ以外の場合は、アセンブラコードを別々のルーチンにオフロードする必要があるため、レジスタを使用する可能性があるPascalコードはありません。依然として呼び出し規約プロトコルを適合させる必要があることに注意してください。すべてのレジスタを自由に使用できるわけではありません。