한참 전에 공부한답시고 엄청 지저분하지만 돌아가는, 그런 소스로 짠 적이 있었다.

이번엔 좀 더 확실히 더 많은 기능을 넣고, 에러도 잘 나지 않는 그런 프로그램을 구현해보려고 만들었다.

예전엔 3일이나 걸려서 만들었던 프로그램이지만 다시 만드니 2시간이면 기능, 대부분의 GUI까지 구현이 끝나는 것을 볼 수 있었다.

크롤러 특성상 사이트 의존성이 매우 강하기 때문에 이 크롤러는 zerochan,net 의 이미지만을 긁어올 수 있다.


다음이 소스이다.



<crawler.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
73
74
75
76
import os
import re
import urllib
from bs4 import *
import threading
 
class Crawler:
    def __init__(self, keywords, page):
        self.keywords = keywords
        self.url = "http://www.zerochan.net/"+keywords
        self.page = page
        self.img_url = []
        self.num = 0
 
    def url_crawl(self):
        for i in range(1, self.page+1):
            tmp_url = self.url+"?p="+str(i)
            source = urllib.urlopen(tmp_url).read()
            if "No such tag. Back to" in source:
                return -1;
            source = BeautifulSoup(source, "html5lib")
            imgTagList = source('img')
            for j in range(0len(imgTagList)):
                try:
                    self.img_url.append(imgTagList[j]['src'])
                except:
                    return -2;
 
    def url_setting(self):
        for i in range(len(self.img_url)):
            tmp = self.img_url[i].split('.')
            self.img_url[i] = "http://static.zerochan.net/.full."+tmp[-2]+"."+tmp[-1]
 
    def crawl(self, url):
        img = urllib.urlopen(url).read()
        f = open(self.keywords+"/"+('.'.join(url.split('.')[-2:])), 'wb')
        f.write(img)
        f.close()
 
    def findImg(self):
        ret = self.url_crawl()
        if ret == -1:
            return -1
        if ret == -2:
            return -2
        self.url_setting()
        self.num = len(self.img_url)
        return len(self.img_url)
 
    def start(self, num):
        try:
            self.keywords = self.keywords.replace(':'"")
            self.keywords = self.keywords.replace('\\'"")
            self.keywords = self.keywords.replace('/'"")
            self.keywords = self.keywords.replace('?'"")
            self.keywords = self.keywords.replace('!'"")
            self.keywords = self.keywords.replace('"'"")
            self.keywords = self.keywords.replace('<'"")
            self.keywords = self.keywords.replace('>'"")
            self.keywords = self.keywords.replace('|'"")
            os.makedirs(self.keywords)
        except:
            pass
        self.thread1 = threading.Thread(target=self.crawls, args=(1, num))
        self.thread1.start()
        self.thread2 = threading.Thread(target=self.crawls, args=(2, num))
        self.thread2.start()
        self.thread3 = threading.Thread(target=self.crawls, args=(3, num))
        self.thread3.start()
        self.thread4 = threading.Thread(target=self.crawls, args=(4, num))
        self.thread4.start()
 
    def crawls(self, num, maxNum):
        for i in range(0, (maxNum+4-num)/4):
            self.crawl(self.img_url[(i*4)+num-1])
 
cs


크롤러 클래스를 만들어서 넣어 두었다.

총 4개의 Thread를 만들어서 크롤링을 하며, 우선적으로 페이지들을 돌며 이미지들의 주소를 받아오고,

이 주소에 각각 Thread를 할당하여 다운받아오는 방식이다.


다음은 GUI 인터페이스이다.




<gui.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
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
from Tkinter import *
import tkMessageBox
from crawler import *
class Crawler:
    def __init__(self, keywords, page):
        self.keywords = keywords
        self.url = "http://www.zerochan.net/"+keywords
        self.page = page
        self.img_url = []
        self.num = 0
 
    def url_crawl(self):
        for i in range(1, self.page+1):
            tmp_url = self.url+"?p="+str(i)
            source = urllib.urlopen(tmp_url).read()
            if "No such tag. Back to" in source:
                return -1;
            source = BeautifulSoup(source, "html5lib")
            imgTagList = source('img')
            for j in range(0len(imgTagList)):
                try:
                    self.img_url.append(imgTagList[j]['src'])
                except:
                    return -2;
 
    def url_setting(self):
        for i in range(len(self.img_url)):
            tmp = self.img_url[i].split('.')
            self.img_url[i] = "http://static.zerochan.net/.full."+tmp[-2]+"."+tmp[-1]
 
    def crawl(self, url):
        img = urllib.urlopen(url).read()
        f = open(self.keywords+"/"+('.'.join(url.split('.')[-2:])), 'wb')
        f.write(img)
        f.close()
 
    def findImg(self):
        ret = self.url_crawl()
        if ret == -1:
            return -1
        if ret == -2:
            return -2
        self.url_setting()
        self.num = len(self.img_url)
        return len(self.img_url)
 
    def start(self, num):
        try:
            self.keywords = self.keywords.replace(':'"")
            self.keywords = self.keywords.replace('\\'"")
            self.keywords = self.keywords.replace('/'"")
            self.keywords = self.keywords.replace('?'"")
            self.keywords = self.keywords.replace('!'"")
            self.keywords = self.keywords.replace('"'"")
            self.keywords = self.keywords.replace('<'"")
            self.keywords = self.keywords.replace('>'"")
            self.keywords = self.keywords.replace('|'"")
            os.makedirs(self.keywords)
        except:
            pass
        self.thread1 = threading.Thread(target=self.crawls, args=(1, num))
        self.thread1.start()
        self.thread2 = threading.Thread(target=self.crawls, args=(2, num))
        self.thread2.start()
        self.thread3 = threading.Thread(target=self.crawls, args=(3, num))
        self.thread3.start()
        self.thread4 = threading.Thread(target=self.crawls, args=(4, num))
        self.thread4.start()
 
    def crawls(self, num, maxNum):
        for i in range(0, (maxNum+4-num)/4):
            self.crawl(self.img_url[(i*4)+num-1])
 
 
class Interface:
    def __init__(self, Master):
        self.master = Master
        self.master.geometry('600x130')
 
        # MainFrame
 
        self.mainFrame = Frame(self.master)
        self.mainFrame.pack(fill=X)
 
        # UrlFrame
 
        self.urlFrame = Frame(self.mainFrame)
        self.urlFrame.pack(side=TOP, fill=X)
 
        self.urlLabel = Label(self.urlFrame)
        self.urlLabel.configure(text='Keywords :')
        self.urlLabel.pack(side=LEFT, padx=5, pady=10)
 
        self.urlEntry = Entry(self.urlFrame)
        self.urlEntry.configure(width=18)
        self.urlEntry.pack(side=LEFT, padx=5, pady=10)
 
        self.pageLabel = Label(self.urlFrame)
        self.pageLabel.configure(text='Page Number : ')
        self.pageLabel.pack(side=LEFT, padx=5, pady=10)
 
        self.pageEntry = Entry(self.urlFrame)
        self.pageEntry.configure(width=10)
        self.pageEntry.pack(side=LEFT, padx=5, pady=10)
 
        self.countLabel = Label(self.urlFrame)
        self.countLabel.configure(text='Image Number :')
        self.countLabel.pack(side=LEFT, padx=5, pady=10)
 
        self.countEntry = Entry(self.urlFrame)
        self.countEntry.configure(width=10)
        self.countEntry.pack(side=LEFT, padx=5, pady=10)
 
        # ButtonFrame
 
        self.buttonFrame = Frame(self.mainFrame)
        self.buttonFrame.pack(side=TOP, fill=X)
 
        self.findButton = Button(self.buttonFrame, command=self.findThreadingStart)
        self.findButton.configure(text='Find', width=25)
        self.findButton.pack(side=LEFT, padx=7, pady=5)
 
        self.startButton = Button(self.buttonFrame, command=self.startThreadingStart)
        self.startButton.configure(text='Start', width=25)
        self.startButton.pack(side=LEFT, padx=7, pady=5)
 
        self.stopButton = Button(self.buttonFrame, command=self.stopCrawling)
        self.stopButton.configure(text='Stop', width=25)
        self.stopButton.pack(side=LEFT, padx=7, pady=5)
 
        # Notification Frame
 
        self.notificationFrame = Frame(self.mainFrame)
        self.notificationFrame.pack(side=TOP, fill=X)
 
        self.numberLabel = Label(self.notificationFrame)
        self.numberLabel.configure(text='Images found : ')
        self.numberLabel.pack(side=LEFT, padx=10, pady=5)
 
        self.numberviewLabel = Label(self.notificationFrame)
        self.numberviewLabel.configure(text='N/A')
        self.numberviewLabel.pack(side=LEFT, padx=10, pady=5)
 
        self.notificationButton = Button(self.notificationFrame, command=self.help)
        self.notificationButton.configure(text = 'Help')
        self.notificationButton.pack(side=RIGHT, padx=10, pady=5)
 
        # Warning Frame
 
        self.warningFrame = Frame(self.mainFrame)
        self.warningFrame.pack(side=TOP, fill=X)
 
        self.warningLabel = Label(self.warningFrame)
        self.warningLabel.pack(side=LEFT, padx=10)
 
    def findThreadingStart(self):
        self.findThread = threading.Thread(target=self.findImage)
        self.findThread.start()
 
    def findImage(self):
        self.warningLabel.configure(text='[*] Finding Images...')
 
        url = self.urlEntry.get()
        if url=="":
            self.warningLabel.configure(text='[*] Please input keywords!')
            return
        page = self.pageEntry.get()
        try:
            page = int(page)
        except:
            self.warningLabel.configure(text='[*] Please input only INTEGER in page form!')
            return
 
        self.crawler = Crawler(url, page)
        self.number = self.crawler.findImg()
 
        if self.number == -1:
            self.warningLabel.configure(text='[*] No such Tag, please use other Tag')
            return
        elif self.number == -2:
            self.warningLabel.configure(text='[*] Exception occured. Please feedback to developer.')
            return
 
        self.numberviewLabel.configure(text=str(self.number))
        self.warningLabel.configure(text='[*] Finding Images finished')
 
    def startThreadingStart(self):
        self.startThread = threading.Thread(target=self.startCrawling)
        self.startThread.start()
 
    def startCrawling(self):
        self.warningLabel.configure(text='[*] Crawling Started')
        try:
            num = self.countEntry.get()
            if num=="MAX":
                num = self.number
            else:
                try:
                    num = int(num)
                except:
                    self.warningLabel.configure(text='[*] Please input only INTEGER in number form!')
                    return
 
                if num > self.number:
                    num = self.number
            self.crawler.start(num)
        except:
            self.warningLabel.configure(text="[*] Please do 'Find' before 'Start'!")
            return
    def stopCrawling(self):
        self.warningLabel.configure(text="[*] It can't be used!!")
 
    def help(self):
        helpString = """        [Usage]
1. Input the keywords in the box. (ex: Kousaka Kirino)
  It can be character's name, emotions(ex: crying, smile),
  objects(ex: wings, sword).
  Please use official name.
2. Input the page's number you want to crawl.
  About 5~15 images in one page.
  !! Too many pages (like above 100) can take many time.
  !! So please be careful.
3. Click the Find button, and wait for finishing.
4. After finished, input the number you want to crawl.
  If there's anything or number is greater than (3),
  It's automatically set as the maximum(3's number).
  If you want to crawl all of them, input 'MAX'.
5. Click the Start Button, and wait for finishing.
=============================================
This Program was made by 5kyc1ad(skyclad1975).
It's made up of Python 2.7.10, with Tkinter, BeautifulSoup.
Please Feedback : skyclad0x7b7@gmail.com
Blog : http://5kyc1ad.tistory.com"""
        tkMessageBox.showinfo("Zerochan_Crawler::Help",helpString)
 
        
root = Tk()
myApp = Interface(root)
root.mainloop()
cs


사실상 이거 짜는게 제일 오래 걸렸다.

크게 어려운 건 없었지만 노가다라고 해야 하나, 역시 코딩은 열심히 두들기는게 답이었다.


다음은 시연 영상이다.




(혹시라도 문제가 된다면 삭제하도록 하겠습니다)

'Programming' 카테고리의 다른 글

[VHDL] Full Adder  (0) 2016.02.11
[VHDL] D Flip-Flop  (0) 2016.02.11
[C++] 가상함수(Virtual Function)  (0) 2016.01.20
Assembly Programming - atoi  (0) 2015.10.11
Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
블로그 이미지

__미니__

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

,


 C++ 구조체(구조체나 클래스나 C++에서는 거의 같으니 하나만 하겠다)에서 가상 함수를 사용하는 경우가 있다. 대표적으로 C++의 다형성이 구현되는 부분이라고 할 수 있겠는데, 자식 클래스에서 부모 클래스를 상속받아 가상함수를 오버라이딩해서 사용하곤 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
 
#pragma pack(push, 1)
struct A{
    int i;
    char ch[0x10];
    virtual void vfoo(){}
    virtual void vbar(){}
    virtual void vbaz(){}
};
#pragma pack(pop)
 
int main()
{
    cout << sizeof(A) << endl;
    return 0;
}
cs



 위와 같은 코드를 생각해 보자.

64비트 환경 기준으로, 위 코드를 빌드하고 실행했을 때, 어떤 결과가 나올까? 

pragma pack은 컴파일러가 자동으로 붙여주는 더미를 없애주는 역할을 한다.


나는 처음에는 가상함수 포인터 세개로 8*3 = 24바이트, int형 4바이트, char 배열 16바이트로 총 44바이트가 나올 거라고 예상했다. 하지만 사실은 28바이트밖에 되지 않는다. 그 이유를 살펴보면, 우선 int 형 4바이트와 char 배열 16바이트는 맞지만 가상함수는 아무리 많이 선언되어도 가상함수 테이블이라는 곳을 가리키는 포인터 하나밖에 할당되지 않아 8바이트만 차지하기 때문에 4+16+8 = 28이 되는 것이다.


그럼 가상함수 테이블이란 무엇일까?

 우리가 가상함수를 사용할 때, 함수를 호출하기 위해서 로우레벨로, 어셈블리 레벨까지 내려가 보자.

보통 함수를 호출하기 위해서 call 명령을 사용한다. 일반적인 경우, call 명령을 사용하기 위해서는 스택에 A 구조체를 할당했다고 가정했을 때, A의 주소를 레지스터에 담고, 해당 주소에서 함수 포인터까지의 옵셋을 구해서 다시 레지스터에 담고, 그대로 call을 하면 된다.


하지만 가상함수를 사용할 경우, 중간에 가상함수 테이블이라는 곳을 한번 더 거친다.


위의 A 객체를 힙에 할당한다고 가정하면, 다음과 같은 힙 상태가 만들어진다.


[ ... ] [ Virtual Function (8 bytes) ] [ i (4 bytes) ] [ ch (16 bytes) ] [ ... ]


 이렇게 가상함수 테이블 포인터는 항상 할당되는곳의 첫 번째에 위치하게 된다.

가상함수 내부에 들어있는 함수를 호출하는 코드를 확인해 보자.

두 번째로 선언한 vbar을 호출해 보면, 어셈블리로 다음과 같이 나타난다.


mov    -0x18(%rbp),%rax

mov    (%rax),%rax

add    $0x8,%rax

mov    (%rax),%rax

mov    -0x18(%rbp),%rdx

mov    %rdx,%rdi

callq  *%rax


힙 영역에 할당된 주소가 들어 있는 %rbp-0x18에서 값을 꺼내어 %rax에 넣는다.

이로써 %rax에는 힙 영역에 할당된 A의 주소가 들어간다.

다음으로 바로 (%rax)를 통해 %rax 내부의 값을 꺼내어 다시 %rax에 집어넣는다.

여기서 %rax에는 가상함수 테이블(vTable) 주소가 들어갔다.

이제 %rax에 +8을 해주고 나서 다시 %rax 내부의 값을 꺼내서 %rax에 집어넣었다.

이제 %rax 내에는 *(vTable+0x8)의 값이 들어갔다.

그리고 call 명령을 통해 %rax 내부의 값을 집어넣고 함수를 실행한다.


 이를 보면 알 수 있겠지만, 가상함수 테이블이라고 하는 것은 선언된 가상함수들을 순서대로 집어넣어두는 하나의 배열이라고 볼 수 있다. 가상함수를 사용할 때는 이 가상함수 테이블을 이용하여 참조한다.


이제 가상함수의 다형성이 드러나는 부분을 살펴보자.


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
#include <iostream>
using namespace std;
 
struct A {
    virtual void vfoo() {
        cout << "A::vfoo()" << endl;
    }
    virtual void vbar() {
        cout << "A::vbar()" << endl;
    }
    virtual void vbaz() {
        cout << "A::vbaz()" << endl;
    }
 
    ~A(){
       cout << "A::Destructor()" << endl; 
    }
};
 
struct Z : A {
    virtual void vbaz(){
        cout << "Z::vbaz()" << endl;
    }
    
    ~Z(){
         cout << "Z::Destructor()" << endl; 
    }
};
 
int main()
{
    Z* z = new Z;
    A* a = z;
    a->vbaz();
    delete z;
    return 0;
}
 
cs


좀 코드가 길긴 하지만, 별로 어려운 코드는 아니다.

여기서 예를 들어 A의 가상함수 테이블의 주소가 0xD000이라고 치자.

또, Z의 가상함수 테이블의 주소가 0xD100이라고 치자.


A의 가상함수 내부를 대충 그려보면

0xD000 :[ &A::vfoo() ] [ &A::vbar() ] [ &A::vbaz() ]

이런 식으로 되어 있을 것이다.


Z는 A를 상속받아서 vbaz 함수를 오버라이딩했다.

이럴 경우 Z는 가상함수 테이블이 따로 할당된다. 이를 대충 그려보면

0xD100 : [ &A::vfoo() ] [ &A::vbar() ] [ &Z::vbaz() ]

이렇게 되어 있다.


보는 바와 같이, 오버라이딩하지 않은 가상함수는 테이블에 부모의 가상함수 주소가 그대로 할당되며

오버라이딩된 가상함수 주소만 바뀌고 오버라이딩한 함수로 대체된다.


위에서 보이듯이 자식인 Z의 객체로 z를 생성하고, 이를 부모인 A의 포인터에 집어넣을 수 있다.

여기서 a->vbaz() 를 실행하면 어떤 함수가 실행되는 것일까?

부모의 함수인 A::vbaz() 인가, 자식의 함수인 Z::vbaz() 인가?


지금까지의 과정을 생각해보면 간단히 답이 나올 것이다.

자식 객체는 부모 객체와 따로 가상함수 테이블의 주소를 갖게 되고, 이는 다른 포인터로 주소를 옮겨 담는다고 바뀌거나 하지 않으니

a->vbaz() 를 실행해도 Z로 생성하여 할당한 객체인 이상 Z::vbaz() 가 실행된다.


이것이 객체지향 프로그래밍의 특징 중 하나인 다형성이다.

그리고 이런 가상함수 테이블은 인스턴스마다 새로 할당하면 메모리의 낭비이므로 같은 가상함수 테이블 주소를 모든 인스턴스가 똑같이 참조한다.

위와 같이 오버라이딩이 일어날 때에만 새로 가상함수 테이블을 할당하여 새로 만들어내는 것이다.


여기서 유의할 점이 하나 있다.

마지막에 객체를 해제할 때, delete 키워드를 이용하여 객체를 할당 해제시키는데,

a->vbaz나 z->vbaz나 같았다는 점과 똑같이 생각하여 같은 주소를 가리키고 있는 a를 z 대신,

즉 delete z 대신 delete a를 사용해 부모 객체로 선언된 포인터를 해제하면 절대 안 된다.


그 이유는 바로 클래스의 소멸자 때문이다.

위에서 A와 Z에는 각각 소멸자가 존재하고, delete z를 이용하여 객체를 할당 해제했을 경우 자식과 부모의 소멸자가 둘 다 실행된다.

즉, ~Z()와 ~A() 가 모두 실행되는 것이다.

하지만 delete a를 통해 객체를 할당 해제했을 경우에는 부모의 소멸자만 진행된다. ~Z는 실행되지 않고 ~A만 실행되는 것이다.

만약 ~Z에(Z객체의 내부에 포인터가 있다고 가정했을 때) free나 delete를 통해 할당된 메모리를 해제하는 부분이 있었다면

이곳에서 심각한 메모리 누수가 발생하게 된다.

이를 이해하고 부모 클래스의 포인터에 자식 클래스를 담을 경우에는 조심해서 사용해야 한다.

(실제로 delete a, delete z를 두번 하여 버그가 발생하는 일도 빈번하다고 한다.)




이런 가상함수 테이블은 모두 힙에 할당되므로 해커 입장에서 봤을 때

가상함수 테이블 내부 주소를 변조하여 원하는 함수를 실행하도록 하는 것도 가능하다.


약간 GOT Overwrite와 비슷한 개념이지만, 나중에 따로 공부하고 시도해 보는 것도 괜찮을 것 같다.

'Programming' 카테고리의 다른 글

[VHDL] D Flip-Flop  (0) 2016.02.11
파이썬 웹 이미지 크롤러 (GUI)  (0) 2016.02.03
Assembly Programming - atoi  (0) 2015.10.11
Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
블로그 이미지

__미니__

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

,

오늘 짠 어셈블리의 끝판왕.

생각보다 atoi를 어셈블리로 짜려고 하니까 여간 힘든게 아니다.

프로그래밍을 하면서 알고리즘이나 자료구조 등을 제대로 공부하지 않아서인지는 모르지만 더 좋은 길도 잘 떠오르지 않았고...

결국 엄청 더러우면서도 작동은 하는 소스가 만들어졌다.

해당 프로그램은 stdin으로 숫자를 입력받아 해당 수만큼 반복하여 "Hello World"를 puts해주는 기능을 가졌다.

만약 숫자가 아닌 값을 입력하면 "It is Not Number!"를 출력하고 종료된다.

많이 조잡하지만 만들고 나니 뿌듯하긴 하다.


.globl main
main:
	push %ebp
	mov %esp, %ebp
	sub $0x04, %esp
	lea -0x04(%ebp), %ecx
	push %ecx
	call gets
	lea -0x04(%ebp), %eax
	push %eax
	call atoi
	mov %eax, %esi
	xor %edi, %edi
	.main_loop:
		cmp %edi, %esi
		jle .main_finish
		push $helloworld
		call puts
		inc %edi
		jmp .main_loop
	.main_finish:
		leave
		ret

atoi:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %esi
	push %esi
	call isNumberStr
	cmp $0x00, %eax
	jle .atoi_notNumber
	mov 0x08(%ebp), %esi
	push %esi
	call strlen
	mov %eax, %edi
	xor %eax, %eax
	mov 0x08(%ebp), %esi
	xor %ecx, %ecx
	xor %edx, %edx
	.atoi_loop:
		xor %ebx, %ebx
		movb (%esi), %bl
		cmpb $0x00, %bl
		je .atoi_finish
		cmp $0x00, %edi
		je .atoi_finish
		sub $0x30, %bl
		mov %edi, %ecx
		.atoi_loop2:
			cmp $0x01, %ecx
			je .atoi_add
			imul $0x0a, %ebx
			dec %ecx
			jmp .atoi_loop2
			.atoi_add:
				add %ebx, %eax
				inc %esi
				dec %edi
				jmp .atoi_loop
	.atoi_notNumber:
		push $notNumber
		call puts
		mov $-0x01, %eax
		jmp .atoi_finish
	.atoi_finish:
		leave
		ret


gets:
	push %ebp
	mov %esp, %ebp
	sub $0x1000, %esp
	mov $0x03, %eax
	mov $0x00, %ebx
	lea -0x1000(%ebp), %ecx
	mov $0x1000, %edx
	int $0x80
	lea -0x1000(%ebp), %eax
	push $0x0a
	push %eax
	call strchr
	movb $0x00, (%eax)
	lea -0x1000(%ebp), %eax
	push %eax
	mov 0x08(%ebp), %eax
	push %eax
	call strcpy
	leave
	ret

isAlpha:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	movb 0x08(%ebp), %al
	movb %al, %bl
	cmpb $0x41, %al
	jl .isAlpha_notalpha
	cmpb $0x5a, %al
	jng .isAlpha_alpha
	cmpb $0x61, %al
	jl .isAlpha_notalpha
	cmpb $0x7a, %al
	jng .isAlpha_alpha
	jmp .isAlpha_notalpha
	.isAlpha_alpha:
		mov $0x01, %eax
		jmp .isAlpha_finish
	.isAlpha_notalpha:
		mov $-0x01, %eax
		jmp .isAlpha_finish
	.isAlpha_finish:
		leave
		ret

isNumber:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	movb 0x08(%ebp), %al
	cmpb $0x30, %al
	jl .isNumber_notnumber
	cmpb $0x39, %al
	jng .isNumber_number
	jmp .isNumber_notnumber
	.isNumber_number:
		mov $0x01, %eax
		jmp .isNumber_finish
	.isNumber_notnumber:
		mov $-0x01, %eax
		jmp .isNumber_finish
	.isNumber_finish:
		leave
		ret

isAlphaStr:
	push %ebp
	mov %esp, %ebp
	xor %ebx, %ebx
	mov 0x08(%ebp), %esi
	.isAlphaStr_loop:
		movb (%esi), %bl
		and $0xff, %ebx
		cmpb $0x00, %bl
		je .isAlphaStr_Alpha
		push %ebx
		call isAlpha
		cmpb $0x00, %al
		jl .isAlphaStr_notAlpha
		inc %esi
		jmp .isAlphaStr_loop
	.isAlphaStr_Alpha:
		mov $0x01, %eax
		jmp .isAlphaStr_finish
	.isAlphaStr_notAlpha:
		mov $-0x01, %eax
		jmp .isAlphaStr_finish
	.isAlphaStr_finish:
		leave
		ret

isNumberStr:
	push %ebp
	mov %esp, %ebp
	xor %ebx, %ebx
        mov 0x08(%ebp), %esi
        .isNumberStr_loop:
                movb (%esi), %bl
                and $0xff, %ebx
                cmpb $0x00, %bl
                je .isNumberStr_Number
                push %ebx
                call isNumber
                cmpb $0x00, %al
                jl .isNumberStr_notNumber
                inc %esi
                jmp .isNumberStr_loop
        .isNumberStr_Number:
                mov $0x01, %eax
                jmp .isNumberStr_finish
        .isNumberStr_notNumber:
                mov $-0x01, %eax
                jmp .isNumberStr_finish
        .isNumberStr_finish:
                leave
                ret
	leave
	ret

puts:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	push %eax
	call strlen
	mov %eax, %edx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov 0x08(%ebp), %ecx
	int $0x80
	push $0x0a
	lea (%esp), %ecx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov $0x01, %edx
	int $0x80
	add $0x04, %esp
	leave
	ret

strcpy:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	mov 0x08(%ebp), %edi
	mov 0x0c(%ebp), %esi
	.strcpy_loop:
		movb (%esi), %al
		movb %al, (%edi)
		cmpb $0x00, %al
		je .strcpy_finish
		inc %esi
                inc %edi
		jmp .strcpy_loop
	.strcpy_finish:
		leave
		ret


strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret

notNumber : .string "It is Not Number!"
helloworld : .string "Hello, World"


'Programming' 카테고리의 다른 글

파이썬 웹 이미지 크롤러 (GUI)  (0) 2016.02.03
[C++] 가상함수(Virtual Function)  (0) 2016.01.20
Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
Assembly Programming - strchr  (0) 2015.10.10
블로그 이미지

__미니__

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

,

요즘 어셈블리로 코딩하는 것에 재미가 들려서 그런지 계속 이런걸 만들고 있다.

어셈블리어로 printf 만드는게 그렇게 어렵다고 하길래 한번 짜보기 위해 그 기반이 될 모듈들을 만들고 있다.

아마도 포맷스트링을 처리해야 하므로 atoi는 기본으로 필요할 것 같다.

그를 위한 isAlpha와 isNumber이다.

전에 짰던 함수들도 좀 포함되어있지만 무시하도록 하자.


.globl main
main:
	push %ebp
	mov %esp, %ebp
	push $alpha
	call isNumberStr
	leave
	ret


isAlpha:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	movb 0x08(%ebp), %al
	movb %al, %bl
	cmpb $0x41, %al
	jl .isAlpha_notalpha
	cmpb $0x5a, %al
	jng .isAlpha_alpha
	cmpb $0x61, %al
	jl .isAlpha_notalpha
	cmpb $0x7a, %al
	jng .isAlpha_alpha
	jmp .isAlpha_notalpha
	.isAlpha_alpha:
		mov $0x01, %eax
		jmp .isAlpha_finish
	.isAlpha_notalpha:
		mov $-0x01, %eax
		jmp .isAlpha_finish
	.isAlpha_finish:
		leave
		ret

isNumber:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	movb 0x08(%ebp), %al
	cmpb $0x30, %al
	jl .isNumber_notnumber
	cmpb $0x39, %al
	jng .isNumber_number
	jmp .isNumber_notnumber
	.isNumber_number:
		mov $0x01, %eax
		jmp .isNumber_finish
	.isNumber_notnumber:
		mov $-0x01, %eax
		jmp .isNumber_finish
	.isNumber_finish:
		leave
		ret

isAlphaStr:
	push %ebp
	mov %esp, %ebp
	xor %ebx, %ebx
	mov 0x08(%ebp), %esi
	.isAlphaStr_loop:
		movb (%esi), %bl
		and $0xff, %ebx
		cmpb $0x00, %bl
		je .isAlphaStr_Alpha
		push %ebx
		call isAlpha
		cmpb $0x00, %al
		jl .isAlphaStr_notAlpha
		inc %esi
		jmp .isAlphaStr_loop
	.isAlphaStr_Alpha:
		mov $0x01, %eax
		jmp .isAlphaStr_finish
	.isAlphaStr_notAlpha:
		mov $-0x01, %eax
		jmp .isAlphaStr_finish
	.isAlphaStr_finish:
		leave
		ret

isNumberStr:
	push %ebp
	mov %esp, %ebp
	xor %ebx, %ebx
        mov 0x08(%ebp), %esi
        .isNumberStr_loop:
                movb (%esi), %bl
                and $0xff, %ebx
                cmpb $0x00, %bl
                je .isNumberStr_Number
                push %ebx
                call isNumber
                cmpb $0x00, %al
                jl .isNumberStr_notNumber
                inc %esi
                jmp .isNumberStr_loop
        .isNumberStr_Number:
                mov $0x01, %eax
                jmp .isNumberStr_finish
        .isNumberStr_notNumber:
                mov $-0x01, %eax
                jmp .isNumberStr_finish
        .isNumberStr_finish:
                leave
                ret
	leave
	ret

puts:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	push %eax
	call strlen
	mov %eax, %edx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov 0x08(%ebp), %ecx
	int $0x80
	push $0x0a
	lea (%esp), %ecx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov $0x01, %edx
	int $0x80
	add $0x04, %esp
	leave
	ret

strcpy:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	mov 0x08(%ebp), %edi
	mov 0x0c(%ebp), %esi
	.strcpy_loop:
		movb (%esi), %al
		movb %al, (%edi)
		cmpb $0x00, %al
		je .strcpy_finish
		inc %esi
                inc %edi
		jmp .strcpy_loop
	.strcpy_finish:
		leave
		ret


strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret

alpha : .string "12345678a0"
helloworld : .string "Hello, World"


'Programming' 카테고리의 다른 글

[C++] 가상함수(Virtual Function)  (0) 2016.01.20
Assembly Programming - atoi  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
Assembly Programming - strchr  (0) 2015.10.10
Assembly Programming - puts  (0) 2015.10.10
블로그 이미지

__미니__

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

,

이번주 구현 목표였던 gets.

만들면 날려먹고 만들면 또 날려먹어서 고생좀 했다.



.globl main
main:
	push %ebp
	mov %esp, %ebp
	sub $0x1000, %esp
	lea -0x1000(%ebp), %eax
	push %eax
	call gets
	lea -0x1000(%ebp), %eax
	push %eax
	call puts
	leave
	ret

gets:
	push %ebp
	mov %esp, %ebp
	sub $0x1000, %esp
	mov $0x03, %eax
	mov $0x00, %ebx
	lea -0x1000(%ebp), %ecx
	mov $0x1000, %edx
	int $0x80
	lea -0x1000(%ebp), %eax
	push $0x0a
	push %eax
	call strchr
	movb $0x00, (%eax)
	lea -0x1000(%ebp), %eax
	push %eax
	mov 0x08(%ebp), %eax
	push %eax
	call strcpy
	leave
	ret

strchr:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	movb 0x0c(%ebp), %bl
	.strchr_loop:
		cmpb (%eax), %bl
		je .strchr_finish
		cmpb $0x00, (%eax)
		je .strchr_notfound
		inc %eax
		jmp .strchr_loop
	.strchr_notfound:
		mov $0x01, %eax
	.strchr_finish:
		leave
		ret


puts:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	push %eax
	call strlen
	mov %eax, %edx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov 0x08(%ebp), %ecx
	int $0x80
	push $0x0a
	lea (%esp), %ecx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov $0x01, %edx
	int $0x80
	add $0x04, %esp
	leave
	ret

strcpy:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	mov 0x08(%ebp), %edi
	mov 0x0c(%ebp), %esi
	.strcpy_loop:
		movb (%esi), %al
		movb %al, (%edi)
		cmpb $0x00, %al
		je .strcpy_finish
		inc %esi
                inc %edi
		jmp .strcpy_loop
	.strcpy_finish:
		leave
		ret


strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret

'Programming' 카테고리의 다른 글

Assembly Programming - atoi  (0) 2015.10.11
Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - strchr  (0) 2015.10.10
Assembly Programming - puts  (0) 2015.10.10
Assembly Programming - strcpy  (0) 2015.10.10
블로그 이미지

__미니__

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

,

gets 함수를 만들기 전 '\n'의 위치 등을 찾아주는 strchr 함수를 구현하는 것이 편할 것 같아 구현하였다.


.globl main
main:
	push %ebp
	mov %esp, %ebp
	push $0x65
	push $helloworld
	call strchr
	push %eax
	call puts
	leave
	ret

strchr:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	movb 0x0c(%ebp), %bl
	.strchr_loop:
		cmpb (%eax), %bl
		je .strchr_finish
		cmpb $0x00, (%eax)
		je .strchr_notfound
		inc %eax
		jmp .strchr_loop
	.strchr_notfound:
		mov $0x01, %eax
	.strchr_finish:
		leave
		ret


puts:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	push %eax
	call strlen
	mov %eax, %edx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov 0x08(%ebp), %ecx
	int $0x80
	push $0x0a
	lea (%esp), %ecx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov $0x01, %edx
	int $0x80
	add $0x04, %esp
	leave
	ret

strcpy:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	mov 0x08(%ebp), %edi
	mov 0x0c(%ebp), %esi
	.strcpy_loop:
		movb (%esi), %al
		movb %al, (%edi)
		cmpb $0x00, %al
		je .strcpy_finish
		inc %esi
                inc %edi
		jmp .strcpy_loop
	.strcpy_finish:
		leave
		ret


strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret


helloworld : .string "Hello, World"


'Programming' 카테고리의 다른 글

Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
Assembly Programming - puts  (0) 2015.10.10
Assembly Programming - strcpy  (0) 2015.10.10
Assembly Programming - strlen  (0) 2015.10.10
블로그 이미지

__미니__

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

,

원래 커맨드라인에 출력하기 위해서는 write 함수를 출력하는 인터럽트를 걸어야 하는데 그걸 하려면 또 strlen으로 문자열의 길이도 가져와야 하고 인자 넣기가 귀찮아 puts 함수를 구현하였다.

문자열 끝에 '\n'을 자동으로 붙여 주기 때문에 조금 고민했었는데 모 학생의 도움으로 간단히 구현하였다.


.globl main
main:
	push %ebp
	mov %esp, %ebp
	push $helloworld
	call puts
	leave
	ret

puts:
	push %ebp
	mov %esp, %ebp
	mov 0x08(%ebp), %eax
	push %eax
	call strlen
	mov %eax, %edx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov 0x08(%ebp), %ecx
	int $0x80
	push $0x0a
	lea (%esp), %ecx
	mov $0x04, %eax
	mov $0x01, %ebx
	mov $0x01, %edx
	int $0x80
	add $0x04, %esp
	leave
	ret

strcpy:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	mov 0x08(%ebp), %edi
	mov 0x0c(%ebp), %esi
	.strcpy_loop:
		movb (%esi), %al
		movb %al, (%edi)
		cmpb $0x00, %al
		je .strcpy_finish
		inc %esi
                inc %edi
		jmp .strcpy_loop
	.strcpy_finish:
		leave
		ret


strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret


helloworld : .string "Hello, World"


'Programming' 카테고리의 다른 글

Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
Assembly Programming - strchr  (0) 2015.10.10
Assembly Programming - strcpy  (0) 2015.10.10
Assembly Programming - strlen  (0) 2015.10.10
블로그 이미지

__미니__

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

,

main에 버퍼를 할당하고 만들어둔 string 을 strcpy를 이용해 복사하여 출력하는 소스

.globl main
main:
	push %ebp
	mov %esp, %ebp
	sub $0x20, %esp
	lea -0x20(%ebp), %eax
	push $helloworld
	push %eax
	call strcpy
	lea -0x20(%ebp), %eax
	push %eax
	call strlen
	mov %eax, %edx
	mov $0x04, %eax
	mov $0x01, %ebx
	lea -0x20(%ebp), %ecx
	int $0x80
	leave
	ret

strcpy:
	push %ebp
	mov %esp, %ebp
	xor %eax, %eax
	mov 0x08(%ebp), %edi
	mov 0x0c(%ebp), %esi
	.strcpy_loop:
		movb (%esi), %al
		movb %al, (%edi)
		cmpb $0x00, %al
		je .strcpy_finish
		inc %esi
                inc %edi
		jmp .strcpy_loop
	.strcpy_finish:
		leave
		ret


strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret


helloworld : .string "Hello, World"


'Programming' 카테고리의 다른 글

Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
Assembly Programming - strchr  (0) 2015.10.10
Assembly Programming - puts  (0) 2015.10.10
Assembly Programming - strlen  (0) 2015.10.10
블로그 이미지

__미니__

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

,


.s파일 빌드 방법은 .c와 같다.

gcc -o prog prog.c

32바이트 빌드는 뒤에 -m32를 붙이면 된다.

%eax에 길이가 들어가겠으나 strlen만으로는 확인할 방법이 없어 GDB로 직접 디버깅하여 실제 문자열 길이가 리턴되는것을 확인하였다.

.globl main
main:
	push %ebp
	mov %esp, %ebp
	push $helloworld
	call strlen
	leave
	ret

strlen:
	push %ebp
	mov %esp, %ebp
	xor %ecx, %ecx
	mov 0x08(%ebp), %eax
	.strlen_loop:
		movb (%eax), %bl
		cmpb $0x00, %bl
		je .strlen_finish
		inc %eax
		inc %ecx
		jmp .strlen_loop
	.strlen_finish:
		mov %ecx, %eax
		leave
		ret

helloworld : .string "Hello, World"


'Programming' 카테고리의 다른 글

Assembly Programming - isAlpha, isNumber  (0) 2015.10.11
Assembly Programming - gets  (0) 2015.10.10
Assembly Programming - strchr  (0) 2015.10.10
Assembly Programming - puts  (0) 2015.10.10
Assembly Programming - strcpy  (0) 2015.10.10
블로그 이미지

__미니__

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

,