파이썬 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 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

PCB (Process Control Block)


 운영체제가 프로세스에 대한 정보를 저장해 놓은 곳으로, 프로세스당 하나씩 1:1로 생성된다.

어떤 프로세스가 Interrupt나 System Call을 만나 다른 프로세스로 작업을 전환하는 것을 Context Switching이라고 하며, 이때 운영체제는 PCB에 실행 중이던 프로세스의 정보를 저장한 후 실행할 프로세스의 정보를 가져와 작업을 수행한다.

부모와 자식 프로세스 사이에도 PCB는 공유하지 않으며 부모든 자식이든 무조건 각 프로세스당 PCB는 1:1로 생성된다.


PCB의 구조는 일반적으로 다음과 같다.




1. Process ID : 프로세스 식별자, PID


2. Process state



 현재 프로세스의 상태를 나타내며 여기에는 생성(create)/준비(ready)/실행(running)/대기(waiting)/완료(terminated)가 있다.


생성(create)       : 프로세스가 생성은 되었지만 아직 운영체제에 의해 실행은 불가능한 상태

준비(ready)        : CPU의 할당을 기다리는 상태

실행(running)     : CPU를 할당 받아 실행되는 상태

대기(waiting)      : 실행 중인 프로세스가 어떤 사건(입출력 등)이 발생할때까지 멈추어 있는 상태

완료(terminated) : 프로세스 실행이 완전히 끝나 CPU 할당이 해제된 상태


3. Program Counter : 프로세스가 다음에 실행할 명령어의 주소 저장


4. Registers : CPU 레지스터의 값 저장


5. Memory Information : 해당 프로세스의 주소 공간 등의 데이터 저장


6. Accounting Information : 페이지 테이블, 소유자, 부모, 경과 시간 등 저장


7. I/O Information : 프로세스에 할당된 입출력장치 목록, 열린 파일 목록 등 저장

'Knowledge' 카테고리의 다른 글

NAT와 NAPT의 개념과 원리  (10) 2016.12.01
멀티 태스킹(Multi-Tasking)의 원리  (0) 2016.11.21
[Assembly] Intel x86, Local JMP [0xE9]  (0) 2016.11.07
[BigData] Spark 공부  (0) 2016.10.19
Visual Studio 2008에서 libcurl 사용하기  (0) 2016.09.05
블로그 이미지

__미니__

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

,


http://x86.renejeschke.de/html/file_module_x86_id_147.html


후킹을 하다 보면 1 byte만으로 점프를 해야 하는 일이 생긴다.

주소값 4바이트를 포함한다는 가정 하에 총 5바이트만으로 점프해야 하는데, 이때 Opcode E9짜리 JMP를 사용한다.


사용법은 굉장히 단순한데, 1바이트 Opcode와 4바이트 주소를 써 주면 된다.

물론 주소는 리틀 엔디안으로 넣어야 한다.


하지만 유의할 점이 하나 있는데, E9를 사용한 JMP는 Local JMP이기 때문에 JMP 명령의 주소로부터 상대적인 거리로 점프한다는 것이다.

그래서 절대 주소로 점프하기를 원한다면 따로 계산을 해서 오퍼랜드에 넣어주어야 한다.


계산 공식은 다음과 같다.


(넣을 주소) = (점프할 주소) - (JMP 명령의 주소) - 5


뒤에 5 bytes를 빼주는 이유는 JMP Opcode (1 byte) + Operand (4 bytes) 로 총 5바이트가 더해지기 때문이다.



'Knowledge' 카테고리의 다른 글

멀티 태스킹(Multi-Tasking)의 원리  (0) 2016.11.21
PCB (Process Control Block)  (0) 2016.11.14
[BigData] Spark 공부  (0) 2016.10.19
Visual Studio 2008에서 libcurl 사용하기  (0) 2016.09.05
RC4 Stream Cipher  (0) 2016.07.19
블로그 이미지

__미니__

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

,

[BigData] Spark 공부

Knowledge 2016. 10. 19. 16:32
spark.html

Basic of Spark

이하의 내용은 ‘Learning Spark’ 책의 3장까지 공부한 내용을 바탕으로 작성한 글입니다.
소스코드는 책의 것을 일부 인용했습니다.

소스는 모두 Python으로 작성됩니다.


About Spark

Spark는 분산 데이터 연산/처리 플랫폼이다.
기존에 Hadoop은 일괄 데이터 처리에 효율적이었으나 현재는 실시간으로 증가하는 빅데이터를 처리해야 하기에 Hadoop의 맵리듀스 프레임워크는 사용하기 힘들다.

맵리듀스 프레임워크는 실시간 데이터 처리 과정에서 많은 트래픽과 디스크 I/O를 요구할 수 있고, 맵->리듀스로 이어지는 2단계 구조로 데이터를 프로세싱하기 때문에 반복 작업이나 산발적으로 일어나는 데이터 연산/처리에는 부적합하다.


Spark는 맵리듀스 모델을 대화형 명령어 쿼리나 스트리밍 처리가 가능하도록 확장하였다.
Spark는 기본적으로 인메모리 기반으로 데이터를 연산하기 때문에 디스크 기반 연산을 수행하는 맵리듀스보다 훨씬 뛰어난 성능을 보이지만 이를 디스크에서 돌린다고 하더라도 맵리듀스보다는 뛰어난 성능을 보여 준다.


Spark는 Scala 기반으로 개발되었으며 Python, Scala, Java, SQL 등의 라이브러리를 내장해 지원한다.
다른 빅데이터 툴들과도 연계가 잘 되는데, Spark는 Hadoop 클러스터 위에서 실행할 때 특히 좋은 성능을 보인다.
그렇다고 Hadoop을 꼭 필요로 하는 것이 아니라 단순히 Hadoop API를 사용하는 저장 시스템을 지원할 뿐이다.



RDD 다루기

모든 Spark 애플리케이션은 클러스터에서 다양한 병렬 연산을 수행하는 드라이버 프로그램으로 구성된다.
드라이버 프로그램들은 연산 클러스터에 대한 연결을 나타내는 SparkContext 라는 객체를 통해 Spark에 접속하는데, Python이나 Scala로 셸을 통해 접속할 경우 자동적으로 sc라는 변수에 만들어지므로 따로 만들 필요는 없다. 단일 프로그램으로 Spark를 다룰 때는 다음과 같이 SparkContext를 만들어 줘야 한다.

from pyspark import SparkConf, SparkContext

conf = SparkConf().setMaster("local").setAppName("My App")
sc = SparkContext(conf = conf)

SparkContext를 이용하면 이것으로 RDD라는 것을 만들어낼 수 있다.
RDD는 Resilient Distributed Dataset의 약자로, 단순하게는 분산되어 존재하는 데이터 요소들의 모임이다.
RDD는 여러 개의 파티션으로 이루어져 있으며 개수는 사용자가 직접 지정이 가능하다. 파티션의 개수에 맞춰서 병렬 연산을 수행하기 때문에 많은 파티션으로 나눠져 있으면 빨라지는 것은 사실이지만, 너무 많은 파티션으로 나누는 것도 효율이 좋지 않을 수 있다. 이를 효율적으로 적용하는 것은 개발자의 몫이다.

이미지
(출처 : http://ourcstory.tistory.com/147)


위 이미지를 보면 코어가 3개인 PC에서 파티션을 4개 만들었을 때와 3개 만들었을 때 작업 처리에 걸리는 시간을 나타내고 있다.

Spark는 Hadoop API를 지원하기 때문에 HDFS, Local File System, Cloud Service 등에서 데이터를 받아와 RDD를 만들 수 있다.
또한 파일을 읽어서 RDD를 만들 수도 있으며 parallelize 함수에 직접 데이터를 입력하여 RDD로 변환하는 것도 가능하다.

1) parallelize 함수 사용

다음과 같이 sc의 parallelize 함수에 데이터를 인자로 넘겨 주면 그것으로 RDD를 만들 수 있다.

sc = SparkContext(conf = conf)
myRDD = sc.parallelize([1, 2, 3, 4])

2) 외부 데이터 사용

대표적으로 textFile 함수를 이용해 외부의 데이터를 받아와 RDD를 만들 수 있다.

sc = SparkContext(conf = conf)
myRDD = sc.textFile("Path_of_File")


RDD가 일단 한 번 만들어지면 이를 수정하는 것은 불가능하다.
RDD는 두 가지 타입의 연산을 지원하는데, 트랜스포메이션(Transformation)액션(Action)이다.
트랜스포메이션은 연산 결과 값으로 새로운 RDD를 리턴하고, 액션은 RDD를 제외한 데이터 타입을 리턴한다.
여기서 트랜스포메이션이 연산 결과 값으로 새로운 RDD를 리턴한다고 했는데, RDD는 수정이 불가능하다고 한 이유가 여기에 있다.
RDD가 수정되는 것이 아닌, 새로운 RDD를 하나 더 만들어서 변경된 값으로 채워 넣는 것이다.

Spark에서 자주 사용하는 트랜스포메이션과 액션은 다음과 같은 것들이 있다.

Transformations & Actions

이미지
(출처 : http://dirtysalt.github.io/spark-rdd-paper.html)
더 자세한 내용은 여기 참고



연산 처리 방식

Spark는 트랜스포메이션과 액션을 처리할 때 늘 여유로운 방식(lazy evaluation)으로 액션이 실행되는 시점에 처리한다.
예를 들어, sc.textFile(…) 을 실행하여 나온 RDD에 filter() 를 실행해 새로운 RDD를 만들고, 그 RDD에 first() 라는 Action을 수행하는 다음과 같은 코드를 생각해 보자.


lines = sc.textFile("README.md")
filteredLines = lines.filter(lambda line: "Python" in line)
print filteredLines.first()


일반적으로 생각해 보면 sc.textFile(…) 에서 이미 모든 파일을 읽어서 버퍼에 내용을 저장했을 것이다.
이후 filter() 를 수행하면서 그 많은 줄들을 전부 필터링했을 것이고 마지막으로 first()에서 가장 첫 번째 줄을 반환했을 것이다.
이는 매우 비효율적인 데이터 처리 방식이다. 결국 필요한 것은 “Python”이라는 문자열이 들어가는 첫 문장인데 저 방식을 그대로 사용하면 파일 전체를 읽어서 전체를 필터링하게 된다.


그래서 Spark는 액션 연산인 first()가 나오기 전까지의 트랜스포메이션은 meta에 저장해 두고 액션이 실행될 때 그에 맞추어서 트랜스포메이션을 실행한다. “Python”이라는 문자열이 나올 때까지만 파일을 읽고, 찾으면 바로 중단 후 반환하는 식으로 실행하는 것이다. 확실히 파일 전체를 읽는 것보다는 훨씬 효율적이다.


이렇게 연산을 실행하면 매우 효율적으로 보이지만 문제점이 하나 있다.
Spark는 액션 연산을 실행할 때마다 트랜스포메이션 연산을 다시 실행하므로 트랜스포메이션을 실행한 후 생성된 같은 RDD로 연산을 연속해서 수행할 경우 이미 트랜스포메이션을 통해 RDD를 한 번 생성했음에도 불구하고 같은 RDD를 계속해서 새로 생성하게 된다.
다시 예를 들어 다음과 같은 코드를 보자.


lines = sc.textFile("README.md")
filteredLines = lines.filter(lambda line: "Python" in line) # Transformation
print filteredLines.first()   # Action
print filteredLines.collect() # Action


비슷한 코드지만 마지막에 액션을 한 번 더 하고 있다.
이렇게 트랜스포메이션 이후 반복적으로 액션을 할 경우 여유로운 방식에 의해 lines.filter()는 3번째 줄에서 한 번, 4번째 줄에서 한 번 더 실행된다.
여기서 보기에는 별거 아닌 것처럼 보일지도 모르지만 데이터를 여러 번 수행하는 반복 알고리즘에 대해서는 매우 무거운 작업일 수 있다.

이런 반복 작업을 방지하기 위한 것이 persist() 라는 함수로, RDD를 메모리에 저장한 후 이후 해당 RDD를 사용할 때 저장된 RDD를 사용하도록 하는 것이다. 이를 영속화(Caching)라고 부른다. 메모리에 저장된 RDD는 LRU(Least Recently Used : 최근에 사용된 것들은 남겨 두고 오래된 것들을 버리는 알고리즘)에 의해 메모리가 가득 차거나 따로 해제하지 않는 이상 자동으로 해제되지 않으므로 프로그램 종료 전에 반드시 unpersist() 를 이용해 메모리에서 해제해야 한다.


lines = sc.textFile("README.md")
filteredLines = lines.filter(lambda line: "Python" in line)
filteredLines.persist(StorageLevel.DISK_ONLY)
print filteredLines.first()
print filteredLines.collect()
filteredLines.unpersist()


persist()는 5종류의 인자를 받을 수 있는데 이는 각각 다음과 같은 특징을 갖는다.



 레벨

 공간 사용

 CPU 사용 시간

 메모리에 저장

 디스크에 저장

 비고

 MEMORY_ONLY

 높음

 낮음

 예

 아니오

 

 MEMORY_ONLY_SER

 낮음

 높음

 예

 아니오

 

 MEMORY_AND_DISK

 높음

 중간

 일부

 일부

 

메모리에 넣기에 데이터가 너무 많으면 디스크에 나눠 저장

 MEMORY_AND_DISK_SER

 낮음

 높음

 일부

 일부

 

메모리에 넣기에 데이터가 너무 많으면 디스크에 나눠 저장. 메모리에 직렬화된 상태로 저장

 DISK_ONLY

 낮음

 높음

 아니오

 예

 



'Knowledge' 카테고리의 다른 글

PCB (Process Control Block)  (0) 2016.11.14
[Assembly] Intel x86, Local JMP [0xE9]  (0) 2016.11.07
Visual Studio 2008에서 libcurl 사용하기  (0) 2016.09.05
RC4 Stream Cipher  (0) 2016.07.19
스트림 암호 (Stream Cipher)  (0) 2016.07.18
블로그 이미지

__미니__

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

,


스킨 진짜 이쁘게 잘 만들어주셨는데, 몇 군데에서 이상한 부분이 있어서 직접 소스 수정해서 쓰고 있다.


1. 우선 '관리자' 부분.

우측 상단의 'Tistory' 클릭하면 '관리자' 부분이 있을건데, 이부분이 '괸리자'로 되어있어서 HTML에서 오타 수정했다.


2. 파일 이미지 비정상적으로 크게 나오는 문제

분명 이미지 크기는 9px인데 이상하게 256px인가로 크게 키워져서 나왔다. 첨부파일 올릴때마다 그러니까 너무 더럽게 보여서 CSS 들어가서 확인해봤다.

.entry .article img " 부분이 문제였다.

width 부분 전부 9로 바꾸는 것으로 해결.


3. colorscriptor에서 붙여넣은 코드들이 전부 가운데 정렬이 됨.

사실 이거때문에 제일 짜증났었는데, 의외로 간단한 문제였다.

CSS 항목에 보면 tr 태그에 " text-align : center; "로 해둔 부분이 있는데, 여길 수정해서 justify로 변경해주면 끝.


이제야 원하던 블로그 스킨으로 돌아왔다...

'잡담' 카테고리의 다른 글

피아노도 갖다뒀으니 슬슬 쳐봐야지  (0) 2016.12.20
오랜만에 잡담  (0) 2016.11.21
수습 조기종료  (0) 2016.10.04
수습기간 :D  (0) 2016.07.29
이스트소프트 최종합격 :D  (11) 2016.07.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 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,

10월

2016. 10. 4. 17:09

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

수습 조기종료

잡담 2016. 10. 4. 10:25



원래 3개월간 진행해야 하는 수습기간을 단축하여 2개월만에 종료하였습니다.

드디어 정사원이 되었네요, 이제 조금 안심할 수 있겠습니다. :)


2개월간 회사에서 일해보면서 꽤 많은 것을 느낄 수 있었는데, 실전에선 역시 많은 문제들과 직면하게 되더군요.

최선을 다해 열심히 한 것도 있지만 그것을 좋게 평가해주신 분들 덕분에 이렇게 정사원이 될 수 있었습니다.

앞으로도 맡은 업무 열심히 하겠습니다!

'잡담' 카테고리의 다른 글

오랜만에 잡담  (0) 2016.11.21
Tistory Flatinum 스킨 수정해서 사용  (0) 2016.10.18
수습기간 :D  (0) 2016.07.29
이스트소프트 최종합격 :D  (11) 2016.07.22
Todo List  (0) 2016.07.18
블로그 이미지

__미니__

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 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,