前言

angr 是一个基于 符号执行 和 模拟执行 的二进制框架,可以用在很多的场景,比如逆向分析,漏洞挖掘等。本文对他的学习做一个总结。

安装

这里介绍 ubuntu 下的安装,其他平台可以看 官方文档

首先安装一些依赖包

sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper

然后使用

mkvirtualenv angr && pip install angr

即可安装

建议使用 virtualenv 来安装,因为 angr 用到的一些库和正常下的不一样,直接 pip 安装可能会安装不上去

angr常用对象及简单使用

使用 angr 的大概步骤

  • 创建 project
  • 设置 state
  • 新建 符号量 : BVS (bitvector symbolic )BVV (bitvector value)
  • 把符号量设置到内存或者其他地方
  • 设置 Simulation Managers , 进行路径探索的对象
  • 运行,探索满足路径需要的值
  • 约束求解,获取执行结果

Project对象

介绍与简单使用

载入二进制文件使用 angr.Project 函数,它的第一个参数是待载入文件的路径,后面还有很多的可选参数,具体可以看 官方文档

p = angr.Project('./issue', load_options={"auto_load_libs": False})

auto_load_libs 设置是否自动载入依赖的库,如果设置为 True 的话会自动载入依赖的库,然后分析到库函数调用时也会进入库函数,这样会增加分析的工作量,也有能会跑挂。

载入文件后,就可以通过 project 对象获取信息以及进行后面的操作

In [11]: proj = angr.Project('/bin/true')

In [12]: proj.loader.shared_objects
Out[12]: OrderedDict([('true', <ELF Object true, maps [0x400000:0x6063bf]>), (u'libc.so.6', <ELF Object libc-2.23.so, maps [0x1000000:0x13c999f]>), (u'ld-linux-x86-64.so.2', <ELF Object ld-2.23.so, maps [0x2000000:0x2227167]>)]) In [13]: proj = angr.Project('/bin/true', load_options={"auto_load_libs": False}) In [14]: proj.loader.shared_objects
Out[14]: OrderedDict([('true', <ELF Object true, maps [0x400000:0x6063bf]>)]) In [15]:

可以看到在使用 {"auto_load_libs": False} 后一些动态链接库没有被载入。

有两个小点还需要了解一下

  • 如果 auto_load_libstrue, 那么程序如果调用到库函数的话就会直接调用 真正的库函数 ,如果有的库函数逻辑比较复杂,可能分析程序就出不来了~~。同时 angr 使用 python 实现了很多的库函数(保存在 angr.SIM_PROCEDURES 里面),默认情况下会使用列表内部的函数来替换实际的函数调用,如果不在列表内才会进入到真正的 library.
  • 如果 auto_load_libsfalse , 程序调用函数时,会直接返回一个 不受约束的符号值。

hook

我们可以在 angr 中使用 hook 来把指定地址的二进制代码替换为 python 代码。angr 在模拟执行程序时,执行每一条指令前会检测该地址处是否已经被 hook ,如果是就不执行这条语句,转而执行 hook 时指定的 python 处理代码。

下面看实例

目标程序地址

https://github.com/angr/angr-doc/tree/master/examples/sym-write

示例脚本

#!/usr/bin/env python
# coding=utf-8
import angr
import claripy def hook_demo(state):
state.regs.eax = 0
state.regs.ebx = 0xdeadbeef p = angr.Project("./examples/sym-write/issue", load_options={"auto_load_libs": False})
p.hook(addr=0x08048485, hook=hook_demo, length=2)
state = p.factory.blank_state(addr=0x0804846B, add_options={"SYMBOLIC_WRITE_ADDRESSES"}) u = claripy.BVS("u", 8)
state.memory.store(0x0804A021, u)
sm = p.factory.simgr(state)
sm.explore(find=0x080484DB)
st = sm.found[0]
print hex(st.se.eval(st.regs.ebx))

介绍一下脚本的流程

  • 首先 使用 angr.Project 载入文件, 设置 auto_load_libsfalse 则不加载依赖的 lib

  • 然后 使用 p.hook0x08048485 处的 2 字节的指令 为 hook_demo,之后执行 0x08048485 就会去执行 hook_demo

  • 然后创建一个 state , 因为要往内存里面设置 符号量 ( BVS ),设置 SYMBOLIC_WRITE_ADDRESSES

  • 然后新建一个 8 位长度的符号量,并把它存到 0x0804A021 (全局变量 u 的位置)

  • 然后开始探索路径,最后求解出使得 程序执行到 you win 代码块的符号量的解。

这里主要讲 p.hook 的处理, 这里使用了 hook 函数的三个参数

p.hook(addr=0x08048485, hook=hook_demo, length=2)
  • addr 为待 hook 指令的地址
  • hookhook 的处理函数,在执行到 addr 时,会执行 这个函数,同时把 当前的 state 对象作为参数传递过去
  • length 为 待 hook 指令的长度,在 执行完 hook 函数以后,angr 需要根据 length 来跳过这条指令,执行下一条指令

在上面的示例中, hook0x08048485 处的指令

这是一条 xor eax, eax 的指令,长度为 2 .

def hook_demo(state):
state.regs.eax = 0
state.regs.ebx = 0xdeadbeef

为了做示范,这里就是把 eax 设置为 0xor eax,eax 的作用), 然后 设置 ebx0xdeadbeef, 因为后续不会用到 ebx , 修改它可以在路径探索完后查看这个值是否符合预期。

可以看到 ebx 被修改成了 0xdeadbeef

SimState对象

这个对象保存着程序运行到某一阶段的状态信息。

通过这个对象可以操作某一运行状态的上下文信息,比如内存,寄存器等

创建state

In [8]: p = angr.Project("./hello_angr")

In [9]: st = p.factory.entry_state()

In [10]: st.regs.rsp
Out[10]: <BV64 0x7fffffffffeff98> In [11]: st
Out[11]: <SimState @ 0x4004a0> In [12]:

首先加载二进制分析文件,创建 project 对象,然后创建一个 entry_state , 之后就可以通过 这个 state 对象,获取或者修改此时程序的运行状态

entry_state : 做一些初始化工作,然后在 程序的 入口停下

还有一个用的比较多的是

st = p.factory.blank_state(addr=0x4004a0)

这会创建一个 blank_state 对象,这个对象里面很多东西都是未初始化的,当程序访问未初始化的数据时,会返回一个不受约束的符号量

基本操作

state 对象一般是作为 符号执行开始前 创建用来 为 后续的执行 初始化一些数据,比如栈状态,寄存器值。

或者在 路径探索结束后 ** 返回一个 state 对象供用户提取需要的值或进行 约束求解 ,解出到达目标分支所使用的符号量的值**。

访问寄存器

通过 state.regs 对象的属性访问以及修改寄存器的数据

In [12]: state.regs.r
state.regs.r10 state.regs.r14 state.regs.rax state.regs.rdi state.regs.rip
state.regs.r11 state.regs.r15 state.regs.rbp state.regs.rdx state.regs.rsi
state.regs.r12 state.regs.r8 state.regs.rbx state.regs.register_default state.regs.rsp
state.regs.r13 state.regs.r9 state.regs.rcx state.regs.rflags # 获取 rip 的值
In [12]: state.regs.rip
Out[12]: <BV64 0x400470> # 获取 rsp 的值
In [13]: state.regs.rsp
Out[13]: <BV64 0x7fffffffffeff78> # 获取 rbp 的值
In [14]: state.regs.rbp
Out[14]: <BV64 reg_38_36_64{UNINITIALIZED}> # 设置 rbp = rsp + 0x40
In [15]: state.regs.rbp = state.regs.rsp + 0x40 In [16]: state.regs.rbp
Out[16]: <BV64 0x7fffffffffeffb8> # 对于 BVV 和 BVS 都需要通过 solver 进行求解得到具体的值
In [26]: hex(state.se.eval(state.regs.rbp))
Out[26]: '0x7fffffffffeffb8L' In [27]: hex(state.solver.eval(state.regs.rbp))
Out[27]: '0x7fffffffffeffb8L'

访问内存

有两种方式访问内存,一个是通过 state.mem 使用数组索引类似的方式进行访问

In [64]: state.mem[state.regs.rsp].qword
Out[64]: <uint64_t <BV64 0x2> at 0x7fffffffffeff78> In [65]: state.mem[state.regs.rsp].qword = 0xdeadbeefdeadbeef In [66]: state.mem[state.regs.rsp].qword
Out[66]: <uint64_t <BV64 0xdeadbeefdeadbeef> at 0x7fffffffffeff78> In [67]: m = state.mem[state.regs.rsp] In [68]: m.
m.STRONGREF_STATE m.double m.int32_t m.register_default m.ssize m.uint32_t m.wstring
m.array m.dword m.int64_t m.resolvable m.ssize_t m.uint64_t
m.byte m.example m.int8_t m.resolved m.state m.uint8_t
m.char m.float m.long m.set_state m.store m.uintptr_t
m.concrete m.init_state m.merge m.set_strongref_state m.string m.void
m.copy m.int m.ptrdiff_t m.short m.types m.widen
m.deref m.int16_t m.qword m.size_t m.uint16_t m.word

通过 得到的是一个 SimMemView 对象, 可以这个对象的属性决定按照什么方式进行内存访问。

In [71]: m.dword   # 按照 dword 进行访问, 4 字节
Out[71]: <uint32_t <BV32 0xdeadbeef> at 0x7fffffffffeff78> In [72]: m.qword # 按照 qword 进行访问, 8 字节
Out[72]: <uint64_t <BV64 0xdeadbeefdeadbeef> at 0x7fffffffffeff78> In [73]: m.int
Out[73]: <int (32 bits) <BV32 0xdeadbeef> at 0x7fffffffffeff78> In [74]: m.uin
m.uint16_t m.uint32_t m.uint64_t m.uint8_t m.uintptr_t In [74]: m.uint64_t
Out[74]: <uint64_t <BV64 0xdeadbeefdeadbeef> at 0x7fffffffffeff78>

这些值如果需要把它转成 python 中的基本数据类型

# 通过 .resolved 转成 BVV 对象
In [75]: state.se.eval(m.qword.resolved)
Out[75]: 16045690984833335023L # 通过求解器拿到具体值
In [76]: hex(state.se.eval(m.qword.resolved))
Out[76]: '0xdeadbeefdeadbeefL'

或者可以通过 state.memoryloadstore 来读取和写入 数据到内存

In [90]: data = claripy.BVV(0xaaaaaaaaabbbbbbbbbbbb, 0x20 * 8)

In [91]: data
Out[91]: <BV256 0xaaaaaaaaabbbbbbbbbbbb> In [92]: state.memory.load(state.regs.rsp, 0x40)
Out[92]: <BV512 0xdeadbeefefbeaddec0fffeffffffff07ccfffeffffffff07000000000000000000000000000000001900000000000000ddfffeffffffff070000000000000000> # 存数据存的是 BVV 对象
In [93]: state.memory.store(state.regs.rsp,data) In [94]: state.memory.load(state.regs.rsp, 0x40)
Out[94]: <BV512 0xaaaaaaaaabbbbbbbbbbbb00000000000000001900000000000000ddfffeffffffff070000000000000000>

此外还可以往内存里面设置符号变量 (BVS)

In [96]: data = claripy.BVS("data", 0x20 * 8)

In [97]: data
Out[97]: <BV256 data_37_256> In [98]: state.memory.store(state.regs.rsp,data) In [99]: state.memory.load(state.regs.rsp, 0x40)
Out[99]: <BV512 data_37_256 .. 0x1900000000000000ddfffeffffffff070000000000000000#256>

此时还需在创建 state 时 设置 SYMBOLIC_WRITE_ADDRESSES, 例如

state = p.factory.blank_state(addr=0x0804846B, add_options={"SYMBOLIC_WRITE_ADDRESSES"})

模拟执行

用的的程序

https://github.com/angr/angr-doc/tree/master/examples/fauxware

可以通过 state 对象来执行代码块

proj = angr.Project('examples/fauxware/fauxware')
state = proj.factory.entry_state()
while True:
succ = state.step()
if len(succ.successors) == 2:
break
state = succ.successors[0] state1, state2 = succ.successors
state1
state2

上面的代码就是一直执行直到出现两个分支时停下

这两个分支位于 authenticate 函数里面

然后使用

In [7]: state1.posix.dumps(0)
Out[7]: '\x00\x00\x00\x00\x00\x00\x00\x00\x00SOSNEAKY\x00' In [8]: state2.posix.dumps(0)
Out[8]: '\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x80N\x00\x00 \x00\x00'

获取进入特定分支, 需要往 stdin 输入的数据。

可以看到这里如果要进入返回 1 的分支( state1 ),只要往 stdin 输入 SOSNEAKY ,从而认证通过,这是一个后门密码。

angr 重写了一些 libc 的函数,比如获取 stdin 数据,会返回符号量,用于符号执行,在某个状态下可以使用 state1.posix.dumps(0) 获取进入该状态时 stdin 需要输入的数据 (0 表示的就是 stdin, 1 则是 stdout )。

传入命令行参数

创建 state 时还可以设置 命令行参数为 符号量 .下面用一个简单的例子

#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{ if (!strcmp(argv[1], "hello args test")) {
printf("you win!");
}
else {
printf("you lose!");
}
return 0;
}

这里需要传入一个命令行参数,参数值如果为 hello args test 就会进入 you win 分支

you win 分支所在代码块的地址为 0x400591. 所以我们就需要通过符号执行让层序执行到 0x400591

脚本如下

#!/usr/bin/env python
# coding=utf-8
import angr
import claripy p = angr.Project("./args_test")
args = claripy.BVS("args", 8 * 16)
state = p.factory.entry_state(args=['./args_test', args]) sm = p.factory.simgr(state)
sm.explore(find=0x400591)
st = sm.found[0]
print st.se.eval(args,cast_to=str)
  • 首先加载文件,然后设置 args 符号量,长度为 16 字节 ( 8*16 位)

  • 然后创建一个 entry_state 同时把 args 作为第一个参数传给程序。

  • 之后创建 simgr 对象进行路径探索, 指定要走到的目标代码块 ( 0x400591 )即 win 所在的代码块

  • 然后使用 求解对象 st.se.eval(args,cast_to=str) 得到进入到该代码块用到的 符号量的值

cast_to=str, 用于把结果转成 字符串, 否则就是 16 进制字符串

建议使用 ipython 来执行脚本,执行完后脚本中的对象还会存在 ipython 的上下文中,可以方便做些其他的操作

SimulationManager对象

这个对象用于具体的路径探索。

以一个简单的例子开始

程序来自

https://github.com/angr/angr-doc/tree/master/examples/fauxware

解决的代码

#!/usr/bin/env python
import angr
p = angr.Project('fauxware')
state = p.factory.entry_state()
sm = p.factory.simgr(state)
sm.explore(find=0x04007BD)
st = sm.found[0]
print st.posix.dumps(0).strip("\x00")
  • 创建一个 entry_state
  • 然后创建 SimulationManager 对象 sm 进行路径探索
  • 指定我们想要走到的位置是 0x04007BD (即认证通过的分支)
  • 找到以后从 sm.found[0] 拿到此时的 state, 然后获取 stdin 输入的数据,就可以知道走到该分支需要从 stdin 输入的数据

其他更多请看

https://docs.angr.io/docs/

实例分析

angr 还搜集了许多使用 angr 解出来的 题目。下面就以其中的一些题来介绍 angr 的使用, 用几遍就知道大概流程了。

csaw_wyvern

简单分析

程序位于

https://github.com/angr/angr-doc/tree/master/examples/csaw_wyvern

通过这个题可以了解到怎么往 stdin 里面放置符号量 以及 设置约束条件

先看看大概逻辑, 首先调用 fgets 获取输入保存到 s

.text:000000000040E1D8                 lea     rcx, [rbp-110h]
.text:000000000040E1DF mov esi, 101h ; n
.text:000000000040E1E4 mov rdi, rcx ; s
.text:000000000040E1E7 mov [rbp-180h], rax
.text:000000000040E1EE mov [rbp-188h], rcx
.text:000000000040E1F5 call _fgets
.text:000000000040E1FA lea rcx, [rbp-120h]

然后进入 start_quest ,对输入进行处理

这里判断输入字符串的长度是不是 28fgets 会把 输入的字符串 + \n 保存到缓冲区, legend>>228

所以要求输入的字符串的长度应该为 28 个字节,且 每个字节都不是 \x00 或者 \n , 第 29 个字节为 \n, 表示输入完成。

解答

最后的脚本为

#!/usr/bin/env python

import angr

p = angr.Project('wyvern')
st = p.factory.full_init_state() # 设置 stdin 的约束条件, 使其 前 28 个字节 不能为 \x00 或者 \n
for _ in xrange(28):
k = st.posix.files[0].read_from(1)
st.se.add(k != 0)
st.se.add(k != 10) # 设置第 29 个字节为终止符,即为 \n
k = st.posix.files[0].read_from(1)
st.se.add(k == 10) # 使得 文件指针指向文件的开头
st.posix.files[0].seek(0)
st.posix.files[0].length = 29 sm = p.factory.simgr(st)
sm.run() # 因为是用的 sm.run() 所以要在 sm.deadended 里面寻找结果
for st in sm.deadended:
out = st.posix.dumps(1)
if "flag" in out:
print out
  • 首先创建一个 full_init_state , 因为这里是 c++ 代码, 而 angr 只是 实现了一些常用的 c 函数,所以得加载所有的库, c++ 的函数在底层才会去调用 c 的函数。创建 full_init_stateangr 就会跟进 c++ 函数里面。

  • 然后对 stdin 里面的前 29 个字节做约束条件, 前面已经分析过,要求输入的字符串长度为 28 , 最后以 \n 结束

  • 最后创建 SimulationManager ,然后调用 .run() 跑到不能进行跑为止,然后遍历 sm.deadended 查看 flag, 因为如果输入正确就会打印出 flag

运行示例

In [1]: %run my.py
WARNING | 2018-05-22 21:58:34,760 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups.
WARNING | 2018-05-22 21:58:41,288 | angr.manager | No completion state defined for SimulationManager; stepping until all states deadend
WARNING | 2018-05-22 22:17:50,610 | angr.state_plugins.symbolic_memory | Concretizing symbolic length. Much sad; think about implementing.
+-----------------------+
| Welcome Hero |
+-----------------------+ [!] Quest: there is a dragon prowling the domain.
brute strength and magic is our only hope. Test your skill. Enter the dragon's secret: success [+] A great success! Here is a flag{dr4g0n_or_p4tric1an_it5_LLVM}

通过打印的日志,可以看到大概运行了 20 分钟。下面介绍两种加速的方法

**使用 pypy **

pypy 是一个 python 的版本,采用 jit 的方法来提升 python 脚本的运行速度。

首先安装

sudo apt install pypy

然后安装 pip

wget https://bootstrap.pypa.io/get-pip.py
sudo pypy get-pip.py

然后使用 pip 安装 angr

sudo pip install angr

然后使用 pypy 来执行脚本即可

23:52 haclh@ubuntu:csaw_wyvern $ pypy my.py
WARNING | 2018-05-22 23:52:09,645 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups.
WARNING | 2018-05-22 23:52:17,667 | angr.manager | No completion state defined for SimulationManager; stepping until all states deadend
WARNING | 2018-05-22 23:58:23,344 | angr.state_plugins.symbolic_memory | Concretizing symbolic length. Much sad; think about implementing.
+-----------------------+
| Welcome Hero |
+-----------------------+ [!] Quest: there is a dragon prowling the domain.
brute strength and magic is our only hope. Test your skill. Enter the dragon's secret: success [+] A great success! Here is a flag{dr4g0n_or_p4tric1an_it5_LLVM}

此时只用了大概 6 分钟就跑完了。

使用 unicorn

还可以设置 angr 的选项,使用 unicorn 引擎来做模拟执行

#!/usr/bin/env python

import angr
p = angr.Project('wyvern')
st = p.factory.full_init_state(add_options=angr.options.unicorn) for _ in xrange(28):
k = st.posix.files[0].read_from(1)
st.se.add(k != 0)
st.se.add(k != 10) k = st.posix.files[0].read_from(1)
st.se.add(k == 10) st.posix.files[0].seek(0)
st.posix.files[0].length = 29 sm = p.factory.simgr(st)
sm.run()
for st in sm.deadended:
out = st.posix.dumps(1)
if "flag" in out:
print out

然后再跑一次

23:58 haclh@ubuntu:csaw_wyvern $ pypy my.py
WARNING | 2018-05-22 23:59:26,853 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups.
WARNING | 2018-05-22 23:59:35,539 | angr.manager | No completion state defined for SimulationManager; stepping until all states deadend
WARNING | 2018-05-23 00:03:58,458 | angr.state_plugins.symbolic_memory | Concretizing symbolic length. Much sad; think about implementing.
+-----------------------+
| Welcome Hero |
+-----------------------+ [!] Quest: there is a dragon prowling the domain.
brute strength and magic is our only hope. Test your skill. Enter the dragon's secret: success [+] A great success! Here is a flag{dr4g0n_or_p4tric1an_it5_LLVM}

只用了 3 分钟就跑完了。

总结

  • 对于 c++ 的程序,如果调用了 c++ 的函数,使用 full_init_state
  • 如果通过 sm.run() 来探索路径,最后遍历 sm.deadended 查看结果
  • 可以通过 st.posix.files[0]stdin 做约束
  • 可以使用 pypyunicorn 来加速脚本的执行

cmu_binary_bomb

这个是 cmu 给学生练习逆向分析能力的一个题,相信大多数计算机专业的都做过这东西。今天看看怎么用 angr 来解决它。

程序文件位于

https://github.com/angr/angr-doc/tree/master/examples/cmu_binary_bomb

这个例子主要用于介绍 使用 angr 设置 内存符号量。

以第一个关卡为例

这个就是判断一个字符串。

于是我们把 一块内存设置为符号量, 然后把第一个参数设置为符号量的地址即可

import angr
import claripy proj = angr.Project('bomb', load_options={'auto_load_libs': False}) start = 0x400ee0
bomb_explode = 0x40143a
end = 0x400ef7 # initial state is at the beginning of phase_one()
# 设置选项让 angr 支持 内存符号量
state = proj.factory.blank_state(addr=start)
state.options |= angr.options.unicorn
state.options |= {"SYMBOLIC_WRITE_ADDRESSES"} # 初始化内存符号量, 128 字节
arg = state.se.BVS("input_string", 8 * 128)
bind_addr = 0x603780 # 符号量存在 内存中, 这块内存就变成了符号量
state.memory.store(bind_addr, arg)
# 设置 rdi (第一个参数为符号量的地址)
state.regs.rdi = bind_addr sm = proj.factory.simgr(state)
sm.explore(find=end, avoid=bomb_explode)
st = sm.found[0]
# 求解符号量
print st.se.eval(arg, cast_to=str)

流程就是

  • 首先设置选项,让 angr 支持 内存符号量
  • 然后初始化一块 128 字节的符号量内存,并把符号量存到 0x603780, 此时 0x603780 处是一块符号量内存
  • 然后把内存符号量的地址设置为参数传个第一关函数 0x400ee0

参考

https://github.com/axt/angr-utils
http://ysc21.github.io/blog/2016-01-27-angr-script.html
http://docs.angr.io/

angr 学习笔记的更多相关文章

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  3. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  4. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  5. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  6. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  7. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  8. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

  9. DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记

    今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...

随机推荐

  1. Swift 基本数据类型与运算符表达式

    // // main.swift // LessonSwift01 // // Created by lanouhn on 16/1/25. // Copyright © 2016年 齐彦坤. All ...

  2. 大数据技术之_19_Spark学习_04_Spark Streaming 应用解析小结

    ========== Spark Streaming 是什么 ==========1.SPark Streaming 是 Spark 中一个组件,基于 Spark Core 进行构建,用于对流式进行处 ...

  3. 转 ZFC公理系统

    http://blog.sina.com.cn/s/blog_5d045b5c0100spld.html 首先,ZFC集合论中的公理大致分为3组: 1.外延公理. 2.子集公理模式.无序对公理.并集公 ...

  4. Linux Cluster环境下批量分发执行补丁

    转自:http://blog.csdn.net/napolunyishi/article/details/18219867 这两天做了一个需求,因为上一个版本的/tmp空间默认只分配了5G,而升级程序 ...

  5. (转)MySQL的Grant命令

    [MySQL] - MySQL的Grant命令 来源:http://yingxiong.javaeye.com/blog/451208 本文实例,运行于 MySQL 5.0 及以上版本. MySQL ...

  6. 小程序api-01-abcdefg

    目录-abcdefg   wx.scanCode(OBJECT) 调起客户端扫码界面,扫码成功后返回对应的结果 wx.scanCode({ success: (res) => { console ...

  7. Windows 8家长控制

    不多说,直接干货! 此刻,限制小孩使用电脑时间已经完成!!! 欢迎大家,加入我的微信公众号:大数据躺过的坑        人工智能躺过的坑       同时,大家可以关注我的个人博客:    http ...

  8. KafkaOffsetMonitor监控kafka

    KafkaOffsetMonitor监控kafka 1. KafkaOffsetMonitor是一个可以用于监控Kafka的Topic及Consumer消费状况的工具,其配置和使用特别的方便.源项目G ...

  9. 从weblogic的一个教训

    部署后一定要检查解压后的文件是否修改了.常常出现部署中存在缓存的情况. weblogic8.1可能出现没有删除缓存情况.血的教训.

  10. [PY3]——发送邮件

    一些概念 MUA:Mail User Agent——邮件用户代理,例如OutLook.Foxmail MTA:Mail Transfer Agent——邮件传输代理,例如163.com.sina.co ...