张墨轩的技术宅

不忘初心,方得始终

第五弹(磁盘保护+穿透更新)

技术虽然每天日新月异,但是对技术的热爱却从未改变,总结过去,展望未来。对于一些公共场所的带硬盘的Windows PC,比如酒店或者网吧,既需要保护硬盘系统不被破坏,又需要对上面的一些软件进行更新,比如游戏。本模块是我以前开发的三层游戏更新平台客户端的核心模块,能保护磁盘的同时又可以更新指定的游戏或者软件。

演示截图:

第四弹(Windows安全防火墙)

技术虽然每天日新月异,但是对技术的热爱却从未改变,总结过去,展望未来。这次发的是2008年开发的一个Windows安全组件,主要包括两个部分,一是内核驱动防火墙,通过白名单机制,阻止未授权的驱动的加载,并且通过HOOK SSDT的方式保护自身进程的安全,二是ARP防火墙,它通过NDIS中间层驱动捕获以太数据报文,过滤掉其中的恶意ARP报文。自夸一下,当年项目代码很工整,并且自定义了C++ stub,用于实现ddk下编译C++代码 :)

截图如下:

ADB与普通APP权限简单对比

最近在做一个车联网的项目,其中一个基本需求是将一台android手机通过USB线缆接入到车机上后,手机屏幕远程映射到车机的屏幕上,我们可以通过操作车机的屏幕来远程控制手机。这个技术本质上就是通过类似VNC的方式在车机上远程控制手机。业内有名的车联网方案如apple的carplay还有google的androidauto本质上其实也都是远程映射,另外一个著名的规范MirrorLink就直接定义了利用VNC技术进行映射。
android平台上有很多VNC相关的程序和库,如开源的libvncserver,如有名的droidVncServer项目,它内部其实就是使用了libvncserver库。还有如Remoteroid等等。这些程序作为远程控制中的《被控端》运行到android上都必须要求root权限,原因也很简单就是需要权限。作为VNC技术中的《被控端》有两个基本工作要做:
第一就是读取本机的屏幕buffer传送给《控制端》,这样《控制端》就可以看到《被控端》的屏幕内容了。
第二就是《被控端》要接受来自《控制端》的操作请求,比如点击了屏幕上某个位置,进而控制android做出相应的反应。
这里获取屏幕buffer暂且不聊,主要说说输入控制相关的部分,android是基于linux的,所以输入控制的部分主要是操作/dev/input下面的设备文件。我们来看看android系统/dev/input下面的设备文件的权限。如图:
可见root用户有读写权限,input组下面的用户也有读写权限。而作为android下面的普通的APP是不具备这样的权限的。
下面我们来看看一个普通的APP的权限,如图:
可见这个普通的APP所属用户为u0_a57,所在组也不包括input组。所以对/dev/input下面的设备文件无权操作,那么输入控制功能将无法实现。所以必须要求root权限才行。
但是别家的车联网项目确实不要求android手机已经root,在没有root的android手机上也能实现类似VNC的远程操作。那么这又是为什么呢。答案就在adb上。adb在设备端所对应的的守护进程叫做adbd,在android启动过程中它会被init进程所加载,程序刚运行的时候是root权限,然后它会给自己降权,最终以shell用户权限运行。而shell属于input组,是可以读写/dev/input下面的设备文件的。如图:
所以当用户把android手机通过USB接入到车机上时(为了方便理解这里可以把车机就想象成一台PC电脑),车机通过adb命令通知android手机上的adbd运行起《被控端》程序,这个时候《被控端》运行在shell用户权限,可以正常读写/dev/input。
因为adb本身就是作为android调试桥的作用存在的。所以它实际上也是具备一定权限的,虽然没有root权限那么大,但是比一般普通的APP的权限还是要大一些。像/system/bin/下面提供的一些工具,比如getevent/sendevent,input,screencap,screenrecord等都只有用adb去调用才能正常工作,普通APP去调用是无法正常工作的,因为缺乏权限。 而像screenshot这样的工具通过adb去调用也无法正常工作,因为这个工具内部是直接操作/dev/graphics/fb0设备的,而这个设备连shell权限也无权操作。如图:
 
只有system用户和graphics组才有权操作。那为什么adb调用screencap又可以呢,通过读取源代码可以知道,它的源代码在frameworks/base/cmds/screencap/ ,它内部实际上是调用SurfaceFlinger提供的接口ScreenshotClient,本质上是通过binder机制与SurfaceFlinger进程间通讯罢了。android自带的调试工具DDMS有一个截图功能,其实也是通过adbd调用screencap完成截图的。


特殊字符转义的一点体会

在编程的过程中经常会碰到一些特殊字符需要进行字符转义才能使程序正常运行,那为什么要进行字符转义才能使程序正常运行呢,我们举几个例子:

例子一:
比如在C语言中 我们想定义一个字符串如 hello,"moses" 那么如果写成 
char *str = "hello,"moses",are you ok"; 
那么程序铁定出错,编译不过,因为双引号"在C语言中是作为定义字符串开始和结束的一个特殊符号来用的,C编译器遇到上面这样的代码直接晕倒,那么必须像下面这样写:
char *str = " hello,\"moses\",are you ok";  
加上转义符号\才能让C编译器正确进行认知,简单来说就是,对于C编译器来讲,在字符串中【\"】才等于一个双引号【“】。
例子二:
比如在JS语言中我们定义一个JSON 如
{"content" : "hello,"moses",are you ok"}
同样JS解析引擎也会出错,因为双引号"在JS中也是作为定义字符串开始和结束的一个特殊符号来用的,那么必须像下面这样写:
{"content" : "hello,\"moses\",are you ok"}
加上转义符号\才能让JS解析引擎正确进行认知,简单来说就是,对于JS解析引擎来讲,在字符串中【\"】才等于一个双引号【“】。
例子三:
比如在SQL中,这里假设是MYSQL,我们定义一个SQL语句:
select * from mytable where content like 'hello,'moses',are you ok';
SQL解析引擎会出错,因为单引号'在SQL中是做为字符串开始和结束的一个特殊符号来用的,必须像下面这样写:
select * from mytable where content like 'hello,\'moses\',are you ok';
加上转义符号\才能让SQL解析引擎正确进行认知,简单来说就是,对于SQL解析引擎来讲,在字符串中【\'】才等于一个单引号【’】。
例子四:
比如在HTML中,我们定义如下一个标签:
<input type="text" value="hello,"moses",are you ok">
大家可以在浏览器中试一试,输出会被截断,并不能完整的输出 hello,"moses" 这样的字符串,因为双引号在这里是用来定义标签属性的,所以必须像下面这样写:
<input type="text" value="hello,&quot;moses&quot;,are you ok">
这里需要注意的是HTML里面的转义符号是&,而不是符号\。在这个例子上为了在标签属性中表示一个双引号【"】必须要用【&quot;】,当然也可以用【&#34;】来表示,前面那种叫实体名称,后面这种叫实体编号,具体请自行google或者baidu。这里需要强调的是在HTML中是否转义需要根据上下文来,比如:
<html>
<body>
<input type="text" value="hello,&quot;moses&quot;,are you ok">
hello,"moses",are you ok
</body>
</html>
这段代码在浏览器中是能正确显示的,因为第二处字符串并没有和任何特殊字符有冲突。当然如果你一定要转义表示那也没任何问题。如下:
<html>
<body>
<input type="text" value="hello,&quot;moses&quot;,are you ok">
hello,&quot;moses&quot;,are you ok
</body>
</html>
你会发现它在浏览器中也能正确显示,并且显示和前面的一模一样。
这里举了四个例子,总结起来,其实都是一次人机交互的过程,这里的“机器”分别指的是【C编译器】【JS解析引擎】【SQL解析引擎】【HTML解析引擎】,当我们“人”想告诉“机器”,比如告诉【JS解析引擎】我要输入的是一个双引号时,因为双引号对【JS解析引擎】有特殊含义,所以我们必须输入【\"】这样的字符序列给到【JS解析引擎】,这样【JS解析引擎】才会明白我们“人”其实只是想输入一个双引号【"】。
人=>输入序列\"=>JS解析引擎=>理解成=>双引号"
这里需要重点强调的就是关于转义字符问题对于不同的开发语言有着不同的规则,虽然也有很多相同的地方,但是不能预先做假设,需要查阅相关文档。
另外在实际开发过程中经常是各种语言混合开发的,比如做一个网站最常见的形式是 服务端用PHP+MySQL开发,客户端是HTML+JS。服务端PHP代码里面可能会包含SQL语句,而客户端的HTML代码里面可能也会混编入JS代码。那么这个时候我们就必须要有非常清晰的认知,对于一段包含有特殊字符的字符序列来说什么时候由【PHP解析引擎】处理,什么时候由【SQL解析引擎】处理,什么时候由【HTML解析引擎】处理,什么时候由【JS解析引擎】处理,必须搞的明明白白,否则就可能会发生莫名其妙的错误。我们来看一次简单的网页输出过程,很可能是:
【PHP解析引擎】=>sql语句序列=>【SQL解析引擎】=>返回各种结果字符序列=>【PHP解析引擎】=>网络输出给浏览器=>【HTML解析引擎】=>js代码字符序列=>【JS解析引擎】=>执行结果字符序列=>【HTML解析引擎】=>电脑屏幕
简单来说可以理解成一种引擎的输出很可能是作为另一种引擎的输入。其实这也符合计算机系统的基本概念,计算机系统本质上就是输入+处理+输出。
这里我们举几个例子:
我们先来看看例子一,这是一段HTML+JS混编代码,代码如下:
<html>
<body>
<input type="button" value="button" onclick="alert("hello,"moses",are you ok");">
</body>
</html>
这段代码包含了HTML代码也包含了JS代码,这段代码的功能是当点击按钮会弹出一个对话框,对话框的内容是:hello,"moses",are you ok
但是当点击按钮的时候,铁定出错,肯定不会弹出对话框,这么多双引号,估计都晕了,哈哈。既然点击按钮执行的是JS代码,那么首先肯定是想当然的利用JS语言的转义规则进行处理,代码如下:
<html>
<body>
<input type="button" value="button" onclick="alert(\"hello,\"moses\",are you ok\");">
</body>
</html>
结果还是不行,而且错得一塌糊涂,看样子想当然是不行的了,还的仔细捋一捋把来龙去脉搞清楚才能解决。首先我们必须得先明白,上面的代码是首先被【HTML解析引擎】解析后才会交给【JS解析引擎】处理,那么看看上面的代码被【HTML解析引擎】解析后是个什么样子,因为符号\并不是【HTML解析引擎】的转义符,所以最后交给JS执行的代码是alert(\,不出错才怪。既然是先被【HTML解析引擎】解析,那么我们肯定得先用HTML的转义规则来处理,修改后的代码如下:
<html>
<body>
<input type="button" value="button" onclick="alert(&quot;hello,&quot;moses&quot;,are you ok&quot;);">
</body>
</html>
可惜的是这样修改后还是不能正常工作,那是为什么呢,仔细分析一下就可以得知,这段代码被【HTML解析引擎】解析出来的JS代码实际如下:
alert(“hello,”moses“,are you ok”);
这行代码交给【JS解析引擎】后肯定会出错,原因就是中间那两个双引号,必须要按JS语言的规则转义,应该修改成下面这样子:
alert(“hello,\”moses\“,are you ok”);
这样才能正常工作,那么现在我们把HTML和JS两种规则结合起来处理后,得到了最终如下的整合代码:
<html>
<body>
<input type="button" value="button" onclick="alert(&quot;hello,\&quot;moses\&quot;,are you ok&quot;);">
</body>
</html>
至此,程序终于能按照期望运行了。当然实际上我们可以通过双引号和单引号配合使用来解决问题,但是这里我们为了很好的说明问题的来龙去脉,所以不会考虑其他途径。
接下来看看例子二,这是一段PHP+SQL混编代码,为了更好的说明原理和问题,这里全部使用单引号,代码如下:
<?php
$con = mysql_connect('localhost','root','123456');
mysql_select_db('db_test', $con);
mysql_query('INSERT INTO T_Test (F_Content) VALUES ('hello,'moses',are you ok')');
mysql_close($con);
?>
代码的目的是将字符序列 hello,'moses',are you ok 插入数据库中,但是上面这代码肯定是错的,因为对于【PHP解析引擎】来说单引号是作为定义字符串开始和结束的一个特殊字符,所以这里肯定必须进行转义处理,而PHP也是用符号\来作为转义符的,所以我们将代码改成如下样子:
<?php
$con = mysql_connect('localhost','root','123456');
mysql_select_db('db_test', $con);
mysql_query('INSERT INTO T_Test (F_Content) VALUES (\'hello,\'moses\',are you ok\')');
mysql_close($con);
?>
不幸的是,这段代码还是无法正常工作,那是为什么呢,我们来分析一下原因。首先要说的是这段代码是先交给【PHP解析引擎】解析后再把其中的SQL语句交给MySQL的【SQL解析引擎】来处理,有了这个前提后就比较好分析了,我们先来看看通过【PHP解析引擎】解析后出来的代码是什么样子:
解析之前:
INSERT INTO T_Test (F_Content) VALUES (\'hello,\'moses\',are you ok\')
解析之后:
INSERT INTO T_Test (F_Content) VALUES ('hello,'moses',are you ok')
然后解析之后的SQL语句会交给【SQL解析引擎】来处理,答案很明显了,肯定出错,因为在【SQL解析引擎】中单引号也是定义字符串开始和结束的特殊符号。那么这里要重点强调的是:
【PHP解析引擎】解析是没问题的,但是在【SQL解析引擎】解析的时候出错了。
然后接下来我们继续修改代码:
<?php
$con = mysql_connect('localhost','root','123456');
mysql_select_db('db_test', $con);
mysql_query('INSERT INTO T_Test (F_Content) VALUES (\'hello,\\'moses\\',are you ok\')');
mysql_close($con);
?>
一运行,代码还是出错,这又是为什么呢,那是因为中间\\'这样的语法出现了问题,前面那个\将后面的\给转义了从而失去了转义的特性导致单引号没被转义,所以直接在【PHP解析引擎】解析的时候就出错了,那么这里要重点强调的是:
这次是【PHP解析引擎】解析的时候就已经出问题了,压根和【SQL解析引擎】没啥关系。
只能继续修改代码了,修改后的代码如下:
<?php
$con = mysql_connect('localhost','root','123456');
mysql_select_db('db_test', $con);
mysql_query('INSERT INTO T_Test (F_Content) VALUES (\'hello,\\\'moses\\\',are you ok\')');
mysql_close($con);
?>
这次终于成功了,顺利执行完毕,数据也正常写入成功。我们来分析一下这次为什么可以:
【PHP解析引擎】解析前的代码:
INSERT INTO T_Test (F_Content) VALUES (\'hello,\\\'moses\\\',are you ok\')
【PHP解析引擎】解析后的代码:
INSERT INTO T_Test (F_Content) VALUES ('hello,\'moses\',are you ok')
然后再把这句SQL交给【SQL解析引擎】解析,根据转义规则,【SQL解析引擎】也非常清楚的明白要写入的数据里面包含有2个单引号,而不会发生冲突,最终数据终于成功的写入了。
如图:
最后要说的是在实际开发中其实有很多办法可以解决类似的问题,比如单引号和双引号配合使用,比如magic_quotes_gpc=on,addslashes和stripslashes,mysql_real_escape_string等等函数都能比较好的解决问题。
好了,具体的技术细节就说到这里了,那么接下来我们从宏观架构的角度来简单讨论一下这个话题。在项目的实际开发中要考虑的转义字符不单单只有引号,还有诸如<,>,&等等很多很多,这里只讨论几个最常见的。这些转义字符如果不做处理,可能会导致很大的安全隐患,比如古老的SQL注入技术,比如XSS攻击,这里举个最简单的例子,比如在一个文章发布系统里面不做任何特殊处理,那么有人在提交的文章里面加入一个<script src='x.js'>这样的标签那就麻烦大了,所以要做处理。那么到底什么时候做处理呢,我们先看一幅图:
这是现在最为常见的系统架构,一个软件可能会有不同平台的客户端,大家都统一通过RESTfulAPI进行通讯,一个典型的例子就是微信,它有PC端,Android端,IOS端,WEB端等等。
我们在设计这样的系统的时候 RESTfulAPI可能会用PHP,Java,NodeJS等语言开发,Android端开发有Java,IOS上有Objective-C,PC上用C/C++,.Net,Delphi等等,WEB端有HTML+JS。
你会发现这么多语言各有各的规则,所以最好的办法就是在保证安全的前提下输入的数据不做转换直接进入到数据库,输出的数据也不做转换直接给到客户端,然后各种客户端都根据自己的实际情况自行处理,这里打个简单的比方: 
比如用户输入一段字符序列 【hello<hr>"<br>】,那么最终进入数据库的也要是同样的序列,而不应该是【hello&lt;hr&gt;&quot;&lt;br&gt;】,因为这样的转换HTML认得,但是其他客户端比如C/C++就不认得,就会造成各种困扰,所以最终的处理过程应该留给各个客户端自行处理。


android-arm逆向学习宏观知识点

一. linux原生层面:

包含三个层次:
1. c/c++语言层面
2. 汇编语言层面
3. 二进制指令层面
正向流程:
c/c++源文件==>gcc编译器==>s汇编源文件==>as汇编器==>包含arm二进制指令的elf格式文件[==>objcopy==>bin格式纯指令]
反向流程:
包含arm二进制指令的elf格式文件==>IDA, objdump==>arm反汇编输出==>人工阅读理解或者IDA-F5插件==>c/c++源文件
学习重点:
1. elf格式及其相关知识
2. 汇编语言(arm-asm语法,gun-asm语法)
3. arm指令二进制级别的编码和构成
4. 反汇编工具的学习和使用


二. android层面:
包含五个层次:
1. java语言层面
2. java汇编语言层面(暂称)
3. java字节码(二进制指令)层面
4. dalvik汇编语言层面(暂称)
5. dalvik字节码(二进制指令)层面
正向流程:
java源文件==>java编译器==>包含java字节码的class格式文件==>dx工具==>包含dalvik字节码的DEX格式文件
dalvik汇编源文件(smali格式的汇编语法)==>smali汇编工具==>包含davlik字节码的DEX格式文件
反向流程:
包含dalvik字节码的DEX格式文件==>baksmali工具==>dalvik反汇编(smali格式的汇编语法文件)==>人工阅读理解==>java源文件
包含dalvik字节码的DEX格式文件==>IDA工具==>dalvik反汇编(IDA格式的汇编语法)==>人工阅读理解==>java源文件
包含dalvik字节码的DEX格式文件==>dex2jar工具==>java字节码(多class文件打包进一个jar包)==>jd-gui工具==>java源文件
学习重点:
1. DEX格式及其相关知识
2. dalvik汇编语言(smali语法)
3. dalvik字节码二进制级别的编码和构成
4. 各种工具的学习和使用

补充:
android目前有两种执行环境:
1. dalvik运行时
2. art运行时
在Dalvik运行时中,APK在安装的时候,安装服务PackageManagerService会通过守护进程installd调用一个工具dexopt对打包在APK里面包含有Dex字节码的classes.dex进行优化,优化得到的文件保存在/data/dalvik-cache目录中,并且以.odex为后缀名,表示这是一个优化过的Dex文件。在ART运行时中,APK在安装的时候,同样安装服务PackageManagerService会通过守护进程installd调用另外一个工具dex2oat对打包在APK里面包含有Dex字节码进翻译。这个翻译器实际上就是基于LLVM架构实现的一个编译器,它的前端是一个Dex语法分析器。翻译后得到的是一个ELF格式的oat文件,这个oat文件同样是以.odex后缀结束,并且也是保存在/data/dalvik-cache目录中。
学习重点:
1. odex格式及其相关知识
2. ELF格式的oat文件相关知识


Android so 调试的几种途径

一:直接使用gdb进行调试,最原始。
二:用ndk-gdb工具进行调试。
三:在Eclipse中add native support后图形界面调试。
四:在Eclipse中利用Debug configurations=>c/c++ Remote Application进行调试(本质就是gdb调试,这时Eclipse作为gdb前端的一个图形外壳而已)
五:  利用IDA的远程调试功能
其中要考虑二种情况:
一:有源码,比如调试自己开发的程序。
二:无源码,比如逆向别人开发的程序。


普通程序员的角度来看密码学

说起密码,说起密码学,第一时间想到的就是各种非常复杂的数学模型,种类繁多的各种标准,似懂非懂的各种专业术语.我相信有相当一部分程序员有这样的感受,我就是典型的代表.我不是数学特别好的那种,读书的时候也没专门学过密码学,所以当后来做与之相关的工作的时候(比如通信安全)总是不得要领,于是决定好好啃啃这块硬骨头,好在努力没白费,在学习和实践中慢慢的对密码学的应用有了些了解,决定把一些心得写出来,给后来的一些新手一点点帮助, ,不敢谈什么数学原理,只是站在一个普通程序员的应用角度来谈谈心得.
先看看密码学对于计算机实际应用到底有什么帮助,至少包括以下4个方面:

一. 保密性
   保密性是指隐藏信息所要表达的真实含义和目的.比如A有个很重要的文件,想通过网络传给B,但是他又不想让B以外的任何其他人知道,那么他首先和B约定好一个密钥,然后在通过网络传输前先把文件内容通过某种算法用约定好的密钥加密,将明文变成密文,然后再传给B,B接收到文件后再用约定好的密钥解密,将密文变成明文.在网络传输过程中就算被其他人窃取,窃取到的也是加密后的文件.从而达到保密的效果.有一种最常见的加密算法称之为AES算法.

二. 完整性
   完整性是指(假定数据内容不会被攻击者非法修改的前提下)确保数据和信息的正确性.比如A有个文件,在传给B之前先通过散列算法算出一个散列值,然后把文件和散列值一起传给B,B收到文件后通过同样的散列算法算出这个文件的散列值,然后和A发过来的散列值做比较,如果一样就证明这个文件是完整的,没被修改. 讲到这里,我想强调的是也许大家注意到了前面提到的一个前提条件(括号中的内容)会很奇怪,呵呵,关于这点后面会详细解释.有几种比较常见的散列算法,比如MD5算法,SHA-1算法.

三. 认证
   举个例子.比如A有个重要信息,想通过网络传给B,假设在传输的过程中可能会被黑客攻击,修改内容,甚至将内容完全替换,那么B要如何才能确认这个重要信息是不是正确?是不是A发过来的呢? 我们采取这样一种方法: 首先A和B要商量好一个密钥, 然后A在发送信息前首先将要发送的重要信息和密钥通过某种算法计算出一串值,这串值称为消息标记,然后把重要信息和计算出来的消息标记一起发给B,然后B将收到的重要信息和密钥通过同样的算法算出消息标记,然后和收到的消息标记做比较,如果相同就意味着数据正确并且完整,同时也能够确认这个信息确实是A发过来的,从而达到认证的目的.如果不相同就意味着可能被黑客给攻击了. 刚才使用的那个算法,我们通常称为 消息认证码(Message Authentication Code,MAC).看到这里,大家心里是不是会有一个疑问,这里提到的方法也可以验证数据的完整性,那么这里与上面所讲的 第二点:完整性 又有什么联系和区别呢? 呵呵 等一下会详细解释这个问题.

四. 不可否认
   什么叫不可否认?举个现实中的例子,比如你和人家签了份合同,当你在合同上签了字后那么这个合同就产生了法律效应,以后你做事就不能违背合同上的规定,你也不能否认你没有签过这个合同,因为合同上有你的签名. 那么在计算机的世界要如何达到这个目的呢?我们可以采取数字签名技术来达到这个目标,那么到底什么又是数字签名呢?文章下面会讲到 .

    前面说了密码学在计算机应用上的四个方面,下面就大概介绍一下密码学中的一些常用算法及其使用.
首先要谈到的是 对称密钥算法  所谓对称的含义是指一个加密算法的加密密钥和解密密钥相同,或者虽然不相同,但是可由其中的任意一个很容易的推导出另一个,即密钥是双方共享的. 这种类型的算法的典型代表是AES(Advanced Encryption Standard)高级加密标准算法,也叫Rijndael算法.它是一种分组密码(block cipher)算法.它是美国国家标准与技术协会(NIST)所认可的一个标准.AES算法的出现是用来替代一个古老的算法:DES(Data Encryption Standar)数据加密标准算法的.DES算法已经被多次证明是不安全的!

按此在新窗口打开图片

   不过这种类型的算法都有个明显的不足, 就是它的密钥是共享的, 只要有一个人泄露了密码,那么整个体系就都不安全了.

    接下来我们要谈到的是散列函数,散列函数也称为(Pseudo Random Function, PRF)伪随机函数.散列函数是指的这样一种算法: 它可以把一个任意大小的输入数据通过一种压缩(compression)的处理过程转换为一个固定大小的不会重复(极少重复)的输出.而且这种压缩处理是不可逆的,意味着你没有办法将输出还原成输入.这种算法的输出有个专业称呼,称之为摘要(digest) 如图:

按此在新窗口打开图片

   比较常见的散列算法有MD5,SHA-1等.先看看MD5,我想这个大家应该太熟悉不过了,MD5的全称是(message-digest algorithm 5)信息-摘要算法,经MD2、MD3和MD4发展而来,MD5算法能够将任意长度的输入数据映射成一个256位(32个字节)长的输出(摘要). 不过MD5算法现在已经被证明是不安全的了. 再看看SHA-1算法,它是SHS(Secure Hash Standard)安全散列标准家族中的一员,而SHS又隶属于美国国家标准与技术协会(NIST).SHA-1能够将任意长度的输入数据映射成一个160位(20个字节)长的输出(摘要). 下面我们来看看散列算法如何来保证数据的完整性,举个实际的例子:BT下载软件大家想必都用过,它首先需要一个种子文件,然后打开这个种子文件才能下载到实际的数据,其实这个种子文件中就包含了要下载的数据的摘要信息,这个摘要是通过SHA-1算法算出来的,当你打开种子文件的时候会通过SHA-1算法计算出已经下载过的数据的数据摘要,和种子文件中纪录的摘要做比较,如果全部相同就证明数据已经全部下完.如果不同就意味着数据还没下完或者是被非法修改过. 
    不过通过散列函数保证数据完整性有时候确会碰上麻烦,不信我们看个例子:A想把一个重要信息通过网络传给B,A在传送之前首先通过散列算法比如SHA-1算法算出这个信息的摘要,然后把摘要附加到信息的尾部一起传送给B,假设这个时候黑客C在传送途中把数据包给拦截下来并且把信息内容进行修改,然后重新用SHA-1算法算出修改后的信息摘要替换原来的摘要,再转发给B,这个时候B收到了信息,B用SHA-1校验信息后发现一切OK,他以为他收到了正确的信息数据,殊不知这个重要信息早已经在网络传输的途中被C给改掉了. 如图:

按此在新窗口打开图片

   可见散列算法虽然能保证数据完整性,但是却有个前提条件,正如文章前面说讲的一样,(假定数据内容不会被攻击者非法修改的前提下), 大家是不是觉得很矛盾, 但事实确实是这样 . 通过前面这个例子可以看出: 散列算法虽然能够对数据完整性提供保证,但是却无法对数据的来源进行认证(B没有办法确认文件就是A给他发送的那个). 那么如果想达到认证的目的要怎么办了,文章前面谈到过MAC算法,那么什么是MAC算法,我们接着看.

    消息认证码(Message Authentication Code,MAC)算法存在的主要目的就是为了认证, 美国国家标准与技术协会(NIST)制定二种MAC标准.
第一种是分组消息认证码(Cipher Message Authentication Code, CMAC)算法. 
第二种散列消息认证码(Hash Message Authentication Code, HMAC)算法.
    CMAC算法是基于分组密码算法的,很多情况下CMAC算法内部使用了AES算法. 而HMAC算法是基于散列算法的,很多情况下HMAC算法内部使用了SHA-1算法. 不管是哪种MAC算法 它们的外部表现形式都是一样的: 它们都接受一个任意大小的输入和一个密钥输入,然后通过计算得出一个固定大小的输出,我们一般把这个输出称为MAC标记.如图:

按此在新窗口打开图片

   我们来看例子: A想把一个重要信息通过网络传给B,首先A和B要先约定好一个密钥,A在传送之前首先利用密钥通过MAC算法算出这个信息的MAC标记,然后把MAC标记附加到信息的尾部一起传送给B,假设这个时候黑客C在传送途中把数据包给拦截下来并且也把信息内容进行了修改,但是他因为不知道密钥,所以他无法重新计算MAC标记,所以攻击失败. 当B收到信息以后,他用约定好的密钥通过MAC算法算出收到的信息的MAC标记和收到的MAC标记做比较, 如果一样就意味着数据是A发来的并且完好无损. 把这个例子和上面讲散列算法的那个例子做比较, 会发现MAC算法比散列算法多了一个输入参数, 那就是密钥 , 正是因为多出的这个密钥参数才保证了MAC算法能够达到认证的目的, 当然同时也能够实现完整性验证.

    前面讲了MAC算法,你会发现它只是提供了完整性和认证的功能, 却没有提供对数据的内容进行加密的功能. 所以实际的应用中很可能要和某种加密算法配合使用,比如我传输数据之前首先用AES算法对数据加密,然后用MAC算法实现数据的认证.  那有什么办法可以把加密和认证的功能合到一起,至少我们写程序的时候可以一步到位 . 答案是有的, 接下来就介绍.

    加密和认证模式(encrypt and authenticate modes) 指的是把加密和认证的任务封装到一个单独的处理过程中,下面是一个非常简化的过程图:

按此在新窗口打开图片

其中最常见的二个标准: 
一个是美国电气及电子工程师学会 IEEE(Institute of Electrical and Electronics Engineers)的GCM(Galois Counter Mode), 它常用于各种无线标准,比如802.16. 
另一个是 NIST的CCM(Counter mode with Cipher-block chaining Message authentication code).

    其实不管是加密也好认证也罢,有个问题都无法直接解决,这个问题就是重放保护问题.那什么是重放保护呢? 举个例子,看看如果没有重放保护会发生什么大问题. 比如我使用网上银行转帐,在这个过程中, 网上银行客户端程序向银行服务器发送了一个数据包,这个数据包里面包含了转帐的请求和金额,当服务器收到这个数据包就会根据请求进行转帐操作, 假如在通讯过程中这个数据包被黑客给拦截了,虽然黑客没办法把数据包解密或是修改,但是他可以把这个数据包保存下来,然后把数据包原封不动的不停的给银行服务器发,那最终我账户上的资金就会被全部转走 , 那可就惨啦! 当然这仅仅只是个假设,但是它反应出重放保护是多么的重要! 为了做到重放保护我们必须做点额外的工作,最常用的方法就是在通讯数据包中加入时间戳或者是计数器. 如果包含了时间戳,接收到数据包后可以对比时间差,如果时间间隔太久就丢弃数据包. 如果包含了计数器,接收到数据包后可以查看包的编号,比如收到的第一个包编号是2, 那么收到的第二个包的编号至少比2要大.不然就丢弃数据包.这样就可以比较好的解决重放保护问题.

    上面介绍的不管是AES算法还是MAC算法它们都是对称算法, 通讯双方的密钥是共享. 所以一但任何一方泄露了密钥, 就再没有安全可言.  而非对称密钥算法就能很好的解决这个问题.

    非对称密钥(asymmetric key)算法,也叫做公钥算法. 它的出现解决了对称加密算法很难解决的两个问题: 密钥分发(key distribution) 和 不可否认(nonrepudiation). 而RSA(Rivest Shamir Adleman)算法是一种最常见的公钥算法. 公钥算法的加密和解密所使用的密钥是不同的,其中公钥是可以公开的,而私钥必须保密.用公钥加的密必须用对应的私钥才能解密,用私钥加的密必须用对应的公钥才能解密. 而且无法由公钥推导出私钥.
    先看看公钥算法如何解决密钥分发的问题.举个例子: A要通过网络传一个文件给B,那A首先问B要一个公钥,B自己持有相应的私钥,然后A用这个公钥加密另一个用于对称加密算法的密钥(用K表示),然后把加了密的密钥发给B,B收到后用自己的私钥解密,得到用于对称加密算法的密钥(K),这个时候A和B就都知道了用于对称加密算法的这个密钥(K),然后A把文件用对称加密算法(比如AES)加密 传给B, B收到后用密钥(K)将收到的文件解密. 在整个过程中,就算黑客拦截了传输密钥(K)的数据包,但是他没有私钥无法解密,所以他不知道密钥(K)是什么,所以他也无法对传输的文件进行解密. 

按此在新窗口打开图片

    大家会发现上面的例子中是混合使用了对称密钥算法非对称密钥算法,这种混合模式在实际的应用当中经常被采用. 那为什么不直接采用非对称密钥算法完成所有的工作呢? 那是因为一般而言,非对称密钥算法加密的速度都比对称密钥算法的多,所以采用这种混合模式可以充分发挥对称密钥算法的速度优势.
    前面在说不可否认那节时说到了数字签名,那什么是数字签名呢? 所谓数字签名就是附加在数据单元上的一些验证数据.接收者利用这些数据确认数据单元的来源和数据单元的正确性并防止别人伪造. 数字签名一般是利用散列算法和公钥算法来完成的. 如图:

按此在新窗口打开图片

    把将要签名的数据用散列函数算出散列值(摘要),再把散列值(摘要)传给公钥算法,最后算出的结果就是这个数据的数字签名.  举个例子: A有一个文件要传给B,A用SHA-1算法算出文件的摘要,再把摘要用自己的私钥通过RSA算法加密,得出数字签名后连同文件本身一起发给B, B收到后,首先用A的公钥通过RSA算法将数字签名解密,解出摘要.然后他把收到的文件也通过SHA-1算法算出摘要, 然后把解密出来的摘要和自己算出的摘要做比较,如果相同就意味着数字签名验证通过. 这样就可以确认文件是完整的而且确实是A发过来的,A也无法抵赖说文件不是他的,因为B是用A的公钥解密的. 

    文章上面讲到了几种典型的算法和几种典型的应用,在实际应用当中可以根据自己项目的需要自由搭配各种加密算法,组合出最适合自己项目的一种安全解决方案. 还有就是在实际应用中, PKI(Public Key Infrastructure)公钥基础设施,是一种遵循既定标准的密钥管理平台.应用范围非常的广,值得我们大家一起学习. http://www.infosecurity.org.cn 这个网站可以看看. 像<<现代密码学>>, <<程序员密码学>>,<<密码学导论>>等书也可以看看. 下面是开源社区的几个加密算法库,比较著名. 可以在自己的项目中使用. 研究这些代码也是学习的好途径.

OpenSSL     http://www.openssl.org 
LIBTOM       http://www.libtom.org 
Crypto++     http://www.cryptopp.com 


«1»

Powered By Z-Blog 2.2 Prism Build 140101

Copyright phonegap.me Rights Reserved.