1월, 2019의 게시물 표시

리버싱 공부
스택 프레임 기법

이미지
스택을 다루는데 있어서 ESP 레지스터, 현재 스택을 가리켜 주는 ESP레지스터를 사용하는데 함수를 만들고 사용할 때 함수를 들어가서 ESP 레지스터를 막 사용하다 보면 원래 돌아가야 할 위치를 알고 있더라도 함수의 시작 위치를 알 수 없게 되고 사용이 굉장히 복잡해진다. 그걸 방지?하기 위해 스택 프레임 기법이라는 방식이 도입되었다고 하는데. 함수를 들어갔을 때의 스택의 위치를 EBP레지스터, 베이직 포인터 레지스터가 가지고 있고, 함수에서 스택을 다룰 때 ESP를 사용 후 나중에 EBP에서 함수의 첫 주소를 가지고 있게 하며 안전하게 리턴할 수 있게 한다. 리버싱 핵심원리에 있는 예제와 비슷하게 코딩해봤다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include   < stdio.h > #include   < stdio.h > int  add( int  num1,  int  num2); int  main() {      int  a, b;     a  =   10 ;     b  =   20 ;      int  c;     c  =  add(a, b);      printf ( "%d\n" , c); } int  add( int  num1,  int  num2) {      int  number1;      int  number2;     number1  =  num1;     number2  =  num2;      return  (number1  +  number2); } cs 함수를 하나 만들어서 메인에서 호출하는 간단한 예제이다. 이걸 올리 디버거에서 보면, 메인이 시작되는 부분이다.

리버싱 공부
레지스터

레지스터라는 cpu에 있는 저장 공간을 이용해 데이터를 저장, 복사, 연산 등을 하는데 범용 레지스터들 EAX EBX ECX EDX AX, AL, AH BX, BL, BH CX, CL, CH CX, DL, DH 가 있고 EAX는 가장 자주 쓰게 되고 함수의 리턴 값이 EAX에 들어가기도, 함수의 인자로 사용하기도 함 ECX는 주로 반복문 등에 쓰여서 반복문의 카운터로 사용 EBX, EDX는 그냥 막 쓰이고 윈도우 API 등에서는 ECX, EDX를 자주 사용한다고 한다(나뭇잎 책에서 발췌) E(Extended)가 빠진 ~X레지스터는 16비트 레지스터 크기 ~L, ~H레지스터는 ~X 레지스터의 각각 하위, 상위 8비트로 나뉘어진 레지스터 크기 그 다음 플래그 레지스터 중 리버싱 핵심 원리에서 알아둬야 한다고 했던 기본적인 3가지 ZF(Zero Flag) OF(Overflow Flag) CF(Carry Flag) 제로 플래그는 어떤 연산 결과가 0이 되면 1이 되는 레지스터 오버플로 플래그는 오버플로우가 일어나게 되면 1이 되는 레지스터같고 캐리 플래그는 연산 결과가 자리수가 넘어가는 일이 발생할 때(2진수 계산에서 캐리가 일어날 때) 1이 되는 것 같다. 그리고 중요한 레지스터 두가지 ESP EBP ESP는 스택 포인터(현 스택의 top 위치) EBP는 베이직 포인터(현 스택의 처음 위치) 그 다음으로 다음 실행할 명령어의 주소가 담긴 포인터 레지스터인 EIP 레지스터 가 있다.

리버싱 연습
점프에 관하여

이미지
리버싱 핵심원리 책을 읽으면서 레지스터에 관한 곳을 읽다가 보니 전에 리버싱 하던 게 생각났다. 반복문을 사용하는 곳에서 비교해서 점프하는 문에 관해서 의문점이 갔었는데,(잘 몰랐기 때문에) jz나 jnz처럼 비교해서 점프하는 어셈블리어는 그 앞에 cmp로 비교하는 어셈블리가 나와야 나오는 줄 알았지만 점프문 전에 연산이 있을 때 무슨 연산이건 그 연산의 결과가(레지스터) 0일 때 바뀌는 ZF(Zero Flag)값을 비교해서 jnz 명령어가 실행되는 거였다. EDI에 SUB명령어로 1을 감산한 연산이 있은 후에 JNZ에서 ZF를 검사해서 실행되고, 오른쪽에 올리디버거가 보여주는 ZF가 1일 때(EDI에 연산을 해서 EDI가 0일 때) 실행되지 않아서 그 다음줄로 넘어간다. 해석하자면 EDI의 연산 결과가 0이면 ZF가 1이 되고, ZF가 1이 아닐 때(Jmp Not Zero) 주소로 점프해라. 참고로 cmp는 A에서 B를 뺐을 때 음수이면 CF(Carry Flag)가 1, 0일경우 ZF가 1이 된다고 한다.

리버싱 연습2
NULL 패딩에 패치

이미지
이전에 했던 실습 이후에 원래 문자열 위치 말고 NULL패딩 위치나 빈 위치에 문자열을 넣어서 그 주소를 push 해서 문자열을 패치해 보았다.  아무 곳에나 Hex 창에서 유니코드로 문자열을 넣어주고  문자열을 push 하는 곳에 주소를 바꿔주고 실행하면 패치가 된다.

리버싱 연습2
직접 문자열 패치

이미지
리버싱 핵심원리(부제:나뭇잎책)의 앞부분을 읽으면서 Hello World 프로그램을 문자열 패치 시키는 부분이 있어 실습해봄. 1 2 3 4 5 6 7 #include   < Windows.h > int  WINAPI WinMain(HINSTANCE hinst, HINSTANCE pinst, LPSTR Cmdline,  int  cmdshow) {     MessageBox( NULL , L "Hello World!" , L "Hello" , MB_OK);      return   0 ; } Colored by Color Scripter cs 왜인지 책이랑 똑같이하면 오류나길래 기억나는 대로 WinMain으로 짰다. WinMain을 찾아 가 보는 과정은 스킵 f8로 실행시켜가다가 MessageBox가 뜨는 곳에서 bp걸고 다시 실행해 가면서 따라갔다. 슬슬 stub code가 눈에 익어야 하는데. IMAGE DOS HEADER 등을 인자로 넣고 부른 주소에는 WinMain의 위치를 부르는 코드가 있다.(주소에 라벨을 붙였다) 점프문을 따라가면 딱 보이는 메인 형태이다. 함수 프롤로그가 보이고 MessageBoxW API를 호출하기 위해 인자를 넣고 API를 호출하는 모습이 보인다. 저 문자열의 주소가 직접 보이니 Hex 창에서 따라가서 직접 바꾸는 간단한 실습이다. 성공이다. 문자열 패치.

리버싱 연습

이미지
c++ 클래스를 간단하게 만들었다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include   < iostream > class  Data { public :     Data();     ~Data();      int   &   operator [] ( int  n)     {          return  data;     }      void   operator =  ( int  data)     {          this - > data  =  data;     }      const   int  initialized_num()     {          return  my_init_num;     } public :      static   int  init_num; //initalize-num; private :      int  my_init_num;      int  data; }; int  Data::init_num  =   0 ; //static member data set int  main() {     Data data[ 10 ];           for  ( int  i  =   0 ; i  <   10 ; i + + )     {         data[i]  =  i  +   1 ;     }      for  ( int  i  =   0 ; i  <

scanf와 scanf_s

마이크로소프트 사에서는 자주 사용하는 개발 IDE인 Visual Studio에서 scanf 사용을 권하지 않는다. 하위 버전에서는 경고를 띄우지만 최상위 버전에서는 SDL(security development lifecycle) 검사가 체킹 되어 있어 비주얼 스튜디오에서만 지원하는 scanf_s를 사용하지 않으면 오류가 뜨게 되어있다. scanf_s()는 문법은 scanf()와 다를게 거의 없다. 그러나 문자열을 입력받는 서식문자 %s를 사용할 땐, 저장할 문자열 뒤에 최대 문자열 입력 개수를 인자로 한 개 더 넣어주어야 한다. 1 2 char name[10]; scanf("%s", name); cs 이전 scanf 사용이 이랬었다면, 1 2 3 #define MAX_LEN 10 char name[MAX_LEN]; scanf_s("%s", name, MAX_LEN); cs scanf_s를 사용해야 할 때 이런 식으로 인자를 한 개 더 추가해 주어야 한다. 참고로 비주얼 스튜디오에서 scanf_s를 사용할 때 문자열 입력을 받는 때에 문자열 길이를 지정해 주지 않아도 문법 오류가 뜨지 않는데, 실행했을 때는 오류가 난다. 문법 오류가 나지 않아도 문법 오류이다. 이는 오묘한 문법때문인데,  scanf_s를 이렇게 사용할 때의 케이스가 있기 때문이다. 1 2 3 4 5 #define MAX_LEN 10 char name[MAX_LEN]; int grade; int age; scanf_s("%s %d %d", name, MAX_LEN, &grade, &age); cs 문자열을 입력 받을 때 저장할 문자열 뒤에 최대 문자열 입력 개수를 적어주고 그 뒤에 입력받는 변수를 두면 된다. 이렇게 sca

구글 블로그
프로그래밍 코드
올리기

올리고 싶은 코드를 https://colorscripter.com/ 에 복사 후 세부설정 - 줄간격 100% 세부설정 - html 태그 자체 복사 체크 후에 오른쪽 아래에 있는 클립보드에 복사 그 후에 줄바꿈 오류가 있는 구글 블로그 html 내용 때문에 복사된 html 소스에서 white-space : pre를 white-space : nowrap 으로 바꿔 주어야 글을 포스팅 하고 나서 줄바꿈이 잘 표시된다. 간단하게 복사한 html을 서브라임 등에 복사해서 한번에 바꿔주면 편할 듯. 그 후 구글 블로그 글 작성 시 복사된 html을 입력해 주면 된다. 아래는 테스트 해 본 소스 1 2 3 4 5 6 #include   < stdio.h > int  main() {      printf ( "hello world!\n" ); } cs 한번 글을 올린 상태로 중간 내용을 수정하면 줄바꿈이나 내용이 이상해질 수도 있다는 듯 합니다.

올리 디버거
단축키

올리 디버거 단축키 [ctrl + f2] : 프로그램 재실행 [f7] : step into (한 줄 실행, call문에서 함수 안을 따라감) [f8] : step over (한 줄 실행, call문에서 함수를 실행하고 다음 줄로 넘어감) [f9] : step until stop (break point가 나오기 전 또는 프로그램이 멈추는 분기점까지 실행) [ctrl + f9] : step until return (return이 나올 때까지 실행) [;] : 주석(코멘트) 작성/수정 [:] : 라벨 작성/수정 [Enter] : 점프문 따라가기