파이썬 9강 클래스, 네트워크 보안 운영 - 3 (교육 77일차)

SW·2023년 3월 15일
0








































-- class1.py --
# 1. 클래스 정의
class Student:
    age = 100
    def sayHello():
        print('Hello')
    def printName():
        print('홍길동')

# 2. 클래스 사용
Student.sayHello()  # Hello
Student.printName() # 홍길동
print(Student.age)  # 100
-- class1.py --

-- class2.py --
# 1. 클래스 정의
class Student:
    age = 25

    def printAge(self):
        print(f'나이: {self.age}')

# 2. 객체(인스턴스) 생성
s1 = Student()

# 3. 객체(인스턴스) 사용
s1.printAge()
-- class2.py --

-- class3.py --
# 1. 클래스 정의
class Student:
    age = 25

    def printAge(self):
        print(f'나이: {self.age}')
        print(id(self))  # 주소 출력

# 2. 객체(인스턴스) 생성
s1 = Student()

# 3. 객체(인스턴스) 사용
s1.printAge()
print(id(s1))  # 주소 출력
print('프로그램 종료')
-- class3.py --

-- class4.py --
# 1. 클래스 정의
class Student:

    def setAge(self, age):
        self.age = age  # 인스턴스에 저장
        
    def printAge(self):
        print(f'나이: {self.age}')  # 인스턴스에 저장된 값 출력


# 2. 객체(인스턴스) 생성
s1 = Student()
s2 = Student()

# 3. 객체(인스턴스) 사용
s1.setAge(10)
s1.printAge()  # 10

s2.setAge(20)
s2.printAge()  # 20

print('프로그램 종료')
-- class4.py --

-- class5.py --
# 1. 클래스 정의
class Student:

    def __init__(self):
        print("생성자 실행!!!")

# 2. 객체(인스턴스) 생성
s1 = Student()

print('프로그램 종료')
-- class5.py --

-- class6.py --
# 1. 클래스 정의
class Student:

    def __init__(self,age):
        self.age = age
        print("생성자 실행!!!")

    def printAge(self):
        print(f'나이: {self.age}')  # 인스턴스에 저장된 값 출력

# 2. 객체(인스턴스) 생성
s1 = Student(10)

# 3. 객체(인스턴스) 실행
s1.printAge()

print('프로그램 종료')

-- class6.py --

실습> TV 만들기

TV를 객체로 만들어서 사용한다.

파일명 : tvclass.py
클래스명 : MyTV
속성(변수) : 
- 모델명 : modelName
- 색상 : modelColor
- 채널 : channel   (1 ~ 100)
- 볼륨 : volume    (1 ~ 30)
- 전원상태 : True(On), False(Off)	

메소드(함수) : 
- TV On/Off 한다.     : tvOnOff
- 채널을 UP 시킨다.     : channelUp 
- 채널을 DOWN 시킨다.   : channelDown
- 채널을 직접 변경한다. : channelChange
- 볼륨을 UP 시킨다.     : volumeUp
- 볼륨을 DOWN 시킨다.   : volumeDown
- 현재 볼륨을 가져온다. : getVolume
- 현재 채널을 가져온다. : getChannel

-- tvclass.py --
"""
파일명 : tvclass.py
프로그램 설명 : TV 클래스 만들기
제작자 : 리눅스마스터
버전 : 2023031501

클래스명 : MyTV
속성(인스턴스 변수) : 
- 모델명 : modelName
- 색상 : modelColor
- 채널 : channel   (1 ~ 100)
- 볼륨 : volume    (1 ~ 30)
- 전원상태 : onOff

메소드(함수) : 
- TV On/Off 한다.       : tvOnOff
- 채널을 UP 시킨다.     : channelUp 
- 채널을 DOWN 시킨다.   : channelDown
- 채널을 직접 변경한다. : channelChange
- 볼륨을 UP 시킨다.     : volumeUp
- 볼륨을 DOWN 시킨다.   : volumeDown
- 현재 볼륨을 가져온다. : getVolume
- 현재 채널을 가져온다. : getChannel
"""

import os

# 1. 클래스 정의
class MyTV:
    """MyTV 클래스"""
   
    def __init__(self):
        """생성자"""

        # 인스턴스 변수를 생성한다.
        self.modelName  = "MyTV"      # 모델명
        self.modelColor = "Green"     # 색상
        self.channel    = 1           # 채널 1 ~ 100
        self.volume     = 1           # 볼륨 1 ~ 30
        self.onOff      = False       # On/Off 상태 True : On, False : Off
        self.tvOffMessage = 'TV의 전원이 꺼졌습니다.'
        self.tvOnMessage  = 'TV의 전원이 켜졌습니다.'

    # TV 전원 On/Off 메소드
    def tvOnOff(self):
        """
        TV On/Off 한다.
        매개 변수 : X
        리턴값 : X
        """
        # print('tvOnOff() 메소드 실행')
        self.onOff = not self.onOff  # 전원 상태를 변경한다.
        if self.onOff:  # 전원 On
            print(self.tvOnMessage)
        else:  # 전원 Off
            print(self.tvOffMessage)
    
    # 채널 UP 메소드
    def channelUp(self):
        """
        채널을 UP 시킨다.
        매개 변수 : X
        리턴값 : X
        """
        #print('channelUp() 메소드 실행')

        # TV의 전원이 켜졌는지 체크하는 부분
        if self.onOff:                 # TV의 전원이 켜졌는가 ?
            if self.channel == 100 :   # 채널이 100이면
                self.channel = 1       # 채널을 1로 설정한다.
                print(f'채널을 1로 설정합니다. ({self.channel})')
            else:
                self.channel += 1      # 채널을 증가한다. 
                print(f'채널을 1증가했습니다. ({self.channel})')
        else:
            print(self.tvOffMessage)

    # 채널 DOWN 메소드
    def channelDown(self):
        """
        채널을 DOWN 시킨다.
        매개 변수 : X
        리턴값 : X
        """
        # print('channelDown() 메소드 실행')

        # TV의 전원이 켜졌는지 체크하는 부분
        if self.onOff:                 # TV의 전원이 켜졌는가 ?
            if self.channel == 1 :     # 채널이 1이면
                self.channel = 100     # 채널을 100으로 설정한다.
                print(f'채널을 100로 설정합니다. ({self.channel})')
            else:
                self.channel -= 1      # 채널을 감소한다. 
                print(f'채널을 1감소했습니다. ({self.channel})')
        else:
            print(self.tvOffMessage)

    # 채널 직접 변경 메소드
    def channelChange(self, channel):
        """
        채널을 직접 변경한다.
        매개 변수 : O
        - channel : 변경할 채널 번호
        리턴값 : X
        """
        # print('channelChange() 메소드 실행')
        if self.onOff:                 # TV의 전원이 켜졌는가 ?        
            if channel >= 1 and channel <= 100:  # 채널범위 1 ~ 100
                self.channel = channel    
                print(f'채널을 직접 설정했습니다. ({self.channel})')
            else:
                print('채널은 1 ~ 100까지 입력해야 합니다.')
        else:
                print(self.tvOffMessage)

    # 볼륨 UP 메소드
    def volumeUp(self):
        """
        볼륨을 UP 시킨다.
        매개 변수 : X
        리턴값 : X
        볼륨의 범위 : 1 ~ 30   
        30까지 갔는데 volumeUp() 메소드를 호출하면 더이상 올라가지 않고 
        현재 최대 볼륨입니다. 라는 메세지를 출력한다.
        """        
        # print('volumeUp() 메소드 실행')

        if self.onOff:                 # TV의 전원이 켜졌는가 ?        
            if self.volume == 30:      # 볼륨이 30이면
                print(f'현재 최대 볼륨입니다. ({self.volume})')
            else:                      # 볼륨이 30보다 작으면
                self.volume += 1
                print(f'볼륨을 1증가했습니다. ({self.volume})')
        else:
            print(self.tvOffMessage)

    # 볼륨 DOWN 메소드
    def volumeDown(self):     
        """
        볼륨을 DOWN 시킨다.
        매개 변수 : X
        리턴값 : X
        볼륨의 범위 : 1 ~ 30   
        1까지 갔는데 volumeDown() 메소드를 호출하면 더이상 내려가지 않고 
        현재 최소 볼륨입니다. 라는 메세지를 출력한다.
        """
        # TV의 전원이 켜졌는지 ???
        if self.onOff:
            if self.volume == 1:      # 볼륨이 1이면
                print(f'현재 최소 볼륨입니다. ({self.volume})')
            else:                      # 볼륨이 1보다 크면
                self.volume -= 1
                print(f'볼륨을 1감소했습니다. ({self.volume})')
        else:
            print(self.tvOffMessage)      

    # 채널을 가져오는 메소드
    def getChannel(self):
        """
        현재 채널을 가져온다.
        매개 변수 : X
        리턴값 : X
        """        
        # print('getChannel() 메소드 실행')                

        if self.onOff:                 # TV의 전원이 켜졌는가 ?
            print(f'현재 채널 : {self.channel}')
        else:
            print(self.tvOffMessage)

    # 볼륨을 가져오는 메소드
    def getVolume(self):
        """
        현재 볼륨을 가져온다.
        매개 변수 : X
        리턴값 : X
        """        
        # print('getVolume() 메소드 실행')   
        if self.onOff:                 # TV의 전원이 켜졌는가 ?
            print(f'현재 볼륨 : {self.volume}')
        else:
            print(self.tvOffMessage)         


    # TV 메뉴 메소드
    def tvmenu(self):
        """
        TV 프로그램 메뉴
        매개 변수 : X
        리턴값 : O
        - menu : 메뉴가 저장된 문자열
        """

        menu  = f'>>> TV 프로그램 ({self.modelName}) <<<\n'
        menu += f'전원: { "On" if self.onOff == True else "Off" }  '
        menu += f'채널: {self.channel}  '
        menu += f'볼륨: {self.volume}\n'
        menu += '1. TV On/Off\n'
        menu += '2. 채널 증가 ▲\n'
        menu += '3. 채널 감소 ▼\n'
        menu += '4. 채널 직접 변경\n'
        menu += '5. 볼륨 증가 △\n'
        menu += '6. 볼륨 감소 ▽\n'
        menu += '7. 현재 볼륨 확인 ★\n'
        menu += '8. 현재 채널 확인 ☆\n'
        menu += '9. 프로그램 정보\n'
        menu += 'q. 프로그램 종료\n'
    
        return menu

    # 프로그램 정보    
    def tvAbout(self):
        """프로그램 정보"""
        print(">>> TV 프로그램 정보  <<<")

        aboutMsg  = "TV 프로그램\n"
        aboutMsg += "버전 : 0.1\n"
        aboutMsg += "프로그램 제작 : 홍길동(hong@naver.com)\n"

        print(aboutMsg)

    # main 함수
    def main(self):
        """main""" 

        while True:
        
            os.system('cls')  # 화면 지우기

            print(self.tvmenu())    # 메뉴 출력
            x = input('선택 >>> ')  # 메뉴 입력

            # 메뉴 체크
            if   x == '1' :  # TV On/Off
                self.tvOnOff()
            elif x == '2' :  # 채널 증가
                self.channelUp()    
            elif x == '3' :  # 채널 감소
                self.channelDown()
            elif x == '4' :  # 채널 직접 변경
                channel = input('변경할 채널 : ')
                channel = int(channel) if channel.isdigit() else ''
                if channel : 
                    if channel >= 1 and channel <= 100:
                        self.channelChange(channel)
                    else:
                        print('채널 번호는 1 ~ 100까지 입니다.')
                else:
                    print('채널 번호를 잘못 입력했습니다.')
            elif x == '5' :  # 볼륨 증가
                self.volumeUp()
            elif x == '6' :  # 볼륨 감소
                self.volumeDown()
            elif x == '7' :  # 현재 볼륨 확인
                self.getVolume()
            elif x == '8' :  # 현재 채널 확인
                self.getChannel()
            elif x == '9' :  # 프로그램 정보
                self.tvAbout()                                                
            elif x == 'q' :  # 프로그램 종료
                print("MyTV 프로그램을 종료합니다...")
                break
            else:            # 그외의 모든 값
                print('1 ~ 9 or q중에서 입력해야 합니다.')

            input()

# 2. 객체(인스턴스) 생성
mytv = MyTV()

# 3. 객체(인스턴스) 실행
mytv.main()
-- tvclass.py --

실습> 함수형 프로그램을 클래스 형태로 변환하기

파일명: 점수관리프로그램.py
클래스명 : Jumsu
인스턴스명 : jumsu

1. class Jumsu 를 생성한다.
2. 모든 함수들을 Jumsu class 안에 넣는다.
3. 모든 메소드(함수) 정의 부분에 self를 추가한다.
4. 각 메소드(함수)에서 변수를 참조하거나 메소드(함수)호출 앞에 self 를 붙인다.
5. 인스턴스 jumsu를 생성한다.
6. jumsu 인스턴스 안에 있는 main() 메소드를 실행한다.

-- 점수관리프로그램.py --
"""
파일명 : 점수관리프로그램.py
프로그램 설명 : 점수 관리 프로그램
작성자 : 리눅스마스터
작성일 : 2023.3.15
"""

import os

def jumsuAdd():
    """점수 추가"""

    global kor, eng, math, total, average

    # 1. 점수 입력
    print("점수를 입력하세요.")
    kor  = input("국어 점수 : ")
    eng  = input("영어 점수 : ")
    math = input("수학 점수 : ")

    # 2. 점수 체크
    kor  = int(kor)  # if kor.isdigit()  else 0
    eng  = int(eng)  # if eng.isdigit()  else 0
    math = int(math) # if math.isdigit() else 0

    # 3. 점수 계산
    total = kor + eng + math  # 총점 
    #total = sum([kor, eng, math])  # 총점  
    average = total / 3  # 평균 

    # 4. 점수 출력
    print("\n===== 점수의 결과 =====\n"
        f"국어 점수 : {kor}\n"
        f"영어 점수 : {eng}\n"
        f"수학 점수 : {math}\n"
        f"총점 : {total}\n"
        f"평균 : {average:.2f}\n"
        "===== 점수의 결과 =====")

    print('엔터키를 누르세요...')
    input()

def jumsuView():
    """점수 확인"""
    
    # 점수 출력
    print("\n===== 점수의 결과 =====\n"
        f"국어 점수 : {kor}\n"
        f"영어 점수 : {eng}\n"
        f"수학 점수 : {math}\n"
        f"총점 : {total}\n"
        f"평균 : {average:.2f}\n"
        "===== 점수의 결과 =====")
        
    print('엔터키를 누르세요...')
    input()

def jumsuMenu():
    """메뉴"""
    menu  = '>>> 점수 관리 프로그램 <<<\n'
    menu += '1. 점수 추가\n'
    menu += '2. 점수 확인\n'
    menu += 'q. 프로그램 종료'
    
    print(menu)

def main():
    """main 함수"""

    while True:

        os.system('cls')  # 화면 지우기

        jumsuMenu()
        x = input('선택 >>> ')  # 메뉴 입력

        # 메뉴 체크
        if   x == '1' :
            jumsuAdd()
        elif x == '2' :
            jumsuView()
        elif x == 'q' : 
            break
        else:
            print('1, 2, q 중에서 입력하세요.')

main()

print('점수 관리 프로그램을 종료합니다.')       
-- 점수관리프로그램.py --

-- 점수관리프로그램.py --
"""
파일명 : 점수관리프로그램.py
프로그램 설명 : 점수 관리 프로그램
작성자 : 리눅스마스터
작성일 : 2023.3.15
"""

import os

class Jumsu:
    def jumsuAdd(self):
        """점수 추가"""

        # 1. 점수 입력
        print("점수를 입력하세요.")
        self.kor  = input("국어 점수 : ")
        self.eng  = input("영어 점수 : ")
        self.math = input("수학 점수 : ")

        # 2. 점수 체크
        self.kor  = int(self.kor)  # if self.kor.isdigit()  else 0
        self.eng  = int(self.eng)  # if self.eng.isdigit()  else 0
        self.math = int(self.math) # if self.math.isdigit() else 0

        # 3. 점수 계산
        self.total = self.kor + self.eng + self.math  # 총점 
        #self.total = sum([self.kor, self.eng, self.math])  # 총점  
        self.average = self.total / 3  # 평균 

        # 4. 점수 출력
        print("\n===== 점수의 결과 =====\n"
            f"국어 점수 : {self.kor}\n"
            f"영어 점수 : {self.eng}\n"
            f"수학 점수 : {self.math}\n"
            f"총점 : {self.total}\n"
            f"평균 : {self.average:.2f}\n"
            "===== 점수의 결과 =====")

        print('엔터키를 누르세요...')
        input()

    def jumsuView(self):
        """점수 확인"""
        
        # 점수 출력
        print("\n===== 점수의 결과 =====\n"
            f"국어 점수 : {self.kor}\n"
            f"영어 점수 : {self.eng}\n"
            f"수학 점수 : {self.math}\n"
            f"총점 : {self.total}\n"
            f"평균 : {self.average:.2f}\n"
            "===== 점수의 결과 =====")
            
        print('엔터키를 누르세요...')
        input()

    def jumsuMenu(self):
        """메뉴"""
        menu  = '>>> 점수 관리 프로그램 <<<\n'
        menu += '1. 점수 추가\n'
        menu += '2. 점수 확인\n'
        menu += 'q. 프로그램 종료'
        
        print(menu)

    def main(self):
        """main 함수"""

        while True:

            os.system('cls')  # 화면 지우기

            self.jumsuMenu()
            x = input('선택 >>> ')  # 메뉴 입력

            # 메뉴 체크
            if   x == '1' :
                self.jumsuAdd()
            elif x == '2' :
                self.jumsuView()
            elif x == 'q' : 
                break
            else:
                print('1, 2, q 중에서 입력하세요.')

jumsu = Jumsu()
jumsu.main()

print('점수 관리 프로그램을 종료합니다.')      
-- 점수관리프로그램.py --

패킷 조작 툴: scapy
패킷 구조를 공부할 수 있는 장점이 있다.
https://scapy.readthedocs.io/en/latest/

실습> 윈도우에서 scapy 설치하기

가상환경으로 사용한다.

가상환경 : 
파이썬을 가상으로 환경을 프로젝트별로 구축해서 사용하는 환경이다.
실무에서 많이 사용하는 것이므로 잘 알아두어야 한다.

가상환경을 이용해서 프로젝트를 생성하는 방법
- bash, CMD/PS(Power Shell) 에서 생성하는 방법
- VScode, Pycharm에서 생성하는 방법

가상환경을 사용하지 않은 환경
Python 3.x
+-----+
|     |
|     |
+-----+

가상환경을 사용한 환경
Python 3.x     복제된 가상환경 Python 3.x
+-----+        +-----+
|     | ---->  |     |
|     |        |     |
+-----+        +-----+
                  :  
                  :(여러 개 만들 수 있다.)



각자 자신의 워크스페이스에서 작업한다.
자신의 워크스페이스는 모두 다를 수 있다.

1. 가상환경 생성
현재 설치된 파이썬을 복제한다.
워크스페이스 폴더: D:\pythonWorkspace (각자 다르다.)
가상환경 폴더: scapyTest
형식: python -m venv <가상환경이름>
D:\pythonWorkspace>python -m venv scapyTest

2. 가상환경 변경
가상환경이 생성되면 가상환경으로 변경한다.
형식: <가상환경이름>\Scripts\activate
D:\pythonWorkspace>cd scapyTest\Scripts
D:\pythonWorkspace\scapyTest\Scripts>dir

복제된 환경으로 사용한다.
D:\pythonWorkspace\scapyTest\Scripts> activate
(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>

VSCode 에서도 가상환경으로 변경한다.
오른쪽 하단에 3.11.2 64bit를 클릭해서 가상환경의 경로를 선택한다.

[3.11.2 64bit] > [3.11.2('scapyTest':venv)

(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>

3. pip 업그레이드
가상환경으로 변경하면 반드시 pip 명령어를 업그레이드해야 한다.
형식 1: python -m pip install --upgrade pip 
형식 2: pip install --upgrade pip
(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>python -m pip install --upgrade pip

4. 패키지 설치
yum 처럼 pip 명령어를 이용해서 파이썬에서 제공되는 패키지를 설치한다.
형식 1: python -m pip install <패키지명>
형식 2: pip install pymysql
패키지가 설치되는 경로: <워크스페이스>\<가상환경이름>\Lib\site-packages

pymysql 패키지를 설치한다.
(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>python -m pip install pymysql

(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>python -m pip list
Package    Version
---------- -------
pip        23.0.1
PyMySQL    1.0.2
setuptools 65.5.0

(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>pip list
Package    Version
---------- -------
pip        23.0.1
PyMySQL    1.0.2
setuptools 65.5.0

패키지가 설치되는 경로: <워크스페이스>\<가상환경이름>\Lib\site-packages
PyMySQL 패키지가 <워크스페이스>\<가상환경이름>\Lib\site-packages에 저장된다.
D:\pythonWorkspace\scapyTest\Lib\site-packages
    <워크스페이스><가상환경명>

5. 가상환경 종료
형식: <워크스페이스>\<가상환경이름>\Scripts deactivate.bat

가상환경을 종료한다.
(scapyTest) D:\pythonWorkspace\scapyTest\Scripts>deactivate.bat
D:\pythonWorkspace\scapyTest\Scripts>python
Python 3.11.2 (tags/v3.11.2:878ead1, Feb  7 2023, 16:38:35) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pymysql
>>> exit()

실습> scapy 설치하기

가상환경 scapyTest를 생성해서 scapy를 설치한다.

1. 패키지 설치
VSCode에서 cmd창을 열어서 작성한다.
(scapyTest) D:\pythonWorkspace>pip install scapy
(scapyTest) D:\pythonWorkspace>pip list
Package    Version
---------- -------
pip        23.0.1
PyMySQL    1.0.2
scapy      2.5.0    <-- 설치된 scapy (<워크스페이스>\<가상환경이름>\Lib\site-packages)
setuptools 65.5.0

2. scapy 실행
(scapyTest) D:\pythonWorkspace>scapy
  :
  :(생략)
>>> a = IP(ttl=10)
>>> a.src
'127.0.0.1'
>>> a
<IP  ttl=10 |>
>>>

실습> Kali Linux에서 scapy 사용하기

[root@kali ~]# scapy 

>>> a = IP()
>>> a
<IP  |>
>>> a = IP(ttl=10)
>>> a
<IP  ttl=10 |>
>>> a.src
'127.0.0.1'
>>> a.dst
'127.0.0.1'
>>> a.src = '200.200.200.4'
>>> a.src
'200.200.200.4'
>>> a.dst = '200.200.200.3'
>>> a
<IP  ttl=10 src=200.200.200.4 dst=200.200.200.3 |>
>>> quit


실습> 리눅스에서 가상환경 사용하기

1. 파이썬 설치
# yum -y install python3

2. 가상환경 프로젝트 생성
가상환경 프로젝트 2개를 생성한다.
venv(Virtual ENvirenment)
가상환경 생성 형식:
리눅스: python3 -m venv 가상환경이름(프로젝트명)
# python3 -m venv scapyTest

3. 가상환경 활성화
첫 번째 가상환경 project1 bin/activate 를 실행해서 가상환경을 활성화 한다.
!!! 여기서는 테스트이므로 root 사용자로 실행하지만 실제로는 일반유저에서 사용해야 한다. !!!
[root@victim3 ~]# . scapyTest/bin/activate
(scapyTest) [root@victim3 ~]# 

가상환경을 해제할 때는 deactivate를 실행한다.
(scapyTest) [root@victim3 ~]# deactivate 


4. pip 업그레이드
가상환경으로 변경되면 반드시 pip 업그레이드를 한번 실행해서 최신버전으로 유지해야 한다.
[root@victim3 ~]# . scapyTest/bin/activate
(scapyTest) [root@victim3 ~]# python -m pip install --upgrade pip

5. 패키지 확인
(scapyTest) [root@victim3 ~]# pip list
Package    Version
---------- -------
php        1.2.1
pip        21.3.1
setuptools 39.2.0

6. 패키지 설치
(scapyTest) [root@victim3 ~]#  pip install pymysql
(scapyTest) [root@victim3 ~]# pip list
Package    Version
---------- -------
php        1.2.1
pip        21.3.1
PyMySQL    1.0.2
setuptools 39.2.0

(scapyTest) [root@victim3 ~]#  pip install scapy

(scapyTest) [root@victim3 ~]# scapy
>>> 


>>> 정리 <<<
가상환경을 일반 유저에서도 사용이 가능하다.
[root@victim3 ~]# yum -y install python3  <-- root 권한이 필요
[root@victim3 ~]# su - user1

나머지는 모두 일반 사용자에서 사용이 가능하다.
[user1@victim3 ~]$ python3 -m venv projectTest
[user1@victim3 ~]$ . projectTest/bin/activate
(scapyTest) [user1@victim3 ~]$ python -m pip install --upgrade pip
(scapyTest) [user1@victim3 ~]$ pip install pymysql
(scapyTest) [user1@victim3 ~]$ pip list
Package    Version
---------- -------
pip        21.3.1
PyMySQL    1.0.2
setuptools 39.2.0
(scapyTest) [user1@victim3 ~]$ deactivate 
[user1@victim3 ~]$ rm -rf projectTest
[user1@victim3 ~]$ exit

scapy 는 패킷을 조작해야 하므로 root권한이 필요하다.
[root@victim3 ~]# python3 -m venv scapyTest
[root@victim3 ~]# . scapyTest/bin/activate
(scapyTest) [root@victim3 ~]# python -m pip install --upgrade pip
(scapyTest) [root@victim3 ~]# python -m pip install scapy
(scapyTest) [root@victim3 ~]# python -m pip list
Package    Version
---------- -------
pip        21.3.1
scapy      2.5.0    <-- /root/scapyTest/lib/python3.6/site-packages/
setuptools 39.2.0

(scapyTest) [root@victim3 ~]# scapy
  :
  :(생략)
>>> 


Ether 헤더를 생성한다.
>>> packet = Ether()
>>> packet.show()
WARNING: Mac address to reach destination not found. Using broadcast.
###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = 00:0c:29:3d:bf:69  <-- 자신의 MAC 주소
  type      = LOOP


ARP 헤더를 생성한다.
>>> packet = ARP()
>>> packet.show()
###[ ARP ]### 
  hwtype    = Ethernet (10Mb)
  ptype     = IPv4
  hwlen     = None
  plen      = None
  op        = who-has
  hwsrc     = 00:0c:29:3d:bf:69
  psrc      = 200.200.200.6
  hwdst     = 00:00:00:00:00:00
  pdst      = 0.0.0.0

IP 헤더를 생성한다.
>>> packet = IP()
>>> packet.show()
###[ IP ]### 
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 64
  proto     = hopopt
  chksum    = None
  src       = 127.0.0.1
  dst       = 127.0.0.1
  \options   \

>>> del packet

ICMP 헤더를 생성한다.
>>> packet = ICMP()
>>> packet.show()
###[ ICMP ]### 
  type      = echo-request
  code      = 0
  chksum    = None
  id        = 0x0
  seq       = 0x0
  unused    = ''


UDP 패킷을 생성한다.
>>> packet = UDP()   <-- s1 = Student()
>>> packet           <-- s1
<UDP  |>
>>> packet.show()    <-- s1.printAge()
###[ UDP ]### 
  sport     = domain
  dport     = domain
  len       = None
  chksum    = None

[Attacker]3000 --------------> 53[DNS]

>>> packet.sport = 3000   
>>> packet.dport = 53
>>> packet.show()
###[ UDP ]### 
  sport     = hbci
  dport     = domain
  len       = None
  chksum    = None

[root@victim3 ~]# grep ^hbci /etc/services 
hbci            3000/tcp                # HBCI
hbci            3000/udp                # HBCI
[root@victim3 ~]# grep ^domain /etc/services 
domain          53/tcp                          # name-domain server

TCP 헤더를 생성한다.
>>> packet = TCP()
>>> packet
<TCP  |>
>>> packet.show()
###[ TCP ]### 
  sport     = ftp_data
  dport     = http
  seq       = 0
  ack       = 0
  dataofs   = None
  reserved  = 0
  flags     = S
  window    = 8192
  chksum    = None
  urgptr    = 0
  options   = ''
>>> del packet




s1 = Student()
s1.setAge(10)  # 10살 세팅
s1.printAge()  # 10살 출력

s1 = Student(10)
s1.printAge()  # 10살 출력
 
>>> packet = IP(src='1.1.1.1',dst='200.200.200.6')
>>> packet.show()
###[ IP ]### 
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 64
  proto     = hopopt
  chksum    = None
  src       = 1.1.1.1
  dst       = 200.200.200.6
  \options   \

참고: https://cafe.naver.com/linuxmasternet/1592

>>> packet = IP(dst='2.2.2.2', ttl=128, src='1.2.3.4')
>>> packet.show()
###[ IP ]### 
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     = 
  frag      = 0
  ttl       = 128
  proto     = hopopt
  chksum    = None
  src       = 1.2.3.4
  dst       = 2.2.2.2
  \options   \

>>> a = Ether(src="11:22:33:44:55:66")
>>> a.show()
WARNING: Mac address to reach destination not found. Using broadcast.
###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = 11:22:33:44:55:66
  type      = LOOP

>>> a.show()
WARNING: Mac address to reach destination not found. Using broadcast.
###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = 00:0c:29:3d:bf:69
  type      = LOOP


-- scapy1.py --
class Ether:
    def __init__(self, 
                 src='00:0c:29:3d:bf:69',
                 dst='ff:ff:ff:ff:ff:ff',
                 type='LOOP'):
        self.src = src
        self.dst = dst
        self.type = type

    def show(self):
        print('###[ Ethernet ]###')
        print(f'dst       = {self.dst}')
        print(f'src       = {self.src}')
        print(f'type      = {self.type}')

a = Ether()
a.show()
-- scapy1.py --

###[ Ethernet ]###
dst       = ff:ff:ff:ff:ff:ff
src       = 00:0c:29:3d:bf:69
type      = LOOP

실습> Ether 클래스 만들기

실제 이렇게 작성되었는지 실제 소스를 봐야하고 가상으로 작성한 것이다.

-- scapy1.py --
class Ether:

    # 생성자
    def __init__(self, 
                 src='00:0c:29:3d:bf:69',
                 dst='ff:ff:ff:ff:ff:ff',
                 type='LOOP'):
        self.src = src
        self.dst = dst
        self.type = type

    def show(self):
        """ 메소드 """
        print('###[ Ethernet ]###')
        print(f'dst       = {self.dst}')
        print(f'src       = {self.src}')
        print(f'type      = {self.type}')

# 1. 객체 생성
a = Ether(src="11:22:33:44:55:66")

# 2. 메소드 호출
a.show()
-- scapy1.py --

###[ Ethernet ]###
dst       = ff:ff:ff:ff:ff:ff
src       = 11:22:33:44:55:66
type      = LOOP

실습> 순서대로 자세히 출력

>>> a = Ether()
>>> ls(a)
>>> b = ARP()
>>> ls(b)
>>> c = IP()
>>> ls(c)
>>> d = ICMP()
>>> ls(d)
>>> e = UDP()
>>> ls(e)
>>> f = TCP()
>>> ls(f)
>>> g = DNS()
>>> ls(g)


>>> packet = Ether()/IP()/TCP()
>>> packet
<Ether  type=IPv4 |<IP  frag=0 proto=tcp |<TCP  |>>>
>>> ls(packet)
dst        : DestMACField                        = 'ff:ff:ff:ff:ff:ff' ('None')
src        : SourceMACField                      = '00:00:00:00:00:00' ('None')
type       : XShortEnumField                     = 2048            ('36864')
--
version    : BitField  (4 bits)                  = 4               ('4')
ihl        : BitField  (4 bits)                  = None            ('None')
tos        : XByteField                          = 0               ('0')
len        : ShortField                          = None            ('None')
id         : ShortField                          = 1               ('1')
flags      : FlagsField                          = <Flag 0 ()>     ('<Flag 0 ()>')
frag       : BitField  (13 bits)                 = 0               ('0')
ttl        : ByteField                           = 64              ('64')
proto      : ByteEnumField                       = 6               ('0')
chksum     : XShortField                         = None            ('None')
src        : SourceIPField                       = '127.0.0.1'     ('None')
dst        : DestIPField                         = '127.0.0.1'     ('None')
options    : PacketListField                     = []              ('[]')
--
sport      : ShortEnumField                      = 20              ('20')
dport      : ShortEnumField                      = 80              ('80')
seq        : IntField                            = 0               ('0')
ack        : IntField                            = 0               ('0')
dataofs    : BitField  (4 bits)                  = None            ('None')
reserved   : BitField  (3 bits)                  = 0               ('0')
flags      : FlagsField                          = <Flag 2 (S)>    ('<Flag 2 (S)>')
window     : ShortField                          = 8192            ('8192')
chksum     : XShortField                         = None            ('None')
urgptr     : ShortField                          = 0               ('0')
options    : TCPOptionsField                     = []              ("b''")

실습> scapy를 이용한 패킷 전송

Attacker(Kali)  : 200.200.200.3 00:0c:29:28:37:34
Victim3(CentOS7): 200.200.200.6 00:0c:29:3d:bf:69

1. 방화벽 중지
[root@victim3 ~]# systemctl stop firewalld
[root@victim3 ~]# iptables -F

2. 웹서버 실행
[root@victim3 ~]# systemctl start httpd

3. 패킷 모니터링
[root@victim3 ~]# tcpdump -nn -i ens33 not port 22 and host 200.200.200.3 and tcp -vv
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

4. scapy 실행
[root@kali ~]# scapy

IPv4 패킷 생성
형식: 변수 = Ether(옵션)/IP(옵션)/TCP(옵션)
>>> packet = Ether(dst='00:0c:29:3d:bf:69')/IP(ttl=2,dst='200.200.200.6')/TCP(dport=80)
>>> ls(packet)
dst        : DestMACField                        = '00:0c:29:3d:bf:69' ('None')  <-- 
src        : SourceMACField                      = '00:0c:29:28:37:34' ('None')
type       : XShortEnumField                     = 2048            ('36864')
--
version    : BitField  (4 bits)                  = 4               ('4')
ihl        : BitField  (4 bits)                  = None            ('None')
tos        : XByteField                          = 0               ('0')
len        : ShortField                          = None            ('None')
id         : ShortField                          = 1               ('1')
flags      : FlagsField                          = <Flag 0 ()>     ('<Flag 0 ()>')
frag       : BitField  (13 bits)                 = 0               ('0')
ttl        : ByteField                           = 2               ('64')  <--
proto      : ByteEnumField                       = 6               ('0')
chksum     : XShortField                         = None            ('None')
src        : SourceIPField                       = '200.200.200.3' ('None')
dst        : DestIPField                         = '200.200.200.6' ('None')
options    : PacketListField                     = []              ('[]')
--
sport      : ShortEnumField                      = 20              ('20')
dport      : ShortEnumField                      = 80              ('80')  <--
seq        : IntField                            = 0               ('0')
ack        : IntField                            = 0               ('0')
dataofs    : BitField  (4 bits)                  = None            ('None')
reserved   : BitField  (3 bits)                  = 0               ('0')
flags      : FlagsField                          = <Flag 2 (S)>    ('<Flag 2 (S)>')
window     : ShortField                          = 8192            ('8192')
chksum     : XShortField                         = None            ('None')
urgptr     : ShortField                          = 0               ('0')
options    : TCPOptionsField                     = []              ("b''")

[root@victim3 ~]# tcpdump -nn -i ens33 not port 22 and host 200.200.200.3 and tcp -vv
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

패킷 1개 보내기
형식: sendp(packet)
count 인수가 없으면 default 매개변수의 값이 1로 설정되므로 패킷이 1개가 전송된다.
sendp(packet)  == sendp(packet, count=1) == sendp(packet, count=1, loop=0)
>>> sendp(packet)

패킷 1000개 전송
count 인수가 1000으로 설정되므로 패킷이 10,000개가 전송된다.
- count변수에 패킷 개수를 설정하고 전송한다.
>>> sendp(packet, count=10000)

패킷 무한루프 전송
- loop변수에 1을 설정하고 전송한다.
- 종료는 Ctrl + C를 누른다.
형식: sendp(packet, loop=1)
>>> sendp(packet, loop=1)

실습> 패킷 전송 프로그램 제작하기

scapy 가 내부적으로 socket 을 이용한다.

1. 패킷 모니터링
[root@victim3 ~]# tcpdump -nn -i ens33 not port 22 and host 200.200.200.3 and port 80

2. 소스코드 작성
[root@kali ~]# vi sendPacket.py 
#!/usr/bin/python3

from scapy.all import *
import sys

# Victim3(CentOS7): 200.200.200.6 00:0c:29:3d:bf:69

if len(sys.argv) != 2:
    print(f"Usage: {sys.argv[0]} <packet count>")
    sys.exit(1)

packetCount = int(sys.argv[1])

packet = Ether(dst='00:0c:29:3d:bf:69')/IP(dst='200.200.200.6')/TCP(dport=80)
sendp(packet, count=packetCount)

3. 프로그램 실행
[root@kali ~]# chmod 755 sendPacket.py 

[root@kali ~]# ./sendPacket.py 
Usage: ./sendPacket.py <packet count>

[root@kali ~]# ./sendPacket.py 1

[root@kali ~]# ./sendPacket.py 100000


profile
정보보안 전문가

0개의 댓글