2015. 3. 9. 13:26ㆍSecurity ★ Development/Reversing
컴파일러가 하이 레벨 언어를 로우 레벨 언어로 바꿀 때 파이프라인을 사용함에 따르는 분기에 의한 손실을 최소화 하기 위해 무분기 로직을 만들어냅니다.
파이프 라인의 끝에 다음 명령을 채울 때 분기가 있을 경우 어떤 분기된 명령들 중 하나를 쓸 수 밖에 없는데 이 때 예측이 틀리면 파이프라인을 비우고 새로 명령을 채워야 합니다. 그렇기 때문에 컴파일러는 분기가 있는 하이 레벨 언어를 분기가 없는 로우 레벨 언어로 바꾸는 작업을 합니다.
- 산술 구현
mov eax, [ebp-10]
and eax, 0x00001000
neg eax
sbb eax, eax
neg eax
ret
위 작업은 분기문을 사용하지 않는데 어셈블리에 익숙하지 않다면 무슨 작업을 하는지 파악하기 어렵습니다. eax와 0x00001000의 값을 and하고 neg를 하고 있습니다. neg는 부호를 바꿔주는 명령으로 va=-va; 와 같은 연산입니다.
sbb는 va-(va+CF);라는 의미인데 CF는 생소한 분들은 여기선 연산결과에 따른 어떤 상태플래그 정도로 알고 계시면 됩니다. 즉 여기서는 neg 연산의 결과 인자값이 0이면 CF는 0으로 설정되고 0이 아니면 1로 설정됩니다. sbb연산은 eax=eax-(eax+CF)가 되는데 결국 eax는 CF에 의해 결정됩니다.
다시 neg연산을 하고 있는데 eax가 0(CF가 0일 경우)이면 0 그대로이고, -1이면(CF가 1일 경우) 1로 만들어 줍니다.
if(va&0x00001000)
return true;
else
return false;
와 같은 작업입니다.
하이레벨 언어의 분기문을 로우 레벨에선 분기명령을 사용하지 않았다는 것을 알 수 있습니다.
call SomeFunc
sub eax, 4
neg eax
sbb eax, eax
and eax, -52
add eax, 54
ret
SomeFunc의 반환값에 4를 빼고 neg와 sbb연산을 하고 있습니다.
4를 뺀 값이 0이면 sbb 연 산후 eax는 0이고 0이 아니면 -1이 됩니다.
0인 경우에는 -52 and 연산을 해도 0이고 여기에 54를 더해 반환합니다. -1인 경우는 0xffffffff이므로 and와 add연산을 거치면 2가 나오게 됩니다. 즉
if(SomeFunc()==4)
return 54;
else
return 2;
라는 분기문을 분기 명령을 쓰지 않도록 바꾼 결과입니다.
- 조건 실행
분기문을 순수하게 산술로 구현하는 것은 제한이 있습니다. 복작한 분기 로직을 위해 프로세서에서는 무분기 로직을 가능하게 해주는 몇개의 명령을 제공해줍니다.
return (result!=FALSE);
라는 하이 레벨 언어가 있을 경우 분기 로직을 포함한 로우 레벨 언어는 다음과 같습니다.
cmp [result], 0
jne NotEquals
mov eax, 0
ret
NotEquals:
mov eax, 1
ret
이를 IA-32 프로세서가 제공하는 setne 명령을 이용해 무분기 로직으로 바꿀 수 있습니다.
xor eax, eax
cmp [result], 0
setne eax
ret
setne 명령은 ZF플래그에 따라 값을 설정합니다. ZF 플래그는 cmp 결과에 따라 둘이 같은 경우에 1로 설정되고 같지 않으면 0으로 설정됩니다. 즉, setne에 의해 ~ZF의 값이 설정되는데 result가 0과 같지 않으면 eax는 1이 설정되고 같으면 0이 설정되게 됩니다.
mov ecx, 2000
cmp edx, 0
mov eax, 1000
cmove eax, ecx
ret
cmove 명령은 edx 레지스터의 값에 따라 작동합니다. edx 레지스터의 값이 0이면 ecx의 값을 eax에 넣고 0이 아니면 그대로 놔둡니다.
if(va==0) //edx의 값이 0이면
return 2000; //ecx의 값을 넣고 리턴
else
return 1000; //아니면 그대로
'Security ★ Development > Reversing' 카테고리의 다른 글
리버스엔지니어링 문제 1 (0) | 2015.03.22 |
---|---|
보안 취약점 (0) | 2015.03.16 |
api 리버싱 3 - RtlGetElementGenericTable (0) | 2015.03.07 |
api 리버싱 2 - RtlNumberGenericTableElements, RtlIsGenericTableEmpty (0) | 2015.01.27 |
api 리버싱 1 - RtInitializeGenericTable (0) | 2015.01.26 |