델파이(Delphi 2010)에서 인라인 어셈블리(inline Assembly) 사용하기
절름발이 프로그래머/Delphi / 2010. 10. 14. 13:49
얼마전 받은 교육이 “악성코드 분석” 이었는데, 교육중에 주로 악성코드보다는 초급자를 위한 Crackme 나 KeygenMe 프로그램을 주로 분석하있다.
간단하게 Keygen을 만들려고 하니, 일일히 어셈블리 코드를 분석해서, C 언어나 Python 으로 변환하기가 너무 귀찮은 경우가 생겨서, 델파이에 인라인 어셈블리를 적용하는 방법을 찾아내었다.
알고보니 너무 간단하고, 참 쉽다.
asm
...
end;
지시어 안에, 어셈코드를 넣기만 하면 된다.
그러나, 올리디버거(OllyDbg v1.10)에서 가져온 어셈블리 코드를 델파이에 적용하는 경우에 반드시 주의하여야 하는 사항은 다음과 같다.
1. 함수 인자값들은 반드시 레지스터에 저장해서 연산하여야 한다. 함수 인자는 바로 연산문에 사용할 수 없다. (지역변수는 변수명으로 바로 사용할 수 있다.)
* 참고: var 지정(내부에서 변하는 변수)이 아닌 일반 함수 인자들은 EBP를 기준으로 접근할 수 있습니다. 인자값이 변하지 않는 상수라면, 레지스터에 복사하지 않고 바로 사용할 수 있습니다. 문자열을 인자로 주었다면, 꼭 레지스터에 복사해야 합니다.
함수인자는 뒤에서 부터 차례로 DWORD PTR [EBP-04h], DWORD PTR [EBP-08h, DWORD PTR [EBP-0Ch] 로 접근 가능합니다.
2. 어셈블리 코드안에서 사용하는 모든 레지스터는 반드시 사용전에 PUSH 하고, 사용후에 POP 해야한다. (레지스터를 초기화하는 것도 오류를 없애기 위해서는 당연히 사용해야 한다.)
3. 올리디버거에서 추출된 어셈블리 코드안의 숫자값은 모두 16진수 이므로, 인라인 어셈블리 코드로 사용하려면, 모든 숫자값에는 16진수 임을 알리는 “h” 문자를 추가하여야 한다. (MOV EAX, 20 이렇게 되어 있다면, 반드시 MOV EAX, 20h 로 바꾸어야 한다.)
4. 문자열은 주소값으로 연산되므로, 포인터 연산에 주의를 해야한다.(너무나도 당연한 말이다.)
예제 코드를 보여본다. (함수내에서 인자값을 바꾸는, 포인터 연산도 포함되어 있다.)
-------------------------------------------------------------------------
function TMainForm.keygen1(aStr: AnsiString; aStr_len: Integer; var ret:Integer):Cardinal;
var
ret2 : Integer;
begin
asm
PUSH EDX // Safe Store before use
PUSH ECX
PUSH EBX
PUSH EAX
PUSH ESI
PUSH EDI
XOR EDI, EDI // init value := 0
XOR ESI, ESI
XOR EDX, EDX
XOR ECX, ECX // ecx := 0, init value
XOR EBX, EBX // ebx := 0, init value
XOR EAX, EAX
MOV EDX, aStr_len // copy string length
MOV ESI, [aStr] // load string address
MOV EDI, [ret] // load integer value address
@@LOOP: // Label
MOV AL, BYTE PTR [ESI+ECX]
INC ECX
XOR EAX, ECX
ADD EBX, EAX
CMP ECX, EDX
JNZ @@LOOP // conditional jump to Label
IMUL EAX, EAX, 6h
MOV DWORD PTR [EDI], EBX // pointer variable value copy
SHL EBX, 7h
MOV ret2, EBX // local variable value copy
ADD EAX, EBX
MOV @Result, EAX // return value : Result
POP EDI // restore value
POP ESI
POP EAX
POP EBX
POP ECX
POP EDX
end;
end;