|
글 수 163
자유게시판에 비슷한 내용이 올려져 있길래, 최근에 제 블로그에 포스팅한 내용을 올려봅니다.
아 죄송합니다. 제가 첫 번째 답변은 일반적인 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 부분에서 함수안으로 들어가고 위로 스크롤을 해주면 아래에 어셈블리가 다 깨집니다. 그리고 다시 다음라인으로 이동해야 정상적으로 나오구요. 하다보면 위에 코드가 궁금해서 전체적인 내용을 보기 위해 스크롤을 해야 하는 경우가 있는데 이렇게 어셈블리 코드가 깨져버리면 문제가 있습니다. 해보시면 정말 답답할것입니다. 이거는 엠바카데로쪽에서 좀 고쳐줘야 할 문제가 아닌듯 싶네요. |



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