MIPS 32bit 환경에서 gdb를 이용해서 분석 해보기.
root@debian-mipsel:/home/user# file hand
hand: ELF 32-bit LSB executable, MIPS, MIPS-II version 1, dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x76cbeddb5054f529eabb50bfdada135f44c0675e, with unknown capability 0xf41 = 0x756e6700, with unknown capability 0x70100 = 0x1040000, not stripped
root@debian-mipsel:/home/user# ./hand
Add : 50
간단한 더하기 프로그램~
0x00400640 <+0>: addiu sp,sp,-48
0x00400644 <+4>: sw ra,44(sp)
0x00400648 <+8>: sw s8,40(sp)
0x0040064c <+12>: sw s0,36(sp)
0x00400650 <+16>: move s8,sp
0x00400654 <+20>: li v0,32
0x00400658 <+24>: sw v0,24(s8)
0x0040065c <+28>: li v0,48
0x00400660 <+32>: sw v0,28(s8)
0x00400664 <+36>: lui v0,0x40
0x00400668 <+40>: addiu s0,v0,2176
0x0040066c <+44>: lw a0,24(s8)
0x00400670 <+48>: lw a1,28(s8)
0x00400674 <+52>: jal 0x4006ac <add>
0x00400678 <+56>: move at,at
0x0040067c <+60>: move a0,s0
0x00400680 <+64>: move a1,v0
0x00400684 <+68>: jal 0x400500 <printf@plt>
0x00400688 <+72>: move at,at
0x0040068c <+76>: move v0,zero
0x00400690 <+80>: move sp,s8
0x00400694 <+84>: lw ra,44(sp)
0x00400698 <+88>: lw s8,40(sp)
0x0040069c <+92>: lw s0,36(sp)
0x004006a0 <+96>: addiu sp,sp,48
0x004006a4 <+100>: jr ra
0x004006a8 <+104>: move at,at
전체 main 함수는 위와 같다.
그림을 그리면서 알아보려고 한다.
먼저 명령어 offset을 보면 명령어들이 4바이트로 정갈하다. RISC 방식인 것을 알 수있다.
0x00400640 <+0>: addiu sp,sp,-48
0x00400644 <+4>: sw ra,44(sp)
0x00400648 <+8>: sw s8,40(sp)
0x0040064c <+12>: sw s0,36(sp)
해당 부분을 보면 함수 프롤로그와 같다.
<+0> 부분을 보면 stack영역을 48 바이트 만큼 할당한다. 0x30 바이트
<+4> 부분에서는 $ra == ret 인데, main 함수를 호출하면서 저장한 ret 값을 넣는다.
<+8> 부분은 ebp 를 설정하는 부분이다. 전 함수의 ebp를 $s8 레지스터가 저장한다.
<+12> 부분은 특정 값인데.. 뭐지 싶다.. [중요한 것일 수도..]
0x00400650 <+16>: move s8,sp
0x00400654 <+20>: li v0,32
0x00400658 <+24>: sw v0,24(s8)
0x0040065c <+28>: li v0,48
0x00400660 <+32>: sw v0,28(s8)
0x00400664 <+36>: lui v0,0x40
<+16> 부분은 아까 확보한 스택 영역을 이제 $s8에 저장한다. 이를 통해서 지역변수에 접근 할 것이다.
<+20> $v0 레지스터에 32 상수 값을 넣는다.
<+24> 지역변수에 접근하는데 sp 에서 +24 된 부분의 4바이트에 $v0 값을 넣는다.
<+28> $v0 레지스터에 48 상수 값을 넣는다.
<+32> 지역 변수 접근, +28 부분, 4바이트 $v0 = 48 값을 넣음
<+36> $v0 = 0x40<<16 이렇게 값을 넣는다.
0x00400668 <+40>: addiu s0,v0,2176
0x0040066c <+44>: lw a0,24(s8)
0x00400670 <+48>: lw a1,28(s8)
0x00400674 <+52>: jal 0x4006ac <add>
<+40> 부분에서는 더하기인데 아까 $v0 = 0x400000 + 2176 한 값을 s0에 넣는다. 아마 Data 영역의 값을 넣은 것 일거다.
한번 찾아가보면...
(gdb) x/s 0x400000+2176
0x400880: "Add : %x\n"
위와 같이 문자열이 넣어져 있다.
<+44 , +48> $a0 와 $a1으로 해서 4까지 인자를 전달 받을 때 사용 한다. 아까 지역 변수에 넣어뒀던 값을 인자로 할당한다.
<+52> 이제 call 하는 부분인데 돌아올 주소[ret]를 $ra에 넣어주고, label로 뛴다.
0x0040066c <+44>: lw a0,24(s8)
0x00400670 <+48>: lw a1,28(s8)
=> 0x00400674 <+52>: jal 0x4006ac <add>
(gdb) info reg $ra
ra: 0x77e4e208
보면 아직 +52 부분이 시작 안된 상태에서의 reg $ra 값이다.
(gdb) si
0x004006ac in add ()
(gdb) info reg $ra
ra: 0x40067c
해당 명령어를 실행 하고 나면, 0x4006ac 부분으로 점프 했고, ret값이 0x0040067c <+60>: move a0,s0 부분으로 바꼈다.
원래 처음 생각은 +56으로 바뀌지 않을까 생각했지만... mov at,at 가 의미 없는 값이여서 그냥 뛰는거 같다.
*추가 : mips의 경우 특징상 link와 관련된 명령어는 속도?를 위해서 pc pc+4 가 같이 수행이 된다.
그래서 side effect를 피하기 위해서 mov at,at 의미 없는 코드를 pc+4에 집어 넣어서 수행된다.
load to delay
Note also that the compiler adds a bunch of instructions who are completely useless to us (like move at, at who is used here as delay slot after a jump instruction).
functino <add>
=> 0x004006ac <+0>: addiu sp,sp,-24
0x004006b0 <+4>: sw s8,20(sp)
0x004006b4 <+8>: move s8,sp
0x004006b8 <+12>: sw a0,24(s8)
0x004006bc <+16>: sw a1,28(s8)
0x004006c0 <+20>: sw zero,8(s8)
0x004006c4 <+24>: lw v1,24(s8)
0x004006c8 <+28>: lw v0,28(s8)
0x004006cc <+32>: addu v0,v1,v0
0x004006d0 <+36>: sw v0,8(s8)
0x004006d4 <+40>: lw v0,8(s8)
0x004006d8 <+44>: move sp,s8
0x004006dc <+48>: lw s8,20(sp)
0x004006e0 <+52>: addiu sp,sp,24
0x004006e4 <+56>: jr ra
0x004006e8 <+60>: move at,at
add 함수이다. stack을 할당하고.. main sp 보관하고, 셋팅한다.
<+12, +16> 부분에서 신기했던게, 지역변수를 안쓰긴 했지만.. 스택 할당한 밖 범위에 값을 저장했다. 전달 받은 인자들을
sfp+24에 arg1, sfp+28에 arg2 이렇게 인자를 저장했다.
아마 지역변수가 아닌걸 사용한다는 것을 알릴려고 하는거..?
<+20> 여기는 지역변수를 0으로 초기화 하는 부분이다
<+24, 28> 부분은 아까 저장한 arg 값을 더하기 위해서 v1, v2에 저장한다.
<+32> 가져온 값을 더한다.
<+36, +40> 그리고 결과 값을 v0에 저장한다.
<+48> main 함수의 sfp를 복구 시킨다.
<+52> 할당 했던 스택을 돌려주고
<+56> ret로 돌아간다.
(gdb) si
0x0040067c in main ()
실행시키고 나면 위와같이 다시 main 함수 아까 저장했단 $ra 값으로 돌아왔다.
=> 0x0040067c <+60>: move a0,s0
0x00400680 <+64>: move a1,v0
0x00400684 <+68>: jal 0x400500 <printf@plt>
이제 보게 되면 다시 함수 호출을 하기위한 인자 셋팅 부분이다.
<+60> 부분은 아까 data 영역의 0x400880: "Add : %x\n" 부분이다.
<+64> 부분은 add 함수의 결과 값이 저장되어 있는 부분이다.
<+68> 그래서 보면 printf("Add : $x\n", $v0);가 된다.
0x0040068c <+76>: move v0,zero
0x00400690 <+80>: move sp,s8
0x00400694 <+84>: lw ra,44(sp)
0x00400698 <+88>: lw s8,40(sp)
0x0040069c <+92>: lw s0,36(sp)
0x004006a0 <+96>: addiu sp,sp,48
0x004006a4 <+100>: jr ra
printf 이후에는 함수의 프롤로그 부분이다. 스택에 저장되어 있던 값을 복귀 시키고 할당을 풀고, 여기서 보면 <+92>: lw s0,36(sp) 은
왜 저장하고 복귀 시키는지 잘 모르겠다...
추후에 공격을 하면서 찾으면 다시 정리해야겠다.
#include "stdio.h"
int add(int arg, int arg2);
int main()
{
int a = 0x20;
int b = 0x30;
printf("Add : %x\n",add(a,b));
return 0;
}
int add ( int arg, int arg2 )
{
int result = 0;
result = arg + arg2;
return result;
}
'reversing > reversing' 카테고리의 다른 글
inline function hooking (0) | 2017.05.12 |
---|---|
[Project] 디버거 만들기 [Python] (0) | 2017.03.30 |
MIPS Instruction[Assembly] (0) | 2017.03.29 |
ARM Handray (0) | 2016.12.06 |
C++ 리버싱 연습 (0) | 2016.11.29 |