[컴][Hack][디버그] disassembly code 분석 방법 - 2



RtlNumberGenericTableElements

63D6E010 >   8BFF           MOV EDI,EDI
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
여기서는 table 의 개수를 알려주는 함수라 짐작하고 분석을 한다. 만약 총개수를 가지고 있는 변수가 있다면, 아마 root node 가 그 변수를 가지고 있을 것이다.

그래서 여기서는 첫번째 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]
sete al ; Set byte to '1' if equal (ZF=1)
결국 아래와 같은 code 가 된다.
cmp dword ptr ds:[ecx],eax
sete al
; c- code
if (*ecx == eax)
  set al
결국 param1 이 0 이면 AL 이 '1' 이 된다. 즉, '1'(true) 을 return 해준다.
참고로, 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 1offset + 0member 1pointer
ecx+4offset + 4member 2pointer
offset + 8member 3pointer
offset + cmember 4pointer
offset + 10member 5
offset + 14member 6element 총개수
offset + 18member 7
offset + 1cmember 8
offset + 20member 9
offset + 24member 10


eaxmember 4pointer
esimember 5
edxmember 6element 총개수
edi(=ebp+c)param 2index of element to get
ebxparam 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
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;
위의 두 jump instructions 이 ntdll_1.63d1c17b 로 jump 하는 것으로 보아, 같은 조건 문 안에 묶여 있는 것을 알 수 있다.
63d1c17b XOR EAX,EAX
eax 를 '0' 으로 만들기 때문에 예외(error 또는 exception) 일 가능성이 있다.
member6 은 위에서 우리가 element 의 개수라는 것을 알았다. 그러면 edi, ebx 가 element 의 개수와 비교하는 것으로 봐서 edi 로 들어온 2nd parameter 가 index 일 것이라고 유추할 수 있다.
; c-code
if( idx == -1 || idx+1 > elementCount)
  return 0;
그런데, 여기서 idx+1 부분은 ebx 에 저장되어서 함수내의 여러 instruction 에서 사용되어 진다. 그래서 우리는 이 녀석이 local variable 에 할당되어서 사용되었다고 가정할 수 있다. 또는 여러 개의 idx+1 를 이용한 연산이 존재하는데 그것을 compiler 가 최적화를 시켰을 수도 있다.
; c-code
nextIdx = idx+1
if( idx == -1 || nextIdx > elementCount)
  return 0;
esi 와 ebx 를 비교하는데, ebx 에는 index 와 관련된 값이 들어 있다. 그러므로 esi, 다시 말하면, offset +10 의 값도 index 와 관련된 값이라고 추측해 볼 수 있다.


eaxmember 4pointer
esimember 5index 와 관련된 어떤 값
edxmember 6element 총개수
edi(=ebp+c)param 2index of element to get
ebxparam 2 +1


cmp esi,ebx ; if( offset_10_from_a == ebx )
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
여기서 return 값을 기존의 eax 값에 12 를 더한 값(offset + C)을 전해준다는 것을 알 수 있다. 우리는 앞에서 GenericTable 의 data structure 의 root 에서 offset+C 가 GenericTable 의 pointer 라는 것을 알고 있다. 그리고 이 pointer 에서 12(0xC) 를 더한 값은 이 pointer 들을 지나 바로 data 에 접근할 수 있게 해 주는 code 인 것이다.


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

  1. http://faydoc.tripod.com/cpu/sete.htm
  2. Appendix A, Reversing: Secrets of Reverse Engineering
  3. http://i5on9i.tistory.com/649
  4. http://wiki.peppercode.net/wiki/published/x86+Flags+Register

댓글 없음:

댓글 쓰기