分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020

技术文章列表

  • python制作海底飞行棋(含源码)

    飞行棋玩过吗?玩过python制作的海底飞行棋玩过吗?额。。。。。。
    今天就来教制作海底飞行棋
    核心玩法两名玩家通过→和←操控游戏角色,最终全部到达终点,(本游戏适合全年龄段,不要太较真)谁的分数越高谁就获胜
    主要代码思想实现游戏角色移动,分数,分数判断
    在游戏中,设立三个游戏状态

    start 开始running 运行game——over 游戏结束
    三个状态都很重要,对于制作游戏有很大帮助
    要依次根据玩家操作变换
    其次就是鼠标和键盘的按下事件
    没有这一步,游戏就变成了动画片,不能控制,就看着它运行
    而且这一步很容易报错,因为代码量多
    最后就是完成所有的调用
    一切完成后就OK了
    1 留言 2020-02-24 20:18:52 奖励46点积分
  • 【Cocos Creator 联机实战教程(1)】——初识Socket.io 精华

    1.Socket.io简介Socket.io是一个实时通信的跨平台的框架
    1.1 websocket 和 socket.io 之间的区别是什么socket.io封装了websocket,同时包含了其它的连接方式,比如Ajax。原因在于不是所有的浏览器都支持websocket,通过socket.io的封装,你不用关心里面用了什么连接方式。你在任何浏览器里都可以使用socket.io来建立异步的连接。socket.io包含了服务端和客户端的库,如果在浏览器中使用了socket.io的js,服务端也必须同样适用。如果你很清楚你需要的就是websocket,那可以直接使用websocket。
    2. 服务器端Windows安装Node.js Express Socket.io2.1 下载Node.js官网下载最新版http://nodejs.cn/
    2.2 打开cmd2.2.1 下载Express
    npm install -g express

    2.2.2 下载Socket.io
    npm install -g socket.io


    3. Creator与服务器通信测试3.1 测试场景
    3.2 客户端脚本我是挂载在Canvas上,也可以选择直接挂载在Label上。
    onLoad: function () { let self = this; if (cc.sys.isNative) { window.io = SocketIO.connect; } else { require('socket.io'); } var socket = io('IP:端口'); socket.on('hello', function (msg) { self.label.string = msg; }); },
    记得下载socket.io并导入为插件
    3.3 服务器脚本(任意位置存放)let app = require('express')();let server = require('http').Server(app);let io =require('socket.io')(server);server.listen(4747,function(){ console.log('listening on:端口');});io.on ('connection',function(socket){ console.log('someone connected'); socket.emit('hello','success');});
    在服务端脚本存放的位置打开cmd
    输入

    npm link express

    输入

    npm link socket.io

    输入

    node test-server.js

    4. 总结不同的环境配置网络连接不同,要善于抓包发现问题。
    不过也从侧面看出cocos creator不是很适合做联网游戏,调试是真的恶心。
    本教程部分素材来源于网络。
    附上监听小程序,测试网络。
    2 留言 2018-12-06 15:02:34 奖励35点积分
  • MVC:开发模式

    MVC:开发模式一、jsp演变历史
    早期只有servlet,只能使用response输出标签数据,非常麻烦后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大量的java代码,有写html表,造成难于维护,难于分工协作再后来,java的web开发,借鉴mvc开发模式,使得程序的设计更加合理性
    二、MVC
    M
    Model,模型。JavaBean

    完成具体的业务操作,如:查询数据库,封装对象
    V
    View,视图。JSP展示数据
    C
    Controller,控制器。Servlet

    获取用户的输入调用模型将数据交给视图进行展示
    三、优缺点优点
    耦合性低,方便维护,可以利于分工协作重用性高生命周期成本低部署快可维护性高有软件工程化管理
    缺点
    使得项目架构变得复杂,对开发人员要求高不适合小型,中等规模的应用程序增加系统结构和实现的复杂性视图与控制器间的过于紧密的连接视图对模型数据的低效率访问一般高级的界面工具或构造器不支持模式
    扩展阅读
    [CSDN] MVC模式简介
    [菜鸟教程] MVC 模式
    [百度百科] MVC框架

    <!--more-->
    0 留言 2020-03-21 15:05:36 奖励12点积分
  • BeanUtils的基本使用

    在《HTTP案例学习:用户登录》的学习中,使用到BeanUtils。
    案例中只涉及到封装username、password两个对象,但是实际上的用户登录界面,有十几个数据对象需要封装。
    按原来的方式,是非常麻烦的。期望能够一次把所有参数获取到,并且通过一个方法,一次把所有数据封装成一个对象。
    <font color='red' size='5'>BeanUtils,一个工具类,简化数据封装</font>
    这里专门挑出来,做一份简单的笔记。
    配合【教学视频】、【BeanUtils工具类常用方法】 食用更佳。
    <!--more-->


    我们将原来写的代码,
    //2.获取请求参数String username = req.getParameter("username");String password = req.getParameter("password");//3.封装user对象User loginUser = new User();loginUser.setUsername(username);loginUser.setPassword(password);使用BeanUtils工具类进行封装,
    //2.获取所有请求参数Map<String, String[]> map = req.getParameterMap();//3.创建User对象User loginUser = new User();//3-2.使用BeanUtils封装try { BeanUtils.populate(loginUser,map);} catch (IllegalAccessException e) { e.printStackTrace();} catch (InvocationTargetException e) { e.printStackTrace();}会发现只用简单几行代码就完成了封装,而不用和之前一样分别对每个数据进行封装。
    BeanUtils
    工具类,简化数据封装

    用于封装JavaBean的JavaBean:标准(简单)的Java类
    概念
    JavaBean成员变量属性:setter和getter方法截取后的产物
    例如:getUsername() —> Username—> username(大多数下,名字一样)

    JavaBean
    标准的Java类一般放在domain等package下
    功能
    <font color='red' size='5'>封装数据</font>



    要求
    类必须被public修饰
    必须提供空参的构造器
    成员变量必须使用private修饰
    提供公共setter和getter方法

    属性
    setter和getter方法截取后的产物
    例如:getUsername() —> Username—> username(大多数下,名字一样)

    调用方法
    setProperty()

    设置属性值

    getProperty()

    获取属性值

    populate(Object obj, Map map)

    将map集合的键值对信息,封装到对应的JavaBean对象中
    Demo配合 《HTTP案例学习:用户登录》 ,现在New一个BeanUtils.java
    package cn.itcast.test;import cn.itcast.domain.User;import org.apache.commons.beanutils.BeanUtils;import org.junit.Test;import java.lang.reflect.InvocationTargetException;public class BeanUtilsTest { @Test public void test(){ User user = new User(); try { BeanUtils.setProperty(user,"username","zhangsan"); System.out.println(user); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }}控制台输出:

    扩展后来看的一篇博客 【BeanUtils工具类常用方法】,觉得写的很不错,一起记录在这里。

    相关资料【教学视频】:https://www.bilibili.com/video/av70420291?p=143
    【BeanUtils工具类常用方法】:https://blog.csdn.net/wzc1991520/article/details/80176679
    0 留言 2020-03-21 15:05:00 奖励30点积分
  • HTTP:超文本传输协议

    概念
    HTTP == Hyper Text Transfer Protocol超文本传输协议

    <font color='blue'>传输协议:定义了客户端和服务器端通信时,发送数据的格式。</font>




    特点
    基于TCP/IP的高级协议默认端口号:80基于请求/响应模型的:一次请求对应一次响应
    无状态的:每次请求之间相互独立,不能交互数据
    网页中每一个文件都是一次单独的请求,几张图片,就是几次请求。如下图所示。

    <!--more-->

    历史版本http 0.9
    只有一个命令GET没有HEADER等描述数据的信息服务器发送完毕,就关闭TCP连接每一次请求响应都会建立新的连接
    http 1.0
    增加了很多命令,如status code和header多字符集支持、多部分发送、权限、缓存等
    http 1.1
    持久连接:keep-alive复用连接(较http1.0的每一次请求响应都会建立新的连接。好处:节约了连接的资源,提升了传输的速度。)提高性能的关键是低延迟而不是高带宽。较http1.0,对缓存的支持更好

    推送:主动发送js、css推送到浏览器。
    二进制流:可以并行发送数据。

    http 2.0
    所有数据以二进制传输同一个连接里面发送多个请求不再需要按顺序来头消息压缩以及推送等提高效率的功能所有的请求共用一个连接,可以更有效的使用tcp连接,通过带宽来提升http性能可以减少服务链接的压力,内存减少了,链接吞吐量大了解决浏览器连接数有限的问题资源合并减少请求的优化手段在http2.0来说是没有效果的
    请求信息数据 格式
    Servlet类中service()方法的参数ServletRequest字符串格式,比如:
    POST /login.html HTTP/1.1Host: localhostUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateReferer: http://localhost/login.htmlConnection: keep-aliveUpgrade-Insecure-Requests: 1username=zhangsan以下关于 <font color='blue'>请求信息数据格式</font> 的内容有缺省,详细见下一篇博客 《HTTP请求信息数据 - Request》 中。
    请求行
    格式:请求方式 请求url 请求协议/版本
    请求方式
    HTTP协议有7种请求协议,常用的有GET、POST两种<font color='orange'>GET</font>



    请求参数在请求行中,在url后请求的url长度有限制的不太安全(参数跟在url之后)浏览器控制台显示👇


    <font color='orange'>POST</font>



    请求参数在请求体中请求的url长度没有限制的相对安全(参数在请求体中)浏览器控制台显示👇

    请求url
    假设为 /login.html
    请求协议/版本
    HTTP/1.1
    请求头
    客户端浏览器告诉服务器一些信息格式:请求头名称 : 请求头值若有多个,则一行一个。
    常见的请求头
    Host
    User-Agent
    Referer
    Host
    请求的主机地址
    User-Agent
    浏览器告诉服务器,我访问你时候使用的浏览器版本信息作用:可以在服务器端获取该头的信息,解决浏览器的兼容性问题
    Referer
    比如上面几张图片的Referer是 http://localhost/login.html告诉服务器,我(当前请求)从哪里来?作用:防盗链、统计工作举个例子:我的网站想播放《战狼2》电影👇

    Connection
    keep-alivehttp1.1,表示该链接可以被复用
    请求空行
    就是一个空行(空白行)作用:分割POST请求的请求头和请求体
    请求体/请求正文
    封装POST请求信息的请求参数
    <br>下一篇博客 《HTTP请求信息数据 - Request》 中,详细学习了 请求信息数据 - Request。
    响应信息数据 格式
    Servlet类中service()方法的参数ServletResponse
    字符串数据,比如:

    HTTP/1.1 200 OKContent-Type: text/html;charset=UTF-8Content-Length: 101Date: Wed, 06 Jun 2018 07:08:42 GMT<html> <head> <title>$Title$</title> </head> <body> hello , response </body></html>响应行
    格式:协议/版本 响应状态码 状态码描述
    响应状态码
    服务器告诉客户端浏览器本次请求和响应的一个状态。
    特点
    状态码都是3位数字
    分类
    1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx状态码2xx:成功。代表:
    200
    3xx:重定向。代表:
    302:重定向304:访问缓存
    4xx:客户端错误。代表:
    403:错误是一种在网站访问过程中,常见的错误提示,表示资源不可用。服务器理解客户的请求,但拒绝处理它,通常由于服务器上文件或目录的权限设置导致的WEB访问错误。 404:请求路径没有对应的资源405:请求方式没有对应的doXxx方法
    5xx:服务器端错误。代表:500(服务器内部出现异常)

    响应头
    格式:响应头名称 :值
    常见的响应头
    Content-TypeContent-disposition
    Content-Type
    服务器告诉客户端本次响应体数据格式以及编码格式
    Content-disposition
    服务器告诉客户端以什么格式打开响应体数据
    其值:
    in-line:默认值,在当前页面内打开
    attachment;filename=xxx:以附件形式打开响应体。涉及文件下载等功能。
    响应空行
    就是一个空行(空白行)作用:分割响应头和响应体
    响应体
    传输的数据文件、HTML网页源码等等。比如,
    <html> <head> <title>$Title$</title> </head> <body> hello , response </body></html><br>
    下下篇博客 《HTTP响应信息数据 - Response》 中,详细学习响应信息数据 - Response。
    0 留言 2020-03-21 15:03:30 奖励36点积分
  • JavaWeb 概述

    JavaWeb使用Java语言开发基于互联网的项目
    今后主要使用B/S架构
    软件架构C/S:Clien/Server 客服端/服务器端在用户本地有一个客户端程序,在远程有一个服务器端。如:QQ,讯雷……
    优点
    用户体验好
    缺点
    开发、安装、部署、维护,麻烦
    ★B/S:Browser/Server 浏览器/服务器端只需要一个浏览器,用户通过不同的网站(URL),客户访问不同的服务器端程序
    优点:
    开发、安装、部署、维护,简单
    缺点
    如果应用过大,用户的体验可能会受到影响对硬件要求过高(带宽要高,……)
    <!--more-->

    B/S架构详解客户端浏览器通过URL,向服务器端发送请求,请求一些资源,资源就包括“静态资源”和“动态资源”。服务器端就会响应,返回这些资源。

    B/S架构是JavaWeb开发中重要的架构


    资源分类静态资源使用静态网页开发技术发布的资源
    特点
    所有用户访问,得到的结果是一样的如:文本,图片,音频,视频,HTML,CSS,JavaScript如果用户请求的是静态资源,那么服务器会直接将静态资源发送给浏览器。浏览器中内置了静态资源的解析引擎静态资源可以直接被浏览器解析

    1.HTML,CSS,JavaScript 统称:静态网页开发技术,也称静态网页三剑客2.经过解析引擎解析后,可以在浏览器中浏览图片、文字、超链接等3.不同浏览器的解析引擎不同,最终显示的网页可能不同

    HTML用于搭建基础网页,展示页面的内容
    CSS用于美化页面,布局页面
    JavaScript控制页面的元素,让页面有一些动态的效果
    ★动态资源使用动态网页技术发布的资源

    动态资源是今后学习JavaWeb的重点之一

    特点
    所用用户访问,得到的结果可能不一样如:jsp/servlet,php,asp……如果用户请求的是动态资源,那么服务器会执行动态资源转换为静态资源,再发送给用户

    学习动态资源前,必须学习静态资源!

    网络通信三要素IP
    电子设备(计算机)在网络中的唯一标识
    端口
    应用程序在计算机中的唯一标识。值范围:0~65536
    传输协议
    规定了数据传输的规则
    基础协议:

    tcp:安全协议,三次握手。 速度稍慢。
    udp:不安全协议。 速度快。


    Web服务器软件服务器
    安装了服务器软件的计算机
    服务器软件
    接收用户的请求,处理请求,做出响应
    Web服务器软件
    接收用户的请求,处理请求,做出响应。
    在Web服务器软件中,可以部署Web项目,让用户通过浏览器来访问这些项目
    Web容器

    常见的Java相关的Web服务器软件
    WebLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
    WebSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
    JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
    Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。


    JavaEE:Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范

    三大组件
    Servlet:服务器小程序Filter:过滤器Listener:监听器

    参照 https://www.cnblogs.com/kefir/p/9426754.html
    0 留言 2020-03-21 09:58:28 奖励35点积分
  • 比特币白皮书:一种点对点的电子现金系统

    比特币白皮书:一种点对点的电子现金系统
    原文作者:中本聪(Satoshi Nakamoto)
    原文地址:http://www.bitcoin.org/bitcoin.pdf
    作者邮箱:Satoshin@gmx.com
    执行翻译:8btc.com 巴比特 QQagent
    转载自:https://www.8btc.com/wiki/bitcoin-a-peer-to-peer-electronic-cash-system


    [摘要]:本文提出了一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。虽然数字签名(Digital signatures)部分解决了这个问题,但是如果仍然需要第三方的支持才能防止双重支付(double-spending)的话,那么这种系统也就失去了存在的价值。我们(we)在此提出一种解决方案,使现金系统在点对点的环境下运行,并防止双重支付问题。该网络通过随机散列(hashing)对全部交易加上时间戳(timestamps),将它们合并入一个不断延伸的基于随机散列的工作量证明(proof-of-work)的链条作为交易记录,除非重新完成全部的工作量证明,形成的交易记录将不可更改。最长的链条不仅将作为被观察到的事件序列(sequence)的证明,而且被看做是来自CPU计算能力最大的池(pool)。只要大多数的CPU计算能力都没有打算合作起来对全网进行攻击,那么诚实的节点将会生成最长的、超过攻击者的链条。这个系统本身需要的基础设施非常少。信息尽最大努力在全网传播即可,节点(nodes)可以随时离开和重新加入网络,并将最长的工作量证明链条作为在该节点离线期间发生的交易的证明。

    1. 简介互联网上的贸易,几乎都需要借助金融机构作为可资信赖的第三方来处理电子支付信息。虽然这类系统在绝大多数情况下都运作良好,但是这类系统仍然内生性地受制于“基于信用的模式”(trust based model)的弱点。我们无法实现完全不可逆的交易,因为金融机构总是不可避免地会出面协调争端。而金融中介的存在,也会增加交易的成本,并且限制了实际可行的最小交易规模,也限制了日常的小额支付交易。并且潜在的损失还在于,很多商品和服务本身是无法退货的,如果缺乏不可逆的支付手段,互联网的贸易就大大受限。因为有潜在的退款的可能,就需要交易双方拥有信任。而商家也必须提防自己的客户,因此会向客户索取完全不必要的个人信息。而实际的商业行为中,一定比例的欺诈性客户也被认为是不可避免的,相关损失视作销售费用处理。而在使用物理现金的情况下,这些销售费用和支付问题上的不确定性却是可以避免的,因为此时没有第三方信用中介的存在。 所以,我们非常需要这样一种电子支付系统,它基于密码学原理而不基于信用,使得任何达成一致的双方,能够直接进行支付,从而不需要第三方中介的参与。杜绝回滚(reverse)支付交易的可能,这就可以保护特定的卖家免于欺诈;而对于想要保护买家的人来说,在此环境下设立通常的第三方担保机制也可谓轻松加愉快。在这篇论文中,我们(we)将提出一种通过点对点分布式的时间戳服务器来生成依照时间前后排列并加以记录的电子交易证明,从而解决双重支付问题。只要诚实的节点所控制的计算能力的总和,大于有合作关系的(cooperating)攻击者的计算能力的总和,该系统就是安全的。
    2. 交易(Transactions)我们定义,一枚电子货币(an electronic coin)是这样的一串数字签名:每一位所有者通过对前一次交易和下一位拥有者的公钥(Public key) 签署一个随机散列的数字签名,并将这个签名附加在这枚电子货币的末尾,电子货币就发送给了下一位所有者。而收款人通过对签名进行检验,就能够验证该链条的所有者。

    该过程的问题在于,收款人将难以检验,之前的某位所有者,是否对这枚电子货币进行了双重支付。通常的解决方案,就是引入信得过的第三方权威,或者类似于造币厂(mint)的机构,来对每一笔交易进行检验,以防止双重支付。在每一笔交易结束后,这枚电子货币就要被造币厂回收,而造币厂将发行一枚新的电子货币;而只有造币厂直接发行的电子货币,才算作有效,这样就能够防止双重支付。可是该解决方案的问题在于,整个货币系统的命运完全依赖于运作造币厂的公司,因为每一笔交易都要经过该造币厂的确认,而该造币厂就好比是一家银行。 我们需要收款人有某种方法,能够确保之前的所有者没有对更早发生的交易实施签名。从逻辑上看,为了达到目的,实际上我们需要关注的只是于本交易之前发生的交易,而不需要关注这笔交易发生之后是否会有双重支付的尝试。为了确保某一次交易是不存在的,那么唯一的方法就是获悉之前发生过的所有交易。在造币厂模型里面,造币厂获悉所有的交易,并且决定了交易完成的先后顺序。如果想要在电子系统中排除第三方中介机构,那么交易信息就应当被公开宣布(publicly announced)[1] ,我们需要整个系统内的所有参与者,都有唯一公认的历史交易序列。收款人需要确保在交易期间绝大多数的节点都认同该交易是首次出现。
    3. 时间戳服务器(Timestamp server)本解决方案首先提出一个“时间戳服务器”。时间戳服务器通过对以区块(block)形式存在的一组数据实施随机散列而加上时间戳,并将该随机散列进行广播,就像在新闻或世界性新闻组网络(Usenet)的发帖一样[2][3][4][5] 。显然,该时间戳能够证实特定数据必然于某特定时间是的确存在的,因为只有在该时刻存在了才能获取相应的随机散列值。每个时间戳应当将前一个时间戳纳入其随机散列值中,每一个随后的时间戳都对之前的一个时间戳进行增强(reinforcing),这样就形成了一个链条(Chain)。

    4. 工作量证明(Proof-of-Work)为了在点对点的基础上构建一组分散化的时间戳服务器,仅仅像报纸或世界性新闻网络组一样工作是不够的,我们还需要一个类似于亚当•柏克(Adam Back)提出的哈希现金(Hashcash)[6] 。在进行随机散列运算时,工作量证明机制引入了对某一个特定值的扫描工作,比方说SHA-256下,随机散列值以一个或多个0开始。那么随着0的数目的上升, 找到这个解所需要的工作量将呈指数增长,而对结果进行检验则仅需要一次随机散列运算。
    我们在区块中补增一个随机数(Nonce),这个随机数要使得该给定区块的随机散列值出现了所需的那么多个0。我们通过反复尝试来找到这个随机数,直到找到为止,这样我们就构建了一个工作量证明机制。只要该CPU耗费的工作量能够满足该工作量证明机制,那么除非重新完成相当的工作量,该区块的信息就不可更改。由于之后的区块是链接在该区块之后的,所以想要更改该区块中的信息,就还需要重新完成之后所有区块的全部工作量。

    同时,该工作量证明机制还解决了在集体投票表决时,谁是大多数的问题。如果决定大多数的方式是基于IP地址的,一IP地址一票,那么如果有人拥有分配大量IP地址的权力,则该机制就被破坏了。而工作量证明机制的本质则是一CPU一票。“大多数”的决定表达为最长的链,因为最长的链包含了最大的工作量。如果大多数的CPU为诚实的节点控制,那么诚实的链条将以最快的速度延长,并超越其他的竞争链条。如果想要对业已出现的区块进行修改,攻击者必须重新完成该区块的工作量外加该区块之后所有区块的工作量,并最终赶上和超越诚实节点的工作量。我们将在后文证明,设想一个较慢的攻击者试图赶上随后的区块,那么其成功概率将呈指数化递减。 另一个问题是,硬件的运算速度在高速增长,而节点参与网络的程度则会有所起伏。为了解决这个问题,工作量证明的难度(the proof-of-work difficulty)将采用移动平均目标的方法来确定,即令难度指向令每小时生成区块的速度为某一个预定的平均数。如果区块生成的速度过快,那么难度就会提高。
    5. 网络运行该网络的步骤如下:

    新的交易向全网进行广播
    每一个节点都将收到的交易信息纳入一个区块中
    每个节点都尝试在自己的区块中找到一个具有足够难度的工作量证明
    当一个节点找到了一个工作量证明,它就向全网进行广播
    当且仅当包含在该区块中的所有交易都是有效的且之前未存在过的,其他节点才认同该区块的有效性
    其他节点表示他们接受该区块,而表示接受的方法,则是在跟随该区块的末尾,制造新的区块以延长该链条,而将被接受区块的随机散列值视为先于新区快的随机散列值

    节点始终都将最长的链条视为正确的链条,并持续工作和延长它。如果有两个节点同时广播不同版本的新区块,那么其他节点在接收到该区块的时间上将存在先后差别。当此情形,他们将在率先收到的区块基础上进行工作,但也会保留另外一个链条,以防后者变成最长的链条。该僵局(tie)的打破要等到下一个工作量证明被发现,而其中的一条链条被证实为是较长的一条,那么在另一条分支链条上工作的节点将转换阵营,开始在较长的链条上工作。 所谓“新的交易要广播”,实际上不需要抵达全部的节点。只要交易信息能够抵达足够多的节点,那么他们将很快被整合进一个区块中。而区块的广播对被丢弃的信息是具有容错能力的。如果一个节点没有收到某特定区块,那么该节点将会发现自己缺失了某个区块,也就可以提出自己下载该区块的请求。
    6. 激励我们约定如此:每个区块的第一笔交易进行特殊化处理,该交易产生一枚由该区块创造者拥有的新的电子货币。这样就增加了节点支持该网络的激励,并在没有中央集权机构发行货币的情况下,提供了一种将电子货币分配到流通领域的一种方法。这种将一定数量新货币持续增添到货币系统中的方法,非常类似于耗费资源去挖掘金矿并将黄金注入到流通领域。此时,CPU的时间和电力消耗就是消耗的资源。 另外一个激励的来源则是交易费(transaction fees)。如果某笔交易的输出值小于输入值,那么差额就是交易费,该交易费将被增加到该区块的激励中。只要既定数量的电子货币已经进入流通,那么激励机制就可以逐渐转换为完全依靠交易费,那么本货币系统就能够免于通货膨胀。 激励系统也有助于鼓励节点保持诚实。如果有一个贪婪的攻击者能够调集比所有诚实节点加起来还要多的CPU计算力,那么他就面临一个选择:要么将其用于诚实工作产生新的电子货币,或者将其用于进行二次支付攻击。那么他就会发现,按照规则行事、诚实工作是更有利可图的。因为该等规则使得他能够拥有更多的电子货币,而不是破坏这个系统使得其自身财富的有效性受损。
    7. 回收硬盘空间如果最近的交易已经被纳入了足够多的区块之中,那么就可以丢弃该交易之前的数据,以回收硬盘空间。为了同时确保不损害区块的随机散列值,交易信息被随机散列时,被构建成一种Merkle树(Merkle tree)[7] 的形态,使得只有根(root)被纳入了区块的随机散列值。通过将该树(tree)的分支拔除(stubbing)的方法,老区块就能被压缩。而内部的随机散列值是不必保存的。

    不含交易信息的区块头(Block header)大小仅有80字节。如果我们设定区块生成的速率为每10分钟一个,那么每一年产生的数据位4.2MB。(80 bytes 6 24 * 365 = 4.2MB)。2008年,PC系统通常的内存容量为2GB,按照摩尔定律的预言,即使将全部的区块头存储于内存之中都不是问题。
    8. 简化的支付确认(Simplified Payment Verification)在不运行完整网络节点的情况下,也能够对支付进行检验。一个用户需要保留最长的工作量证明链条的区块头的拷贝,它可以不断向网络发起询问,直到它确信自己拥有最长的链条,并能够通过merkle的分支通向它被加上时间戳并纳入区块的那次交易。节点想要自行检验该交易的有效性原本是不可能的,但通过追溯到链条的某个位置,它就能看到某个节点曾经接受过它,并且于其后追加的区块也进一步证明全网曾经接受了它。

    当此情形,只要诚实的节点控制了网络,检验机制就是可靠的。但是,当全网被一个计算力占优的攻击者攻击时,将变得较为脆弱。因为网络节点能够自行确认交易的有效性,只要攻击者能够持续地保持计算力优势,简化的机制会被攻击者焊接的(fabricated)交易欺骗。那么一个可行的策略就是,只要他们发现了一个无效的区块,就立刻发出警报,收到警报的用户将立刻开始下载被警告有问题的区块或交易的完整信息,以便对信息的不一致进行判定。对于日常会发生大量收付的商业机构,可能仍会希望运行他们自己的完整节点,以保持较大的独立完全性和检验的快速性。

    9. 价值的组合与分割(Combining and Splitting Value)虽然可以单个单个地对电子货币进行处理,但是对于每一枚电子货币单独发起一次交易将是一种笨拙的办法。为了使得价值易于组合与分割,交易被设计为可以纳入多个输入和输出。一般而言是某次价值较大的前次交易构成的单一输入,或者由某几个价值较小的前次交易共同构成的并行输入,但是输出最多只有两个:一个用于支付,另一个用于找零(如有)。 需要指出的是,当一笔交易依赖于之前的多笔交易时,这些交易又各自依赖于多笔交易,但这并不存在任何问题。因为这个工作机制并不需要展开检验之前发生的所有交易历史。
    10. 隐私(Privacy)
    传统的造币厂模型为交易的参与者提供了一定程度的隐私保护,因为试图向可信任的第三方索取交易信息是严格受限的。但是如果将交易信息向全网进行广播,就意味着这样的方法失效了。但是隐私依然可以得到保护:将公钥保持为匿名。公众得知的信息仅仅是有某个人将一定数量的货币发所给了另外一个人,但是难以将该交易同特定的人联系在一起,也就是说,公众难以确信,这些人究竟是谁。这同股票交易所发布的信息是类似的,股票交易发生的时间、交易量是记录在案且可供查询的,但是交易双方的身份信息却不予透露。 作为额外的预防措施,使用者可以让每次交易都生成一个新的地址,以确保这些交易不被追溯到一个共同的所有者。但是由于并行输入的存在,一定程度上的追溯还是不可避免的,因为并行输入表明这些货币都属于同一个所有者。此时的风险在于,如果某个人的某一个公钥被确认属于他,那么就可以追溯出此人的其它很多交易。
    11. 计算设想如下场景:一个攻击者试图比诚实节点产生链条更快地制造替代性区块链。即便它达到了这一目的,但是整个系统也并非就此完全受制于攻击者的独断意志了,比方说凭空创造价值,或者掠夺本不属于攻击者的货币。这是因为节点将不会接受无效的交易,而诚实的节点永远不会接受一个包含了无效信息的区块。一个攻击者能做的,最多是更改他自己的交易信息,并试图拿回他刚刚付给别人的钱。 诚实链条和攻击者链条之间的竞赛,可以用二叉树随机漫步(Binomial Random Walk)来描述。成功事件定义为诚实链条延长了一个区块,使其领先性+1,而失败事件则是攻击者的链条被延长了一个区块,使得差距-1。 攻击者成功填补某一既定差距的可能性,可以近似地看做赌徒破产问题(Gambler’s Ruin problem)。假定一个赌徒拥有无限的透支信用,然后开始进行潜在次数为无穷的赌博,试图填补上自己的亏空。那么我们可以计算他填补上亏空的概率,也就是该攻击者赶上诚实链条,如下所示[8] :

    假定p>q,那么攻击成功的概率就因为区块数的增长而呈现指数化下降。由于概率是攻击者的敌人,如果他不能幸运且快速地获得成功,那么他获得成功的机会随着时间的流逝就变得愈发渺茫。那么我们考虑一个收款人需要等待多长时间,才能足够确信付款人已经难以更改交易了。我们假设付款人是一个支付攻击者,希望让收款人在一段时间内相信他已经付过款了,然后立即将支付的款项重新支付给自己。虽然收款人届时会发现这一点,但为时已晚。 收款人生成了新的一对密钥组合,然后只预留一个较短的时间将公钥发送给付款人。这将可以防止以下情况:付款人预先准备好一个区块链然后持续地对此区块进行运算,直到运气让他的区块链超越了诚实链条,方才立即执行支付。当此情形,只要交易一旦发出,攻击者就开始秘密地准备一条包含了该交易替代版本的平行链条。 然后收款人将等待交易出现在首个区块中,然后在等到z个区块链接其后。此时,他仍然不能确切知道攻击者已经进展了多少个区块,但是假设诚实区块将耗费平均预期时间以产生一个区块,那么攻击者的潜在进展就是一个泊松分布,分布的期望值为:

    当此情形,为了计算攻击者追赶上的概率,我们将攻击者取得进展区块数量的泊松分布的概率密度,乘以在该数量下攻击者依然能够追赶上的概率。

    化为如下形式,避免对无限数列求和:

    写为如下C语言代码:
    #include <math.h> double AttackerSuccessProbability(double q, int z) { double p = 1.0 - q; double lambda = z * (q / p); double sum = 1.0; int i, k; for (k = 0; k <= z; k++) { double poisson = exp(-lambda); for (i = 1; i <= k; i++) poisson *= lambda / i; sum -= poisson * (1 - pow(q / p, z - k)); } return sum;}
    对其进行运算,我们可以得到如下的概率结果,发现概率对z值呈指数下降。
    当 q=0.1 时,z=0 P=1.0000000z=1 P=0.2045873z=2 P=0.0509779z=3 P=0.0131722z=4 P=0.0034552z=5 P=0.0009137z=6 P=0.0002428z=7 P=0.0000647z=8 P=0.0000173z=9 P=0.0000046z=10 P=0.0000012当 q=0.3 时,z=0 P=1.0000000z=5 P=0.1773523z=10 P=0.0416605z=15 P=0.0101008z=20 P=0.0024804z=25 P=0.0006132z=30 P=0.0001522z=35 P=0.0000379z=40 P=0.0000095z=45 P=0.0000024z=50 P=0.0000006求解令P<0.1%的z值:
    为使 P < 0.001 则q=0.10 z=5q=0.15 z=8q=0.20 z=11q=0.25 z=15q=0.30 z=24q=0.35 z=41q=0.40 z=89q=0.45 z=34012.结论我们在此提出了一种不需要信用中介的电子支付系统。我们首先讨论了通常的电子货币的电子签名原理,虽然这种系统为所有权提供了强有力的控制,但是不足以防止双重支付。为了解决这个问题,我们提出了一种采用工作量证明机制的点对点网络来记录交易的公开信息,只要诚实的节点能够控制绝大多数的CPU计算能力,就能使得攻击者事实上难以改变交易记录。该网络的强健之处在于它结构上的简洁性。节点之间的工作大部分是彼此独立的,只需要很少的协同。每个节点都不需要明确自己的身份,由于交易信息的流动路径并无任何要求,所以只需要尽其最大努力传播即可。节点可以随时离开网络,而想重新加入网络也非常容易,因为只需要补充接收离开期间的工作量证明链条即可。节点通过自己的CPU计算力进行投票,表决他们对有效区块的确认,他们不断延长有效的区块链来表达自己的确认,并拒绝在无效的区块之后延长区块以表示拒绝。本框架包含了一个P2P电子货币系统所需要的全部规则和激励措施。
    参考文献
    [1] W. Dai, “b-money,” http://www.weidai.com/bmoney.txt, 1998.
    [2] H. Massias, X.S. Avila, and J.-J. Quisquater, “Design of a secure timestamping service with minimal trust requirements,” In 20th Symposium on Information Theory in the Benelux, May 1999.
    [3] S. Haber, W.S. Stornetta, “How to time-stamp a digital document,” In Journal of Cryptology, vol 3, no2, pages 99-111, 1991.
    [4] D. Bayer, S. Haber, W.S. Stornetta, “Improving the efficiency and reliability of digital time-stamping,” In Sequences II: Methods in Communication, Security and Computer Science, pages 329-334, 1993.
    [5] S. Haber, W.S. Stornetta, “Secure names for bit-strings,” In Proceedings of the 4th ACM Conference on Computer and Communications Security, pages 28-35, April 1997.
    [6] A. Back, “Hashcash - a denial of service counter-measure,” http://www.hashcash.org/papers/hashcash.pdf, 2002.
    [7] R.C. Merkle, “Protocols for public key cryptosystems,” In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980.
    [8] W. Feller, “An introduction to probability theory and its applications,” 1957.
    0 留言 2020-03-16 09:40:10 奖励45点积分
  • 【Cocos Creator 联机实战教程(2)】——匹配系统 精华

    1.知识点讲解大型多人互动游戏一般都会有一个匹配系统,用来让玩家进行联网游戏,现在我们来讲一讲这种系统吧,我们可以做个比较简单的双人对战匹配系统。
    我们让每一对匹配成功的玩家进入一个独立的房间,所以不同的房间的通信应该互不影响,由于不同场景的通信内容不同,所以不同场景的通信也应该独立
    我们把这个游戏的匹配过程比作开房的过程,

    如果有一个人进入了宾馆,那么他最先进入的区域就是hall(大厅),当然他可能就是逛逛,又推门出去
    当他想休息时他就去前台开个房,那么他就进入了queue(队列),并断开hall的通信
    当另一个人也想休息的时候也去前台排队,当个queue里有两个人的时候,前台小姐就给了他俩一个空闲房间的钥匙,他们就一起进入了一个独立的room,并断开queue的通信
    以上循环,房间数有限,在房间满的时候不能匹配成功

    当然,你也可以根据实际情况升级这个匹配系统,比如,分等级的匹配(开不同的队列等待)。
    注意:房卡游戏虽然也用到了房间这个概念,但不是匹配,这种游戏更像唱卡拉OK。进入大厅后,组织者去开个房间,其他人一起进。或者迟到的人拿着房间号直接进去。
    2. 步骤我们的游戏分为三个场景

    游戏启动的时候进入menu场景,当玩家点击对战时进入match场景,匹配成功进入game场景,取消匹配返回menu场景,游戏结束返回menu场景
    我们在Global里定义socket
    window.G = { globalSocket:null,//全局 hallSocket:null,//大厅 queueSocket:null,//队列 roomSocket:null,//房间 gameManager:null, chessManager:null, stand:null,}
    menu场景启动时,我们连接hallSocket,开始匹配时,断开hallSocket
    cc.Class({ extends: cc.Component, onLoad: function () { G.globalSocket = io.connect('127.0.0.1:4747'); //断开连接后再重新连接需要加上{'force new connection': true} G.hallSocket = io.connect('127.0.0.1:4747/hall',{'force new connection': true}); }, onBtnStart() { G.hallSocket.disconnect(); cc.director.loadScene('match'); }});
    进入match场景,连接queueSocket,先进入queue的玩家主场黑棋先手,后进入客场白棋后手(这个逻辑是服务端判断的),匹配成功时,服务端会发送roomId,玩家进入相应的房间,并断开queueSocket的通信
    const Constants = require('Constants');const STAND = Constants.STAND;cc.Class({ extends: cc.Component, onLoad: function () { G.queueSocket = io.connect('127.0.0.1:4747/queue', { 'force new connection': true }); G.queueSocket.on('set stand', function (stand) { if (stand === 'black') { G.stand = STAND.BLACK; } else if (stand === 'white') { G.stand = STAND.WHITE; } }); G.queueSocket.on('match success', function (roomId) { cc.log('match success' + roomId); G.roomSocket = io.connect('127.0.0.1:4747/rooms' + roomId, { 'force new connection': true }); G.queueSocket.disconnect(); cc.director.loadScene('game'); }); }, onBtnCancel() { G.queueSocket.disconnect(); cc.director.loadScene('menu'); }});
    在game场景中,如果游戏结束我们就断掉roomSocket回到menu场景
    startGame() { this.turn = STAND.BLACK; this.gameState = GAME_STATE.PLAYING; this.showInfo('start game'); },endGame() { let onFinished = () =>{ G.roomSocket.disconnect(); cc.director.loadScene('menu'); } this.infoAnimation.on('finished',onFinished,this); this.gameState = GAME_STATE.OVER; this.showInfo('game over'); },
    服务端完整逻辑
    let app = require('express')();let server = require('http').Server(app);let io = require('socket.io')(server);server.listen(4747, function() { console.log('listening on:4747');});let MAX = 30;//最大支持连接房间数let hall = null;//大厅let queue = null;//匹配队列let rooms = [];//游戏房间function Hall() { this.people = 0; this.socket = null;}function Room(){ this.people = 0; this.socket = null;}function Queue(){ this.people = 0; this.socket = null;}hall = new Hall();queue = new Queue();for(let n = 0;n < MAX;n++){ rooms[n] = new Room();}function getFreeRoom(){ for(let n = 0;n < MAX;n++){ if(rooms[n].people === 0){ return n; } } return -1;}io.people = 0;io.on('connection',function(socket){ io.people++; console.log('someone connected'); socket.on('disconnect',function(){ io.people--; console.log('someone disconnected'); });})hall.socket = io.of('/hall').on('connection', function(socket) { hall.people++; console.log('a player connected.There are '+hall.people+' people in hall'); hall.socket.emit('people changed',hall.people); socket.on('disconnect',function(){ hall.people--; console.log('a player disconnected.There are '+hall.people+' people in hall'); hall.socket.emit('people changed',hall.people); });});queue.socket = io.of('/queue').on('connection',function(socket){ queue.people++; console.log('someone connect queue socket.There are '+queue.people+' people in queue'); if(queue.people === 1){ socket.emit('set stand','black'); }else if(queue.people === 2){ socket.emit('set stand','white'); let roomId = getFreeRoom(); console.log(roomId+"roomId"); if(roomId >= 0){ queue.socket.emit('match success',roomId); console.log('match success.There are '+queue.people+' people in queue'); }else{ console.log('no free room!'); } } socket.on('cancel match',function(){ queue.people--; console.log('someone cancel match.There are '+queue.people+' people in queue'); }); socket.on('disconnect',function(){ queue.people--; console.log('someone disconnected match.There are '+queue.people+' people in queue'); });});for(let i = 0;i < MAX;i++){ rooms[i].socket = io.of('/rooms'+i).on('connection',function(socket){ rooms[i].people++; console.log('some one connected room'+i+'.There are '+rooms[i].people+' people in the room'); socket.on('update chessboard',function(chessCoor){ socket.broadcast.emit('update chessboard',chessCoor); }); socket.on('force change turn',function(){ socket.broadcast.emit('force change turn'); }); socket.on('disconnect',function(){ rooms[i].people--; console.log('someone disconnected room'+i+'.There are '+rooms[i].people+' people in the room'); }); });}
    3. 总结我们做的是比较简单的匹配系统,实际上还有匹配算法(选择排队的顺序不仅仅是先来后到)。
    这是我们需要掌握的新知识,除此之外我们都可以使用之前的知识点完成游戏。
    注意以下问题:

    跨场景访问变量
    在util下面有两个脚本,Constants用来存储游戏常量,然后其他地方需要常量时
    const Constants = require('Constants');const GAME_STATE = Constants.GAME_STATE;const STAND = Constants.STAND;const CHESS_TYPE = Constants.CHESS_TYPE;
    Global存储全局控制句柄,需要访问他们的时候,就可以通过(G.)的方式

    控制单位应该是脚本而不是节点
    本教程部分素材来源于网络。
    4 留言 2018-12-07 14:58:43 奖励35点积分
  • 基于AheadLib工具进行DLL劫持 精华

    背景或许你听过DLL劫持技术,获取你还没有尝试过DLL劫持技术。DLL劫持技术的原理是:

    由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。

    现在,本文就使用 AheadLib 工具生成劫持代码,对程序进行DLL劫持。现在就把实现原理和过程写成文档,分享给大家。
    实现过程本文选取劫持的程序是从网上随便下的一个程序“360文件粉碎机独立版.exe”,我们使用 PEview.exe 查看改程序的导入表,主要是看有程序需要导入哪些DLL文件。

    观察导入的DLL,类似KERNEL32.DLL、USER32.DLL等受系统保护的重要DLL,劫持难度比较大,所以,我们选择VERSION.DLL。至于,判断是不是受系统保护的DLL,可以查看注册表里面的键值,里面的DLL都是系统保护的,加载路径固定:
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\knowndlls然后,确定劫持的DLL文件之后,我们使用 AheadLib 工具来生成DLL劫持代码:

    接着,新建一个DLL工程,把AheadLib工具生成的代码拷贝到DLL工程中,同时,我们在DLL的入口点函数DllMain中增加一行弹窗代码,这样可以提示我们DLL劫持成功。然后编译链接,生成DLL文件。这个我们自己编译生成的DLL文件,就可以把DLL名称改成“VERSION.DLL”,放到和“360文件粉碎机独立版.exe”程序在同一目录下,运行程序,则会加载同一目录下的“VERSION.DLL”。
    为了验证DLL程序是否能成功劫持,我们把改名后的“VERSION.DLL”和“360文件粉碎机独立版.exe”放在桌面,然后,运行程序,这是,成功弹窗:

    我们使用 Process Explorer 工具查看下“360文件粉碎机独立版.exe”进程加载的DLL情况:

    可以看到,我们自己的version.dll成功被加载,而且还加载了系统的version.dll。之所以会加载系统的version.dll文件,是因为我们自己的DLL文件中,会加载version.dll文件。
    编码实现现在,我给出version.dll劫持部分入口点部分的代码,其余的代码都是使用AheadLib工具生成的,自己在入口点添加了一行弹窗的代码而已。
    // 入口函数BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved){ if (dwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); ::MessageBox(NULL, "I am Demon", "CDIY", MB_OK); return Load(); } else if (dwReason == DLL_PROCESS_DETACH) { Free(); } return TRUE;}
    总结有了AheadLib劫持代码生成工具的帮助,使得DLL劫持变得很轻松。本文的文档自己极力简化了,大家只要认真跟着步骤操作,应该可以看得懂的。
    注意,本文演示的例子实在 Windows7 32位系统上,对于64位系统,原理是一样的,对于代码劫持工具也可以换成 AheadLib 64位版本的。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2018-12-10 09:49:26 奖励40点积分
  • 驱动环境下的任务进程获取枚举

    驱动环境下的,枚举系统任务,属于新手驱动学习,还望各位大佬,发现失误能够指点一二,辛得指点,感恩各位!
    #include "ntddk.h"typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, // 0 SystemProcessorInformation, // 1 SystemPerformanceInformation, // 2 SystemTimeOfDayInformation, // 3 SystemNotImplemented1, // 4 SystemProcessesAndThreadsInformation, // 5 SystemCallCounts, // 6 SystemConfigurationInformation, // 7 SystemProcessorTimes, // 8 SystemGlobalFlag, // 9 SystemNotImplemented2, // 10 SystemModuleInformation, // 11 SystemLockInformation, // 12 SystemNotImplemented3, // 13 SystemNotImplemented4, // 14 SystemNotImplemented5, // 15 SystemHandleInformation, // 16 SystemObjectInformation, // 17 SystemPagefileInformation, // 18 SystemInstructionEmulationCounts, // 19 SystemInvalidInfoClass1, // 20 SystemCacheInformation, // 21 SystemPoolTagInformation, // 22 SystemProcessorStatistics, // 23 SystemDpcInformation, // 24 SystemNotImplemented6, // 25 SystemLoadImage, // 26 SystemUnloadImage, // 27 SystemTimeAdjustment, // 28 SystemNotImplemented7, // 29 SystemNotImplemented8, // 30 SystemNotImplemented9, // 31 SystemCrashDumpInformation, // 32 SystemExceptionInformation, // 33 SystemCrashDumpStateInformation, // 34 SystemKernelDebuggerInformation, // 35 SystemContextSwitchInformation, // 36 SystemRegistryQuotaInformation, // 37 SystemLoadAndCallImage, // 38 SystemPrioritySeparation, // 39 SystemNotImplemented10, // 40 SystemNotImplemented11, // 41 SystemInvalidInfoClass2, // 42 SystemInvalidInfoClass3, // 43 SystemTimeZoneInformation, // 44 SystemLookasideInformation, // 45 SystemSetTimeSlipEvent, // 46 SystemCreateSession, // 47 SystemDeleteSession, // 48 SystemInvalidInfoClass4, // 49 SystemRangeStartInformation, // 50 SystemVerifierInformation, // 51 SystemAddVerifier, // 52 SystemSessionProcessesInformation // 53} SYSTEM_INFORMATION_CLASS;typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; KPRIORITY BasePriority; ULONG ContextSwitchCount; LONG State; LONG WaitReason;} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryDelta;//构成结构系列的偏移量也就是下一个进程 ULONG ThreadCount;//线程的数目 ULONG Reserved1[6];// 暂时未知 LARGE_INTEGER CreateTime;//创建时间 LARGE_INTEGER UserTime;//用户模式的CPU时间 LARGE_INTEGER KernelTime;//内核模式下的时间 UNICODE_STRING ProcessName;//进程的名称 KPRIORITY BasePriority;//进程的优先权 ULONG ProcessId;//进程的标识符 ULONG InheritedFromProcessId;//父进程的标识符 ULONG HandleCount;//句柄数目 ULONG Reserved2[2];// VM_COUNTERS VmCounters;//虚拟存储器的机构 IO_COUNTERS IoCounters;//io计数器 //SYSTEM_THREAD_INFORMATION Threads[1];//进程相关的线程结构数组这里我们不使用} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;extern "C"NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );//--------------------------------------------------------------//-----------------------------------------------------------------NTSTATUS Ring0EnumProcess(){ ULONG cbuffer=0x8000; PVOID pBuffer=NULL; NTSTATUS Status; PSYSTEM_PROCESS_INFORMATION pInfo; do { pBuffer=ExAllocatePool(NonPagedPool,cbuffer); if (pBuffer==NULL) { return 1; } Status=ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,pBuffer,cbuffer,NULL); if (Status==STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(pBuffer); cbuffer*=2; }else if (!NT_SUCCESS(Status)) { ExFreePool(pBuffer); return 1; } } while (Status==STATUS_INFO_LENGTH_MISMATCH); pInfo=(PSYSTEM_PROCESS_INFORMATION)pBuffer; for (;;) { LPWSTR pszProcessName=pInfo->ProcessName.Buffer; if (pszProcessName==NULL) { pszProcessName=L"null"; } DbgPrint("ProcessID%d 进程名::%S 父进程ID%d",pInfo->ProcessId,pInfo->ProcessName.Buffer,pInfo->InheritedFromProcessId); if (pInfo->NextEntryDelta==0) { break; } pInfo=(PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo)+pInfo->NextEntryDelta); } ExFreePool(pBuffer); return 0;}VOID Unload(IN PDRIVER_OBJECT DriverObject){}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { DriverObject->DriverUnload = Unload; Ring0EnumProcess(); return STATUS_SUCCESS; }
    0 留言 2020-02-16 13:55:01 奖励16点积分
  • 基于SSM框架的B/S微博系统的设计与实现

    第一章 绪 论时至今日,网络对于现代人来说,早已成为人类科技发展进步的桥梁,而通过网络衍生出的一系列产品也不断的冲击着人们的日常生活,截至2016年年底,我国网名数量达到7亿,有超多一半多人在使用网络,而它天涯咫尺的作用,不但消除了人与人地域上的距离,更是拉近了心灵的距离,沟通与互动变的异常频繁与重要。
    随着互联网新时代的来临,微博借着互联网的桥梁,逐渐进入网名的视野中,没有博客的长篇大论,也不需要严谨的逻辑层次,这使得网络中一大批的原创文章被生产发掘,短短几句话便可以在网络中激起千层浪,普通人也可能在一夜之间成为拥有数千万粉丝的“网红”。微博的便捷、原创、和草根性使它成为中国网民茶余饭后的网上生活。
    早在2006年3月,Obvious推出了Twitter服务,这个世界上最早同时也是最著名的微博系统,而在那是,微博也仅仅只是为了给好友发送手机短信,在中国,饭否网的上线标志中国微博的开端,之后腾讯滔滔、叽歪、嘀咕等微博的开荒者终究也都没能站在最后。
    2010年,我国的微博得到迅速发展,这一年,无论微博的用户还是影响力都达到前所未有的高度,以新浪门户为首的腾讯、新浪、搜狐等微博展现出全新的活力。到了2013年6月,中国微博用户规模高达3.31亿人,在微博中流动的信息有超过2亿多条。
    2010年11月,新浪微博推出群组功能,这个功能不但可以与好友实时联系,又可以随时发布最新信息,2012年添加新功能“悄悄关注”,在用户关注后不通知被关注用户,也不显示给被关注用户,2013年,微博推出包括iPhone和Android的移动客户端,新增“密友”功能,同年10月份新浪微博“粉丝服务平台”上线,粉丝服务平台帮助认证用户为订阅自己的用户提供精彩内容和互动服务,自此微博由“营销”向“营销+服务”转型!而新浪微博依旧延续这自己的名人效应,一个普通账号,在没有粉丝关注的情况下,发布的微博却很难被他人看到,如果需要在微博中求助,没有“名人大V”的帮助,很难被别人所注意到。想要在微博中寻求帮助,就需要微博提供更多的服务。而本系统通过使用积分悬赏功能使得用户可以通过积分悬赏自己的问题,来让更多的用户回答自己的问题,得到更多人的帮助。
    第二章 相关技术介绍2.1 架构概述B/S架构(Browser/Server,浏览器/服务器模式):是一种通过将浏览器作为客户端的网络结构模式,利用已经逐步成熟的web浏览器技术,结合浏览器的多种功能,使用浏览器来作为早先C/S(Client/Serve)架构下复杂的客户端,使用C/S架构使得用户的客户端得到统一,将软件系统的核心功能集中在服务器端,系统的升级和维护更加简单,开发人员只需要管理服务器就可以做到对如软件系统的更新和维护,B/S架构所带来的众多优点使得它成为将来信息化发展的主流方向。
    MVC模式:即模型(Model),视图(View),控制器(Controller)是一种软件开发的设计模式,Model主要负责分离出来的业务逻辑的数据交互,View主要负责数据的提交和展示,Controller主要负责获取请求和请求的转发。
    SSM框架(SpringMVC+Spring+Mybatis):由这三个开源框架组成的web项目的框架,是标准的MVC模式,spring MVC负责请求的转发和视图管理,spring实现业务对象管理和事务控制,mybatis作为持久化引擎,相对于早先流行的SSH(Struts+Spring+Hibernate)框架来说,SSM作为轻量级框架更适合开发中小型项目。
    2.2 关键技术简介前端技术
    JSP(Java Server Pages):嵌入了java代码的html页面,本质是一个servlet,它实现了在htmld的语法中扩展java代码,使用<% %>格式。
    JavaScript:是一种弱类型的脚本语言,由ECMAScript描述JavaScript的基本对象和语法,文档对象模型(DOM),浏览器对象模型(BOM)三部分组成。
    Ajax(Asynchronous Javascript And XML):异步的JavaScript和XML,实现前后台之间的异步交互,在不需要重新加载整个页面的前提下对页面上的部分内容做局部刷新。

    后台技术java:一种面向对象的编程语言,主要特性包括有

    简单性:抛弃了C++中复杂的语法和指针多继承等特征,开发人员不需要关注底层优化,只需要关注业务逻辑就行
    面向对象性,对程序员而言,只需要注意对应的数据和处理数据的方法,不用严格按照过程的方式来编写代码,因为java语言是面对对象的,因此也拥有面向对象的三大特征,继承、封装和多态
    跨平台性,java语言的跨平台性也就是JVM(java虚拟机)的跨平台性。Java文件编译后不会直接生成机器直接运行的二进制文件,而是编译成class文件,JVM会将编译的class文件根据不同操作系统下的JVM来生成对应系统的二进制文件,只需要编译一次,就可以在不同的平台上运行

    SpringMVC:是Spring框架提供的一个模块,通过实现MVC模式来地将数据、业务与展示进行分离,简化了Web开发。和其他MVC框架一样都是请求驱动,控制转发,它的核心Servlet是DispatcherServlet,即前端控制器,用来分发请求到控制器,它和Spring框架完全整合。这也使得SpringMVC成为目前非常流行的MVC框架。
    Spring:一款轻量级java开发框架,Spring框架有着分层的体系结构,因此可以使用Spring中 独立的任意部分。而它的架构依旧是内在稳定的,Spring提供了J2EE应用各层解决方案,贯穿于三层架构的每一层,但Spring依旧可以很好的兼容其他框架,本项目主要用到了Spring中IOC(控制反转)和AOP(面向切面编程)模块。
    Mybatis:是一个简化Java持久化层的轻量级开源框架。并且支持注解的Mapper,Mybatis消除了绝大部分的JDBC代码,使得java对象持久化到数据库的过程变的更加容易,相对于其他的java持久化框架,Mybatis的优点在于,消除了大量的JDBC冗余代码、简单易学、可见的Sql语句、提供了于Spring的整合,引入更好的性能。
    2.3 开发工具Tomcat服务器:是一个Web应用服务器,它是轻量且开源的,是中小型Web项目和开发调试和学习的首选。
    Oracle数据库(Oracle Database):是由Oracle公司开发的一款关系型数据库,是商业运用第一的关系型数据库,系统使用方便,功能强大,可移植性强。适用于各种大中小环境,在大数据时代,作为分布式数据库,它实现了分布式处理的功能,具有完整的数据管理功能、完备的关系型数据库、分布式处理功能。
    Eclipse开发工具:一个基于java开源的可扩展开发平台,它不但包括java集成开发环境,还包括插件开发环境,如SVN、CVS等团队开发插件。
    2.4 本章小结本章主要介绍了开发项目用到的一些主要技术,项目所使用的架构和设计模式,项目中所使用到的主要框架技术,项目在浏览器端展示用到的前端技术和展示方式,后台代码使用的开发语言,使用的服务器技术,数据持久层所使用的数据库等,在本章最后又介绍了开发使用到的开发工具。
    第三章 系统需求分析3.1 可行性分析3.1.1技术可行性在已有技术方面,为了统一客户端,消除因版本升级和维护带来的复杂性,因此采用成熟的B/S架构在项目的实现上完全可行,在开发语言和框架方面,java和j2ee体系的强大,可以让开发人员精心的构建web项目,以及一系列的开源框架,都为项目的可行性提供了强大的依据,在服务器方面,使用开源服务器Tomcat,足以支持该小型项目的正常使用,而不断发展的前端技术和前端框架可以制作精美的前台页面,提高用户的体验和交互,这在项目的页面展示技术上完全可行,强大的关系型数据库为项目数据的持久化提供强有力的后援。综上所述,日趋成熟化的java和j2ee体系、完全开源的java框架和服务器、功能强大的关系型数据库、运用Web前端技术提供用户交互页面,所以该项目在技术方面完全可行。
    3.1.2 经济可行性在互联网发展到信息时代的今天,单一获取信息的方式已经不能满足人们,人们每天接触的信息越来越多,而获取信息的形式也越来越多,但大多数获取信息的方式,留给用户交互的方式并不多,大都数情况下,人们只能被动获取,而很难找到自己的喜好和需求来获取信息,而本微博项目可以让人们获取实时热点信息和他们所关注的信息,实时与微博信息交互。而使用大量成熟技术与开源框架下,也使得小项目的开发更加简单,经济,高效,因微博而兴起的微博效应也能带来一定的经济效益。
    3.1.3 操作可行性微博系统使用B/S架构,用户不需要下载客户端,只需要用户有浏览器,就可以在浏览器上登陆微博系统,微博系统的界面颜值高,用户交互性高,用户操作简单方便,只需要了解基本的计算机操作就能使用,用户体验性高。因此在系统操作上完全可行。
    3.2 需求分析3.2.1 系统总体需求该微博系统主要由前台用户模块和后台管理模块组成,当用户进入首页时可以选择登陆或不登陆,登陆时可以使用已有账号登陆或注册新账号。用户未登陆时,在首页显示最近更新的热门微博,而登陆后的用户可以在首页看到 自己关注用户最近更新的微博。
    未登陆用户只能搜索查看微博信息和访问用户主页,登陆用户可以登陆系统后修改自己的基本信息例如签名、性别等,在验证用户信息后还可以修改密码和密保信息。以及修改用户头像和密码,编辑自己的个人主页,对微博进行点赞、评论、收藏等功能,还可以关注/取消关注用户,拉黑用户、私聊用户等操作。
    后台管理员可以查看系统所有的数据,包括用户、微博、评论、海螺、回答的总量,最近一个的数据库,最近一周的数据量。具体所具有的功能包括管理不良微博信息与不良账号,对微博、微博评论、海螺、回答等信息的删除和恢复功能,对不良账号的封禁等操作。
    微博查找模块:用户可以输入关键字来查找相对应的微博或查找用户。
    微博发布模块:用户点击发布,在内容中添加自己想要发送的内容,可以选择表情,也可以插入图片,但对输入字符数有着限制,同时还可以插入一张图片。
    微博评论模块:用户可以查看微博的评论,发布微博评论等。
    神奇海螺模块:用户可以发布一个神奇海螺,海螺主要用来记载用户提出的各种问题,由其他用户来查看并回答问题,当回复者的答案被提问者采纳后,回答者可以增加自己的海螺积分,不同的海螺积分有着不同的海螺称号。
    积分模块:用户每天登陆,发微博,做任务等方式可以增加自己的积分,不同的积分拥有不同的称号,神奇海螺模块的积分有着不同的称号,称号显示在用户名称的后面。
    好友模块:用户可以查看系统中其他的用户,找到自己喜爱的用户然后关注他们,关注后可以在好友模块中查看自己关注的用户,以及好友最近发布的微博等信息,也可以私信好友,发送私信信息给好友。
    3.2.2 用例图需求1. 当用户进入系统时候,可以选择登陆或注册用户,如果忘记了密码还可以通过密保问题来重置密码。

    2. 当用户登陆后,可以管理用户的个人基本信息,修改用户基础信息,修改用户密保信息。修改用户头像等功能。

    3. 用户微博管理系统,当用户登陆进去系统时,可以在首页发布微博,通过关键字搜索微博内容中关键字的微博信息。产看微博,包括查看个人微博、好友微博、推荐微博。

    4. 评论管理,评论管理依赖于微博模块,用户可以查看微博的评论,对微博信息发布评论,以及删除自己的评论。

    5. 海螺管理,用户登陆后可以在海螺模块发布海螺问题,发布问题时可以选择悬赏的积分数目,同时减少自己的积分数,用户可以参加回答他人的海螺问题,当回答的答案被采纳时,就可以获得用户悬赏的积分数。

    6. 好友管理,当用户登陆时可以关注系统推荐好友,也可以自己搜索用户,查看用户的主页面,添加关注或取消关注用户,还可以给用户发送私人信息,或者拉黑用户。

    7. 消息通知,当用户的关注,微博评论,点赞,收藏时调用消息通知。

    8. 管理员:管理员登陆系统,可以管理微博用户,对不良用户进行封禁和注销账户的操作,也可以恢复用户的状态,同时也可以对微博信息进行管理,如删除微博,恢复被管理员删除的微博信息等,对微博评论的删除和恢复等操作。

    第四章 系统功能设计4.1 系统类分析4.1.1 实体类实体类主要用来传递数据,主要包括User、Friend、PointAction、PointRecord、Weibo、WeiboCollect、WeiboComm、WeiboLike、WeiboTrans等,用户类中包括用户的基本信息,微博类中包括微博的基本信息同时包含实体用户类,好友类包括好友编号,好友创建时间与关注双方的用户类等,私信类包含私信双方的用户类与私信的基本属性,微博收藏类包含收藏的微博类与用户id等收藏属性,微博评论类包含微博类和用户类以及评论内容等属性,微博转发类包含用户类和微博类以及转发时间等属性,微博点赞类包含微博类和用户类以及点赞时间等属性。实体类之间的类关联关系如图所示。

    4.1.2 控制器类在controller层包含MainController、WeiboController、FrendController、AdminController四个JAVA类,在SpringMVC框架中主要用来接收浏览发送给服务器的请求和数据处理并控制请求的转发,将从Service层中获取的数据响应给浏览器端。MainController主要用发来接收来自用户相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    WeiboController主要用发来接收来自微博相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图4所示。

    FriendController主要用发来接收来自好友相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    AdminController主要用发来接收来自管理员相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    4.1.3 业务逻辑类在Service层中主要包含四个Service接口和他们的实现类,包括IUserService用来处理用户业务例如用户注册、登陆、修改个人信息等,如图4.6所示。IWeiboService用来处理微博相关的业务例如查找微博,发布微博,删除微博以及对微博的相关操作例如点赞、评论等功能的业务实现,如图4.7所示。IFriendService用来处理和用户好友相关的业务例如点赞、取消点赞、私信、拉黑等功能的业务实现,如图4.8所示。IAdmoinService用来处理和管理员相关的业务例如管理员登陆、图表展示、用户管理、微博管理、海螺管理功能的业务实现,如图4.8所示。

    图4.6用户业务处理类图

    图4.7微博业务处理类图

    图4.8微博控制器类图
    4.1.4 数据库交互类由于系统采用了MyBatis持久化框架,开发人员不需要关注和数据库之间具体的JDBC代码,而只需要处理业务逻辑,因此只需要在Dao层接口中声明方法而不需要写接口的实现类来实现方法,则是通过配置对应的配置文件,在配置文件中编写对应接口方法中的SQL语句和数据库交互。
    4.2 关键业务设计4.2.1 登陆系统
    图4.9系统登录顺序图
    用户进入主页后,可以在左边选择注册用户,或者在右边登陆系统,在注册模块,用户输入用户邮箱,密码,昵称等信息,当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台,查找用户邮箱是否已经被注册,如果被注册则在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,保存用户注册信息。
    登陆功能:当用户输入邮箱地址和密码后,如果点击下次自动登陆密码在点击登陆后,判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的 积分,然后跳转至微博首页。
    4.2.1 用户信息系统
    图4.10用户修改信息顺序图
    用户基本信息:在系统的首页中点击个人账号设置后,跳转到修改用户基本信息页面,用户可以修改这些基本信息,并将修改后的信息保存在数据库中。
    修改密码:在用户个人资料页面点击修改密码,跳转修改密码页面,用户可以输入用户的当前密码,系统判断密码是否正确,如果密码不正确,显示当面密码错误,如果输入密码正确,用户则可以输入新密码,点击修改后将修改后的密码更新到数据库中。
    修改用户头像:点击用户个人资料中修改头像,跳转至修改头像页面,用户选择头像文件,点击上传,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,点击修改后将修改后的头像地址保存在数据库中。
    修改密保:用户先要根据之前设置的密保问题来填写答案,如果密保答案错误,提示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改保存用户新的密保问题和答案。
    4.2.2微博模块
    图4.11系统登录顺序图
    发布微博:用户可以在首页发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,最后将微博信息保存在数据库中。
    搜索微博:用户可以在首页的搜索框中输入微博中提到的内容来搜索,系统在数据库中通过迷糊查询查询相关的微博信息。在页面中将搜索到的页面展示出来,并且将关键字标红显示。
    微博操作:用户可以查看个人微博、好友微博、收藏微博等信息,对微博的操作有点赞,转发,收藏,评论,删除等。
    4.2.3 好友模块关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,或者是通过页面中的搜索功能来搜索用户,对搜索到的用户进行关注。关注用户后个人关注+1,同时被关注用户粉丝数+1。
    取消关注:和关注功能类型,在导航栏中点击我的好友,在我的好友首页中查看我的关注好友,就能查看到我所关注的所有好友和好友数以及他们的个人信息,通过点击对应的取消关注就可以取消关注该用户,取消关注用户后个人关注数-1,同时被关注用户粉丝数-1。
    拉黑用户:即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。
    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值展示在前台页面中显示。
    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会将私信信息发送给对应用户。同时增加提示该用户的未读私信数。
    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,并将查询到的用户展示在页面中,同时将用户输入的关键字标红显示。
    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。
    4.2.4 海螺模块发布:用户通过点击导航栏中的神奇海螺进入海螺主页,在海螺页面的上面是发布海螺的问题框,下面的可以插入的表情按钮和问题的悬赏积分数,中间的导航栏可以选择查看最新发布、已解决、待解决、我的海螺不同的筛选条件。
    查看海螺问题:用户点击任意海螺问题,系统跳转到展示海螺的具体信息的页面,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等。
    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息等,点击回复后将回复信息保存在数据库中。
    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,点击采纳后修改海螺状态,被采纳的答案变为采纳答案,增加采纳者的海螺积分。
    4.2.5 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面。当管理员输入账号信息和密码信息后,如果用户名和密码正确就跳转到管理员首页。如果错误则给出提示。
    管理员首页:在管理员首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。
    用户管理首页:在页面上方显示导航栏,页面内容中显示所有用户的编号、邮箱、昵称、注册日期、上次登陆日期以及用户的状态,由于考虑到用户数量多以以及为了方便查找,因此在页面中一次显示10条用户数据,同时添加用户搜索功能,用户能够在输入用户昵称的关键字后来搜索用户,并且支持迷糊查询。 搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵 称为输入关键字的那部分显示为红色。
    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作。
    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博。
    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来。
    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作。
    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作。
    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以能够在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。
    4.3 数据库设计4.3.1 概述微博系统数据库表主要包括:

    1. 用户表:用来保存用户的个人信息,例如编号、昵称、姓名、密码。邮箱等信息,以用户编号作为主键,如表4-1
    2. 微博关注表:用来保存用户的关注用户信息,以关注ID作为主键,如表4-2所示
    3. 微博表:用来保存微博信息,例如微博发布时间,微博内容,微博点赞,转发,收藏数等,以微博编号作为主键,如表4-3所示
    4. 微博收藏表:用来保存用户收藏的微博信息,如微博编号,收藏时间等,以收藏编号作为主键,如表4-4所示
    5. 评论表:保存微博的评论消息,例如评论人编号、评论日期、评论内容等,以评论编号作为主键,如表4-5所示
    6. 积分表:用来保存用户获取积分的方式,例如通过每天的登陆发布微博等获取积分,以积分编号作为主键, 如表4-6所示
    7. 点赞表:用来保存微博的点赞信息,主要包括点赞人编号,点赞编号,点赞微博编号等信息。以点赞编号作为主键,如表4-7所示
    8. 积分流水表:用来保存用户获取积分的信息,主要包括积分编号、用户编号、获取日期等信息,以积分编号作为主键,如表4-8所示
    9. 转发表:用来保存用户转发的信息,主要有转发编号、转发人、微博编号等信息,以转发编号作为主键,如表4-9所示

    4.3.2 概念设计一个用户可以发布多条微博,因此用户表和微博表之间存在一对多的关系如图4.12所示。

    一条微博可以对应多个点赞、转发、收藏和评论,因此微博表和收藏、点赞、转发、收藏表之间存在一对多的对应关系,如图4.13所示。

    一个用户可以发布多个海螺,每个海螺问题可以对应多条评论。如图4.14所示。

    4.3.3 数据库表
    用户表:数据库表名USER_TAB,引用序列名SEQ_USER。


    关注表:数据库表FRIEND_TAB,引用序列名SEQ_FRIEND。


    微博表:数据库表名WEIBO_TAB,引用索引名SEQ_WEIBO。


    收藏表:数据库表名COLLECT_TAB,引用索引名SEQ_COLLECT。


    评论表:数据库表名COMM_TAB,引用索引名SEQ_COMM。


    积分表:数据库表名INTEGRAL_TAB,引用索引名SEQ_INTEGRAL。


    点赞表:数据库表名LIKE_TAB,引用索引名SEQ_LIKE。


    积分流水表:数据库表名RECORD_TAB,引用索引名SEQ_RECORD。


    转发表:数据库表名TRANS_TAB,引用索引名SEQ_TRANS。

    第五章 系统功能实现5.1 登陆系统在系统登陆注册页面当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台控制器,调用Service层中对应的方法,是Service方法中调用Dao层接口查找用户邮箱是否已经被注册,如果被注册则通过前台javaScript显示在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,将会把用户输入的注册信息通过浏览器发送请求到后台控制器中,控制器控制请求的转发页面和将用户注册信息传给Service,在Service中初始化用户的一些基本信息,例如默认头像、默认状态、初始化用户积分等操作,组装用户数据源,调用Dao层方法保存用户注册信息如图5.1所示。核心代码如下:

    登陆功能:如果点击下次自动登陆密码在点击登陆后,先将用户名和密码信息传递到Service层做业务处理,再调用Dao层接口判断邮箱地址和密码信息是否正确,如果正确并且点击了下次登陆,就将邮箱名和密码保存在浏览器Cookie中,将登陆用户保存在Session中,在处理用户登陆的Service中判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的积分,修改用户数据库中的积分数,然后跳转至微博首页,如图5.2所示。核心代码如下:



    5.2 用户信息系统用户基本信息:如图5.3所示当用户点击修改后浏览器会提交form表单,发送请求携带用户基本信息到SpringMVC的控制器中接收请求,并把form表单中的数据组装成User对象,作为参数传入Service层中对应的方法进行处理,调用Dao层接口修改用户的基本信息,并修改当前服务器中Session中的User对象信息。核心代码如下:


    修改密码:在用户个人资料页面点击修改密码,将会跳转至如图5-4所示的修改密码页面,用户可以输入用户的当前密码,前台页面通过Ajax发送异步请求,后台控制器接收请求,从数据库中获取用户当前密码是否正确,如果密码不正确,在前台页面中通过javaScript动态提示给用户当面密码错误,如果输入密码正确,用户则可以输入新密码,确认新密码,点击确认修改后浏览器提交form表单,将用户新密码传给后台Service层中对应的方法,在Service的方法中调用Dao层接口更改数据库中的用户密码,更改服务器Session中的用户信息。核心代码如下:


    修改用户头像:点击用户个人资料中修改头像,将会跳转至如图5.5所示的修改头像页面,用户选择头像文件,点击上传,form表单将图片传到后台控制器中,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,并将用户新的头像地址保存在用户信息中,传入Service中调用Dao层接口修改数据库中用户的头像信息。核心代码如下:


    修改密保:当用户点击修改密保页面时浏览器跳转至修改密保页面如图5-6所示,用户先要根据之前设置的密保问题来填写答案,前台页面通过Ajax将用户输入的密保答案传入后台控制器,与数据库中用户的密保问题答案做判断,如果密保答案错误,在页面上通过javaScript展示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改提交form表单后,浏览器发送请求在后台数据库中修改用户的密保问题和答案。核心代码如下:


    5.3 微博模块发布微博:在用户主页如图5.7所示,用户可以发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,在前台页面中,表情使用javaScript动态生成div标签并显示在页面上,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,从Session中获取当前登陆用户,组装微博数据源,将数据源传入Service中,调用Dao层接口将微博信息保存在数据库中。核心代码如下:


    搜索微博:如图5.8所示,在微博首页搜索框输入关键字点击搜索提交form表单,浏览器发送请求将关键字传到后台控制器中,在数据库中通过迷糊查询查询相关的微博信息。在Service层中具体方法中调用Dao层接口获得相关的微博信息,遍历集合,组装微博的基本信息和发布人对象,将微博中表情转换成对应的gif图片,调整微博时间格式为对应格式,修改微博内容中搜索关键字为红色显示,将修改好的数据返回到前台页面展示,如图5.9所示。核心代码如下:


    微博操作:点击微博的点赞,转发和收藏功能类型,通过Ajax将微博id提交请求到后台控制器,从Session中获取登陆用户的信息,传递参数到Service对应的方法中通过对微博不同的操作调用对应的Dao层接口将微博的点赞,转发和收藏信息保存在数据库中。点击评论后通过前台页面的点击事件跳转至JavaScript中通过Ajax发送异步请求到后台控制器中,将微博id传递到Service层中对应的方法中嗲用Dao层接口查找数据库中对应微博编号的评论信息,微博评论的分页通过对应的PageBean类控制,在数据库层通过SQL语句来控制分页要显示的条数,在控制器中传递json数据到前台页面展示。在评论信息的最后面可以发布用户自己的评论,可以添加表情,点击发表将form表单提交到后台控制器中,在后台代码中调用Dao层接口保存用户的评论信息。如图5.10所示。核心代码如下:


    个人微博:如图5.11所示:在页面上方导航栏可以点击进入用户个人主页查看我的微博,在页面中页面上方展示导航栏,下面显示用户的基本信息,中间显示用户所发布过的微博,点击微博插入的图片还可以通过JavaScript将图片放大,在页面的右侧展示系统为用户推荐的好友。点击我的微博按钮后发送的请求会被后台控制器接收,从服务器Session中获取当前登陆用户的用户id,传入Service层,在Service层中调用Dao层接口从数据库中获取当前登陆用户的微博信息组装成List集合,遍历List集合,更改微博数据源的日期格式,调用工具类将微博正文中的表情替换成对应的图片信息,在微博的分页中,使用了Oralce数据库中的伪列来获取对应区间的微博信息,实现数据库层的分页,将所有需要在页面上展示的数据传递给控制器,控制器再将数据封装在Response响应中传递到前台页面。核心代码如下:


    5.4 好友模块查看好友微博:通过点击页面导航栏中我的好友来跳转至好友微博页面,在后台控制器中先从Session中获得当前登陆用户的id值,传递给Service,在Service中调用Dao层方法先在数据库的好友表中查找对应用户的所有好友信息,然后再在数据库微博表中查看这些好友对应的微博信息按照微博发布时间倒序排列,将所有查找到的微博信息和发布用户信息封装成List集合,遍历List集合修改微博日期各式,微博表情字符转换为对应图片名称,将加工过后的集合传递给控制器,控制器将所有的组装好的数据响应到前台页面中,在前台页面中通过C标签遍历List展示微博数据如图5.12所示。核心代码如下:

    关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,点击关注后前台页面通过Ajax技术发送异步请求将被关注用户的用户id发送到后台控制器中,在控制器中获取Session中的登陆用户信息,将登陆用户的id和被关注用户的id传给Service层中对应的方法,在Service层对应的方法中做处理,组装好友信息数据源,调用Dao层接口保存用户的关注信息,同时更新用户的关注数,更新被关注用户的粉丝数,然后在前台页面中通过javaScript将页面文本信息关注修改为已关注。核心代码如下:

    取消关注:和关注功能类型,在导航栏中点击我的好友,在中间点击关注,就能查看到我所关注的所有好友和好友数以及他们的个人信息,如图5.13所示,点击取消关注,通过Ajax将好友id传递到后台控制器中,在控制器中获取请求中的用户信息,将好友id和用户id传入Service层中对应的方法,调用Dao层接口将对应用户id和好友id的好友信息删除掉,同时更改用户的关注数和被关注用户的粉丝数。然后返回响应在前台页面刷新好友列表。

    拉黑用户:和取消关注用户类似,只是在Dao层接口中,不是删除已有的用户记录而是逻辑删除,即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。核心代码如下:


    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,如图5.14所示,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值传递给前台页面,前台页面中所有展示的数值通过EL表达式从服务器发送回来的响应中获取。点击未读私信或者通过图5.13中显示的好友列表中的私信按钮,跳转至用户私信页面。核心代码如下:


    查看私信:如图5.15所示,用户通过点击私信关注好友时,通过后台控制器跳转至我的私信用户页面,页面左侧显示和该用户最近的私信信息,页面右侧显示所有有过私信的用户列表,点击列表中的用户就可以直接私信这个用户,在私信中允许插入表情。在后台代码中,将要发送私信的用户id和从Session中获取的登陆用户id传递到Service层中对应的方法,调用Dao层接口在数据库中查找与该用户相关的所有私信记录同时修改和该用户的所有私信状态为以阅读,将获取的记录组装到集合中,遍历集合调用工具类修改私信的日期格式和文本中的表情格式,同时从数据库中获取和当前登陆用户有过私信记录的所有用户,将组装好的集合返回给控制器,控制器将数据响应给前台页面中,遍历集合中的数据,展示在前台JSP页面中。核心代码如下:


    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会提交form表单,浏览器发送请求到后台控制器中,控制器获取发送用户的id和接收用户的id,调用Service层中的方法,组装数据源为私信信息,设置私信信息为未阅读状态,在Service层中对应的方法中调用Dao层接口将私信信息保存在数据库中。核心代码如下:

    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,当用户输入要查询的用户昵称,浏览器发送请求携带关键字等信息跳转至控制器中特定的方法,在控制器方法内部调用Service中的方法处理逻辑,业务层调用Dao层接口中的查找方法查找用户昵称中包含有关键字的用户,将从数据库获得的对应用户组装成一个集合,遍历集合将用户昵称中包含的关键字改成红色,控制器返回响应跳转到搜索结果页面,遍历集合展示所有查找到的用户信息。如图5.16所示。核心代码如下:


    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。在后天代码中,当用户点击其他用户的头像或名称时,浏览器发送携带用户id的请求到后台控制器中。控制器调用Service中对应的方法,在Service方法中调用Dao层接口从数据库中查看用户的基本信息和用户的微博信息,将所有的数据存放在集合中。返回到控制器中,控制器携带数据返回到前台页面中做展示,如图5.17所示。核心代码如下:


    5.5 海螺模块发布:当用户进入海螺主页时,浏览器页面发送请求到后台控制器中,调用业务层中特定的方法,Service中调用Dao层接口在数据库中查找有关不同筛选条件的海螺问题,并将查找到的问题封装在集合中,通过控制器发送服务器响应,跳转到海螺首页,并循环展示所有的海螺问题。如果用户需要发布问题,在填写了问题描述和所要悬赏的积分数后,点击发布,浏览器提交表单数据到后台控制器中,在Service层中首先判断用户的海螺积分是否大于悬赏积分,如果小于悬赏积分就返回浏览器页面提示用户积分不足。如果积分足够就调用Dao层接口把用户的海螺问题保存在数据库中,同时减少用户的海螺积分修改用户基本信息。核心代码如下:


    查看海螺问题:用户点击海螺首页具体的问题时,浏览器发送请求给后台控制器中调用Service层对应的方法,在Service层中调用Dao层接口通过海螺问题的编号来查看海螺的具体信息,通过发布问题的用户id在数据库中查找对应的发布人信息,服务器返回响应到浏览器中,展示海螺的具体信息,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等,如图5.19所示。核心代码如下:


    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息,点击回复提交表单,浏览器请求携带表单数据到后台控制器中,被控制器中具体的方法接收,获取Session中的用户编号,组装回复信息的数据源,传递参数到Service中,在Service中调用Dao层接口保存海螺的回复信息,如图5.20所示。核心代码如下:


    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,被采纳的答案变为采纳答案,增加采纳者的海螺积分,如图5.21所示,在后台代码中,当用户点击采纳后,浏览器请求携带海螺问题id和问题回复信息被后台控制器中具体的方法接收,在控制器中调用Service层,业务层中调用Dao层接口修改数据库中海螺问题的状态为已解决,修改评论表中被采纳用户的评论状态为被采纳,刷新前台海螺问题页面。核心代码如下:


    5.6 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面如图5.22所示,管理员登陆后,页面发送请求到后台控制器中,后台Controller层接收请求,将用户名和密码作为参数调用业务层中的方法,在Service层中调用Dao层接口和数据库中管理员账号表中查询,如果存在就返回管理员类,如果不存在,则抛出异常,异常层层上抛,在控制器层中接收,并将错误信息保存在方法的返回值中,在页面提示,如果用户名和密码正确,就跳转到Controller中管理员首页的处理方法中,在管理员首页的处理方法中获取需要显示的数据并展示。核心代码如下:


    管理员首页:登陆后,系统会跳转到管理员首页,在首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。后台首先在Controller层中跳转至管理员首页对应的处理方法中,调用Service层中对应的方法获取首页展示数据,并将获取到的数据组装到Map集合中,在服务层中调用Dao层中的方法来获取首页需要展示的用户、微博、评论、回答等数据保存在Map集合中,在控制层中获取到返回值数据并保存在服务器响应中,返回给前台页面使用EL表达式展示数据,如图5.23所示。核心代码如下:


    用户管理首页:后台代码通过调用Service层中对应的方法,Service方法里面则调用Dao层接口和数据库交互,获取数据库中所有的用户信息封装成List集合,返回给Controller层将用户List集合响应给浏览器,在页面中通过c标签遍历循环显示用户信息,页面的分页使用自定义分页类PageBean来保存分页信息,在数据库层做分页一次获取10条数据。核心代码如下:


    搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵称为输入关键字的那部分显示为红色。在后台代码中,点击搜索会将管理员输入的用户昵称关键字传给后台控制器中,在控制器中调用Service层中对应的搜索用户的方法,在Service层中调用Dao层接口在数据库中通过迷糊查询来获取用户。并将获取到的数据层层返回,在Controller中响应给前台页面,然后在页面中做展示,如图5.25所示。核心代码如下:


    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作,具体页面如图5.26所示。在后台代码中则是在Service层中调用Dao层接口,更改用户状态和封禁日期,解除封禁和封禁用户类似,因此不做具体说明。核心代码如下:


    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博,微博管理页面如图5.27所示,在后台代码中,当用户点击导航栏中微博管理时,浏览器会发送相应的请求到SpringMVC框架的Controller中对用的更能处理方法中,在控制器中调用Service层中显示所有微博的方法中,在Service层中再调用Dao层方法获取所有的微博信息,在分页方面没有采用在前端页面中做分页的方法,而是在数据库中通过Oracle的伪列来做分页,一次获取10条数据,最后在Controller中将获取到的微博信息响应给浏览器,浏览器中通过c标签遍历显示微博信息。核心代码如下:


    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来,具体页面如图5.28所示,在后台代码中,控制器中特定的方法接收浏览发送的搜索微博请求,调用Service层中对应的方法,将关键字作为参数传给Dao层接口中,在Dao层接口中查找数据库微博表中微博内容包含该关键字的微博信息,在数据库中则是通过模糊查询来查找对应微博。然后将查找到微博信息封装到List集合中,层层返回到Controller层中对应的方法,在方法中将数据响应给浏览器,浏览器接收响应在页面中通过c标签展示数据。核心代码如下:


    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作,当用户点击删除时,页面会携带着微博编号等参数发送请求给服务器,请求会被控制器中对应的方法所接收,将微博编号作为参数嗲用Service中对应的方法,在方法中调用Dao层接口在修改数据库中对应微博编号的微博状态为已删除,恢复微博和删除类似,只是修改数据库中对应微博信息状态为正常即可。核心代码如下:

    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,如图5.29所示,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作,在后台代码方面,当用户点击导航栏中的海螺管理时,浏览器发送请求,请求在控制器中被对应的方法接收,Service中调用Dao层接口在数据库海螺表中查找所有的海螺信息,保存在List集合中,遍历List集合组装发布人用户信息,修改日期格式等,最后将组装号的List集合返回到Controller中,响应给浏览器跳转海螺管理首页,通过使用c标签遍历显示查找到的海螺信息,因为系统中使用的分页方法一致,因此在这里不再赘述。核心代码如下:


    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以通过在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。具体页面如图5.30所示。在后台代码中,当用户输入关键字点击搜索时,浏览器发送请求到控制器中指定方法接收,在Controller中调用Service层中对应的方法处理业务逻辑,然后在Service层中调用Dao层接口通过模糊查询在数据库海螺表中查找对应的海螺信息,最后在Controller中将查找到的海螺信息响应给浏览器,在页面中通过c标签遍历展示数据。因为系统使用一样的分页方法,因此不做赘述。核心代码如下:


    第六章 总 结时光荏苒,岁月如梭,转眼之间为期半年的毕业设计以及论文的编写终于落下帷幕,回顾毕业设计的每一个阶段都使我受益匪浅,在毕设初期,经过了长达一周的深思熟虑之后我决定将B/S微博系统作为我这次毕业设计和论文的选题,因为平时对微博的接触以及近几年微博的火热程度,使我对微博系统的具体功能和优缺点都有了一个全体的把控,这也使得在之后系统功能设计时能够更加的得心应手,在毕业设计中期时进入代码编写阶段时,我选择了J2EE体系开发Web项目的B/S微博,使用面向对象语言JAVA作为开发语言,使用个人熟练掌握的SSM框架来搭建系统,SSM框架强大的功能减少了大量的冗余代码,使得系统代码的编写更加轻松,提高了系统的开发效率,然而开发一个系统并不是那么简单就能完成,在代码的编写阶段,问题接连出现,但在指导老师的指导下以及自己通过网上查阅资料,最终解决了这些问题,提高了自己代码的编写能力,同时也提高了自我学习能力,这对以后的生活学习和工作中都有着非同寻常的意义,在项目编写完成后的测试阶段,之前在编写阶段没有被发现的系统缺陷逐渐跃出水面,在每一次修复这些问题的时候,我都能感觉到自己的能力在慢慢提高,一个没有缺陷的系统是不存在的,经过对测试用例中的测试方案一一测试,然后修复掉一个又一个的缺陷后,微博系统中大多数可见性高的BUG都被修复了,但是在之后的使用中还会不断的完善系统让它变的更加成熟,而不单单只是作为一份毕设设计而存在。
    在这次毕业设计系统的开发中也让我看到了自身的一些问题,例如前端技术的不熟练导致在前台页面的修改和开发中浪费了大量的时间,微博系统中关键性的用户交互不够美观,用户体验性差,以及在系统类设计时没能正确的把控全局,设计出合理的接口,只能在后期代码编写阶段中不断的去完善。
    总而言之,在对微博系统的开发中,我学习到了很多以前没有注意到和忽略到的东西,也使我认识到了自身的一些缺陷,让我在以后的生活和工作中都能更好的认识自己,提高自己的能力,然后服务于社会,做一个对社会发展有帮助的人。
    参考文献[1] 贾文潇,邓俊杰. 基于Java的Web开发技术浅析[J]. 电子测试,2016
    [2] 李传扬. 微博分析系统的设计与实现[D]. 北京邮电大学 2015
    [3] 刘运臣. 网站设计与建设[M]. 清华大学出版社, 2008
    [4] 秦雅华. 基于WEB2.0的微博网站的设计与实现[D]. 北京工业大学 2012
    [5] 陈玲,夏汛. 利用Mybatis的动态SQL实现物理分页[J]. 数字技术与应用. 2011(11)
    [6] 萨师煊,王珊. 数据库系统概论(第三版)[M].北京:高等教育出版社,1998
    [7] 基于Java的数据库访问技术研究[J]. 科技资讯. 2009(04)
    [8] 张峰. 基于Ajax技术与J2EE框架的Web应用研究与实现[D]. 中国地质大学 2008
    [9] 基于Java多线程技术的网络编程[J]. 电脑编程技巧与维护. 2009(22)
    [10] 李威. 一种小型实用即时网络聊天通讯系统的设计[J]. 长江大学学报(自然科学版). 2011(12)
    [11] 钟睿祺. 基于微博嵌入小伙伴阅读网的分析与设计[D]. 华南理工大学 2011
    [12] 王少锋编著.面向对象技术UML教程[M]. 清华大学出版社, 2004
    [13] 徐春绵. 关于网站开发相关问题的探究[J]. 通讯世界. 2015(09)
    [14] 张宇,王映辉,张翔南. 基于Spring的MVC框架设计与实现[J]. 计算机工程. 2010(04)
    [15] 胡以谰,张立平. J2EE开发模式的选择[J]. 计算机系统应用. 2002(08)
    [16] 王丽爱. 《Java程序设计》课程网站的设计与实现[J]. 电脑知识与技术. 2016(27)
    [17] 荣艳冬. 关于Mybatis持久层框架的应用研究[J]. 信息安全与技术. 2015(12)
    6 留言 2019-12-20 13:12:40 奖励50点积分
  • 基于B-S架构的学生信息管理系统设计与实现 精华

    第1章 前言1.1 课题研究的背景及意义1.1.1 课题研究的背景目前,随着计算机和移动互联网技术的快速发展,社会正迈向大数据时代,相应地,信息管理系统也越发显得重要。信息化、自动化、智能化在日常生活中的作用越来越大,让我们从繁杂的手工劳动中解脱出来,实现高效的服务工作。在信息技术广泛应用的今天,传统手工管理已经不能适应大数据的规范和管理。采用计算机管理各类数据,不仅能提高工作效率,还能保证信息的安全性。
    计算机的技术迅速发展带动其它技术的发展,自然也推动着信息技术高度发展快速前进。信息、信息技术和信息化的重要性日益被人们所认识,高科技电子通讯也日益被大众所接受,所以现代化通讯方式是今后发展的趋势,所以建立一套符合实际的、简便快捷的、易于掌握的学生信息管理系统成为必然趋势。
    1.1.2 课题研究的意义
    通过研究,了解并掌握基于Web的平台开发流程及设计方法,系统学习Web的架构与应用开发模式,深入理解Web系统体系架构的内部机制和管控方式
    以MySQL为后台数据库的基础上,熟悉Eclipse的开发环境、编辑环境及运行环境,熟练掌握Java语言的运用
    通过对该系统开发流程及开发方式的理解,总结用户需求,归纳开发思想和设计技术,为相似系统研发提供技术支持
    通过翻阅文献,研究主要模块功能的实现,掌握功能实现中所需要用到的算法,实现系统的自动编排等功能帮助管理人员减轻负担,更好的满足用户的需求。

    完成本设计、可以培养学生系统意识。加深学生对MySQL数据库,Java语言,html5+css3前端页面设计有关技术的理解。培养学生的针对实际应用开发网页的能力。使用Eclipse完成代码的编写,实现基本功能。使用MySQL数据库实现网站前后端数据交互,实现用户所操作的增、删、改等功能。该系统的设计与使用,将大大减少工作人员的工作量和工作强度,减少人工操作不可避免的错误,提高工作效率并能够保证数据的安全性,完整性,防止数据的丢失。也得学生信息管理系统平台模式变得规范化、程序化,这也是平台管理的发展方向。
    第2章 系统分析2.1 系统需求分析需求指的是用户需求。用户需求不仅是系统的体系结构规划的主要依据,系统建设与应用的主要驱动因素,也是解决系统社会认知度低、不受用户欢迎等问题的关键所在。需求分析是从用户的角度对他们需要解决的业务问题进行顶层设计,主要包括功能需求分析。
    2.1.1 需求分析实现学校对学生信息的集中管理。可供学校管理人员对学生信息的增加、删除、修改、查询等基本操作,并对系统的可登录人员进行管理,如管理员和学生用户等。在登录管理方面,用管理员身份登录,可对本系统的可登录人员进行管理,有权增加及删除本系统的登录人员,进行自身密码的修改;本系统最终目标是实现学生信息管理的系统化、规范化和自动化。用户模块主要功能有登录模块、学生信息管理、系统管理界面等。

    登录模块:可以提供用户登入系统的入口,用户通过用户名和密码可以登入系统并进行相应模块的操作
    学生信息管理模块:管理员可以添加不同学生类型的学生信息,如小学生,中学生,大学生,通过类型不一样,所要添加的属性也不一样,添加完可以对信息进行修改,删除,查询操作
    管理员模块:管理员登陆可以修改自己的个人信息,如密码等

    2.2 可行性分析
    技术可行性:当今社会,运用计算机管理信息已经越来越普遍。该系统在技术方面较为先进,有利于对学生信息管理系统的操作。经过详细的分析和调查,本学生信息管理系统平台利用计算机信息处理的迅速、准确、可靠且有强大存储能力的突出特点,全面提高平台的管理水平和工作效率,为管理平台的及时转换提供一定的支持
    操作可行性:在进行系统设计之前,已经深入学习开发设计系统中所需的JSP、数据库等技术知识,熟练掌握Java程序设计语言,并已经安装了相关的Eclipse平台和Tomcat服务器等。上述工作为网站的设计与实现做好了技术准备
    经济可行性:传统模式开发的系统不易维护,开发费用高,也不易于升级。而该模式下系统将不存在上述问题,它易维护、易升级。系统的维护和升级所带来的费用低于需求者的预期,所以在经济上是可行的

    第3章 数据库设计3.1 系统数据库设计系统在运行过程中,各功能模块的使用通常都需要调用数据库,所以说数据库的合理设计对于系统设计来说至关重要,通常来说,整个系统的开发时间应当有40%用于数据库设计。合理的数据库结构能够简化系统工作任务流程,同时能提高数据存储的效率。数据库的设计也要有一定的可扩展性,即不仅要收集资料对数据现状进行分析,还要为未来可能扩展的功能预留数据空间。
    本系统采用Mysql数据库技术,在设计数据库前,对学生信息管理系统的数据需求进行了分析,而后才确定该数据库结构。同时,数据库设计可分为概念结构设计和物理结构设计,以下就两者展开具体讨论。
    3.1.1 概念结构设计系统设计中数据库是核心设计内容所在,概念设计为数据库设计提供了良好的设计工具。概念设计中通过E-R图,将抽象的概念进行具体关系的描述和表达。
    大学生信息E-R图

    中学生信息E-R图

    小学生信息E-R图

    管理员信息E-R图

    3.1.2 数据库表设计根据数据库物理结构设计的原则(即数据结构要具有相对的稳定性;结构设计与操作设计相结合;尽可能减少数据库冗余和重复)进行学生信息管理系统数据库表结构及代码设计。
    学生信息管理系统采用MYSQL数据库作为支撑环境,整个数据库系统由多张数据表构成。数据库各表结构涉及包括确定表的名称、表中的字段类型及长度等参数,下面将介绍本系统数据库中各数据表的详细结构。
    大学生信息表



    字段名
    类型
    字段长度
    描述




    id
    Int
    11
    编号


    stu_xuehao
    varchar
    50
    学号


    stu_realname
    varchar
    50
    真实姓名


    stu_sex
    varchar
    50
    性别


    stu_age
    varchar
    50
    年龄


    nj
    varchar
    50
    班级


    yy
    varchar
    50
    英语成绩


    sx
    varchar
    50
    数学


    yw
    varchar
    50
    语文


    dl
    varchar
    50
    地理


    ls
    varchar
    50
    历史


    address
    varchar
    50
    地址


    major
    varchar
    50
    专业


    phone
    varchar
    50
    联系电话


    type
    varchar
    50
    类型



    中学生信息表



    字段名
    类型
    字段长度
    描述




    id
    Int
    11
    编号


    stu_xuehao
    varchar
    50
    学号


    stu_realname
    varchar
    50
    真实姓名


    stu_sex
    varchar
    50
    性别


    stu_age
    varchar
    50
    年龄


    nj
    varchar
    50
    班级


    yy
    varchar
    50
    英语成绩


    sx
    varchar
    50
    数学


    yw
    varchar
    50
    语文


    dl
    varchar
    50
    地理


    ls
    varchar
    50
    历史


    address
    varchar
    50
    地址


    type
    varchar
    50
    类型



    小学生信息表



    字段名
    类型
    字段长度
    描述




    id
    Int
    11
    编号


    stu_xuehao
    varchar
    50
    学号


    stu_realname
    varchar
    50
    真实姓名


    stu_sex
    varchar
    50
    性别


    stu_age
    varchar
    50
    年龄


    nj
    varchar
    50
    班级


    yy
    varchar
    50
    英语成绩


    sx
    varchar
    50
    数学


    yw
    varchar
    50
    语文


    type
    varchar
    50
    类型



    管理员信息表



    字段名
    类型
    字段长度
    描述




    Id
    Int
    11
    编号


    loginname
    varchar
    50
    用户名


    password
    varchar
    50
    密码



    第4章 系统设计4.1 系统功能架构设计该系统软件采用B/S架构,本文设计的学生信息管理系统平台主要有界面设计、逻辑功能的实现部分以及数据库采用MySQL实现网站前后端数据交互,实现用户所操作的增、删、改等功能。
    学生信息管理系统软件主要登录界面模块、学生管理界面、管理员管理模块和系统管理界面组成。用户需要进行登录。他们必须在这个页面输入相应的信息。输入信息后由后台的数据库监测,如果匹配即可进入主页面,如果不正确则继续返回登录页面,显示用户信息错误。当完成登录后,用户进入了主页面模块进入过后,用户可以查看管理平台管理系统数据信息。用户可以查询各模块的数据。具体的系统结构如图4-1。

    4.2 JDBC数据库连接Java中主要是使用JDBC来访问数据库,JDBC API是Java语言访问数据库的一种规范,是Java数据的编程接口,是一组标准的Java接口跟类,当我们使用这些时,就可以访问不同的数据库。不同数据库的类型虽然不同,但是用它的连接步骤是一样的,只是在获取驱动的URL上有所不同而已。有了JDBC,向各种关系数据发送SQL语句就是一件很容易的事。换言之,有了JDBC API,就不必为访问Sybase数据库专门写一个程序,为访问Oracle数据库又专门写一个程序,或为访问Informix数据库又编写另一个程序等等,程序员只需用JDBC API写一个程序就够了,它可向相应数据库发送SQL调用。同时,将Java语言和JDBC结合起来使程序员不必为不同的平台编写不同的应用程序,只须写一遍程序就可以让它在任何平台上运行。
    第5章 系统详细实现5.1 系统平台的实现该系统软件采用B/S架构,系统软件的运行平台采用Windows 10,用Eclipse开发软件程序,MySQL进行数据库的设计,服务器选用Apache Tomcat7.0。整个系统软件的设计和开发使用了面向对象的方法和技术,体现了可视化的友好的人机界面。在个模块的软件的设计上,主要是对界面、逻辑功能、数据库三个方面进行了设计。其中界面设计采用JSP编程和CSS网页设计;逻辑功能的实现采用了ssh框架技术;数据库采用MySQL实现网站前后端数据交互,实现用户所操作的增、删、改等功能。系统功能模块的软件设计采用了MVC设计模式,界面层设计采用JSP技术对服务器系统主界面和数据图表等界面进行设计;逻辑层采用Servlet实现对服务器相关业务逻辑处理;数据层采用了MySQL数据库实现对服务器的系统数据存储调用。系统软件开发模式如图5-1所示。

    5.2 系统主要功能模块的实现5.2.1 登录界面系统软件主要有登录界面、个人信息界面、学生管理界面组成。该系统软件经过验证已经能够成功运行。用户需要进行登录。他们必须在这个页面输入相应的信息。输入信息后由后台的数据库监测,如果匹配即可进入主页面,如果不正确则继续返回登录页面,显示用户信息错误。当完成登录后,用户进入了主页面模块进入过后,用户可以查看学生信息管理系统。登录界面模块,界面模块如图5-2。
    首先运行学生信息管理系统Web项目,成功显示学生信息管理系统的登录页面,在登录页面的右上角有个登录信息的确认,用户登录时的passname和password输入框。输入帐号与密码信息进行验证,当提交登录信息时,触发login.js脚本文件,通过UserAction类中的方法login得到输入的帐号和密码,然后通过getUser()函数进行校验,通过Hibernate进行数据库连接,当数据库中的注册的帐号和密码与用户输入的信息相匹配时,后端UserAction类中输出的“SUCCESS”会传到前端的login.js脚本文件,js会控制界面跳转到首界面main.jsp。如果登录框中没有输入信息,点击登录则提示:用户名或密码不能为空。如果输入的帐号密码在数据库中检测后不存在,则不能实现跳转,会提示用户名与密码错误。

    5.2.2 登陆后信息界面学生个人信息界面分为三个部分分别是顶部top.jsp,中部的right.jsp,以及左边的left.jsp.第一个部分是一张图片,学生信息管理系统。第二部分是main页面,下面说的设置界面和数据查看页面都是从这里实现跳转。图中的第三部分是right一行字。在main.jsp第一个frame里面打开链接top.jsp。这里面放的一个图片,用户进入即可看到健身房管理系统这个图片。在第二个frame里面我们放的是项目所实现的功能,第三个frame里面的连接是right.jsp,打开以后里面放的也是一行字母和数字。我们点击左侧菜单栏不同的部分会显示不同的界面。我们如果已经点开了某一个页面,则不能继续点开,我们用到了add table函数,点击其他页面逻辑也相同,如果存在则不能重复打开,否则跳转至相应的页面。点击左侧的按钮可以跳转至相应页面。firstnode对应左侧的第一个数据查询。点击数据查询按钮会触发addTab1()函数跳转至leftjsp界面。学生信息主界面如图5-3。

    后台信息管理主要由四个功能模块组成,分别为:管理员信息、学生信息管理、退出系统。
    管理员信息
    管理员信息功能模块学生可以点击我的个人信息进行个人信息的编辑和修改等操作,其中涉及到的个人信息有账号、姓名、密码、状态、注册时间。
    另外可以通过该模块进行登录账号和密码的修改。
    学生管理
    分为三个模块的大学生管理,中学生管理,小学生管理。
    而每个学生信息管理包括两个子功能模块,分别为学生信息管理和学生信息添加。学生信息管理中包括学生信息列表,可以通过学生序号和学生姓名进行查询信息,并且对学生的信息里可以点击修改学生信息或者删除学生信息,具体实现界面如图5-5所示。




    添加学生信息管理界面,有三种界面。
    小学生信息
    在填写必要的信息中,还需要填写英语成绩,数学成绩和语文成绩等,具体实现界面如图5-10所示。

    中学生管理
    在添加小学生信息的基础上,还需填写地理成绩,历史成绩和家庭住址信息。添加界面5-11所示。

    大学生类型
    在添加中学生信息的基础上,还需填写专业信息,联系方式信息。具体界面实现如图5-13所示。

    添加信息的同时,系统会提示未填写信息,如果在填写有重复学号的信息,系统也会给出相应的提醒。具体界面实现如图5-14所示。

    5.2.3 学生管理代码实现学生管理的主要代码实现如下:在主页面的左侧是垂直导航菜单,按数据查询会进入大学生xuesheng/xuesheng_d.jsp页面,中学生xuesheng/xuesheng_z.jsp页面,小学生xuesheng./xuesheng_x.jsp我们可以实现用户信息查询,数据会保存在数据库中,当用户查询时,从后端数据库中调出显示在页面上。这个页面上,input为输入框,点击数据查询按钮会触发XueshengAction方法,然后通过数据库进行查询 ,最后将查询到的信息显示。界面模块如图5-6所示。其主要代码如下:
    public String selectStu1() throws Exception { //设置请求的页数 if(this.pageIndex != null) { this.pageModel.setPageIndex(pageIndex); } //查询总条数 Integer recordCount = sshService.getStuCount1(); //把总数设置到poageModel当中 this.pageModel.setRecordCount(recordCount); //查询数据 stus1 = sshService.selectStuPage1(this.pageModel); return SUCCESS; } public String selectStu2() throws Exception { //设置请求的页数 if(this.pageIndex != null) { this.pageModel.setPageIndex(pageIndex); } //查询总条数 Integer recordCount = sshService.getStuCount2(); //把总数设置到poageModel当中 this.pageModel.setRecordCount(recordCount); //查询数据 stus2 = sshService.selectStuPage2(this.pageModel); return SUCCESS; } public String selectStu3() throws Exception { //设置请求的页数 if(this.pageIndex != null) { this.pageModel.setPageIndex(pageIndex); } //查询总条数 Integer recordCount = sshService.getStuCount3(); //把总数设置到poageModel当中 this.pageModel.setRecordCount(recordCount); //查询数据 stus3 = sshService.selectStuPage3(this.pageModel); return SUCCESS; } //通过姓名或者学号查询 public String selectStuByName1() throws Exception { //设置请求的页数 if(this.pageIndex != null) { this.pageModel.setPageIndex(pageIndex); } //查询总条数 Integer recordCount = sshService.getCountByName1(stud); if(recordCount==0) { Map request=(Map)ServletActionContext.getContext().get("request"); request.put("mess", "无查询记录!"); }else { //把总数设置到poageModel当中 this.pageModel.setRecordCount(recordCount); //查询数据 stus1 = sshService.selectStuByName1(pageModel, stud); } return SUCCESS; } //通过姓名或者学号查询 public String selectStuByName2() throws Exception { //设置请求的页数 if(this.pageIndex != null) { this.pageModel.setPageIndex(pageIndex); } //查询总条数 Integer recordCount = sshService.getCountByName2(stuz); if(recordCount==0) { Map request=(Map)ServletActionContext.getContext().get("request"); request.put("mess", "无查询记录!"); }else { //把总数设置到poageModel当中 this.pageModel.setRecordCount(recordCount); //查询数据 stus2 = sshService.selectStuByName2(pageModel, stuz); } return SUCCESS; } //通过姓名或者学号查询 public String selectStuByName3() throws Exception { //设置请求的页数 if(this.pageIndex != null) { this.pageModel.setPageIndex(pageIndex); } //查询总条数 Integer recordCount = sshService.getCountByName3(stux); if(recordCount==0) { Map request=(Map)ServletActionContext.getContext().get("request"); request.put("mess", "无查询记录!"); }else { //把总数设置到poageModel当中 this.pageModel.setRecordCount(recordCount); //查询数据 stus3 = sshService.selectStuByName3(pageModel, stux); } return SUCCESS; } /** * 1 跳转带添加页面 * 2 提交到数据库 */ public String addStu1() throws Exception { if(flag.equals("1")) { return "showAddStu"; }else { //添加学生的时候判断下,是否有学号重复 int num= sshService.findStuxh1(stud.getStuXuehao()); if(num==0) { sshService.saveStu1(stud); return SUCCESS; }else { Map request=(Map)ServletActionContext.getContext().get("request"); request.put("mess", "学号重复,请重新添加!"); return "showAddStu"; } } } /** * 1 跳转带添加页面 * 2 提交到数据库 */ public String addStu2() throws Exception { if(flag.equals("1")) { return "showAddStu"; }else { //添加学生的时候判断下,是否有学号重复 int num= sshService.findStuxh2(stuz.getStuXuehao()); if(num==0) { sshService.saveStu2(stuz); return SUCCESS; }else { Map request=(Map)ServletActionContext.getContext().get("request"); request.put("mess", "学号重复,请重新添加!"); return "showAddStu"; } } } /** * 1 跳转带添加页面 * 2 提交到数据库 */ public String addStu3() throws Exception { if(flag.equals("1")) { return "showAddStu"; }else { //添加学生的时候判断下,是否有学号重复 int num= sshService.findStuxh3(stux.getStuXuehao()); if(num==0) { sshService.saveStu3(stux); return SUCCESS; }else { Map request=(Map)ServletActionContext.getContext().get("request"); request.put("mess", "学号重复,请重新添加!"); return "showAddStu"; } } } /** * 1 跳转修改页面 * 2 提交到数据库 */ public String updateStu1() throws Exception { if(flag.equals("1")) { stud = sshService.findStuId1(stuId); //大学生 return "showUpdateStu3"; }else { sshService.updateStu1(stud); return SUCCESS; } } /** * 1 跳转修改页面 * 2 提交到数据库 */ public String updateStu2() throws Exception { if(flag.equals("1")) { stuz = sshService.findStuId2(stuId); //中学生 return "showUpdateStu2"; }else { sshService.updateStu2(stuz); return SUCCESS; } } /** * 1 跳转修改页面 * 2 提交到数据库 */ public String updateStu3() throws Exception { if(flag.equals("1")) { stux = sshService.findStuId3(stuId); //小学生 return "showUpdateStu1"; }else { sshService.updateStu3(stux); return SUCCESS; } } //删除学生信息 public String removeStu1() throws Exception { System.out.println("removeStu() -->"); /** * ids传过来是字符串>>"1,2,3,4" * 通过split分割再存进数组》》》》id[0]=1 id[1]=2 id[2]=3 */ String[] temp = ids.split(","); System.out.println("temp>>>>"+temp); for(String id:temp) { sshService.removeStu1(Integer.parseInt(id)); } return SUCCESS; } //删除学生信息 public String removeStu2() throws Exception { System.out.println("removeStu() -->"); /** * ids传过来是字符串>>"1,2,3,4" * 通过split分割再存进数组》》》》id[0]=1 id[1]=2 id[2]=3 */ String[] temp = ids.split(","); System.out.println("temp>>>>"+temp); for(String id:temp) { sshService.removeStu2(Integer.parseInt(id)); } return SUCCESS; } //删除学生信息 public String removeStu3() throws Exception { System.out.println("removeStu() -->"); /** * ids传过来是字符串>>"1,2,3,4" * 通过split分割再存进数组》》》》id[0]=1 id[1]=2 id[2]=3 */ String[] temp = ids.split(","); System.out.println("temp>>>>"+temp); for(String id:temp) { sshService.removeStu3(Integer.parseInt(id)); } return SUCCESS; }第6章 测试本系统中相关的测试都是通过JS来操作完成的,通过页面提交来判断空值,然后页面上给出相应的提示框。
    管理员登陆测试:不输入用户名或者密码,页面提示,如下面

    添加学生信息不填:学号,姓名等信息不填,也给出相应的提醒

    第7章 总结与展望通过此次努力地学习,系统各个模块的功能都按照任务需求基本完成了。在网页上进行展示。主要完成登录界面、学生个人信息界面、学生管理界面的设计。给出有关模块的详细分析和逻辑代码实现。通过这个软件,用户可以方便快捷的查看自己想要了解的信息。在软件模块设计的过程中我重新回顾了以往所学习了大学课堂上学习的基础知识,而且查阅了很多网页开发文献,这些都提升了我对前沿科技有所认识,知道当下研究的方向,这些都对我开发这个系统软件有极大的帮助。开始时,软件设计需要系统能够接收数据,从现在测试的结果来看,所有结果令人满意。
    在今年开学的时候我们开始着手做这个项目,学习了一些基础的CSS,js,jQuery,MySQL,SSH框架到最后的做出简单的jsp文件,再到最后完整的做出项目中间付出了很多遇到了很多问题但是都一一解决了。虽然很困难但是当解决了之后就觉得很快乐,尤其是后期一起整合的时候遇到的问题是最多的最艰辛的时候,遇到各种各样的逻辑问题,,虽然很累很辛苦但是印证了一句俗语风雨之后总会看见彩虹,一直到最后的整合结束静下心来写论文的时候才感觉到自己学到了很多以前没有学到的知识和很多解决问题的经验。才感受到自己确实收获了很多学习了很多的知识,自己在这段时间过的很充实。
    经过几个月的努力,最终完成系统的设计与实现,这期间虽然遇到了很多难题,虽然最终完成了这个系统的所有功能模块,但是和一个上线的系统相比还是有很多的不足,需要改进的地方还是有很多。比如在页面的设计上做的还是不够,页面的样式设计的还有点瑕疵。当然,在本次的毕业设计的制作过程中,我不仅学到了许多的新知识,也巩固了以前学到的一些知识,更是将理论的知识用于实践的过程中,为以后的工作打下了扎实的基础。
    参考文献
    引用[1]Gui Xiu Ouyang. Design and Implementation of Student Information Management System Based on Java Technology[J]. Applied Mechanics and Materials,2014,3634(687).[2]Hui Yan Zhao,Qi Sun. Research on the Design and Implementation of Student Information Management System[J]. Applied Mechanics and Materials,2014,3634(687).[3] Jeremy Keith.JavaScript DOM 编程艺术[M]. 杨涛,译.北京:人民邮电出版社,2011.[4] 张容铭.JavaScript 设计模式[M].北京:人民邮电出版社,2015.[5] 大漠.图解 CSS3:核心技术与案例实践[M]. 北京:机械工业出版社,2014.[6]郑金明,佟施.基于HTML5的校园网高校管理系统的设计与实现[J].广西教育学院学报,2013(02):157-161.[7]魏蓉,常青青.学生信息管理系统的设计探讨[J].价值工程,2011,30(19):166.[8]范亮,潘永才,王雄兵.B/S架构下的学生信息管理系统的设计[J].物联网技术,2016,6 (12):55-56.[9]李超.学生信息管理系统的设计与实现[J].电脑知识与技术,2016,12(09):100-109.[10]乔普拉. JSP高级程序设计[M]. 北京:人民邮电出版社, 2006.[11]杨珏. JSP网络开发技术[M]. 北京:人民邮电出版社, 2001.[12]乔晶.高校学生成绩管理系统设计与实现[J].电脑编程技巧与维护,2015(23):59-72.[13]隋郁.高校学生信息管理的系统设计与实现[J].教育发展研究,2017(S1):13-15.[14]孙红丽.基于JSP的学生信息管理系统设计与实现[J].智能计算机与应用,2017,7(02): 108-112.
    0 留言 2020-01-20 15:48:54 奖励45点积分
  • PC微信逆向分析の绕过加密访问SQLite数据库

    作者:zmrbak(赵庆明老师)
    前言微信,无疑是国内目前最为流行的应用软件,其在社交领域的霸主地位依然无人可以撼动!它不但撑起了庞大的腾讯软件帝国的一角,而且足以让腾讯傲立于BAT中国互联网三巨头之首。微信,改变了这个世界,而腾讯也成了无数优秀程序员梦想的归宿。围绕着微信这个软件的延伸,腾讯也无私地为代码世界贡献了近百个优秀的开源项目。
    从古到今,任何一个巨人,都是站在前一辈巨人的肩膀上而成长为新的巨人。百度如此,阿里如此,腾讯亦是如此。SQLite数据库,一个不为人熟知,但已超过10000亿个设备在使用的开源数据库,已经渗入人们生活的各个环节。目前超过11亿个活跃的微信号,每时每刻都在与这个深藏功与名的SQLite数据库共舞着。
    由于微信的巨大成功,从而成为众多痴迷技术的探秘者的实验品。同样,嵌入微信内部那个深藏功名的SQLite数据库,也成为这些人的又一个解剖对象。接下来,准备好我们的工具,让我们一起来窥探其中的奥妙!
    微信中的数据库无论是在微信安装还是运行的时候,普通用户是不会觉察到微信中居然还有一个默默无闻的SQLite数据库。就算在微信的安装目录中,你也不会发现有任何对于SQLite数据库的描述、说明和感谢(基于SQLite的许可协议,不允许以SQLite的名义来推销产品),但是却在用户的文件夹下(C:\Users\xxxxxx\Documents\WeChat Files\xxxx\Msg\)留下一堆以db为扩展名的文件。对于细心的探索者来说,微信留下来的任何蛛丝马迹,都会变成重要的探秘线索。
    这些db文件,都是经过加密处理的。即使你怀疑它是一个SQLite数据库文件,但是你根本无法用任何一个SQLite管理器打开。虽然网上有不少的文章告诉你,去到微信中寻找那个密码,然后使用某某工具进行解密还原。先不说这些工具各种各样的莫名其妙的问题,就算你确实可以顺利搞到密码,再到完成了解密,再进行访问,其实已经晚了半拍。当然,你要相信,腾讯的那些顶级程序员,会在保证软件功能和性能的情况下,会为这些探秘者设置各种各样的障碍。至少我们知道,那些db文件的确是一个个SQLite数据库,只是经过了加密处理而已。
    窥探SQLite数据库SQLite数据库是一个开源的软件,也就是说,任何人都可以免费地获取它的源代码,对它进行研究、分析,当然还有修改。如果你要把你修改的内容“贡献”给SQLite官方,那么你必须申明你放弃你修改的这些代码的“版权”,不过你提供的代码基本不会原样照搬,而是被SQLite官方按照自己的标准重写一次(开源,但不开放)。当然,如果你要修改后自己用的话,那就随便你啦,SQLite官方并不会找你要钱。即便如此,谷歌依然每年给予SQLite巨额的赞助,当然国内腾讯、阿里等这些巨头也毫不吝啬。虽然这个软件包括源代码在内都是免费开放的,但由于有不少的巨头为其撑腰,就算SQLite不收你的钱,它也会活得很好。如果你非要交钱买个许可的话,那就花6000美元买一个吧,官方称这些钱会用于资助SQLite的持续改进和支持。
    既然SQLite数据库是开放源代码的,而且还有来自微信开发团队的严苛加密,那么将其与微信一起研究,则更为令人振奋。登上SQLite的官方主页,http://www.sqlite.org ,你可以随时把SQLite诞生以来近20年的各个版本抓来研究一番。网站上还有各种示例,教你如何使用SQLite数据库,详尽的各种解说,如果你愿意,你可以通过这个网站了解到它的各种之末细节和详尽的实现方式。虽然,网站没提供中文版,不过,语言并不是障碍,谷歌浏览器的翻译功能,可以让你了解个八九不离十。
    一个简单的SQLite示例如果你还不知道这个SQLite数据库怎么用,网站上提供了TCL的示例代码和C语言的示例代码。现摘抄C示例代码如下(代码来源:https://www.sqlite.com/quickstart.html)。
    #include <stdio.h>#include <sqlite3.h>static int callback(void *NotUsed, int argc, char **argv, char **azColName){ int i; for(i=0; i<argc; i++){ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0;}int main(int argc, char **argv){ sqlite3 *db; char *zErrMsg = 0; int rc; if( argc!=3 ){ fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]); return(1); } rc = sqlite3_open(argv[1], &db); if( rc ){ fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return(1); } rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } sqlite3_close(db); return 0;}
    示例很简单,也很完善。由于我们的目的是为了探索和研究,因此,我们在确保程序示例可用的情况下,再继续精简。我们删除了很多代码,只剩下核心的几条:
    #include <stdio.h>#include <sqlite3.h>static int callback(void *NotUsed, int argc, char **argv, char **azColName){ for(int i=0; i<argc; i++){ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } return 0;}int main(int argc, char **argv){ sqlite3 *db; sqlite3_open(argv[1], &db); sqlite3_exec(db, argv[2], callback, 0, &zErrMsg); sqlite3_close(db); return 0;}
    先从main函数开始,首先定义一个sqlite3的db作为数据库的句柄,然后让执行sqlite3_open打开数据库,接下来执行sqlite3_exec查询数据库,最后执行sqlite3_close关闭数据库。在sqlite3_exec执行期间,使用回调函数callback来处理查询的结果。这,就是一个完整的流程。
    执行sqlite3_open打开数据库这个操作,要执行不少的操作,相对比价耗费系统资源。如果一个软件在运行过程中要不断地查询数据库,修改数据库,那么最优的方式就是打开后不要关闭,等到软件关闭的时候再关闭数据库。这样可以节约系统资源,也可以提高数据库的响应速度。
    绕过加密访问SQLite数据库无论是给数据库加上密码,还是使用其它更强有力的方式为数据库加密,无非是为了防止数据库被非法操作。但是,由于基于性能的考虑,应用软件打开SQLite数据库后,只有等到应用程序被关闭的时候,才去关闭数据库。那么在整个应用软件运行期间,SQLite数据库一旦被打开,则将一直保持打开状态。也就是说,上面示例的db,在程序还未调用sqlite3_close函数前,一直是有效的,随时可以接受sqlite3_exec函数的查询操作。这个查询并不是指select,而是指所有可能的sql语句。我们暂且不讨论具有破坏性的操作,如增加(INSERT)、修改(UPDATE)、删除(DELETE)等,在这里我们只来讨论“无破坏性”的“查”(SELECT)的操作。
    数据库的加密验证,往往只在于打开数据库的时候,一旦数据库被打开,剩下的操作,则不再对加密进行验证。因此,我们等数据库打开之后,获取db这个数值,然后就可以“非法”地调用sqlite3_exec函数,来执行我们期望的操作。如果这样操作,那么无论软件设计人员设计的任何加密方式,都形同虚设。也就是说,我们可以轻松地实现“绕过加密来访问SQLite数据库”。
    为了实现“绕过加密来访问SQLite数据库”,接下来需要解决三个问题。

    如何取得已打开的数据库的句柄db的具体数值?
    可被非法地调用的sqlite3_exec函数在哪里?
    如何非法地调用sqlite3_exec函数?

    接下来,让我们依次来解决这三个问题。
    如何取得打开数据库的句柄?如果从正向编程的思维来思考的话,似乎没有答案。但是如果用逆向的思维,你会发现,其实很简单,在调用sqlite3_open函数的时候,其中一个参数就是db,这个db的数值,就是我们正在寻找的打开数据库的句柄。因此,这个问题又转换成,在什么地方调用了sqlite3_open函数。那么,只要我们在调用完sqlite3_open函数之后,马上获取db的值,这个问题就解决了。
    如果你使用过OD或者IDA,你就知道,这些软件可以帮你分析出,在程序的什么地方调用了这个函数,而且每一个调用的地方都可以帮你分析出来。如果再配合inline hook,取数据就如同探囊取物一般容易。
    我们知道,在调用sqlite3_open函数的时候,必然会打开一个文件。在Windows系统中,必然会调用Windows中的CreateFileW函数,而这个函数可以被OD、IDA等逆向工具所识别。因此,通过倒推的方式,就能顺藤摸瓜找到sqlite3_open函数。如果使用OD来动态调试的话,sqlite3_open就非常容易找到,而且还可以找到调用这个函数的代码段,同时还可以直观地观察到某内存地址中的具体数值。因此,db的值就可以很容易地获取。如果你再编写了inline hook后,只要程序调用了sqlite3_open函数,就可以马上获取db的值。
    sqlite3_exec函数在哪里?在软件中往往会执行一些sql语句,而sqlite3_exec函数对多条函数进行了包装,让程序员执行sql语句更为简单。因此,在应用软件中会有大量的sqlite3_exec函数调用。这恰好是一个切入点。
    sqlite3_exec函数的第一个参数是数据库句柄,其实也就是一个DWORD,第二个参数是一条被执行的sql语句,其实就是一个字符串的地址。这些字符串,往往在程序员编程的时候手工写入的,并且嵌入到软件之中,而这些字符串在OD、IDA等这些软件中,可以很容易地识别出来。从而成为找到这个函数的切入点。
    比如在软件中寻找“select * from”之类的sql语句,就可以简单地定位到sqlite3_exec函数,从而确定它在应用软件中的具体地址。
    如何调用sqlite3_exec既然我们已经获得到数据库的句柄db的值,而且已经找到和sqlite3_exec的地址,接下来我们就可以来“非法”地调用这个sqlite3_exec函数了。首先,我们来了解一下这个函数的所需的参数:
    sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
    第二个参数,是一个查询的sql语句。我们可以定义一个sql的字符串即可,比如“select * from sqlite_master”。由于这条语句是用来查询这个数据库的结构的,因此,在任何一个sqlite数据库中都可以执行成功。当然,你也可以写一个你自己喜欢的sql语句。为了确保一定能查询到信息,我们就用这个字符串。
    第三个参数,是一个回调函数,参照SQLite官方的示例代码编写。
    第四个参数,是为这个回调函数提供的参数,如果没有,或者不需要提供,那么可以直接填写0或NULL。
    第五个参数,返回执行的错误信息,如果不需要的话,可以填写为0或者NULL。
    我们只是找到了sqlite3_exec在软件中的地址而已,但地址并不是一个函数,我们也无法直接用字符sqlite3_exec来调用这个函数。但是,我们可以通过C语言内嵌汇编来调用这个函数,也可以用“函数”指针的方式来调用。使用汇编语言,不是很直观,因此使用函数指针,是一个比较好的方法。
    我们从SQLite源代码中找到sqlite3_exec函数,把它复制过来,然后做一些修改。把sqlite3_exec第一个字符变成大写Sqlite3_exec,加上typedef、__cdecl*,删除形参,再将db所在的形参改为DWORD:
    typedef int(__cdecl* Sqlite3_exec)( DWORD, /* The database on which the SQL executes */ const char *, /* The SQL to be executed */ sqlite3_callback, /* Invoke this callback routine */ void *, /* First argument to xCallback() */ char ** /* Write error messages here */);
    假如sqlite3_exec的地址是:0x12345678,那么我们可以这样定义和调用:
    Sqlite3_ exec p_Sqlite3_ exec = (Sqlite3_ exec) 0x12345678;p_Sqlite3_ exec(db, zSql, callback, 0, 0);`
    运行结果示例



    总结很多初次接触到逆向的人,会被黑压压的汇编代码、一串串的C代码、还有风格诡异的e语言所吓倒。常常有人问我“逆向分析很难吗?”。我也套用一句经典来回答:“天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣!”
    逆向分析,如同玩猜谜游戏,当你看到一个个谜底慢慢浮现的时候,只有身在其中的人,才能享受其中的刺激和乐趣。作为看客,永远无缘体验这份乐趣。你并不是孤身一人,你还有我们。加入我们,一起在玩这个其乐无穷的猜谜游戏。
    源码分享https://github.com/zmrbak/SQLiteReverse
    交流QQ群:456197310 点击链接加入群聊【软件逆向分析入门】
    0 留言 2020-01-03 10:02:51 奖励35点积分
  • PC微信逆向分析の强制输出微信调式信息

    作者:zmrbak(赵庆明老师)
    前言2019年4月份 JieKeH的一篇文章《PC微信逆向—-分析获取登录二维码的数据》一文,让不少的朋友对微信这个软件产生了浓厚的兴趣,当然也包括我。令我印象最深刻的莫过于“打开微信的Xlog日志输出”这部分内容。
    虽然腾讯公司在微信的发布版本中对调试信息进行了屏蔽,但JieKeH通过IDA反汇编的伪代码与Xlog源码对比的方式找到了输出调试信息的关键点,并提供了突破屏蔽的方法。在学习了这篇文章后,本人也通过OD中的反汇编代码与Xlog源码对比的方式,同样找到了相应的关键点,之后将其录制成一个视频分享给了身边一起研究的朋友们。
    新问题新对策很不幸,该文章出现后不久,新版本的微信进行了相应的调整,JieKeH文章中所讲述的方法完全失效。身边的朋友不时告诉我,这个方法失效了,同时请教我还有什么办法可以输出这些调试信息。其实,玩过HOOK的朋友,会马上想到另一个办法,就是写一个dll注入,在dll中编写相应的逻辑实现需要的功能。我用这种方法录制了一个小视频分享之后,再也没有人问过这个问题。看来这个办法还是不错的!
    更简单的方式编写DLL,所涉及到的知识较多,涉及到编码、编译、注入等一系列操作,不但操作复杂而且修改也不容易。近日,最新正式版的微信2.8.0.106推出,我在此新版本上进程了相关的尝试,其实只是在CE中进行简单的一些设置,即可满足需求,就算是新手也不难掌握。由于剪辑视频太耗时间,而且相比于上一个视频也没多少新意,因此仅整理成文档分享给大家。部分内容,之前的视频中有讲解,这里就不再赘述,直接进入本文主题。
    定位“是否启用调试”的地址在OD中附加微信后,查询二进制代码“74 14 FF B5 EC FE FF”(为何是这个二进制代码,请参考之前的视频讲解),定位到如下代码片段,并在接下来的CALL(WeChatWi.79164604)上下一个断点:

    ds:[0x79A6CA01],这个地址,就是是否启动调试的开关。计算出偏移地址:0x160CA01。在微信中,这个地址中的数据被设置成0,因此调试信息将不会输出。只要将这个地址中的数据更改为1,那么输出调试信息的开关就被打开了。但是,调试信息还有其他开关,那就是调试级别。
    定位“调试级别”的地址查询二进制代码“56 89 9D F0 FB FF FF”(为何是这个二进制代码,请参考之前的视频讲解),定位到如下代码片段:

    进入 WeChatWi.79154DB0这个CALL后,到达如下代码片段:

    ds:[0x79A1CD54],这个地址,就是调试级别的设置。计算出偏移地址:0x15BCD54。在微信中,这个地址中的数据被设置成2,也就是高于“kLevelDebug”的信息才会被输出。只要将这个地址中的数据更改为0,也就是“kLevelAll”或“kLevelVerbose”,也就是输出全部调试信息。
    定位调试数据的代码段在JieKeH的文章中,完成这两步设置后,调试信息即可输出,在DebugView中课捕获微信自身的调试信息。正如前文所述,随着微信版本的升级,此方法很快失效。于是,基于此设置,我再用HOOK的方式,重新让微信具备了调试信息的输出的功能。到目前最新的2.8.0.106版本,HOOK方法依然有效。当然,无论怎么做,定位调试数据这个步骤还是不能少的。
    用鼠标动一动微信,OD中程序将暂停在调用“WeChatWi.79164604”处(之前下的断点处),按F7进入该函数。注意观察堆栈窗口,持续按F8,单步执行,直到堆栈顶部出现如下类似的信息。这些信息,就是原本要输出的调试信息。接下来,在HOOK中,同样把这个数据提取出来,从调试信息中输出,然后在DebugView中捕获。

    这时候,观察程序运行的代码部分,计算出该代码片段中“add esp,0x4c“所在汇编代码的偏移地址:0x‭CE7853‬。其代码片段如下:‬

    在上一个HOOK的视频中,我在这里进行了HOOK。在本文中,我使用CE来进行一小段“代码注入”即可代替编写DLL这种难度高、而且手续繁杂的工作。
    定位堆栈中数据的地址虽然数据在堆栈顶部,但是由于下来要对堆栈进行操作,如果使用ESP来取数据,还需一些手动的计算,稍显麻烦。由于EBP在堆栈操作过程中会保持不变,因此我们使用EBP加上一定的偏移量来取数据,这样就不需要额外的手动计算。
    在OD堆栈窗口中,点右键选择“转到EBP“,在该行的地址上双击,变成”$==>“

    然后,在OD堆栈窗口中,点右键选择“转到ESP“,记录该行相对于EBP的偏移:$-40E0。也就是说,当前的数据位于[ebp-0x40E0]这个地方,也就是ESP所指向的地方。因此,我们取到了数据存储的地址:ebp-0x40E0。

    使用CE编写代码注入脚本启动CE,附加微信。点击“手动添加地址”,分别添加“WeChatWin.dll+0x160CA01”与“WeChatWin.dll+0x15BCD54”,再分别命名为“输出调试信息”和“设置调试级别”,再将其值分别更改为1与0,如下图所示:

    点击“查看内存”,定位到“WeChatWin.dll+CE7853”,并选中此行。

    接下来,在“内存浏览器”窗口中依次点击“工具\自动汇编”,在弹出的新窗口中,依次点击“模板\CT表框架代码”,再依次点击“模板\代码注入”,确保弹出对话框中内容为“”WeChatWin.dll”+CE7853”(代码选中的位置),点击“OK”。

    接下来,依次点击“文件\分配到当前的CT表”,然后点击关闭。CE的主窗口中将多出一行内容。将其改名为“OutPutDebug”。记得随时按Ctrl+s(保存)。

    双击OutPutDebug中的数值栏中的“脚本”,在打开的新窗口中对该脚本进行编辑。在newmem与originalcode之间添加如下代码(备注:0x7747efd0,就是OutputDebugStringA函数地址。相关理论,不赘述):
    pushadpush [ebp-0x40E0]call 0x7747efd0popad
    点击“确定”。记得再按Ctrl+s保存。
    开始测试
    输出调试信息:1
    设置调试级别:0
    OutPutDebug:启用(那个方框中间画一个叉)


    以管理管身份启动“DebugView”软件,用鼠标动一动微信,即可观察到微信输出的调试信息。

    源码及工具https://github.com/zmrbak/PcWeChatHooK
    0 留言 2020-01-09 08:41:46 奖励30点积分
  • 获取指定进程的加载基址

    背景之前,自己写过一个进程内存分析的小程序,其中,就有一个功能是获取进程在内存中的加载基址。由于现在Windows系统引入了ASLR (Address Space Layout Randomization)机制,加载程序时候不再使用固定的基址加载。VS默认是开启基址随机化的,我们也可以设置它使用固定加载基址。至于什么是ASLR?或者ASLR有什么作用?本文就不深入探讨了,感兴趣的,可以自己私下了解了解。
    本文就是开发这样的一个小程序,使用两种方法来获取获取指定进程的加载基址。一种是使用进程模块快照方式,然后遍历加载模块并获取基址,另一种是直接调用WIN32 API函数EnumProcessModules,遍历加载模块基址。这两种方法本质上都是一样的。现在,我就把分析过程和实现方式写成文档,分享给大家。
    函数介绍CreateToolhelp32Snapshot 函数
    可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。
    函数声明
    HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
    参数

    dwFlags指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个:



    VALUE
    MEANING




    TH32CS_INHERIT
    声明快照句柄是可继承的


    TH32CS_SNAPALL
    在快照中包含系统中所有的进程和线程


    TH32CS_SNAPHEAPLIST
    在快照中包含在th32ProcessID中指定的进程的所有的堆


    TH32CS_SNAPMODULE
    在快照中包含在th32ProcessID中指定的进程的所有的模块


    TH32CS_SNAPPROCESS
    在快照中包含系统中所有的进程


    TH32CS_SNAPTHREAD
    在快照中包含系统中所有的线程




    th32ProcessID指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。
    返回值

    调用成功,返回快照的句柄;调用失败,返回INVALID_HANDLE_VALUE 。

    Module32First 和 Module32Next 函数当我们利用函数CreateToolhelp32Snapshot()获得指定进程的快照后,我们可以利用Module32First函数来获得进程第一个模块的句柄,Module32Next函数来获得进程下一个模块的句柄。
    OpenProcess 函数
    打开一个已存在的进程对象,并返回进程的句柄。
    函数声明
    HANDLE OpenProcess( DWORD dwDesiredAccess, //渴望得到的访问权限(标志) BOOL bInheritHandle, // 是否继承句柄 DWORD dwProcessId// 进程标示符);
    参数

    dwDesiredAccess [in]访问进程对象。 此访问权限将针对进程的安全描述符进行检查。 此参数可以是一个或多个进程访问权限。如果调用者启用了SeDebugPrivilege权限,则无论安全描述符的内容如何,都会授予所请求的访问权限。bInheritHandle [in]如果此值为TRUE,则此进程创建的进程将继承该句柄。 否则,进程不会继承此句柄。dwProcessId [in]要打开的本地进程的标识符。
    返回值

    如果函数成功,则返回值是指定进程的打开句柄。如果函数失败,返回值为NULL。要获取扩展错误信息,请调用GetLastError。

    EnumProcessModules 函数
    在指定的进程中检索每个模块的句柄。要控制64位应用程序是否枚举32位模块,64位模块或两种类型的模块,请使用EnumProcessModulesEx函数。
    函数声明
    BOOL WINAPI EnumProcessModules( _In_ HANDLE hProcess, _Out_ HMODULE *lphModule, _In_ DWORD cb, _Out_ LPDWORD lpcbNeeded);
    参数

    hProcess [in]过程的句柄。lphModule [out]接收模块句柄列表的数组。cb [in]lphModule数组的大小,以字节为单位。lpcbNeeded [out]将所有模块句柄存储在lphModule数组中所需的字节数。
    返回值

    如果函数成功,返回值不为零。如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

    实现思路该程序实现进程基址的主要原理是,遍历进程里的所有加载的模块,那么,第一个加载模块的加载基址就是该进程的加载基址。
    那么,对于进程模块加载基址的遍历就有两种两种方法。一种是根据进程模块快照获取,另一种市直接调用EnumProcessModules函数获取。现在,对这两种方法的原理分别进行介绍。
    使用进程模块快照的方式获取模块基址的原理是:

    首先,使用CreateToolhelp32Snapshot 函数获取指定进程的所有模块快照。然后,根据模块快照,使用Module32First 和 Module32Next 函数进行遍历快照,并获取快照信息。其中,就包括有模块的加载基址信息。第一个模块的加载基址便是该进程的加载基址。最后,关闭上面获取的快照的句柄。
    使用EnumProcessModules函数获取模块基址的原理:

    首先,我们需要使用OpenProcess函数打开指定进程并获取进程的句柄
    然后,根据进程句柄调用EnumProcessModules函数获取进程加载的所有模块的加载基址,并保存在数组中。那么,第一个模块的加载基址便是该进程的加载基址
    关闭打开的进程句柄

    编程实现获取指定进程模块快照的方式遍历进程模块PVOID GetProcessImageBase1(DWORD dwProcessId){ PVOID pProcessImageBase = NULL; MODULEENTRY32 me32 = { 0 }; me32.dwSize = sizeof(MODULEENTRY32); // 获取指定进程全部模块的快照 HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if (INVALID_HANDLE_VALUE == hModuleSnap) { ShowError("CreateToolhelp32Snapshot"); return pProcessImageBase; } // 获取快照中第一条信息 BOOL bRet = ::Module32First(hModuleSnap, &me32); if (bRet) { // 获取加载基址 pProcessImageBase = (PVOID)me32.modBaseAddr; } // 关闭句柄 ::CloseHandle(hModuleSnap); return pProcessImageBase;}
    直接使用EnumProcessModules函数获取进程模块基址PVOID GetProcessImageBase2(DWORD dwProcessId){ PVOID pProcessImageBase = NULL; //打开进程, 获取进程句柄 HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) { ShowError("OpenProcess"); return pProcessImageBase; } // 遍历进程模块, HMODULE hModule[100] = {0}; DWORD dwRet = 0; BOOL bRet = ::EnumProcessModules(hProcess, (HMODULE *)(hModule), sizeof(hModule), &dwRet); if (FALSE == bRet) { ::CloseHandle(hProcess); ShowError("EnumProcessModules"); return pProcessImageBase; } // 获取第一个模块加载基址 pProcessImageBase = hModule[0]; // 关闭句柄 ::CloseHandle(hProcess); return pProcessImageBase;}
    程序测试我们在 main 函数中调用上述封装的函数进行测试,main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ PVOID pProcessImageBase1 = NULL; PVOID pProcessImageBase2 = NULL; pProcessImageBase1 = GetProcessImageBase1(4500); pProcessImageBase2 = GetProcessImageBase2(4500); printf("pProcessImageBase1=0x%p\npProcessImageBase2=0x%p\n", pProcessImageBase1, pProcessImageBase2); system("pause"); return 0;}
    测试结果
    我们运行程序,程序执行成功,并显示两种方法获取的进程加载基址,而且获取结果都相同。

    然后,我们使用 Process Explorer 软件查看PID为 4500 的进程的加载基址,程序基址获取正确,程序测试成功。

    总结这两种方式,本质上都是一样的,只是遍历进程加载模块所使用的方法不相同。那么,进程加载的第一个模块的加载基址,便是进程的加载基址。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2018-11-06 22:45:42 奖励5点积分
显示 0 到 15 ,共 15 条
eject