레지스터는 CPU 연산에 필요한 데이타를 사용하기 위한 저장고이다. IA32 체계에서 EAX, EBX ECX, ESI, EDI 등등의 명칭으로 사용되고 있는 데, 각 register가 사용되는 용도가 구분되어 있다. (참고: http://ko.wikipedia.org/wiki/IA32#.EB.A0.88.EC.A7.80.EC.8A.A4.ED.84.B0) Debugging 하다 보면, Assembly 언어를 분석할 때, register의 용도에 따라서 적절히 분석되는 것이 필요한데 몇가지 문서에 언급외에 독특한 register의 사용에 대해서 언급하고자 한다.
0:000> u 766dbf97-10 766dbf97+10
wininet!MEMMAP_FILE::ValidateListGroupOffset+0x38:
766dbf87 038d34118bc6 add ecx,dword ptr [ebp-3974EECCh]
766dbf8d 5e pop esi
766dbf8e 5d pop ebp
766dbf8f c20400 ret 4
766dbf92 90 nop
766dbf93 90 nop
766dbf94 90 nop
766dbf95 90 nop
766dbf96 90 nop
wininet!MEMMAP_FILE::GrowMapFile:
766dbf97 8bff mov edi,edi
766dbf99 55 push ebp
766dbf9a 8bec mov ebp,esp
766dbf9c 56 push esi
766dbf9d 8bf1 mov esi,ecx
766dbf9f 8b4604 mov eax,dword ptr [esi+4]
766dbfa2 8b4e18 mov ecx,dword ptr [esi+18h]
766dbfa5 69c080ed0100 imul eax,eax,offset <Unloaded_es_kr.dll>+0x1ed6f (0001ed80)
Nop 는 No-operation이다. 근데, Function starting point에 (노란색 블럭)mov edi, edi 가 보인다. 사실 그 아래 push ebp; mov ebp, esp; 는 일반적으로 Stack Frame을 만드는 시작 code이다. ebp가 stack의 base address이므로, 앞선 Function의 stack frame에 대한 base address를 stack에 save 하여 보존하고, 현 시점의 stack pointer를 base address로 지정하여 새로운 stack frame을 시작하도록 하는 역할을 한다. 그러므로, 그 앞서서 있는 mov edi, edi는 아무런 의미가 없다. 즉, nop와 동일하다. 다른 점이라면, 2bytes opcode로써의 nop라는 점이다. 이는 나중에 jmp code로 변경하여 Hot patching에 이용되도록 집어 넣어둔 code이다. 이와 같은 방식은 CLR에서 method jitting의 경우도 방법자체로 보면 유사하다. MethodDesc 는 RVA라는 거의 의미 없는 값을 가지고 있다가 나중에 JIT에 의해서 code가 generation된 이후에는 jmp instruction으로 해당 code address를 가리키도록 op code가 변경되는 구조를 갖는 다.
그 다음은 ecx/esi 에 대한 부분이다. ecx는 일반적으로 loop의 counter역할을 담당한다. 하지만, Debugging 할때 가장 많이 경험하는 것은 counter의 역할보다는 this pointer를 넘기는 데, 많이 사용된다.
ChildEBP RetAddr Args to Child
00125bfc 76698bd7 00004000 00125c74 00000020 wininet!MEMMAP_FILE::GrowMapFile+0xb0 (FPO: [1,0,0])
00125c38 7667ff81 00001000 00000031 00000000 wininet!MEMMAP_FILE::AllocateEntry+0x4d (FPO: [Non-Fpo])
0:000> ub 76698bd7
. . .
76698bcf 50 push eax
76698bd0 8bcb mov ecx,ebx <- - - this pointer wininet!MEMMAP_FILE Class
76698bd2 e8c0330400 call wininet!MEMMAP_FILE::GrowMapFile (766dbf97)
This pointer는 곧바로 esi register로 mov 된다. ecx register는 Frame 안에서 counter와 같은 범용레지스터로 사용되기 때문에 this pointer보존을 위해 esi로 mov 하고, esi+offset 형태로 this class의 member function이나 member property에 접근할 수 있다. 아래와 같은 assembly code 형태가 말해준다.
766dbf9c 56 push esi
766dbf9d 8bf1 mov esi,ecx
766dbf9f 8b4604 mov eax,dword ptr [esi+4]
766dbfa2 8b4e18 mov ecx,dword ptr [esi+18h]
그리고, 또한 eax register 의 역할이다. 아래 assembly code를 보면, function이 call되기 전에 push에 의해서 parameter를 전송하는 아름다운 모습이 보인다. 이는 Function의 calling convention에 의존적이다. 그리고, 나면, 저 아래께 쯤에 cmp instruction에 뜬금없는 eax와 ecx의 비교가 보인다. ecx는 esi+offset의 언급한 this class의 member property로 부터 온것임을 알 수 있다. 그럼, eax는 무엇인가 .
766dbfc2 6a02 push 2
766dbfc4 6a00 push 0
766dbfc6 57 push edi
766dbfc7 ff761c push dword ptr [esi+1Ch]
766dbfca ff1570116676 call dword ptr [wininet!_imp__SetFilePointer (76661170)]
766dbfd0 8b4e18 mov ecx,dword ptr [esi+18h]
766dbfd3 03cf add ecx,edi
766dbfd5 3bc1 cmp eax,ecx
이는 SetFilePointer function의 return value 이다. eax는 항상 function call이후에 return 값을 저장하는 데, 사용된다.
덧글