Delphi:在editbox中放置字符串时访问违规行为?
-
17-09-2020 - |
题
完整代码在这里:
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/29944312。png
bigger version: http://img831.imageshack.us/img831/365/29944312.png
.
谢谢!
解决方案
代码是什么,
对于那些不会说汇编程序的人,这就是代码可能应该做的,帕斯卡尔。 “可能”,因为原件包含一些错误:
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生成的代码(相比,与OP )提出的汇编代码相比,我注意到了一些优化:
- Delphi反转循环(Downto 0)。这节省了一个“CMP”指令,因为编译器可以简单地“Dec ESI”并循环在零标志上。
- 使用“XOR EDX”和“DIV EBX”为第二划分,节省了一些微小的循环。
为什么提供的汇编程序代码失败?
这是原始汇编代码,注释。该错误在例程结束时,在“CMP”指令中 - 它将ESI与键的长度进行比较,而不是名称的长度。如果键较长,那么名称较长,“加密”就会在名称顶部覆盖,覆盖内容(在被覆盖的内容中是字符串的空终止器,导致调试器在正确的字符后显示有趣的字符。
在覆盖EBX和ESI时不允许,这不是将代码导致AV的,可能是因为周围的Delphi代码没有使用EBX或ESI(刚刚尝试过这个)。
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试图使用汇编程序混淆代码正在做什么。没有帮助,它只花了几分钟才能弄清楚代码正在做的事情,我是不是一个汇编专家。
其他提示
首先,您需要保留EDI和ESI。只能在没有保存的情况下使用EAX,EDX和ECX(除了加载它并需要保留它)。
尝试添加一些推送EDI,按ESI和POP ESI,在代码周围流行EDI。
您不能简单地在内联ASM中为自己的目的加入寄存器,而无需保留(保存和恢复)寄存器内容。
在您的代码中,您正在践踏EAX(包含“self”)和EDX(哪个 - 带有默认<强>寄存器调用约定 - 最有可能持有“发件人”)。
并且当我理解它,它也可以用于局部变量。
提示:如果ESI或EAX或其他任何持有自我,该怎么办?你的装配者垃圾垃圾。下一行,您正在尝试使用Edit2,这需要访问自我,这是......好,不再与我们一起。
两个编译器,您使用寄存器。您需要玩得很好,并与编译器合作,这意味着保存/恢复寄存器的值。
否则我认为需要将汇编代码卸载到单独的例程,因此不会有帕斯卡代码,也可以使用寄存器。注意,您仍然需要符合调用约定协议:并非所有寄存器都可以自由使用。