심플스 - 프로그램과 책 이야기로 가득한 곳, (Simples.Kr)

  


   심플스 배너



자유게시판에 비슷한 내용이 올려져 있길래, 최근에 제 블로그에 포스팅한 내용을 올려봅니다.


델파이(Delphi 2010)에서 인라인 어셈블리(inline Assembly) 사용하기


얼마전 받은 교육이 “악성코드 분석” 이었는데, 교육중에 주로 악성코드보다는 초급자를 위한 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;

profile

정보공개는 자신감에서 온다. 당신은 그 자신감이 있는가?

엮인글 :
http://simples.kr/25914/089/trackback

profile

lain

2010.10.19 20:11:17

1번에서 제가 알기로 함수에 첫 번째 인자에 접근할 때에는 [EBP+0x8]을 해주는것으로 알고 있습니다.

[EBP-0x4]는 첫 번째 로컬 변수에 접근할 때 사용하구요.

profile

훅크선장

2010.10.20 09:39:50

값을 찍어보면서, 여러번 테스트해봤을때, 일반적인 어셈블리 개념하고는 조금 틀린 것 같았습니다.

또 한번 테스트 해보겠습니다.

profile

lain

2010.10.20 11:45:07

아 죄송합니다. 제가 첫 번째 답변은 일반적인 stdcall, cdecl 에 대해서 컴파일러가 최적화되지 않았을 경우에
매우 일반적인 접근방법에 대해서 말한것입니다.

말씀하시는 내용이 델파이에서 함수인자에 접근하는 경우를 말한것이었네요.
EBP가 중간에 나오는 바람에 제가 헷갈렸습니다.ㅠㅠ

올리신 글을 보다가 제가 개인적으로 궁금해서 델파이 함수인자를 넘긴 후에 받는쪽에서 어떻게 처리하는지 잠깐 
조사해보았습니다.

위에 소스에 해당 함수를 호출 할 때 다음과 같이 호출합니다.

edx = 첫 번째 인자
ecx = 두번째 인자
스택 = 세번째 인자 

그리고 keygen1 함수내에서 스택을 할당하고 
mov [ebp-$04],ecx = 두 번째 인자
mov [ebp-$08],edx = 첫 번째 인자
이렇게 임시로 할당한 로컬변수에 집어넣구요.

실제 사용할 때에는 다음과 같이 사용합니다.
Unit1.pas.44: MOV EDX, aStr_len // copy string length
004A0E38 8B55FC           mov edx,[ebp-$04]  <= 두 번째 인자 접근
Unit1.pas.45: MOV ESI, [aStr] // load string address
004A0E3B 8B75F8           mov esi,[ebp-$08] <= 첫 번째 인자 접근
Unit1.pas.46: MOV EDI, [ret] // load integer value address
004A0E3E 8B7D08           mov edi,[ebp+$08] <= 세 번째 인자 접근

보시면은 아시다시피 상당히 일반적이지 않은 방법으로 접근하고 있습니다. ( 최적화가 꺼져있어도 접근방식은 같았습니다. )
지금 클래스에 함수를 호출하기 때문에 Self ( VC++로 따지자면 this 포인터, thiscall 방식 )
를 넘겨야 되는데 이걸 eax에다가 넘기고 있습니다. 백업은 ebx에다가 하고 있구요.

VC++은 이 정도까지는 아닌데 델파이에 경우에는 너무 컴파일러 지맘대로 하네요. 최적화가 꺼져있는 상태에서도요.
그리고 fastcall에 규칙대로 어느정도 맞게 호출은 하였는데 막상 쓰는걸 보면 
왠 로컬스택을 할당해서 저렇게 희한하게 접근하고 있죠.

다음과 같은 비어있는 함수를 테스트삼아 만들어봤는데

procedure AA(A, B, C: Integer);
begin

end;

eax = 첫 번째 인자
edx = 두 번째 인자
ecx = 세 번째 인자
를 넘기고 해당 함수내부에서

004A0E06 894DF4           mov [ebp-$0c],ecx
004A0E09 8955F8           mov [ebp-$08],edx
004A0E0C 8945FC           mov [ebp-$04],eax

다음과 같이 로컬스택에 인자를 셋팅합니다.
이제 훅크선장님에 말씀대로 조금 비슷하게 됬는데 앞에서부터 순서대로 
DWORD PTR [EBP-04h], DWORD PTR [EBP-08h, DWORD PTR [EBP-0Ch] 가 됩니다.

여기서 MyInteger: Integer; 라는 전역변수를 두고 다음과 같이 함수를 만듭니다.
procedure AA(A, B, C: Integer);
begin
    MYInteger := C;
end;

004A0E06 894DFC           mov [ebp-$04],ecx = 세 번째 인자
004A0E09 8955F4           mov [ebp-$0c],edx = 두 번째 인자
004A0E0C 8945F8           mov [ebp-$08],eax = 첫 번째 인자

형태로 됩니다.

여기서 함수 코드를 다음과 같이 바꿔볼 수 있습니다.
전역변수 MyInteger에 대입하는 인자가 B로 바꼈을 뿐입니다.
procedure AA(A, B, C: Integer);
begin
    MYInteger := B;
end;

004A0E06 894DF4           mov [ebp-$0c],ecx = 세 번째 인자
004A0E09 8955FC           mov [ebp-$04],edx = 두 번째 인자
004A0E0C 8945F8           mov [ebp-$08],eax = 첫 번째 인자

이 전에 두 번째 인자가 ebp-0xc에 들어갔는데 이번에는 ebp-0x4에 들어갔죠.
상황에 따라서 완전히 다릅니다. 단순히 참조하는 인자만 바꿨을 뿐인데 내부에서 쓰는 스택에 규칙도 완전히
변경되어버렸구요.

또한  fastcall 방식이 아니라 기존에 self를 넘기던 eax를 여기서는 첫 번째 인자로 사용하고 있습니다. ( 클래스 함수가 아니므로 )
완전히 델파이 전용 콜링컨벤션이죠.

이건 지극이 매우 개인적인 생각인데 델파이로 인라인 어셈블리를 넣는거는 상관이 없는데 
crack me나 이런 코드를 델파이에 인라인 어셈으로 옮기는 것은 비추천입니다.
왜냐하면 델파이에서 만들어지는 내부적인 코드가 너무 다양하기 때문에 ( 최적화를 꺼도 ) 차라리 VC++로 최적화를 끈 상태에서
naked 함수를 쓰는게 훨씬 효율적이라고 생각됩니다.
무엇보다 델파이에 어셈블리 뷰어에 치명적인 버그가 있는데 call 부분에서 함수안으로 들어가고 위로 스크롤을 해주면
아래에 어셈블리가 다 깨집니다. 그리고 다시 다음라인으로 이동해야 정상적으로 나오구요.
하다보면 위에 코드가 궁금해서 전체적인 내용을 보기 위해 스크롤을 해야 하는 경우가 있는데 이렇게 어셈블리 코드가
깨져버리면 문제가 있습니다. 해보시면 정말 답답할것입니다. 
이거는 엠바카데로쪽에서 좀 고쳐줘야 할 문제가 아닌듯 싶네요.
profile

훅크선장

2010.10.20 11:49:31

일반적인 디버깅에서 볼 수 있는 EBP 기준의 값을 가지고 있지 않는 것 같습니다.

테스트를 통해서 볼때, EBP+04h 등에는 함수의 로컬 변수가 저장되어 있지 않네요~

이유가 무엇인지 한번 검사해볼 필요는 있을 것 같습니다.

파르셀

2010.12.28 18:51:12

begin

  asm

  end;

end;

 

 

asm

end;

 

또한 다릅니다.

 

List of Articles
번호 제목 글쓴이 날짜sort 조회 수
공지 강좌/팁/정보 델파이로 프로그램이 제작되었는지 확인하는 방법 file [1] esniper 2009-11-26 86175
공지 강좌/팁/정보 주변에 델파이로 제작된 프로그램이 있으면 올려주세요~ esniper 2009-11-25 80279

해외 프로그램 VMProtect file [4]

  • lain
  • 2011-01-15
  • 조회 수 17238

강좌/팁/정보 Delphi에서 인라인 어셈블리 사용하기 [5]

해외 프로그램 Doc-O-Matic file [4]

  • lain
  • 2010-09-30
  • 조회 수 20911

컴포넌트/보조툴 ImageEn - 그래픽 라이브러리 [1]

  • lain
  • 2010-09-05
  • 조회 수 24024

컴포넌트/보조툴 GLScene 3D Controls (GL3DC) file [1]

  • lain
  • 2010-08-30
  • 조회 수 21736

해외 프로그램 PortableApps.com: Portable Software/USB file [1]

  • lain
  • 2010-08-30
  • 조회 수 21478

해외 프로그램 PeaZip - 압축 프로그램 file [1]

  • lain
  • 2010-08-30
  • 조회 수 21255

해외 프로그램 Ares Galaxy - 음악, 영화 공유 file [1]

  • lain
  • 2010-08-30
  • 조회 수 21683

컴포넌트/보조툴 TurboPower Async Professional - 시리얼 제어 등.. file [1]

  • lain
  • 2010-08-30
  • 조회 수 20467

컴포넌트/보조툴 TurboPower Abbrevia - 압축 컴포넌트 file [4]

  • lain
  • 2010-08-30
  • 조회 수 19652

컴포넌트/보조툴 ThinVNC- HTML5 Remote Desktop file [1]

  • lain
  • 2010-08-28
  • 조회 수 21276

컴포넌트/보조툴 Andorra 2D - 2D 그래픽엔진 file [1]

  • lain
  • 2010-08-28
  • 조회 수 19534

강좌/팁/정보 Auto-increment build number file [2]

  • lain
  • 2010-08-28
  • 조회 수 18802

강좌/팁/정보 RAD Studio XE 프리뷰 #3 - 멀티 티어, 클라우드 및 웹

  • esniper
  • 2010-08-26
  • 조회 수 20345

강좌/팁/정보 Base64 Encoding & Decoding 소스 file [1]

해외 프로그램 WinLicense - 소프트웨어 보호 및 라이센스 적용 file [1]

  • lain
  • 2010-08-20
  • 조회 수 20988

국내 프로그램 사내 인트라넷용 후이즈 화상회의 file [1]

해외 프로그램 FreeCommander - 파일관리툴 file [2]

  • esniper
  • 2010-08-20
  • 조회 수 17769

강좌/팁/정보 RAD Studio XE 프리뷰 #2 - 최적화와 자동화 [1]

  • lain
  • 2010-08-19
  • 조회 수 20191

컴포넌트/보조툴 JSON delphi library 1.07 - 한글 및 다국어 지원 file [1]

  • lain
  • 2010-08-18
  • 조회 수 17934

  • 이용약관
  • 개인정보취급방침
  • 사이트맵