RtlNumberGenericTableElements
63D6E010 > 8BFF MOV EDI,EDI여기서는 table 의 개수를 알려주는 함수라 짐작하고 분석을 한다. 만약 총개수를 가지고 있는 변수가 있다면, 아마 root node 가 그 변수를 가지고 있을 것이다.
63D6E012 /. 55 PUSH EBP
63D6E013 |. 8BEC MOV EBP,ESP
63D6E015 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
63D6E018 |. 8B40 14 MOV EAX,DWORD PTR DS:[EAX+14]
63D6E01B |. 5D POP EBP
63D6E01C \. C2 0400 RETN 4
그래서 여기서는 첫번째 parameter 가 root node 일 것이라고 가정해 보는 것이다.
여기를 통해 param1 번째 녀석(ebp+8) 의 offset +14 이 GenericTable 의 element 의 개수 를 가리키는 것을 알았다.
RtlIsGenericTableEmpty
63D6DDAD > 8BFF MOV EDI,EDI
63D6DDAF /. 55 PUSH EBP
63D6DDB0 |. 8BEC MOV EBP,ESP
63D6DDB2 |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
63D6DDB5 |. 33C0 XOR EAX,EAX
63D6DDB7 |. 3901 CMP DWORD PTR DS:[ECX],EAX
63D6DDB9 |. 0F94C0 SETE AL
63D6DDBC |. 5D POP EBP
63D6DDBD \. C2 0400 RETN 4
cmp x, y ; x 와 y 가 같은 경우, ZF = 1 로 set 된다.[2]결국 아래와 같은 code 가 된다.
sete al ; Set byte to '1' if equal (ZF=1)
cmp dword ptr ds:[ecx],eax결국 param1 이 0 이면 AL 이 '1' 이 된다. 즉, '1'(true) 을 return 해준다.
sete al
; c- code
if (*ecx == eax)
set al
참고로, AL 은 EAX 의 lower part 를 이야기 한다. EAX 는 return value 를 저장하는 데에도 쓰인다. [3]
여기서 param1 이 '0' 이면 table 이 empty 를 뜻한다는 것을 알았다. 주의할 점은 [ECX]를 가지고 비교한 것이다. 그렇기 때문에 이건 null check 가 아니라 param 1의 member1 가 pointer 일 수도 있다는 뜻일 것이다.
RtlGetElementGenericTable
63D1C124 > 8BFF MOV EDI,EDI
63D1C126 . 55 PUSH EBP
63D1C127 . 8BEC MOV EBP,ESP
63D1C129 . 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8] ; ecx = root of structure A
63D1C12C . 8B51 14 MOV EDX,DWORD PTR DS:[ECX+14] ; edx = offset +14 from A
63D1C12F . 8B41 0C MOV EAX,DWORD PTR DS:[ECX+C] ; eax = offset +C from A
63D1C132 . 53 PUSH EBX
63D1C133 . 56 PUSH ESI
63D1C134 . 8B71 10 MOV ESI,DWORD PTR DS:[ECX+10] ; esi = offset +10 from A
63D1C137 . 57 PUSH EDI
63D1C138 . 8B7D 0C MOV EDI,DWORD PTR SS:[EBP+C] ; edi = 2nd param
63D1C13B . 8D5F 01 LEA EBX,DWORD PTR DS:[EDI+1] ; ebx = edi + 1
63D1C13E . 895D 08 MOV DWORD PTR SS:[EBP+8],EBX ; 1st param = ebx
63D1C141 . 83FF FF CMP EDI,-1 ; if( edi == -1 )
63D1C144 . 74 35 JE SHORT ntdll_1.63D1C17B
63D1C146 . 3BDA CMP EBX,EDX ; if ( ebx > member6 )
63D1C148 . 77 31 JA SHORT ntdll_1.63D1C17B ; jump above
63D1C14A . 3BF3 CMP ESI,EBX ; if( offset_10_from_A == ebx )
63D1C14C . 74 23 JE SHORT ntdll_1.63D1C171
63D1C14E . 0F87 542B0800 JA ntdll_1.63D9ECA8
63D1C154 . 8BFB MOV EDI,EBX ; edi = ebx
63D1C156 . 2BD3 SUB EDX,EBX ; edx - ebx
63D1C158 . 2BFE SUB EDI,ESI
63D1C15A . 42 INC EDX
63D1C15B . 3BFA CMP EDI,EDX
63D1C15D . 77 20 JA SHORT ntdll_1.63D1C17F
63D1C15F . 85FF TEST EDI,EDI
63D1C161 . 74 05 JE SHORT ntdll_1.63D1C168
63D1C163 > 4F DEC EDI
63D1C164 . 8B00 MOV EAX,DWORD PTR DS:[EAX]
63D1C166 .^75 FB JNZ SHORT ntdll_1.63D1C163
63D1C168 > 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8]
63D1C16B . 8941 0C MOV DWORD PTR DS:[ECX+C],EAX
63D1C16E . 8951 10 MOV DWORD PTR DS:[ECX+10],EDX
63D1C171 > 83C0 0C ADD EAX,0C ; eax = eax + 0xC
63D1C174 > 5F POP EDI
63D1C175 . 5E POP ESI
63D1C176 . 5B POP EBX
63D1C177 . 5D POP EBP
63D1C178 . C2 0800 RETN 8
63D1C17B > 33C0 XOR EAX,EAX
63D1C17D .^EB F5 JMP SHORT ntdll_1.63D1C174
ecx(= ebp+8) | param 1 | offset + 0 | member 1 | pointer |
ecx+4 | offset + 4 | member 2 | pointer | |
offset + 8 | member 3 | pointer | ||
offset + c | member 4 | pointer | ||
offset + 10 | member 5 | |||
offset + 14 | member 6 | element 총개수 | ||
offset + 18 | member 7 | |||
offset + 1c | member 8 | |||
offset + 20 | member 9 | |||
offset + 24 | member 10 |
eax | member 4 | pointer |
esi | member 5 | |
edx | member 6 | element 총개수 |
edi(=ebp+c) | param 2 | index of element to get |
ebx | param 2 +1 |
우리는 RtlNumberGenericTableElement 에서 root node 가 "첫번째 parameter" 로 넘어오는 것을 확인했다. 보통 api 는 이런 parameter ordering 을 잘 지킨다. 그러므로 여기서 첫번째로 넘어오는 parameter 도 root 라고 가정해 보는 것도 틀린 이야기는 아닐 것이다.
JA (jump if above)를 사용한 것으로 보아 EBX, EDX 가 unsigned 로 사용되었다고 알 수 있다. 보통, signed 로 사용된 경우에는 JG 를 사용한다.
mov edi,dword ptr ss:[ebp+c] ; edi = 2nd param위의 두 jump instructions 이 ntdll_1.63d1c17b 로 jump 하는 것으로 보아, 같은 조건 문 안에 묶여 있는 것을 알 수 있다.
lea ebx,dword ptr ds:[edi+1] ; ebx = edi + 1 …
cmp edi,-1 ; if( edi == -1 )
je short ntdll_1.63d1c17b
cmp ebx,edx ; if ( ebx > member6 )
ja short ntdll_1.63d1c17b ; jump above
…
XOR EAX,EAX ; ntdll_1.63D1C17B
; c-code
if( idx == -1 || idx+1 > elementCount)
return 0;
63d1c17b XOR EAX,EAXeax 를 '0' 으로 만들기 때문에 예외(error 또는 exception) 일 가능성이 있다.
member6 은 위에서 우리가 element 의 개수라는 것을 알았다. 그러면 edi, ebx 가 element 의 개수와 비교하는 것으로 봐서 edi 로 들어온 2nd parameter 가 index 일 것이라고 유추할 수 있다.
; c-code그런데, 여기서 idx+1 부분은 ebx 에 저장되어서 함수내의 여러 instruction 에서 사용되어 진다. 그래서 우리는 이 녀석이 local variable 에 할당되어서 사용되었다고 가정할 수 있다. 또는 여러 개의 idx+1 를 이용한 연산이 존재하는데 그것을 compiler 가 최적화를 시켰을 수도 있다.
if( idx == -1 || idx+1 > elementCount)
return 0;
; c-codeesi 와 ebx 를 비교하는데, ebx 에는 index 와 관련된 값이 들어 있다. 그러므로 esi, 다시 말하면, offset +10 의 값도 index 와 관련된 값이라고 추측해 볼 수 있다.
nextIdx = idx+1
if( idx == -1 || nextIdx > elementCount)
return 0;
eax | member 4 | pointer |
esi | member 5 | index 와 관련된 어떤 값 |
edx | member 6 | element 총개수 |
edi(=ebp+c) | param 2 | index of element to get |
ebx | param 2 +1 |
cmp esi,ebx ; if( offset_10_from_a == ebx )여기서 return 값을 기존의 eax 값에 12 를 더한 값(offset + C)을 전해준다는 것을 알 수 있다. 우리는 앞에서 GenericTable 의 data structure 의 root 에서 offset+C 가 GenericTable 의 pointer 라는 것을 알고 있다. 그리고 이 pointer 에서 12(0xC) 를 더한 값은 이 pointer 들을 지나 바로 data 에 접근할 수 있게 해 주는 code 인 것이다.
je short ntdll_1.63D1C171
ja ntdll_1.63d9eca8
mov edi,ebx ; edi = ebx // 이 부분은 if(offset_10_from_a < ebx) 일 때
sub edx,ebx ; edx - ebx
…
; from 63D1C171
add eax,0c ; eax += 0xC
pop edi
pop esi
pop ebx
pop ebp
retn 8
63D1C163 > 4F DEC EDI
63D1C164 . 8B00 MOV EAX,DWORD PTR DS:[EAX]
63D1C166 .^75 FB JNZ SHORT ntdll_1.63D1C163
이 부분은 loop 이다.
JNZ 는 ZF 가 not '0' 이면 계속 63D1C163 으로 jump한다. ZF 는 연산결과가 0 이면 set 된다.[ref. 4] 그러므로 위의 loop 은 EDI 가 '0' 이 될 때까지 loop 을 돌게 된다.
References
- http://faydoc.tripod.com/cpu/sete.htm
- Appendix A, Reversing: Secrets of Reverse Engineering
- http://i5on9i.tistory.com/649
- http://wiki.peppercode.net/wiki/published/x86+Flags+Register
댓글 없음:
댓글 쓰기