[网络安全自学篇] 十五.Python攻防之多线程、C段扫描和数据库编程(二)

这是做者的系列网络安全自学教程,主要是关于网安工具和实践操做的在线笔记,特分享出来与博友共勉,但愿您们喜欢,一块儿进步。前文分享了Python网络攻防相关基础知识,包括正则表达式、Web编程和套接字通讯,本文将继续分析Python攻防之多线程、C段扫描和数据库编程。本文参考了爱春秋ADO老师的课程内容,这里也推荐你们观看他Bilibili和ichunqiu的课程,同时也结合了做者以前的经验进行讲解。html

做者做为网络安全的小白,分享一些自学基础教程给你们,但愿大家喜欢。同时,更但愿你能与我一块儿操做深刻进步,后续也将深刻学习网络安全和系统安全知识并分享相关实验。总之,但愿该系列文章对博友有所帮助,写文不容易,大神请飘过,不喜勿喷,谢谢!python

下载地址:https://github.com/eastmountyxz/NetworkSecuritySelf-study
百度网盘:https://pan.baidu.com/s/1dsunH8EmOB_tlHYXXguOeA 提取码:izebgit

前文学习:
[网络安全自学篇] 一.入门笔记之看雪Web安全学习及异或解密示例
[网络安全自学篇] 二.Chrome浏览器保留密码功能渗透解析及登陆加密入门笔记
[网络安全自学篇] 三.Burp Suite工具安装配置、Proxy基础用法及暴库示例
[网络安全自学篇] 四.实验吧CTF实战之WEB渗透和隐写术解密
[网络安全自学篇] 五.IDA Pro反汇编工具初识及逆向工程解密实战
[网络安全自学篇] 六.OllyDbg动态分析工具基础用法及Crakeme逆向破解
[网络安全自学篇] 七.快手视频下载之Chrome浏览器Network分析及Python爬虫探讨
[网络安全自学篇] 八.Web漏洞及端口扫描之Nmap、ThreatScan和DirBuster工具
[网络安全自学篇] 九.社会工程学之基础概念、IP获取、IP物理定位、文件属性
[网络安全自学篇] 十.论文之基于机器学习算法的主机恶意代码
[网络安全自学篇] 十一.虚拟机VMware+Kali安装入门及Sqlmap基本用法
[网络安全自学篇] 十二.Wireshark安装入门及抓取网站用户名密码(一)
[网络安全自学篇] 十三.Wireshark抓包原理(ARP劫持、MAC泛洪)及数据流追踪和图像抓取(二)
[网络安全自学篇] 十四.Python攻防之基础常识、正则表达式、Web编程和套接字通讯(一)github

前文欣赏:
[渗透&攻防] 一.从数据库原理学习网络攻防及防止SQL注入
[渗透&攻防] 二.SQL MAP工具从零解读数据库及基础用法
[渗透&攻防] 三.数据库之差别备份及Caidao利器
[渗透&攻防] 四.详解MySQL数据库攻防及Fiddler神器分析数据包web

[python] 专题七.网络编程之套接字Socket、TCP和UDP通讯实例
[python] 专题八.多线程编程之thread和threading
[python] 专题九.Mysql数据库编程基础知识正则表达式

参考文献:
《安全之路Web渗透技术及实战案例解析》陈小兵老师
《Wireshark数据包分析实战》第二版 Chris Sanders
《TCP/IP协议栈详解卷一》 W.Richard Stevens算法

《Wireshark协议分析从入门到精通》-51cto老师
https://www.bilibili.com/video/av29479068
2019 Python黑客编程:安全工具开发 - bilibili 白帽黑客教程
http://www.heibanke.com/lesson/crawler_ex00/
python subprocess模块使用总结sql

声明:本人坚定反对利用社会工程学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络须要咱们共同维护,更推荐你们了解它们背后的原理,更好地进行防御。shell


一.Python多线程

1.进程和线程

进程: 是程序的一次执行,每一个进程都有本身的地址空间、内存、数据栈及其余记录运行轨迹的辅助数据。
线程: 全部的线程都运行在同一个进程当中,共享相同的运行环境。线程有开始、顺序执行和结束三个部分。数据库

因为单线程效率低,这里引入了多线程编程。
计算机的核心是CPU,它承担了全部的计算任务,它就像一座工厂,时刻运行着。假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其余车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
进程就比如工厂的车间,它表明CPU所能处理的单个任务。任一时刻,CPU老是运行一个进程,其余进程处于非运行状态。一个车间里,能够有不少工人。他们协同完成一个任务。线程就比如车间里的工人,一个进程能够包括多个线程。

2.thread模块

Python thread模块能够调用下述函数实现多线程开启。它将产生一个新线程,在新的线程中用指定的参数和可选的kwargs来调用这个函数。
start_new_thread(function, args kwargs=None)

注意:使用这个方法时,必定要加time.sleep()函数,不然每一个线程均可能不执行。此方法还有一个缺点,遇到较复杂的问题时,线程数不易控制。

# -*- coding: utf-8 -*-
# By:CSDN Eastmount 2019-10-05
import thread
import time

def fun1():
print “hello world %s” % time.ctime()

#多线程
def main():
thread.start_new_thread(fun1, ()) #无参数
thread.start_new_thread(fun1, ())
time.sleep(2)
print “over”

#程序成功在同一时刻运行两个函数
if name == main:
main()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

输出结果以下图所示:

在这里插入图片描述

上面的代码简单讲述了thread模块的多线程使用。但实际应用中这种例子遇到比较少,而哪种状况比较多呢?假如说全部C段的地址ping探测其是否存活,代码以下。

# -*- coding: utf-8 -*-
# By:CSDN Eastmount 2019-10-05
import thread
import time
from subprocess import Popen, PIPE

def ping_check():
#check = Popen([’/bin/bash’, ‘-c’, 'ping -c 2 ’ + ‘127.0.0.1’], stdin=PIPE, stdout=PIPE)
ip = ‘127.0.0.1’
#ping指定次数后中止ping 但报错访问被拒绝,选项 -c 须要具备管理权限。
#check = Popen(“ping -c 3 {0} \n”.format(ip), stdin=PIPE, stdout=PIPE, shell=True)
check = Popen(“ping {0} \n”.format(ip), stdin=PIPE, stdout=PIPE, shell=True)
data = check.stdout.read() #数据
print data

#程序成功在同一时刻运行两个函数
if name == main:
ping_check()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

若是输入的ip地址为本机127.0.0.1,则输出正常连通结果,以下所示。

在这里插入图片描述

若是输入的ip地址为本机220.0.0.1,则提示超时,以下图所示。

在这里插入图片描述

接着思考:如何对一个C段网址进行ping探测呢?
设计一个循环,若是主机不存在,返回的是timeout;若是主机存在,则包含TTL字样,这里以TTL为判断标准,从而判断存活的数据。

# -*- coding: utf-8 -*-
# By:CSDN Eastmount 2019-10-05
import thread
import time
from subprocess import Popen, PIPE

def ping_check():
ip = ‘127.0.0.1’
check = Popen(“ping {0} \n”.format(ip), stdin=PIPE, stdout=PIPE, shell=True)
data = check.stdout.read() #数据
if ‘TTL’ in data: #存活
print ‘UP’

#程序成功在同一时刻运行两个函数
if name == main:
ping_check()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

输出结果为“UP”。

3.检测ichunqiu地址

接下来咱们尝试检测ichunqiu网站的ip地址存活状况。首先,调用ping命令检测该网站的ip地址,即:1.31.128.240。

在这里插入图片描述

这里将ping_check()函数设置一个传递参数,对应ip地址,对它进行探测;经过thread线程实现ip地址存活性探测,能探测到不少存活的主机。

# -*- coding: utf-8 -*-
# By:CSDN Eastmount 2019-10-05
import thread
import time
from subprocess import Popen, PIPE

def ping_check(ip):
check = Popen(“ping {0} \n”.format(ip), stdin=PIPE, stdout=PIPE, shell=True)
data = check.stdout.read() #数据
if ‘TTL’ in data: #存活
print ‘%s is UP’ % ip

#主函数
if name == main:
#寻找目标 ichunqiu 1.31.128.240
for i in range(1,255):
ip = ‘1.31.128.’ + str(i)
#多线程方法
thread.start_new_thread(ping_check, (ip, ))
time.sleep(0.1)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出结果以下图所示:

在这里插入图片描述

问题:
在多线程编程中,几个线程是同时启动,因此输出也是输出在一行,那怎么才能换行输出呢?这里使用系统输出。

# -*- coding: utf-8 -*-
# By:CSDN Eastmount 2019-10-05
import thread
import time
from subprocess import Popen, PIPE
import sys

def ping_check(ip):
check = Popen(“ping {0} \n”.format(ip), stdin=PIPE, stdout=PIPE, shell=True)
data = check.stdout.read() #数据
if ‘TTL’ in data: #存活
sys.stdout.write(’%s is UP \n’ % ip)

#主函数
if name == main:
#寻找目标 ichunqiu 1.31.128.240
for i in range(1,255):
ip = ‘1.31.128.’ + str(i)
#多线程方法
thread.start_new_thread(ping_check, (ip, ))
time.sleep(0.1)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

按行输出结果,以下图所示:

在这里插入图片描述

4.threading模块

thread模块存在一些缺点,尤为是线程数不能被控制。下面使用threading解决线程数可控制的问题。

  • 使用threading模块
  • 子类化Thread类
# -*- coding: utf-8 -*-
import threading
import time
import sys

def fun1(key):
sys.stdout.write(‘hello %s:%s \n’%(key, time.ctime()))

def main():
threads = []
keys = [‘a’, ‘b’, ‘c’]

<span class="token comment">#线程数</span>
threads_count <span class="token operator">=</span> <span class="token builtin">len</span><span class="token punctuation">(</span>keys<span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    t <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>fun1<span class="token punctuation">,</span>  args<span class="token operator">=</span><span class="token punctuation">(</span>keys<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    threads<span class="token punctuation">.</span>append<span class="token punctuation">(</span>t<span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    threads<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    threads<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>

if name == main:
main()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

输出结果以下图所示,三个线程同时发生。

在这里插入图片描述

多线程threading方法实现了咱们想要的功能,可以控制线程数,例如想写成requests模块,获取网站的status_code状态码。

# -*- coding: utf-8 -*-
import threading
import time
import requests
import sys

def fun1():
time_start = time.time()
r = requests.get(url=“http://www.eastmountyxz.com/”)
times = time.time() - time_start
#print(‘Status:%s–%s–%s’%(r.status_code, times, time.strftime(’%H:%M:%S’)))
sys.stdout.write(‘Status:%s–%s–%s\n’%(r.status_code, times, time.strftime(’%H:%M:%S’)))

def main():
threads = []
#线程数 网页访问10次
threads_count = 10

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    t <span class="token operator">=</span> threading<span class="token punctuation">.</span>Thread<span class="token punctuation">(</span>target<span class="token operator">=</span>fun1<span class="token punctuation">,</span>  args<span class="token operator">=</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    threads<span class="token punctuation">.</span>append<span class="token punctuation">(</span>t<span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    threads<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    threads<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>

if name == main:
main()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

输出结果以下图所示:

在这里插入图片描述


二.多线程+Queue实现C段扫描

生产者-消费者问题和Queue模块:

  • Queue模块:qsize()、empty()、full()、put()、get()
  • 完美搭配,Queue+Thread

虽然threading解决了线程数可控问题,可是面对复杂问题的时候,好比生产者和消费者问题,仍然不能很好地解决。

生产者和消费者问题
生产者生产货物,将货物放到队列数据中,生产者在生产这些货物时,它的时间是不肯定的;当生存着将货物交给消费者,消耗的时间也是不肯定的;因为两个时间都不肯定,多线程编程存在必定问题。

这里引入Queue模块解决该问题。

import Queue

queue = Queue.Queue()

for i in range(10):
queue.put(i)
print(queue.empty())
print(queue.qsize())

#取数据 get依次取出里面的数据
print(queue.get())
print(queue.get())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出结果以下所示:

在这里插入图片描述

生产者利用Queue将全部数据货物按顺序放入Queue,接着消费者依次取出Queue中的数据。接着实现C段扫描。

# -*- coding: utf-8 -*-
# By:CSDN Eastmount 2019-10-05
import threading
import Queue
import sys
from subprocess import Popen, PIPE

#定义一个类 传入参数queue
class DoRun(threading.Thread):
def init(self, queue):
threading.Thread.init(self)
self._queue = queue

<span class="token keyword">def</span> <span class="token function">run</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment">#非空取数据</span>
    <span class="token keyword">while</span> <span class="token operator">not</span> self<span class="token punctuation">.</span>_queue<span class="token punctuation">.</span>empty<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        ip <span class="token operator">=</span> self<span class="token punctuation">.</span>_queue<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token comment">#print ip</span>
        check_ping <span class="token operator">=</span> Popen<span class="token punctuation">(</span><span class="token string">"ping {0} \n"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>ip<span class="token punctuation">)</span><span class="token punctuation">,</span> stdin<span class="token operator">=</span>PIPE<span class="token punctuation">,</span> stdout<span class="token operator">=</span>PIPE<span class="token punctuation">,</span>  shell<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
        data <span class="token operator">=</span> check_ping<span class="token punctuation">.</span>stdout<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token string">'TTL'</span> <span class="token keyword">in</span> data<span class="token punctuation">:</span>
            sys<span class="token punctuation">.</span>stdout<span class="token punctuation">.</span>write<span class="token punctuation">(</span>ip<span class="token operator">+</span><span class="token string">' is UP.\n'</span><span class="token punctuation">)</span>

def main():
threads = []
threads_count = 100
queue = Queue.Queue()

<span class="token comment">#放入ip地址</span>
<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">255</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    queue<span class="token punctuation">.</span>put<span class="token punctuation">(</span><span class="token string">'1.31.128.'</span> <span class="token operator">+</span> <span class="token builtin">str</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span>threads_count<span class="token punctuation">)</span><span class="token punctuation">:</span>
    threads<span class="token punctuation">.</span>append<span class="token punctuation">(</span>DoRun<span class="token punctuation">(</span>queue<span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> threads<span class="token punctuation">:</span>
    i<span class="token punctuation">.</span>start<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">for</span> i <span class="token keyword">in</span> threads<span class="token punctuation">:</span>
    i<span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">)</span>

if name == main:
main()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

最终输出结果以下图所示,经过该代码能够实现检测某网站ip段的存活状况。

在这里插入图片描述


三.数据库编程

1.基础概念

Python数据库编程,这里直接引入核心代码便可,更多参考前文:[python] 专题九.Mysql数据库编程基础知识

  • Python DB API
  • Python MySQL 开发环境
  • Python数据库编程实例

早期每一个数据库和Python创建链接时,都须要有对应的接口程序,这样致使数据库比较混乱,须要学习不少新的接口程序并修改大量代码。

在这里插入图片描述

后来为了解决该问题,咱们定义了相关的接口规范,全部数据库接入其中便可编程。咱们只要学会Python DB API,就能实现对不一样版本数据库的操做,这样更加方便快捷,更改的代码较少。

在这里插入图片描述
Python访问数据库的基本流程以下图所示:
在这里插入图片描述

在这里插入图片描述

2.MySQLdb

Python调用MsSQL须要导入MySQLdb库,以下:

import MySQLdb

connect()函数
主要使用的方法是connect对象。connect()方法生成一个connect对象,用于访问数据库,其参数以下:

user:Username
password:Password
host:Hostname
database:DatabaseName
dsn:Data source name

注意并不是全部的接口程序都严格按照这种格式,如MySQLdb。

 
 
 
 
  • 1
  • 2
import MySQLdb conn = MySQLdb.connect(host='localhost', db='test01', user='root', passwd='123456', port=3306, charset='utf8')

connect()对象方法以下:

lose():关闭数据库链接,或者关闭游标对象
commit():提交当前事务
rollback():取消当前事务
cursor():建立游标或类游标对象
errorhandler(cxn,errcls,errval):做为已给游标的句柄

注意,执行close()方法则上述的链接对象方法不能再使用,不然发生异常。commit()、rollback()、cursor()或许更对于支持事务的数据库更有意义。数据库事务(Database Transaction) ,是指做为单个逻辑工做单元执行的一系列操做,要么完整地执行,要么彻底地不执行。 一旦你完成了数据库链接,关闭了游标对象,而后在执行commit()提交你的操做,而后关闭链接。

游标对象
上面说了connect()方法用于提供链接数据库的接口,若是要对数据库操做那么还须要使用游标对象。游标对象的属性和方法:

fetchone():能够看做fetch(取出) one(一个),也就是获得结果集的下一行(一行)。
fetchmany(size):能够看做fetch(取出)many(多个),这里的参数是界限,获得结果集的下几行(几行)
fetchall():顾名思义,取得全部。
execute(sql):执行数据库操做,参数为sql语句。
close():不须要游标时尽量的关闭

下面是一个获取MySQL数据库版本的代码,它覆盖了Python连接数据库的基本过程。

# -*- coding: utf-8 -*-
import MySQLdb

#创建链接
conn = MySQLdb.connect(
host = ‘127.0.0.1’,
port = 3306,
user = ‘root’,
passwd = ‘123456’
)

#执行最简单的语句
cus = conn.cursor()

#查看MySQL的版本
sql = ‘select version()’

cus.execute(sql)

#查看返回结果
print(cus.fetchone())

#关闭链接和对象
cus.close()
conn.close()

  • 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

输出结果以下图所示:

在这里插入图片描述

Python网络爬虫和MySQL数据库结合的文章参考做者前文:
[python爬虫] 招聘信息定时系统 (一).BeautifulSoup爬取信息并存储MySQL
[python爬虫] Selenium爬取内容并存储至MySQL数据库


四.总结

但愿这篇文章对你有所帮助,这是Python网络攻防很是基础的一篇博客,后续做者也将继续深刻学习,制做一些经常使用的小工具供你们交流。

下午经过CSDN认识了一位计算机视觉方向的博友,咱们相约博士毕业回各自的家乡教书,虽天各一方,而且不认识,但已经是朋友,祝好。

在这里插入图片描述

晚上终于看懂了第一篇恶意代码检测的论文,溯源和扫描还须要多学习,多实验。同时,看了几篇讲梵高的文章,挺不错的。分享一段:

梵高的生活较为落魄和不堪,但他的做品里永远是明亮的,美好的,纯真的,积极的。1889年,他在精神病院里画出了《星空》,那大概是梵高心里最纯洁的颜色,罗纳河上的星空,让处于压迫的心里仍闪烁着点点星光。善良淳朴的人性之美,和坚持纯粹的艺术之美交合着。这大概就是为何他的做品特别能打动人的缘由!
晚安,女神,我也准备学学油画

(By:Eastmount 2019-10-05 晚上11点 http://blog.csdn.net/eastmount/ )

</div>