[PyQt4] 0x03. QYolk I

Programming 2016. 11. 15. 14:42

Python package 중에는 Yolk라고 하는 것이 있다.

이 사이트에 설명이 되어 있는데

자신의 시스템에 설치된 PyPI(Python Package Index)를 관리할 수 있는 툴이다.




보다시피 커맨드라인 기반 툴이기 때문에 GUI로는 볼 수 없는데, 이를 Qt로 GUI화한 것이 QYolk이다.

사실 이미 만들어져 있는 툴이기는 하다. (https://www.linux-apps.com/content/show.php/QYolk?content=107046)


처음엔 일단 List로 현재 설치된 package와 그 버전, 상태를 보도록 했다.


우선 pip install yolk 를 통해 yolk를 설치해야 하고, 파이썬 내에서 yolklib을 가져와서 사용한다.


++ 2016.11.18

- Yolk 라이브러리를 https://pypi.python.org/pypi/yolk3k 로 업데이트하면서 함수를 일부 변경하였습니다. 






 [ Source ]


- Main.py


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
# -*- coding: utf-8 -*-
 
# Form implementation generated from reading ui file 'Main.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!
 
from PyQt4 import QtCore, QtGui
 
try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s
 
try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)
 
class Ui_QYolk(object):
    def setupUi(self, QYolk):
        QYolk.setObjectName(_fromUtf8("QYolk"))
        QYolk.resize(565321)
        self.centralwidget = QtGui.QWidget(QYolk)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.treeList = QtGui.QTreeWidget(self.centralwidget)
        self.treeList.setGeometry(QtCore.QRect(1010541271))
        self.treeList.setMaximumSize(QtCore.QSize(54116777215))
        self.treeList.setObjectName(_fromUtf8("treeList"))
        self.treeList.headerItem().setTextAlignment(0, QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter|QtCore.Qt.AlignCenter)
        self.treeList.headerItem().setBackground(0, QtGui.QColor(230230230))
        self.treeList.headerItem().setTextAlignment(1, QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter|QtCore.Qt.AlignCenter)
        self.treeList.headerItem().setBackground(1, QtGui.QColor(230230230))
        self.treeList.headerItem().setTextAlignment(2, QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter|QtCore.Qt.AlignCenter)
        self.treeList.headerItem().setBackground(2, QtGui.QColor(230230230))
        self.treeList.header().setCascadingSectionResizes(True)
        self.treeList.header().setHighlightSections(True)
        self.treeList.header().setSortIndicatorShown(False)
        QYolk.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(QYolk)
        self.menubar.setGeometry(QtCore.QRect(0056521))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        QYolk.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(QYolk)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        QYolk.setStatusBar(self.statusbar)
 
        self.retranslateUi(QYolk)
        QtCore.QMetaObject.connectSlotsByName(QYolk)
 
    def retranslateUi(self, QYolk):
        QYolk.setWindowTitle(_translate("QYolk""QYolk", None))
        self.treeList.headerItem().setText(0, _translate("QYolk""Package Name", None))
        self.treeList.headerItem().setText(1, _translate("QYolk""Version", None))
        self.treeList.headerItem().setText(2, _translate("QYolk""Status", None))
 
 
cs


- Start.py


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
import sys
from PyQt4 import QtCore, QtGui
from Main import Ui_QYolk
from yolk import yolklib
 
class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_QYolk()
        self.ui.setupUi(self)
 
        # set Column Width
        self.ui.treeList.setColumnWidth(0200)
        self.ui.treeList.setColumnWidth(1200)
 
        packages = yolklib.get_distributions('all')
        for pkg in packages:
            newItem = QtGui.QTreeWidgetItem(self.ui.treeList)
            pk = str(pkg[0]).split(' ')
            if pkg[1]:
                status = 'Active'
            else:
                status = 'Not Active'
                newItem.setTextColor(0, QtGui.QColor(128128128))
                newItem.setTextColor(1, QtGui.QColor(128128128))
                newItem.setTextColor(2, QtGui.QColor(128128128))
                
            newItem.setText(0, pk[0])
            newItem.setText(1, pk[1])
            newItem.setText(2, status)
 
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())
cs


github : https://github.com/skyclad0x7b7/StudyPyQt4/tree/master/0x03.%20QYolk%20I

'Programming' 카테고리의 다른 글

[PyQt4] 0x05. Final Text Editor  (0) 2016.11.17
[PyQt4] 0x04. QYolk II  (0) 2016.11.16
[PyQt4] 0x02. Extended Text Editor  (0) 2016.11.15
[PyQt4] 0x01. PyQt4 설치, Simple text editor  (0) 2016.11.15
[Go] Defer, Panic, Recovery  (0) 2016.10.09
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

기능 구현이 너무 쉽고 간단하다.

전에 만들었던 Simple Text Editor에 기능 몇가지를 추가했다.


- File Close 기능

- Save Button Disable 기능, textBrowser에 변화가 있을 경우 활성화됨

- 파일 변경 후 Save 없이 Close할 경우 메시지박스로 저장할 것인지 확인함




 [ Source ]


- Main.py


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
# -*- coding: utf-8 -*-
 
# Form implementation generated from reading ui file 'Main.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!
 
from PyQt4 import QtCore, QtGui
 
try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s
 
try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)
 
class Ui_notepad(object):
    def setupUi(self, notepad):
        notepad.setObjectName(_fromUtf8("notepad"))
        notepad.resize(802625)
        self.centralwidget = QtGui.QWidget(notepad)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.textBrowser = QtGui.QTextBrowser(self.centralwidget)
        self.textBrowser.setGeometry(QtCore.QRect(1040781541))
        self.textBrowser.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByKeyboard|QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextBrowserInteraction|QtCore.Qt.TextEditable|QtCore.Qt.TextEditorInteraction|QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse)
        self.textBrowser.setObjectName(_fromUtf8("textBrowser"))
        self.button_open = QtGui.QPushButton(self.centralwidget)
        self.button_open.setGeometry(QtCore.QRect(10107523))
        self.button_open.setObjectName(_fromUtf8("button_open"))
        self.button_save = QtGui.QPushButton(self.centralwidget)
        self.button_save.setGeometry(QtCore.QRect(90107523))
        self.button_save.setCheckable(False)
        self.button_save.setAutoDefault(False)
        self.button_save.setDefault(False)
        self.button_save.setFlat(False)
        self.button_save.setObjectName(_fromUtf8("button_save"))
        self.button_close = QtGui.QPushButton(self.centralwidget)
        self.button_close.setGeometry(QtCore.QRect(710107523))
        self.button_close.setObjectName(_fromUtf8("button_close"))
        notepad.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(notepad)
        self.menubar.setGeometry(QtCore.QRect(0080221))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        notepad.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(notepad)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        notepad.setStatusBar(self.statusbar)
 
        self.retranslateUi(notepad)
        QtCore.QMetaObject.connectSlotsByName(notepad)
 
    def retranslateUi(self, notepad):
        notepad.setWindowTitle(_translate("notepad""Extended Text Editor", None))
        self.button_open.setText(_translate("notepad""Open", None))
        self.button_save.setText(_translate("notepad""Save", None))
        self.button_close.setText(_translate("notepad""Close", None))
 
 
cs


- Start.py


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
import sys
import codecs
from os.path import isfile
from PyQt4 import QtCore, QtGui
from Main import Ui_notepad
 
class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_notepad()
        self.ui.setupUi(self)
        self.filename = ""
 
        QtCore.QObject.connect(self.ui.button_open, QtCore.SIGNAL("clicked()"), self.file_dialog)
        QtCore.QObject.connect(self.ui.button_save, QtCore.SIGNAL("clicked()"), self.file_save)
        QtCore.QObject.connect(self.ui.textBrowser, QtCore.SIGNAL("textChanged()"), self.enable_save)
        QtCore.QObject.connect(self.ui.button_close, QtCore.SIGNAL("clicked()"), self.file_close)
        self.initalize()
 
    def initalize(self):
        self.ui.textBrowser.setPlainText("")
        self.ui.button_save.setEnabled(False)
        self.setWindowTitle("Extended Text Editor")
 
    def file_dialog(self):
        fd = QtGui.QFileDialog(self)
        self.filename = fd.getOpenFileName()
        if isfile(self.filename):
            s = codecs.open(self.filename, 'r''utf-8').read()
            self.ui.textBrowser.setPlainText(s)
            self.setWindowTitle(self.filename)
            self.ui.button_save.setEnabled(False)
 
    def enable_save(self):
        self.ui.button_save.setEnabled(True)
 
    def file_save(self):
        if isfile(self.filename):
            file = codecs.open(self.filename, 'w''utf-8')
            file.write(unicode(self.ui.textBrowser.toPlainText()))
            file.close()
            self.ui.button_save.setEnabled(False)
 
    def file_close(self):
        if isfile(self.filename):
            # not saved
            if self.ui.button_save.isEnabled():
                message = QtGui.QMessageBox(self)
                message.setText('Would you like to save the file before closing?')
                message.setWindowTitle("Warning")
                message.setIcon(QtGui.QMessageBox.Question)
                message.addButton('Save', QtGui.QMessageBox.AcceptRole)
                message.addButton('Discard', QtGui.QMessageBox.DestructiveRole)
                message.addButton('Cancel', QtGui.QMessageBox.RejectRole)
                message.setDetailedText('Unsaved changes in file: ' + str(self.filename))
                message.exec_()
                response = message.clickedButton().text()
                if response == 'Save':
                    self.file_save()
                    self.ui.button_save.setEnabled(False)
                elif response == 'Discard':
                    self.initalize()
                else:
                    pass
            else:
                self.initalize()
                    
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())
cs


github : https://github.com/skyclad0x7b7/StudyPyQt4/tree/master/0x02.%20Extended%20Text%20Editor

'Programming' 카테고리의 다른 글

[PyQt4] 0x04. QYolk II  (0) 2016.11.16
[PyQt4] 0x03. QYolk I  (6) 2016.11.15
[PyQt4] 0x01. PyQt4 설치, Simple text editor  (0) 2016.11.15
[Go] Defer, Panic, Recovery  (0) 2016.10.09
[DLL Injection] DLL Injector  (0) 2016.10.05
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


파이썬 GUI로는 tkInter만을 사용했었는데, 이번에 새로 GUI를 작성하고 싶다는 마음이 들어서 전에 BoB 프로젝트에서도 C++ GUI를 짜면서 사용했던 Qt를 사용하기로 했다.

Qt는 상업적으로 이용하기 위해서는 라이센스를 구매해야 하지만 개인 공부용으로 사용할 경우 무료이다.



 [ 설치 ]


- 파이썬 설치


https://www.python.org/downloads/


- PyQt4 설치


http://www.riverbankcomputing.co.uk/software/pyqt/download


원하는 Python을 설치하고, 이후 자신의 플랫폼과 Python 버전에 맞는 PyQt4를 설치해주면 된다.


전에 BoB에서 Qt를 사용한 것이 도움이 되었고 Qt 자체가 워낙 GUI를 쉽게 지원해주는 라이브러리라 금방 익숙해질 수 있었다.


공부는 여기의 소스를 따라해가며 진행할 것이다.

하나하나 다 따라가는 건 아니고, 적당히 소스를 참고하고 입맛에 맞게 변형시켜가며 쓸 예정이다.






 [ Source ]


- Main.py


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
# -*- coding: utf-8 -*-
 
# Form implementation generated from reading ui file 'Main.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!
 
from PyQt4 import QtCore, QtGui
 
try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s
 
try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)
 
class Ui_notepad(object):
    def setupUi(self, notepad):
        notepad.setObjectName(_fromUtf8("notepad"))
        notepad.resize(802625)
        self.centralwidget = QtGui.QWidget(notepad)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.textBrowser = QtGui.QTextBrowser(self.centralwidget)
        self.textBrowser.setGeometry(QtCore.QRect(1040781541))
        self.textBrowser.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByKeyboard|QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextBrowserInteraction|QtCore.Qt.TextEditable|QtCore.Qt.TextEditorInteraction|QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse)
        self.textBrowser.setObjectName(_fromUtf8("textBrowser"))
        self.button_open = QtGui.QPushButton(self.centralwidget)
        self.button_open.setGeometry(QtCore.QRect(10107523))
        self.button_open.setObjectName(_fromUtf8("button_open"))
        self.button_save = QtGui.QPushButton(self.centralwidget)
        self.button_save.setGeometry(QtCore.QRect(90107523))
        self.button_save.setObjectName(_fromUtf8("button_save"))
        notepad.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(notepad)
        self.menubar.setGeometry(QtCore.QRect(0080221))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        notepad.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(notepad)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        notepad.setStatusBar(self.statusbar)
 
        self.retranslateUi(notepad)
        QtCore.QMetaObject.connectSlotsByName(notepad)
 
    def retranslateUi(self, notepad):
        notepad.setWindowTitle(_translate("notepad""Simple Text Editor", None))
        self.button_open.setText(_translate("notepad""Open", None))
        self.button_save.setText(_translate("notepad""Save", None))
 
 
cs


- Start.py


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
import sys
import codecs
from os.path import isfile
from PyQt4 import QtCore, QtGui
from Main import Ui_notepad
 
class StartQT4(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_notepad()
        self.ui.setupUi(self)
        QtCore.QObject.connect(self.ui.button_open, QtCore.SIGNAL("clicked()"), self.file_dialog)
        QtCore.QObject.connect(self.ui.button_save, QtCore.SIGNAL("clicked()"), self.file_save)
        self.filename = ""
 
    def file_dialog(self):
        fd = QtGui.QFileDialog(self)
        self.filename = fd.getOpenFileName()
        if isfile(self.filename):
            s = codecs.open(self.filename, 'r''utf-8').read()
            self.ui.textBrowser.setPlainText(s)
            self.setWindowTitle(self.filename)
 
 
    def file_save(self):
        if isfile(self.filename):
            file = codecs.open(self.filename, 'w''utf-8')
            file.write(unicode(self.ui.textBrowser.toPlainText()))
            file.close()
 
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = StartQT4()
    myapp.show()
    sys.exit(app.exec_())
cs



말그대로 간단한 텍스트 에디터이다.

File dialog를 이용해 파일을 열고, 파일이 열리면 해당 파일 경로로 Window Title이 바뀐다.

Save도 작동한다.


github : https://github.com/skyclad0x7b7/StudyPyQt4/tree/master/0x01.%20Simple%20Text%20Editor

'Programming' 카테고리의 다른 글

[PyQt4] 0x03. QYolk I  (6) 2016.11.15
[PyQt4] 0x02. Extended Text Editor  (0) 2016.11.15
[Go] Defer, Panic, Recovery  (0) 2016.10.09
[DLL Injection] DLL Injector  (0) 2016.10.05
[C/C++] 공용체 (union)  (0) 2016.09.22
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


Go에는 에러를 처리하기 위한 다양한 예약어들과 내장 함수들이 존재한다.

그 중 defer, panic, recovery에 대해서 공부해 봤다.

확실히 쓸만하고 자주 쓸 것 같은 예약어들이었다.




1. defer


defer는 특정 list에 LIFO 형식으로 명령어를 쌓고, defer를 실행한 함수가 끝날 경우 순서대로 이를 실행해주는 예약어이다.


함수를 실행했을 때, 의도치 않게 에러가 나거나 해서 다음 실행해야 하는 명령어가 실행되지 않을 때가 있다.

대표적인 에러의 예는 다음과 같은 코드에서 발생할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
 
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
 
    written, err = io.Copy(dst, src)
    dst.Close()
    src.Close()
    return
}
cs


파일을 복사하여 다른 파일에 다시 붙여넣는 간단한 함수이다.

여기서 7번 줄의 os.Create에서 익셉션이 발생한다고 가정해 보자.

2번 줄에서 src에는 원본 파일이 열려서 핸들이 반환되어 있는 상태고, 익셉션의 발생으로 따로 핸들을 닫지 않고 함수가 종료된다.

이때 defer를 이용하여 명시해 주면 간단히 Close 처리를 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()
 
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()
 
    return io.Copy(dst, src)
}
cs


위와 같이 코드를 수정해 주면, os.Create에서 에러가 나더라도 함수가 종료되면서 src.Close가 수행되어 핸들은 정상적으로 닫힌다.


defer에는 몇 가지 알아둬야 할 특징들이 있다.

첫째로, defer 의 list에 추가되는 명령어와 그 인자들은 추가되는 시점의 값을 그대로 가지며, defer의 list는 스택과 같이 LIFO로 작동하기 때문에 마지막에 넣은 명령어에서 처음 넣은 명령어까지 역순으로 명령어가 실행된다는 것이다.

다음 코드를 보자.


1
2
3
4
5
func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}
cs


이 함수는 실행되면서 defer에 총 4개의 명령어를 쌓고, 이때 i는 각각 0, 1, 2, 3의 순서대로 쌓인다.

b 함수가 끝나면서 출력되는 결과는 그 역순으로 각각 3, 2, 1, 0이 출력된다.


두번째로, defer문은 함수에 정의된 named results를 사용하는 것이 가능하다.

예를 들어,


1
2
3
4
5
6
7
8
9
10
11
12
package main
 
import "fmt"
 
func c() (i int) {
    defer func() { i++ }()
    return 1
}
 
func main() {
    fmt.Printf("return : %d", c())
}
cs


이 코드에서 c 함수가 최종적으로 리턴하기 전에 defer이 실행되면서 return되는 i가 1 증가하게 되어 리턴 값은 2가 된다.

에러 처리 등에 쓸만 한 예약어인 듯 하다.




2. panic


panic은 golang의 내장 함수로, 함수 콜 스택을 거슬러 올라가면서 프로그램이 종료될 때까지 계속 진행중인 작업을 중단하는 함수이다.

콜 스택의 끝까지 도달할 경우 익셉션을 내면서 종료된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
package main
 
import "fmt"
 
func test_panic() {
    fmt.Println("Hello")
    panic("Unknown Error :D")
    fmt.Println("World!")
}
 
func main() {
    test_panic()
}
cs


위와 같은 예시 코드를 실행하면 다음과 같은 결과가 나온다.





3. recover


recover라는 말만 봐도 대충 예상이 되는데, 이 함수는 panic() 으로 인해서 함수가 종료되기 시작했을 경우 이를 중지하고 정상 작동하도록 만드는 함수이다.

함수 특성상 defer 내에서 사용하지 않으면 의미가 없다.


위에서 짰던 코드를 살짝 바꾸어서 이렇게 만들어 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
 
import "fmt"
 
func test_panic() {
    defer func() {
        r := recover()
        if r != nil {
            fmt.Println("Recovered in test_panic : ", r)
        }
    }()
 
    fmt.Println("Hello")
    panic("Unknown Error :D")
    fmt.Println("World!")
}
 
func main() {
    test_panic()
}
cs


이걸 실행시키면 panic에 의해 test_panic이 종료되고, 이후 defer이 실행되면서 내부의 recover이 한번 더 실행되어 panic은 멈추고 정상 종료되는것을 볼 수 있다.



블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


DLLInjector.cpp


C++로 작성한 DLL Injector.

Eject 기능은 없습니다.


사용법은 소스에 있듯이 " DLLInjector.exe [Target] [DLL] " 입니다.

인젝션 및 후킹 공부 진행하면서 작성하였습니다.


CreateRemoteThread를 이용해 LoadLibraryA 함수를 실행하게 하여 DLL을 삽입합니다.

디버깅 편의상 gle를 자주 출력하도록 해 두었습니다.


삽입할 DLL 내부에서 Thread를 돌리게 했더니 에러가 나기도 하더군요...


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
#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h>
 
DWORD findPID(LPCTSTR szProcessName);
BOOL injectDLL(DWORD dwPID, LPCTSTR szDLLName);
 
int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("[*] Usage : %s [Target] [DLL]", argv[0]);
        return 1;
    }
 
    DWORD pid = findPID(argv[1]);
    if (pid == 0xFFFFFFFF) {
        printf("[*] Process not found\n");
        return 1;
    }
    else {
        printf("[*] pid : %u\n", pid);
    }
    if (!injectDLL(pid, argv[2])) {
        printf("[*] Injection Failed\n");
        return 1;
    }
    else {
        printf("[*] Injection Successed\n");
    }
    return 0;
}
 
DWORD findPID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapshot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;
    
    pe.dwSize = sizeof(PROCESSENTRY32);
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        printf("[*] CreateToolhelp32Snapshot Error");
        return 0xFFFFFFFF;
    }
 
    Process32First(hSnapshot, &pe);
    do {
        if (!_stricmp(szProcessName, pe.szExeFile)) {
            dwPID = pe.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapshot, &pe));
 
    CloseHandle(hSnapshot);
    return dwPID;
}
 
BOOL injectDLL(DWORD dwPID, LPCTSTR szDLLName)
{
    HANDLE hProcess, hThread;
    HMODULE hMod;
 
    LPVOID pRemoteBuf;
    DWORD dwBufSize = lstrlen(szDLLName) + 1;
    LPTHREAD_START_ROUTINE pThreadProc;
 
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) return FALSE;
    if (hProcess == INVALID_HANDLE_VALUE) {
        printf("[*] OpenProcess Error");
        return FALSE;
    }
    printf("gle : %u\n", GetLastError());
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
    if (pRemoteBuf == INVALID_HANDLE_VALUE) {
        printf("[*] VirtualAllocEx Error");
        return FALSE;
    }
    printf("gle : %u\n", GetLastError());
    WriteProcessMemory(hProcess, pRemoteBuf, szDLLName, dwBufSize, NULL);
 
    hMod = GetModuleHandle("kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");
    if (pThreadProc == INVALID_HANDLE_VALUE) {
        printf("[*] GetProcAddress Error");
        return FALSE;
    }
    printf("gle : %u\n", GetLastError());
    hThread = CreateRemoteThread(hProcess, NULL0, pThreadProc, pRemoteBuf, 0NULL);
    if (hThread == INVALID_HANDLE_VALUE) {
        printf("[*] CreateRemoteThread Error");
        return FALSE;
    }
    printf("gle : %u\n", GetLastError());
 
    WaitForSingleObject(hThread, INFINITE);
 
    CloseHandle(hThread);
    CloseHandle(hProcess);
    printf("gle : %u\n", GetLastError());
    return TRUE;
}
cs


'Programming' 카테고리의 다른 글

[PyQt4] 0x01. PyQt4 설치, Simple text editor  (0) 2016.11.15
[Go] Defer, Panic, Recovery  (0) 2016.10.09
[C/C++] 공용체 (union)  (0) 2016.09.22
[Go] struct{} 와 &struct{} 선언의 차이점?  (3) 2016.07.23
[C++] Reference In Low-level  (0) 2016.07.02
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,


공용체라는 것은 쓸 일이 별로 없지만, 실제 업무에서 보게 되었으므로 공부해 봤다.


사실 공부라고 할 것도 없을만큼 간단한 내용이라...


우선 공용체는 구조체와 비슷하게 생겼다.


하지만 구조체는 내부에 변수나 함수(C++)가 추가됨에 따라 그만큼 메모리를 차지하기 때문에 사용하는 메모리가 증가하지만, 공용체는 가장 큰 변수의 크기만큼 메모리를 할당하고 해당 메모리를 말 그대로 '공유'하기 때문에 더 커지지 않는다.


기본적인 개념은 다른 곳에서도 쉽게 설명하고 있기 때문에 예제를 적도록 하겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
 
union MyUnion {
    unsigned char c;
    unsigned short s;
    int i;
    unsigned char myCharArray[6];
};
 
int main()
{
    MyUnion myUnion;
    memcpy(myUnion.myCharArray, "\xAA\xBB\xCC\xDD\xEE\xFF"6);
    printf("char  : %012X\n", myUnion.c);
    printf("short : %012X\n", myUnion.s);
    printf("int   : %012X\n", myUnion.i);
    printf("array : %02X%02X%02X%02X%02X%02X\n", myUnion.myCharArray[0], myUnion.myCharArray[1], myUnion.myCharArray[2], 
        myUnion.myCharArray[3], myUnion.myCharArray[4], myUnion.myCharArray[5]);
    return 0;
}
cs



간단한 예제로, union 내부에 char, short, int, char array의 4가지 변수가 선언되어 있다.


여기서 가장 큰 변수는 myCharArray로 총 6바이트를 차지한다.

따라서 공용체 MyUnion은 선언 시 6바이트의 메모리 공간을 차지하게 되며(패딩은 고려하지 않는다) 해당 공간을 나머지 변수들이 공유한다.

공유한다고 해서 따로 가지는 것이 아니라 변수마다 가장 앞에서부터, 즉 offset 0에서부터 변수의 크기만큼 메모리를 가진다.


그림으로 나타내면 다음과 같다.



[그림 1] MyUnion 메모리 구조도


 그리고 위 프로그램을 실행시켜서 나온 output은 다음과 같다.



[그림 2] 프로그램 실행 결과


왜 short와 int의 출력 결과가 저렇게 나오는지는 바이트 오더(Byte order)에 대해서 공부하면 알 수 있을 것이다.

'Programming' 카테고리의 다른 글

[Go] Defer, Panic, Recovery  (0) 2016.10.09
[DLL Injection] DLL Injector  (0) 2016.10.05
[Go] struct{} 와 &struct{} 선언의 차이점?  (3) 2016.07.23
[C++] Reference In Low-level  (0) 2016.07.02
[MITM] Create Repository 'WLAN-Crack'  (0) 2016.06.17
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,



GoLang을 공부하다가 문득 생각한 것이 있다.

package main

import "fmt"

type Temp struct {
	A int
	B int
}

func main () {
	tmp1 := Temp{10, 20}
	tmp2 := &Temp{10, 20}
	fmt.Println(tmp1.A, tmp1.B)
	fmt.Println(tmp2.A, tmp2.B)
}

이런 코드가 있다고 했을 때, tmp1과 tmp2는 대체 어떤 차이가 있는 것일까?

배울 때 본 것을 그대로 적용하면 tmp1에는 변수 자체가 들어간 것이고,

tmp2에는 변수의 포인터가 들어가 있기 때문에,

두 번째 Println은 자동으로 (*tmp2).A, (*tmp2).B 형식으로 참조한다고 한다.


그런데 그게 어떻게 다르다고 하는 것일까?

실제로 값을 변경해 봐도 똑같이 변경되고, 출력해도 똑같이 출력된다.


http://stackoverflow.com/questions/33593545/golang-difference-between-struct-vs-struct

인터넷을 찾아보니, 여기서 설명하기를 메서드를 이용해 구조체 멤버변수를 바꿀 때 차이가 난다고 한다.


우선 알고 가야 할 것이, GoLang에서는 구조체 내부에 포함되는 메서드라는 개념이 없다.

대신 기본 타입이 아니라면 어디에든 메서드를 갖다 붙일 수 있다.

이를 이용하여 C++이나 Java의 의 클래스 멤버 함수처럼 구현이 가능하다.


이의 구현은 아래 코드로 예시를 들 수 있다.

package main

import "fmt"

type Temp struct {
	A int
	B int
}

func (tmp *Temp) PrintVar() {
	fmt.Println(tmp.A, tmp.B)
}

func main () {
	tmp1 := Temp{10, 20}
	tmp2 := &Temp{10, 20}
	tmp1.PrintVar()
	tmp2.PrintVar()
}

결과는 둘 모두 똑같이 10과 20을 출력한다.


여기까지만 봐서는 뭐가 다른지 감이 잡히지 않을 것이다.

다른 점은 메서드 내에서 두 구조체의 변수를 변경할 때 생긴다고 한다.

흔히 말하는 Setter의 개념이다.

위 링크의 답변에 있는 소스를 그대로 가져와 보자.

package main
import "fmt"



type test_struct struct {
  Message string
}

func (t test_struct)Say (){
   fmt.Println(t.Message)
}

func (t test_struct)Update(m string){
  t.Message = m; 
}

func (t * test_struct) SayP(){
   fmt.Println(t.Message)
}

func (t* test_struct) UpdateP(m string)  {
  t.Message = m;
}

func main(){
  ts := test_struct{}
  ts.Message = "test";
  ts.Say()
  ts.Update("test2")
  ts.Say() // will still output test

  tsp := &test_struct{}
  tsp.Message = "test"
  tsp.SayP();
  tsp.UpdateP("test2")
  tsp.SayP() // will output test2

}

이를 실행하면 ts에서는 두번 다 "test"가 출력되고,

tsp에서는 두 번째 Print에서 값이 변경되어 "test2"가 출력된다.

위 링크의 답변에서는 이런 방식으로 struct{}와 &struct{} 선언의 차이가 생긴다고 한다.


의문은 여기서 생긴다.

잘 보면 ts와 tsp가 실행하는 메서드가 다르다.

ts가 실행하는 메서드는 (t test_struct) Update 이고,  tsp가 실행하는 메서드는 (t *test_struct) UpdateP 이다.

메서드의 선언부분에 차이가 있고, 애초부터 메서드명부터 다르므로 일단 다른 함수인 것이다.

혹시 저 메서드명을 같게 만들어서 struct{}와 &struct{} 각각의 선언에 따라 다른 메서드가 실행되는지 실험해보기 위해 메서드명을 같게 하고 컴파일을 시도해보기도 하였으나, 같은 메서드명이 있다며 컴파일되지 않았다.


요약하면 저 글의 답변은 struct{}와 &struct{}의 차이점이 아닌, 포인터 리시버를 갖는 메서드와 그렇지 않은 메서드의 차이를 설명하고 있는 것이다.

실제로 ts와 tsp 모두 UpdateP 메서드를 실행시키도록 하면 둘 모두 "test2"로 바뀐 결과를 확인할 수 있으며, 

둘 모두 Update 메서드를 실행시키도록 하면 둘 모두 "test"만을 출력하는 결과를 확인할 수 있다.


포인터 리시브를 하는 이유는 간단하다.

이것이 아주 좋은 예라고 생각하는데, C++에서 인자로 클래스 포인터를 넘기는 경우를 생각해 보자.

만약 클래스 포인터가 아니라 그냥 클래스 인스턴스로 인자를 넘길 경우, 해당 인스턴스가 그대로 복사되어 들어가기 때문에

함수가 실행되면서 인자가 전달될 때 오버헤드가 생기고, 내부에서 해당 클래스 멤버 변수를 변경해도 '복사된 클래스'이기 때문에

원본 클래스 멤버 변수의 값은 바뀌지 않는다.

하지만 클래스 포인터 타입으로 인자를 넘겨서 참조하여 값을 변경할 경우, 내부 멤버변수의 값을 바꾸는 것이 가능하다.


오버헤드를 줄이고, 클래스 멤버변수의 변경을 가능하게 하기 위함이라는 두 가지 이유 때문에

GoLang에서 포인터 리시브를 하여 메서드를 struct에 '갖다 붙이는' 것이다.



결론은, 구글링을 해봐도 struct{} 선언과 &struct{} 선언의 차이는 알 수가 없었다.


struct{}는 그냥 변수로 받고, &struct{}는 포인터로 받지만

&struct{}로 선언된 객체를 참조할 때는 자동으로 (*struct).var 형식으로 참조하기 때문에 동작에 다를 게 없다.

멤버변수 변조도 메서드단에서 포인터 리시버를 이용하면 struct{}든 &struct{}든 둘 다 변조가 가능하고,

포인터 리시버를 이용하지 않으면 둘 다 변조가 되지 않는다는 점에서도 똑같다.

대체 무슨 차이인 걸까...


interface를 공부하는 도중에 왜 struct{}와 &struct{}를 달리 사용하는지를 일단 알게 되었다.

우선 Minescroll 서버팀 팀원이자 후배가 직접 리버싱하여 살펴본 결과, &struct{}는 struct의 포인터를 받아오기 때문에 참조 횟수가 늘어나 struct{}에 비해 약간 더 느리다.

여기서 간과해서는 안 되는 부분이, struct{}로 받은 타입은 당연하지만 struct이고, &struct{}로 받은 타입은 *struct라는 것이다.


직접 실험해 본 결과, interface를 사용하지 않고 Pointer Receiver를 사용한 메서드를 struct에 갖다 붙였을 때,

struct{}를 사용하던 &struct{}를 사용하던 결과는 같다.

그런데 interface에서는 얘기가 조금 달라진다.

interface에서 선언만 해 둔 메서드를 이후에 (다른 OOP 언어의 말을 빌려) 오버라이딩할 경우가 있다.

이 때 메서드에 포인터 리시버를 사용하게 될 경우, 해당 함수를 실행하는 객체는 꼭 *struct 타입이어야 한다.

*struct 타입이어야 한다는 것은 struct{}로 선언한 이후, 객체 앞에 &를 붙여서 넘겨 주어도 성립한다는 것이다.

만약 멤버변수가 변하지 않아도 괜찮다면, 메서드를 정의할 때 포인터 리시버를 사용하지 않고 그냥 struct형으로 선언하여 실행하면 된다.

그럼 아래와 같은 에러는 발생하지 않을  것이다.


.\main.go:37: cannot use struc (type structType) as type MyInterface in argument to DisplayInfo:

structType does not implement MyInterface (DisplayA method has pointer receiver)

'Programming' 카테고리의 다른 글

[DLL Injection] DLL Injector  (0) 2016.10.05
[C/C++] 공용체 (union)  (0) 2016.09.22
[C++] Reference In Low-level  (0) 2016.07.02
[MITM] Create Repository 'WLAN-Crack'  (0) 2016.06.17
[MITM] TCP Packet Capture & Display  (0) 2016.06.03
블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

C++에서 Reference란 참조자라고 하여, 변수 자체가 아닌

변수를 가리키는 또다른 별명이라고 할 수 있다.

예를 들어 int형 변수 a가 있다고 할 때,

int &b = a 로 선언하고 b 값을 변경하면 실제 a의 값도 같이 바뀐다.


그래서 보통 swap 함수를 만들 때, 포인터로 넘겨주고 값을 변경하기도 귀찮고 하기 때문에 레퍼런스를 자주 사용한다.



1
2
3
4
5
void swap(int& a, int& b) {
    int tmp = a;
    a = b;
    b = tmp;
}
cs


처럼 말이다.

이렇게 변수로 함수의 인자를 받지 않고 레퍼런스로 받는 것을 Call-by-reference라고 한다.

(변수로 받는 것은 Call-by-value 라고 한다)


메모리를 공부했다면 여기서 어떻게 이게 되는지 신기할 수 있다.

포인터로 받아서 참조해서 값을 변경해준다고 하면 이해가 되겠지만, 레퍼런스로 받아서 값을 변조시키는 것은

그냥 봤을 때는 32비트 기준으로 EBP+8 주소의 값을 바꾸는 것과 같은 행동으로 보이기 때문에, 원본의 값과는 아무런 상관이 없어 보인다.

그래서 직접 디스어셈블링해서 구조를 확인해 봤다.




코드는 위와 같다.

간단하게 main에 a라는 int형 변수를 하나 선언하고 한번 출력한 다음,

이를 func에 Call-by-reference로 넘겨 값을 변경시킨 후 다시 출력하는 소스이다.



컴파일을 마치고 실행시켜 보면 의도했던 대로 a의 값이 변경되어 있는 것을 볼 수 있다.



그리고 gdb로 디스어셈블하여 내부를 살펴 보면, 나는 하지도 않았는데 자동으로 포인터 연산을 통해 값을 넣는 것이 보인다.

EBP+8에는 실제로 a의 주소가 들어 있었고, 이 주소를 참조하여 10을 넣어 준 것이다.

코드에서 내가 직접 짜주지는 않았지만, 컴파일러가 이렇게 되도록 컴파일한것이다.



이렇게 보고 나니 또 레퍼런스가 참조하는 변수를 바꿀 수는 없는가에 대한 생각이 들었다.

구글링해가면서 살펴보니 가능하기는 하더라.




이런식으로, functional을 include한 뒤, std::ref에 해당 변수를 넣어서 레퍼런스를 바꿔줄 수 있다.

저 std::ref같은 경우 'std::reference_wrapper' 타입을 리턴하기 때문에 쓰기 귀찮으니 auto를 쓰는게 편하다.

딱 봐도 레퍼런스를 변조하려고 하니 코드가 좀 복잡해졌다.

실제로 컴파일하고 실행시켜 보면,


(g++로 컴파일하려고 하니 -std=c++11 옵션을 줘야 하더라)


의도한 대로 값이 변경된 것을 알 수 있다.


이것도 로우레벨로 내려가서 좀 더 분석해보자.


(히익 이게 뭐야 무서워)


C++ 특성상 컴파일하고 나니 심볼이 엄청 더럽게 바뀌었다.

이래서 C++ 리버싱하는게 정말 싫은 것이다.


보아하니 EBP-0x20에 a 변수가 존재하고, EBP-0x24에 b 변수가 존재한다.

lea 명령으로 EBP-0x28의 주소를 edx에 넣고, EBP-0x20의 주소를 eax에 넣은 후,

push edx와 push eax를 수행한다.

이후 call하는걸 보니 std::ref의 첫 번째 인자로 &c, 두 번째 인자로 &a가 들어갔다.

이후 ostream이 나올 때까지 딱히 변화가 없는 것을 보아, 여기에서 c에는 이미 std::ref(a)가 들어갔음을 알 수 있다.

출력 이후, 맨 밑을 보면 c.get()이 실행되어 그곳에 포인터로 0xa를 넣는 것이 보인다.



아랫부분을 보니 또 이렇게 ebp-0x24 주소를 인자로 넘기고,

또 ebp-0x28 주소도 인자로 넘기는데, call을 여러번 수행한다.

아무래도 std::ref(b)를 수행한 뒤, 연산자 오버로딩 된 '='을 이용해 넣는 부분인 것 같다.

잘렸지만 이 부분 다음에 c.get()을 수행하고 0x14를 mov하는 부분도 있다.


결국 결론을 내리면 레퍼런스는 컴파일러딴에서 알아서 잘 포인터로 처리해 준다는 것.

우리는 그냥 실제 변수가 있는 것처럼 쓰기만 하면 된다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

https://github.com/skyclad0x7b7/WLAN-Crack


 C++과 libtins를 이용하여 ARP Spoofing을 이용한 MITM 공격에 성공하였다.


 헤더파일로 나눠서 클래스화했고, 나중에 더 많은 기능을 추가하던지 해서 무선 랜 해킹 라이브러리를 만들어 볼 생각이다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

For MITM Project

This source used Libtins


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
#include <iostream>
#include <unistd.h>
#include <string>
#include <tins/tins.h>
 
#define TIME_LAST 60
 
using namespace std;
using namespace Tins;
 
void tcpSniff(IPv4Address &);
void signalHandler(int signum) {
    clog << "Exit" << endl;
    exit(0);
}
 
int main(int argc, char *argv[])
{
    signal(SIGALRM, signalHandler);
    if(argc != 2) {
        fprintf(stderr, "[*] Usage : %s MY_IP\n", argv[0]);
        return 1;
    }
 
    IPv4Address myip;
    try {
        myip = argv[1];
    } catch(runtime_error& e) {
        fprintf(stderr, "[*] Please input ip address correctly!!\n");
        return 1;
    }
 
    tcpSniff(myip);
    return 0;
}
 
void tcpSniff(IPv4Address &myip)
{
    Sniffer tcpSniffer("wlan0");
    clog << "[*] Sniffing Started : " << endl;
    alarm(TIME_LAST);
    while(true) {
        Packet p = tcpSniffer.next_packet();
        if(p.pdu()->find_pdu<IP>() &&
           p.pdu()->find_pdu<TCP>() &&
           p.pdu()->find_pdu<TCP>()->sport() != 443 &&
           p.pdu()->find_pdu<TCP>()->dport() != 443 &&
           p.pdu()->find_pdu<TCP>()->find_pdu<RawPDU>()) {
            const IP &ip = p.pdu()->rfind_pdu<IP>();
            const TCP &tcp = p.pdu()->rfind_pdu<TCP>();
            const RawPDU& raw = tcp.rfind_pdu<RawPDU>();
            const RawPDU::payload_type& payload = raw.payload();
            string data(payload.begin(), payload.end());
 
            clog << " *** [" << ip.src_addr() << ":" << tcp.sport() << "] => [" << ip.dst_addr() << ":" << tcp.dport() << "] ***" << endl;
            clog << data << endl << endl;
        }
    }
}
cs


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,