分类

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

技术文章列表

  • 上传资源,获取积分 精华

    上传资源,获取积分“WRITE-BUG技术共享平台”是一个专注于校园计算机技术交流共享的平台,面向的主要目标群体是我们计算机相关专业的大学生。在平台上,大家既可以交流学校课内学习的心得体会,也可以分享自己课外积累的技术经验。
    为了充实平台的资源库,更好地服务于各位同学,平台决定推出“众源计划”,有偿征集同学们自己计算机专业的作业、课程设计或是毕业设计等资源。“众源计划”的主要目的是创建一个具有一定规模的“技术资源库”,资源库里的每一份资源,都必须有详细的开发文档和可编译的源代码。
    作业、课程设计或是毕业设计等资源是同学们自己辛苦付出的成果,也是自己技术进步的见证。这部分资源通常都有详细的开发文档和完整的程序源代码,能够帮助其他初学者更好地消化和吸收将要学习的技术,降低学习门槛。所以,平台决定积分奖励征集这些资源。
    具体要求奖励方式
    资源上传并审核通过后,根据资源质量,奖励每贴 10 - 100 点积分
    上传流程
    会员登录自己的账号上传资源
    资源上传后,管理员会在 24 小时之内审核资源
    审核通过后,管理员会立即发放奖励积分至所对应账户

    审核重点
    重点审核资源是否具有详细的文档和完整的源代码
    审查资源是否原创,切勿重复提交

    资源要求“众源计划”仅对两类资源进行积分奖励征集,分别是“课内资源”和“课外资源”,各类资源具体要求如下所示。

    课内资源

    内容范围:计算机相关专业课内的毕业设计、课程设计、小学期、大作业等课程内开发的程序,程序包括游戏、PC程序、APP、网站或者其他软件形式
    内容要求:资源必须要包括完整的程序源代码和详细的开发文档或报告

    课外资源

    内容范围:计算机相关专业的课外自己主导研究游戏、项目、竞赛、个人研究等,区别于课程设计和毕业设计等课内资源
    内容要求:资源必须要包括完整的程序源代码和详细的开发文档或报告


    使用须知** 在上传资源前,请花两分钟阅读 “使用须知” 部分内容 **
    35 留言 2019-01-24 09:26:15 奖励100点积分
  • 基于SSM的超市订单管理系统

    1 系统需求分析超市订单管理系统是一个专为连锁店、超市等商业场所提供订单管理平台的系统。该系统的目标是建立一个订单管理平台,为需要合理规划超市供应链、供应商以及工作人员提供的便捷的平台。该系统的主要业务需求包括记录并维护某超市的供应商信息,以及该超市与供应商之间的交易订单信息,包括三种角色,系统管理员经理,普通员工。
    1.1 系统功能分析本系统主要的功能是实现超市订单管理功能,以便为超市、连锁店提供以及其他负责人提供订单详情、联系方式等,系统的主要功能有以下五个方面:

    登录/注销:管理员可以在网站上登录浏览,离开时注销并退出
    订单管理:管理员可以浏览所有订单信息,并且通过点击查看了解订单详情信息
    供应商管理:管理员可以在网站浏览所有供应商信息,并在在与其他供应商达成合作之后,添加相关供应商信息,并且通过点击查看了解他们的联系方式等
    用户管理:管理员可以管理所有超市员工用户,对用户进行增删改查,对于离职或其他原因的未工作用户给予注销管理
    密码修改:管理员可对自己的账号密码进行修改,填写对应之前的正确密码以及新密码之后,即完成相关修改密码操作
    搜索功能:在以上管理界面中,均允许了管理员根据关键字进行搜索,要求搜索框中输入的字段必须完全包含在物品名称中,否则无法查询


    1.2 系统功能需求根据系统功能要求,该超市订单管理系统以管理员为中心的用户角色,可以将系统分解成几个模块来分别设计应用程序界面,如图 1.1所示。

    1.3 系统性能需求超市订单管理系统的开发是在Window10平台上,以SSM为架构,采用MySQL 作为数据库管理系统管理后台数据库。本系统是超市信息管理建设中必不可少的一部分,它实现了现代管理信息系统的大部分功能需要。使用本系统可以使超市管理更加方便快捷,合理的页面设计也使得这个用户充分享受到基于Internet管理信息系统的优越。本系统开发说明:
    1.3.1 功能完备在开发初期,查看了大量关于电子商务,管理信息系统,J2EE等方面的资料,同时借鉴了很多其他电子商务网站和管理信息的流程。经过总结,确定了满足需求分析的基本模块。系统总体设计上实现了整个系统模块的划分,系统主要包含5大模块,分别是:订单管理信息,供应商管理,用户管理,修改密码,登陆退出系统,基本上实现了综合管理系统的所有功能。 
    1.3.2 界面友好系统用户登陆到管理页面后,每页有导航和引领的作用。系统具有自适应的能力,同时导航条方便快捷的引导用户进行各种合理的操作。
    1.3.3 管理科学本系统一开始就从管理学的角度做出了详细细致的考虑,后来有参考了电子商务管理等,最后才做出了系统总体设计,因此可以讲该系统是较为科学的。
    系统的性能需求主要表现在数据库中的各个表需要频繁地被插入、删除以及更新。对于用户来说,系统地响应时间不宜太长,否则会降低用户体验。为此要求我们建立良好的表结构,加上足够的存储空间以及硬件性能。
    2 可行性分析2.1 研究前提随着我国经济情况的日新月异,飞速发展,涌现出许许多多的超市和便利店。越来越多的人喜欢到超市购物,超市里销售的商品也呈现出多种多样的变化趋势。我们开发一个超市订单管理系统,它可以对仓储各环节实施全过程控制管理,对整个进货、退货、盘点等各个环节的规范化作业,控制整个过程的正常运行。去掉了手工书写票据和送到机房输入的步骤,解决库房信息陈旧滞后的弊病,方便了仓库管理人员对物品的放置和调配,提高了工作效率。
    该系统容易被接受,具有简单易学性,便于管理等功能,是对超市订单管理的一种有效工具。
    2.2 设计要求2.2.1 安全性超市订单管理增强对产品规范的审计,重点确定该项目中需要审计的产品。买家只能针对卖家允许公开的信息进行查阅。买家只享受对自己账号内数据的查阅权,与定后处理权,订货支付权,申请退货权,不允许偷窥其他人。卖家只能针对买家允许公开的信息进行查阅。卖家只享受对自己账号内数据的查阅权,发货权,退款相应处理权,不允许偷窥其他人。
    2.2.2 系统性能管理员登录查看超市供应商与超市员工用户管理,可以进行增、删、改、查等操作。超市订单系统可以使超市的管理趋于正规化、现代化和系统化。本项目的产品可以达到以下目标:

    提高工作效率,减少返工
    业务流程的流水线化
    符合相关标准和规则
    与目前的应用产品相比较,提高了可用性或减少了失效程度

    2.2.3 可扩展性所有信息呈现,操作完全由打开的网页呈现并完成。本系统所占有的是超市市场,它追求的是简单、易学、易用,能够更好地解决管理人员的负担,能够辅助超市有效的管理物品。对于订单管理系统的用户,可满足对订单管理的需求,且此种需求被接受并且满足,其系统便可以推广。

    3 数据库设计3.1 数据库需求分析经过对超市管理系统的调查分析,得出用户的需求大致如下:

    管理员可以在系统中对订单、供应商以及用户进行增、删、改、查的处理
    管理员需要输入账号密码登录,并且可以增添新的管理员

    如下是利用数据流图方法对数据库做需求分析:
    第一步:由用户的需求,可以得到顶层数据流图如图3.1.1所示。

    第二步:超市订单管理系统的第1层数据流图如图3.1.2所示。

    第三步:超市订单管理系统的第2层数据库流图——订单管理的细化数据流图如图3.1.3所示。

    第四步:超市订单管理系统的第2层数据流库——供应商管理的细化数据流图如图3.1.4所示。

    第五步:超市订单管理系统的第2层数据流库——用户管理的细化数据流图如图3.1.5所示。

    根据如上的数据流程图,可以列出以下记录超市订单管理所需的数据项和数据结构:

    管理员:管理员ID、管理员姓名、管理员密码、管理员性别、管理员角色、管理员出生日期、管理员电话、管理员住址
    订单:订单编码、商品名称、供应商名称、订单金额、是否付款
    供应商:供应商编码、供应商名称、联系人、联系电话、微信

    3.2 数据库概念结构设计本系统一共有用户、供应商、订单、角色、地址这五个基本实体。
    管理员可以对应多个订单,而一个订单只能对应于一个管理员。管理员可以管理多个供应商,而一个供应商只能对应于一个管理员。一个供应商可以对应多条订单,但一条订单只能对应于一个供应商。此外,有一个用户对应一个角色,一个角色对应多个用户;一个地址对应多个订单,一个订单对应一个地址。数据库表之间的关系如下:


    用户:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    账单:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    供应商:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    地址:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    角色:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间

    3.3 数据库逻辑结构设计将概念结构设计中的各个模型转化为DBMS支持的表结构,同时保持不会出现插入异常、删除异常和修改异常,表结构应该做到符合3NF。根据系统 E-R 图,需要设计4个数据表来存放信息。在本系统中,一共有五个实体,实体转化为数据库模型为如下所示:
    一对多联系转化为一个关系模式:

    用户—订单(用户编号,订单编号)
    供货商—订单(供货商编号,订单编号)
    用户—身份(用户编号,身份编号)
    用户—地址(用户编号)

    利用以上关系模式得到的所有数据表如下所示:
    用户表(smbms_user)

    数据项:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    说明:用户ID是唯一的用户标识,使此表的主键。如表3.3.1所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID


    userCode
    varchar
    15
    Not null
    用户编码


    userName
    varchar
    15
    Not null
    用户名称


    userPassword
    varchar
    15
    Not null
    用户密码


    gender
    int
    10

    性别


    birthday
    date


    出生日期


    phone
    varchar
    15

    手机


    address
    varchar
    30

    地址


    userRole
    int
    10

    用户角色


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    idPicPath
    varchar
    300

    用户头像


    workPicPath
    varchar
    300

    工作照



    供应商表(smbms_provider)

    数据项:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    说明:这张表标识的是超市管理信息系统中商品供应商的信息列表,供应商ID是该表的主键
    编号方法:商品供应商ID采用自动生成方式,如表3.3.2所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    Bigint
    20
    Not null
    供货商ID(主键)


    proCode
    Varchar
    20
    Not null
    供货商编码


    proName
    varchar
    20
    Not null
    供货商名称


    ProDesc
    varchar
    50

    供应商详细描述


    proContact
    varchar
    20
    Not null
    供货商联系人


    proPhone
    Varchar
    20
    Not null
    联系电话


    ProAddress
    Varchar
    50
    Not null
    供货商地址


    proFax
    varchar
    20

    微信


    CreateBy
    bigint
    20

    创建者


    CreatationDate
    datetime


    创建时间


    modifyDate
    datetime


    更新时间


    modifyBy
    bigint
    20

    更新者


    companyLicPicPath
    varchar
    300

    营业执照


    orgCodePicPath
    varchar
    300

    组织机构代码证



    订单表(smbms_bill)

    数据项:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    说明:这张表标识的是超市管理信息系统订单信息列表,订单ID是该表的主键
    编号方法:订单ID采用自动生成方式,供应商ID与供应商表中供应商ID一一对应,如表3.3.3所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    订单ID(主键)


    billCode
    varchar
    20
    Not null
    订单编码


    ProductName
    Varchar
    20
    Not null
    商品名称


    ProductDescent
    Varchar
    50
    Not null
    商品描述


    ProductUnit
    Varchar
    10
    Not null
    商品单位


    ProductCount
    Decimal
    20,2
    Not null
    商品数量


    totalPrice
    Decimal
    20,2
    Not null
    商品总额


    isPayment
    int
    10
    Not null
    是否支付


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    providerID
    Int
    20

    供应商ID



    身份表(smbms_role)

    数据项:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间
    说明:这张表标识的是超市订单管理信息系统中用户身份列表,身份编号是该表的主键
    编号方法:用户身份编号与用户表中的员工身份编号一一对应,如表3.3.4所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    角色ID(主键)


    RoleCode
    varchar
    15
    Not null
    角色编码


    roleName
    Varchar
    15
    Not null
    角色名称


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间



    地址表(smbms_address)

    数据项:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    编号方法:用户ID与用户表中的用户ID一一对应,如表3.3.5所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID(主键)


    Contact
    varchar
    15
    Not null
    联系人姓名


    addressDesce
    Varchar
    50
    Not null
    收货地址明细


    postcode
    Varchar
    15

    邮编


    Tel
    Varchar
    20
    Not null
    联系人电话


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间


    userID
    Bigint
    20

    用户ID



    数据库连接利用了SSM框架的底层的MyBatis,建立了实体类与MySQL之间映射关系,从而实现数据持久化、封装数据库连接等操作。
    3.4 数据库物理结构设计3.4.1 选择关系模式的存取方式对数据库逻辑结构设计中建立的表结构,供应商表的供应商编号属性唯一决定每一个供应商元组,所以对供应商表建立以供应商编号为主关键字的索引。同理,对管理员关系模式、订单关系模式也采用类似的索引存取方法。
    3.4.2 数据表存储结构设计本系统的所有数据表均存放在物理磁盘中。用户表、供应商表和订单表的结构是相对稳定的,表中的已有记录是要长期保存的,在此基础上系统会相应用户的操作对数据表进行增、删、改、查等操作。

    3.5 数据库的建立3.5.1 数据库的建立创建数据库
    create database smbms;USE smbms;
    创建表smbms_address
    DROP TABLE IF EXISTS `smbms_address`;CREATE TABLE `smbms_address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contact` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人姓名', `addressDesc` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '收货地址明细', `postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编', `tel` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人电话', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', `userId` bigint(20) DEFAULT NULL COMMENT '用户ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_bill
    DROP TABLE IF EXISTS `smbms_bill`;CREATE TABLE `smbms_bill` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `billCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '账单编码', `productName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品名称', `productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述', `productUnit` varchar(10) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品单位', `productCount` decimal(20,2) NOT NULL COMMENT '商品数量', `totalPrice` decimal(20,2) NOT NULL COMMENT '商品总额', `isPayment` int(10) NOT NULL COMMENT '是否支付(1:未支付 2:已支付)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `providerId` int(20) DEFAULT NULL COMMENT '供应商ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_provider
    DROP TABLE IF EXISTS `smbms_provider`;CREATE TABLE `smbms_provider` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `proCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商编码', `proName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商名称', `proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述', `proContact` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商联系人', `proPhone` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系电话', `proAddress` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '微信', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `companyLicPicPath` varchar(300) DEFAULT NULL COMMENT '营业执照', `orgCodePicPath` varchar(300) DEFAULT NULL COMMENT '组织机构代码证', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_role
    DROP TABLE IF EXISTS `smbms_role`;CREATE TABLE `smbms_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `roleCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色编码', `roleName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色名称', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_user
    DROP TABLE IF EXISTS `smbms_user`;CREATE TABLE `smbms_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `userCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户编码', `userName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户名称', `userPassword` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户密码', `gender` int(10) DEFAULT 2 COMMENT '性别(1:女、 2:男)', `birthday` date DEFAULT NULL COMMENT '出生日期', `phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '手机', `address` varchar(30) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `userRole` int(10) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `idPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户头像', `workPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '工作照', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    3.5.2 初始数据的输入数据表创建成功后,数据库中还没有实际的数据。为了保证外部键能使用,数据需要提前输入,如用户编码、用户姓名、订单名称和供应商等等。具体插入语句如下:
    向smbms_address表插入数据
    insert into `smbms_address`(`id`,`contact`,`addressDesc`,`postCode`,`tel`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`userId`) values (1,'王丽','北京市东城区东交民巷44号','100010','13678789999',1,'2020-04-13 00:00:00',NULL,NULL,1),(2,'张红丽','北京市海淀区丹棱街3号','100000','18567672312',1,'2020-04-13 00:00:00',NULL,NULL,1),(3,'任志强','北京市东城区美术馆后街23号','100021','13387906742',1,'2020-04-13 00:00:00',NULL,NULL,1),(4,'曹颖','北京市朝阳区朝阳门南大街14号','100053','13568902323',1,'2020-04-13 00:00:00',NULL,NULL,2),(5,'李慧','北京市西城区三里河路南三巷3号','100032','18032356666',1,'2020-04-13 00:00:00',NULL,NULL,3),(6,'王国强','北京市顺义区高丽营镇金马工业区18号','100061','13787882222',1,'2020-04-13 00:00:00',NULL,NULL,3);
    向smbms_bill表插入数据
    insert into `smbms_bill`(`id`,`billCode`,`productName`,`productDesc`,`productUnit`,`productCount`,`totalPrice`,`isPayment`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`providerId`) values (1,'BILL2016_001','洗发水、护发素','日用品-洗发、护发','瓶','500.00','25000.00',2,1,'2020-06-14 13:02:03',NULL,NULL,13),(2,'BILL2016_002','香皂、肥皂、药皂','日用品-皂类','块','1000.00','10000.00',2,1,'2020-03-23 04:20:40',NULL,NULL,13),(3,'BILL2016_003','大豆油','食品-食用油','斤','300.00','5890.00',2,1,'2020-05-14 13:02:03',NULL,NULL,6),(4,'BILL2016_004','橄榄油','食品-进口食用油','斤','200.00','9800.00',2,1,'2020-04-10 03:12:13',NULL,NULL,7),(5,'BILL2016_005','洗洁精','日用品-厨房清洁','瓶','500.00','7000.00',2,1,'2020-05-14 13:02:03',NULL,NULL,9),(6,'BILL2016_006','美国大杏仁','食品-坚果','袋','300.00','5000.00',2,1,'2020-04-14 06:08:09',NULL,NULL,4),(7,'BILL2016_007','沐浴液、精油','日用品-沐浴类','瓶','500.00','23000.00',1,1,'2020-07-01 10:10:22',NULL,NULL,14),(8,'BILL2016_008','不锈钢盘碗','日用品-厨房用具','个','600.00','6000.00',2,1,'2020-04-14 05:12:13',NULL,NULL,14),(9,'BILL2016_009','塑料杯','日用品-杯子','个','350.00','1750.00',2,1,'2020-02-04 11:40:20',NULL,NULL,14),(10,'BILL2016_010','豆瓣酱','食品-调料','瓶','200.00','2000.00',2,1,'2020-01-29 05:07:03',NULL,NULL,8),(11,'BILL2016_011','海之蓝','饮料-国酒','瓶','50.00','10000.00',1,1,'2020-04-14 16:16:00',NULL,NULL,1),(12,'BILL2016_012','芝华士','饮料-洋酒','瓶','20.00','6000.00',1,1,'2020-06-09 17:00:00',NULL,NULL,1),(13,'BILL2016_013','长城红葡萄酒','饮料-红酒','瓶','60.00','800.00',2,1,'2020-04-14 15:23:00',NULL,NULL,1),(14,'BILL2016_014','泰国香米','食品-大米','斤','400.00','5000.00',2,1,'2020-05-09 15:20:00',NULL,NULL,3),(15,'BILL2016_015','东北大米','食品-大米','斤','600.00','4000.00',2,1,'2020-05-14 14:00:00',NULL,NULL,3),(16,'BILL2016_016','可口可乐','饮料','瓶','2000.00','6000.00',2,1,'2020-03-27 13:03:01',NULL,NULL,2),(17,'BILL2016_017','脉动','饮料','瓶','1500.00','4500.00',2,1,'2020-05-10 12:00:00',NULL,NULL,2),(18,'BILL2016_018','哇哈哈','饮料','瓶','2000.00','4000.00',2,1,'2020-06-24 15:12:03',NULL,NULL,2);
    向smbms_provider表插入数据
    insert into `smbms_provider`(`id`,`proCode`,`proName`,`proDesc`,`proContact`,`proPhone`,`proAddress`,`proFax`,`createdBy`,`creationDate`,`modifyDate`,`modifyBy`) values(1,'BJ_GYS001','北京三木堂商贸有限公司','长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等','张国强','13566667777','北京市丰台区育芳园北路','010-58858787',1,'2020-03-21 16:52:07',NULL,NULL),(2,'HB_GYS001','石家庄帅益食品贸易有限公司','长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等','王军','13309094212','河北省石家庄新华区','0311-67738876',1,'2020-04-13 04:20:40',NULL,NULL),(3,'GZ_GYS001','深圳市泰香米业有限公司','初次合作伙伴,主营产品:良记金轮米,龙轮香米等','郑程瀚','13402013312','广东省深圳市福田区深南大道6006华丰大厦','0755-67776212',1,'2020-03-21 16:56:07',NULL,NULL),(4,'GZ_GYS002','深圳市喜来客商贸有限公司','长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉','林妮','18599897645','广东省深圳市福龙工业区B2栋3楼西','0755-67772341',1,'2020-03-22 16:52:07',NULL,NULL),(5,'JS_GYS001','兴化佳美调味品厂','长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料','徐国洋','13754444221','江苏省兴化市林湖工业区','0523-21299098',1,'2020-02-22 16:52:07',NULL,NULL),(6,'BJ_GYS002','北京纳福尔食用油有限公司','长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等','马莺','13422235678','北京市朝阳区珠江帝景1号楼','010-588634233',1,'2020-03-21 17:52:07',NULL,NULL),(7,'BJ_GYS003','北京国粮食用油有限公司','初次合作伙伴,主营产品:花生油、大豆油、小磨油等','王驰','13344441135','北京大兴青云店开发区','010-588134111',1,'2020-04-13 00:00:00',NULL,NULL),(8,'ZJ_GYS001','慈溪市广和绿色食品厂','长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品','薛圣丹','18099953223','浙江省宁波市慈溪周巷小安村','0574-34449090',1,'2020-01-21 06:02:07',NULL,NULL),(9,'GX_GYS001','优百商贸有限公司','长期合作伙伴,主营产品:日化产品','李立国','13323566543','广西南宁市秀厢大道42-1号','0771-98861134',1,'2020-03-21 19:52:07',NULL,NULL),(10,'JS_GYS002','南京火头军信息技术有限公司','长期合作伙伴,主营产品:不锈钢厨具等','陈女士','13098992113','江苏省南京市浦口区浦口大道1号新城总部大厦A座903室','025-86223345',1,'2020-03-25 16:52:07',NULL,NULL),(11,'GZ_GYS003','广州市白云区美星五金制品厂','长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等','梁天','13562276775','广州市白云区钟落潭镇福龙路20号','020-85542231',1,'2020-01-21 06:12:17',NULL,NULL),(12,'BJ_GYS004','北京隆盛日化科技','长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等','孙欣','13689865678','北京市大兴区旧宫','010-35576786',1,'2020-01-21 12:51:11',NULL,NULL),(13,'SD_GYS001','山东豪克华光联合发展有限公司','长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等','吴洪转','13245468787','山东济阳济北工业区仁和街21号','0531-53362445',1,'2020-01-28 10:52:07',NULL,NULL),(14,'JS_GYS003','无锡喜源坤商行','长期合作伙伴,主营产品:日化品批销','周一清','18567674532','江苏无锡盛岸西路','0510-32274422',1,'2020-04-23 11:11:11',NULL,NULL),(15,'ZJ_GYS002','乐摆日用品厂','长期合作伙伴,主营产品:各种中、高档塑料杯,塑料乐扣水杯(密封杯)、保鲜杯(保鲜盒)、广告杯、礼品杯','王世杰','13212331567','浙江省金华市义乌市义东路','0579-34452321',1,'2020-06-22 10:01:30',NULL,NULL);
    向smbms_role表插入数据
    insert into `smbms_role`(`id`,`roleCode`,`roleName`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'SMBMS_ADMIN','系统管理员',1,'2020-01-01 00:00:00',NULL,NULL),(2,'SMBMS_MANAGER','经理',1,'2020-02-02 00:01:00',NULL,NULL),(3,'SMBMS_EMPLOYEE','普通员工',1,'2020-02-03 00:00:00',NULL,NULL);
    向smbms_user表插入数据
    insert into `smbms_user`(`id`,`userCode`,`userName`,`userPassword`,`gender`,`birthday`,`phone`,`address`,`userRole`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'admin','系统管理员','1234567',1,'1983-10-10','13688889999','山东省日照市东港区成府路207号',1,1,'2020-03-21 16:52:07',NULL,NULL),(2,'liming','李明','0000000',2,'1983-12-10','13688884457','山东省日照市东港区前门东大街9号',2,1,'2020-03-01 00:00:00',NULL,NULL),(5,'hanlubiao','韩路彪','0000000',2,'2001-06-05','18567542321','山东省日照市东港区北辰中心12号',2,1,'2020-02-11 19:52:09',NULL,NULL),(6,'zhanghua','张华','0000000',1,'1980-06-15','13544561111','山东省日照市东港区学院路61号',3,1,'2020-02-11 10:51:17',NULL,NULL),(7,'wangyang','王洋','0000000',2,'2001-12-31','13444561124','山东省青岛市三二二区西二旗辉煌国际16层',3,1,'2020-06-11 19:09:07',NULL,NULL),(8,'zhaoyan','赵燕','0000000',1,'1999-03-07','18098764545','山东省青岛市东科区回龙观小区10号楼',3,1,'2020-04-21 13:54:07',NULL,NULL),(10,'sunlei','孙磊','0000000',2,'1998-01-04','13387676765','山东省日照市朝阳区管庄新月小区12楼',3,1,'2020-05-06 10:52:07',NULL,NULL),(11,'sunxing','孙兴','0000000',2,'1997-03-12','13367890900','北京市朝阳区建国门南大街10号',3,1,'2020-01-09 16:51:17',NULL,NULL),(12,'zhangchen','张晨','0000000',1,'1986-03-28','18098765434','朝阳区管庄路口北柏林爱乐三期13号楼',3,1,'2019-06-09 05:52:37',1,'2020-04-14 14:15:36'),(13,'dengchao','邓超','0000000',2,'1981-11-04','13689674534','北京市海淀区北航家属院10号楼',3,1,'2020-07-01 08:02:47',NULL,NULL),(14,'yangguo','杨过','0000000',2,'1989-01-01','13388886623','北京市朝阳区北苑家园茉莉园20号楼',3,1,'2020-02-01 03:52:07',NULL,NULL),(15,'zhaomin','赵敏','0000000',1,'1989-12-04','18099897657','山东省临沂市昌平区天通苑3区12号楼',2,1,'2020-01-12 12:02:12',NULL,NULL);
    此外,本系统中所用到的用户性别和用户身份代码如表3.5.1至表3.5.2所示。
    用户性别代码表



    代码
    说明




    1



    2




    用户身份代码



    代码
    说明




    1
    系统管理员


    2
    经理


    3
    普通员工



    4 各功能模块的设计与实现4.1 系统开发条件4.1.1 开发语言系统使用的开发语言是Java。Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统应用程序等。正是因为Java语言拥有如此诸多的优秀特性,所以我们选择了它作为开发超市订单管理系统,使得整个开发、调试过程更加高效。
    4.1.2 开发框架超市订单管理系统以SSM架构作为支撑,分为表现层、业务层和持久层三层,实现后台数据更新。该架构由Spring MVC、Spring和MyBatis三个开源框架整合而成,用于开发结构合理,性能优越,代码健壮的应用程序。

    4.1.3 前端框架由于本系统是Web应用,所以使用了HTML5+CSS3+JavaScript的方式实现前端页面。实现过程中参考了Bootstrap前端开发框架。Bootstrap是Twitter退出的一个用于前端开发的开源工具包。在设计前端页面时,参考了Bootstrap的相关开源代码。
    4.1.4 集成开发环境编程所使用的集成开发环境是Eclipse,是著名的跨平台的自由集成开发环境(IDE)。Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。本次系统便选用了Eclipse作为开发平台。
    4.1.5 Web应用服务器Tomcat由Apache、Sun和其他一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和JSP规范可以在Tomcat中得到体现。因为Tomcat技术先进、性能稳定,因而成为目前比较流行的Web应用服务器。本次系统选用的便是Tomcat作为应用服务器。
    4.1.6 数据库管理系统本系统使用的数据库管理系统是MySQL Community。MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发。在WEB应用方面,MySQL是最好的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件。
    系统中的数据库以及数据库中的所有关系模式都使用MySQL进行处理。
    4.2 用户界面设计完成数据库创建和功能说明以后,我们进行下一步工作,即设计用户界面,完成了系统要求的 5 项主要功能。
    我们把超市订单管理系统的窗体分成5个主要部分,如下图所示。
    4.2.1 订单管理
    4.2.2 供应商管理
    4.2.3 用户管理
    4.2.4 修改密码
    4.2.5 登录注销
    4.3 功能模块说明5.3.1 订单信息添加、查询、修改与删除订单信息查看:为了对订单浏览信息,能够实现浏览的功能是十分必要的。管理员输入需要搜索的相应信息,点击查看按钮后系统将寻找到的数据展示到网页中。

    订单信息添加:作为超市订单管理系统,订单信息的管理是很重要的。每当采购部门增加新的订单时,订单信息就要增加。超市也可能因为其它原因增加订单信息,订单添加模块都可以做出快捷的解决方案。管理员输入相应的信息,点击提交后系统将数据保存到数据库中。

    订单信息修改:根据订单编号可以查询订单详细信息,然后修改订单的所有信息。系统从数据库中读取数据并显示到页面上,管理员修改数据后,点击修改按钮,系统将更新表中的数据。

    订单信息删除:根据订单编号可以删除该订单的信息。管理员选择需要删除订单名称并点击删除按钮,系统将从数据库中删除相应数据。
    订单信息查询:在成千上万种商品种,如果人为寻找某一个商品肯定是不可能的,只有通过商品信息查询模块才能为用户或管理人员解决这个难题。根据订单名称可以查询该订单的信息。管理员输入订单名称并点击查询按钮,系统将从数据库中查询相应的数据并显示到页面上。

    5.3.2 供应商信息添加、查询、修改与删除供应商查询界面:供应商查询界面提供了供应商的信息,可根据供应商名称的关键字进行筛选查询,并提供了添加供应商、查看供应商详细信息、修改供应商信息、删除供应商的功能。

    供应商查看详情界面:在供应商查询界面点击具体供应商操作列表的查看按钮,可以查看供应商的具体信息,包括:供货商编码、供货商名称、详细描述、联系人、联系电话、地址、微信。

    供应商修改页面:若供应商信息变动,管理员可通过供应商信息修改功能对供应商信息进行更新,更新后的数据将保存到数据库中。

    商品供应商信息删除:企业倒闭或者经营策略的改变,当它对超市商品的供应没有作用时,商品供应商厂家信息的删除是正常的。管理员输入供应商名称查询数据表中的数据并显示到页面上,点击删除后系统将表中的相应数据删除。
    供应商添加界面:与供应商达成交易后,管理员在供应商添加页面填写供应商具体信息,填写完毕点击提交,添加后的数据将保存到数据库中。

    5.3.3 用户信息添加、查询、修改与删除用户管理页面:通过输入用户名和身份查询用户。当不记得用户名的具体名字时,只输入用户名的其中一个字,会检索出所有带这个字的用户,方便管理员查询管理。点击右边链接添加用户,会连接到相关网页添加用户信息。点击操作里的查看、修改等可以进行相应的改、删、查操作。

    用户信息删除:当企业员工离职时,或者经过一段时间后,会发现用户表中一些信息时无用的,用户删除模块可以解决这样的问题。
    添加用户信息:填写用户相关信息,下面有两个按钮,可以选择重置或者提交。

    5.3.4 修改密码为了系统的安全,用户的应该只有用户个人才能修改,这不仅保证了整个公司的利益也保护了个人隐私。用户在输入相应的用户编号,填写旧密码以及新密码后,点击提交,重置密码成功。发现输入错误时,可以手动删除或者点击重置按钮,重新填写。

    修改用户密码成功后,会弹出修改用户密码成功页面,如图4.3.14所示。

    5.3.5 登录/注销输入用户名以及用户密码登录进入超市订单管理界面,可以查看管理信息。管理员可以对相关数据进行增、改、查等操作,也可以注销退出系统。
    5 实训总结5.1 所遇困难在实现本系统时遇到的困难主要体现在两个方面,一是系统的前端页面的设计,二是怎样Web与数据库实现交互。
    系统前端页面的设计困难的解决是通过参考著名的前端框架Bootstrap实现的。Bootstrap框架提供了许多精美的组建、布局,还开放了源代码供参考。在此基础上我们还加入了一些利用JavaScript代码实现的美化效果,使得前端设计更加美观。
    实体Web与数据库交互的解决得益于SSM框架的三层Spring MVC、Spring和MyBatis,能够分离处理数据库与Web层的视图,从而达到交互的目的。
    此外,在编写后端的时候,变量的大小写、系统配置也是困难重重。好在,在反复编写之后,迅速熟悉的技巧,能够让页面自由切换。系统配置更是反复在网上求证,得以解决。
    5.2 实验心得这一次合作开发超市订单管理系统,从开始选择课题的困惑到最终完成了一个我们还算满意的作品,使我学到了很多东西。从设计数据库到编写后台代码,链接数据库,在网页上显示,令人印象深刻。反复查阅资料,启动Tomcat到凌晨0点,都是藏着对这次项目的努力。其实,从一开始选择哪个题目是否用SSM框架来开发我一直也犹豫过,像国内势头正旺的ThinkPHP,易学易用,完善的中文开发文档,遇到问题或者bug可以非常容易的在中文社区得到解答。但是我最后选择了SSM框架,不仅仅因为它广泛,而是我希望能够挑战自己。经过这一个周的磨练,我最大的收获除了学到了真正可以应用的知识外,更重要的是学会了项目合作开发的经验。
    3 留言 2020-08-05 15:32:05 奖励46点积分
  • 深度学习人脸性别识别

    1 实验目的
    了解机器学习的研究对象、研究内容、研究方法(包括理论基础和实验方法),和近年来的主要发展技术路线
    培养学生以机器学习理论为基础从事相关应用研究的能力,通过看——查阅资料、做——复现已有资料的实验或做一个机器学习应用课题、写——将自己的研究工作写成技术报告,完成整个大作业
    了解机器学习领域的新进展、新应用,并结合课堂教学和教材的内容,应用某一种或几种算法,解决问题并分析实验结果
    理解相关算法原理和关键技术,并以实验加以验证和实现
    撰写技术报告,将所做工作的方法、原理、实验设计、所得结果及其比较分析等写成技术报告,并附上主要参考文献,说明程序的运行环境和运行方法

    2 实验内容2.1 实验内容要求针对提供的人脸数据集,预测人脸性别。
    本次将提供 20000 多张已经分割的人脸图像,要求基于人脸图像自动识别该人性别。数据集的年龄从 1 岁覆盖到 100 多岁,包括了白种人、黄种人、黑种人等多种种族数据。数据集存在人脸姿态、光照、年龄等多种干扰,具有一定的挑战性。
    2.2 数据集数据集包括以下 csv 文件:

    train.csv:训练集,其中包括两列,第一列 id 是人脸图像的编号,即对应的文件名,第二列 label 是性别标签,0 表示男性,1 表示女性
    test.csv:测试集,只包括一列 id,即测试集中所有的人脸图像的编号。测试集中没有性别标签
    train文件夹:所有的训练图像,扩展名名.jpg,其命名与train.csv中的id命名一致
    test 文件夹:所有的测试图像,扩展名为jpg,其命名与test.csv中的id命名一致
    sampleSubmit.csv:提交文件的样本,其中包括两列,第一列 id 是所有测试集的人脸图像的编号,即对应的文件名,第二列 label 是模型输出的性别标签,0 表示男性,1 表示女性

    数据说明:

    id:图像文件名,其对应图像为 id.jpg
    label:图像标签,0 表示男性,1表示女性

    3 运行环境
    虚拟环境:Anaconda 4.5.4
    Python环境:Python 3.6.5
    编译器:vs code
    深度学习框架:pytorch1.5.1 , tensorflow2.3.0
    其他环境:jupyter, Kaggle notebook
    Python库:numpy,pandas,os,PIL,matplotlib

    4 算法原理与分析4.1 损失函数损失函数是一个非负实数函数,用来量化模型预测与真实标签之间的差异,下面介绍几种常用的损失函数:
    4.1.1 平方损失函数(MSE)平方损失函数较为容易理解,它直接测量机器学习模型的输出与实际结果之间的距离。这里可以定义机器学习模型的输出为y, 实际的结果为y_t,那么平方损失函数可以被定义为:

    4.1.2 交叉熵损失函数(CrossEntropy)二分类
    在二分的情况下,模型最后需要预测的结果只有两种情况,对于每个类别我们预测得到的概率分别为p和(1-p),此时表达式为:

    其中:

    y_i表示样本i的label,正类为1,负类为0
    p_i表示样本i预测为正的概率

    多分类
    多分类实际上是对二分类的扩展:

    其中:

    y_i表示第i类的标签
    p_i表示第i类的概率

    .2 梯度下降众所周知,在求最优解时常用到两个方法,两者均可用于线性/非线性拟合,梯度下降法通过不断地向梯度反方向迭代可以得到最优解,过程通常分为计算梯度和调整权值直至收敛
    4.2.1 随机梯度下降(SGD)随机梯度下降是指对于一个单样本计算损失,即随机一个样本更新一次梯度。

    这样的好处是计算f(x)的梯度和计算f(x+1)的梯度是可以并行计算的,大大提高了运行速度,而且其可以避免冗余数据的影响,但是并不是每次都朝着全局最优迭代,多以通常说地SGD都是mini-batch SGD。
    4.2.2 带动量的梯度下降(momentum)如下图所示,如果进行梯度下降法的一次迭代, 可以看到是慢慢摆动到最小值, 这种上下波动减慢了梯度下降法的速度, 理想的情况是,在纵轴上, 你希望学习慢一点, 因为你不想要这些摆动, 但是在横轴上,你希望加快学习,你希望快速从左向右移,移向最小值,移向红点。

    其基本的想法就是计算梯度的指数加权平均数,并利用该梯度更新你的权重。
    4.3 卷积神经网络基础的CNN由卷积(convolution),激活(activation)和池化(pooling)三种结构组成。CNN输出的结果是每幅图像的特定特征空间。当处理图像分类任务时,我们会把CNN输出的特征空间作为全连接层或全连接神经网络(fully connected neural network, FCN)的输入,用全连接层来完成从输入图像到标签集的映射,即分类。当然,整个过程最重要的工作就是如何通过训练数据迭代调整网络权重,也就是后向传播算法。目前主流的卷积神经网络(CNNs),比如VGG, ResNet都是由简单的CNN调整,组合而来。
    CNN层级结构

    数据输入层/ Input layer
    卷积计算层/ CONV layer
    ReLU激励层 / ReLU layer
    池化层 / Pooling layer
    全连接层 / FC layer

    卷积计算过程

    4.4 深度学习训练技巧4.4.1 划分验证集和K交叉验证模型什么时候达到最好,首先看对训练集的拟合程度,也就是通常所说的loss,但是并不是loss越小越好,更重要的是模型在新数据下的表现能力,也就是val_loss。我们通常将训练集划分出一部分作为验证集,来查看模型的泛化能力,但是这也会产生一个弊端,也就是没有充分的运用到训练数据,要知道,很多任务中的训练数据本身也并不多,所以我们这里采用k折交叉验证来最小化这个弊端。

    4.4.2 ReduceLROnPlateau配合EarlyStopping在训练过程中通过手工调整超参数费时费力,ReduceLROnPlateau通过监测验证集上的损失(val_loss)来动态的调整学习率,而且配合EarlyStopping,可以在val_loss多次不再变化后停止训练,更容易获取训练的最佳点。
    4.4.3 Dropoutdropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。
    4.4.4 Batch normalization在我们以前在神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理。要知道,虽然我们对输入数据进行了归一化处理,但是输入数据经过σ ( W X + b ) 这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大。如果我们能在网络的中间也进行归一化处理,既可以加快训练速度,又可以避免采用其他正则化手段,还能提高模型精度。
    5 实验设计5.1 数据读入该任务给出的原始数据是由csv文件和相关的图片文件夹构成,操作起来很不方便,本文通过pandas的读取方法将csv文件里的图片名和标签读取到统一的txt文件中,如下图所示。

    通过继承pytorch里提供的DataSet类重写自己的数据集类,然后通过DataLoader打乱并生成batch。

    5.2 图像增强为了丰富图像训练集,更好的提取图像特征,泛化模型(防止模型过拟合),一般都会对数据图像进行数据增强。数据增强,常用的方式,就是旋转图像,剪切图像,改变图像色差,扭曲图像特征,改变图像尺寸大小,增强图像噪音(一般使用高斯噪音,盐椒噪音)等。
    归一化和标准化

    下边展示一些常用的图像增强方法:
    亮度变化

    随机灰度化

    随机旋转

    水平垂直翻转

    5.3 模型设计5.3.1 模型一模型1结构

    损失函数:BCELoss
    参数选择
    以1:4将训练集划分为验证集(validation)和训练集,以下将探讨学习率(learning_rata)、epoch、batch_size、优化器,正则化系数λ对验证集准确率的影响。

    总结
    在提高模型一精度的过程中,各种手段(包括数据增强,各种超参数调节均未取得较大进步,最高准确率只有89%,有理由确定该模型是影响精度的主要原因,猜测原因主要是模型深度还不够,可调节的参数不够多)
    5.3.2 模型二模型结构
    模型二通过使用小的3*3的卷积核进行处理,叠加5层,拉平后接全连接层,这样使得网络深度足够,有足够多的参数。下图是部分网路结构。

    该网络除了使用小的卷积核之外,还使用了dropout方法,这也是一种正则化方法,随机的让部分神经元失活,可以防止过拟合的出现。而且该网络在每一个卷积层的输出添加了Batch Normalization层,该层可以让网络训练过程中使得每一层神经网络的输入保持相同分布。
    训练过程
    在模型1的训练过程中通过手工调整超参数费时费力,在该模型中我们使用ReduceLROnPlateau,通过监测验证集上的损失(val_loss)来动态的调整学习率,而且配合EarlyStopping,可以在val_loss多次不再变化后停止训练,更容易获取训练的最佳点。
    实验表明,通过多次训练,该模型最佳可达到91.2%的准确率。
    5.3.3 模型三迁移学习
    模型迁移利用上千万的图象训练一个图象识别的系统,当我们遇到一个新的图象领域,就不用再去找几千万个图象来训练了,可以原来的图像识别系统迁移到新的领域,所以在新的领域只用几万张图片同样能够获取相同的效果。模型迁移的一个好处是可以和深度学习结合起来,我们可以区分不同层次可迁移的度,相似度比较高的那些层次他们被迁移的可能性就大一些。
    所以在遇到特征比较相似的情况时,可以使用预训练模型来达到自己的目的。
    VGG16迁移学习
    通过大量实践,并跑了多个预训练模型,发现对于人脸性别识别任务上VGG16效果最好,验证集上效果最高可到到92.7%,下表展示了VGG16训练过程中的部分数据。

    在此之前,或许因为训练经验不足,其他的预训练模型都没有产生理想的结果,也因为kaggle上gpu使用时间的限制和速度的限制,没有对其他模型进行大量的训练工作,也可能是其他模型识别的特征与该任务相差太大,训练结果见下表所示。

    5.4 模型融合在获取了大量的训练结果数据后,所谓三个臭皮匠,胜过诸葛亮,模型融合是一个很关键的步骤,能够大大提高精度。模型融合方法很多,下面介绍使用到的几个方法。
    5.4.1 投票如果是三个性能较差,但是高度不相关的模型:

    可以看到大大的提高了精确性。
    5.4.2 带权重的投票得到了一堆准确率差别挺大的数据,经过实验,随着低准确率样本数增多,投票过后的精度并不会再升高,反而不如用精确率最高的几组数据进行投票。可以想到这样一个场景:对于一道题,就算有2个成绩一般的人选A,反而你更宁愿相信成绩很好的人说选B,复原到该任务中,一个准确率很高的结果的票数远不止1票才合理。基于这种思想,将得分高的结果票数增多取得了比平均投票更好的效果。但是探索合适的权重仍然是一个复杂的工作,相信合适权重分配能取得更好的表现。
    6 实验结果与分析6.1 实验结果通过大量实验,模型一的最佳测试集结果为0.89,模型二的最佳表现为0.916,模型三的VGG16迁移学习最佳测试集结果可达到0.927,在通过投票和带权重的投票模型融合手段下,最佳测试集结果可达到0.93447。
    6.2 结果分析与展望对于这个结果,我觉得还存在很多不足,主要还有很多工作没有做完,主要有以下工作:

    在模型融合上,权重探索还不够多,而且单纯的对结果投票并没有对各个模型物尽其用,基于这个问题,我觉得可以将各个表现比较好的模型最后一个卷积层拉平的向量V保存下来,然后将几个模型的向量V进行拼接,通过一个或者多个全连接层进行训练,相信可以最大化的发掘出每个模型的优点
    对于一个不错的模型,因为gpu和设备的限制,只用到了部分数据,并且没有去做K交叉验证,所以应该还有一定的提升空间
    我一直有一个想法:模型输出的是为男性或者女性的概率,那么当模型输出值在0.45-0.55的时候,说明模型也觉得既像男性,又像女性,那么这个时候模型输出的结果属于“值得怀疑的结果”,我们可以对于这部分的输出再进行训练,提取更多的特征
    对于诸多复杂的预训练模型,没有进行大量而丰富的训练,并没有得出其最好的结果,相信其中还有很多潜力等待挖掘
    其次,对于各种trick还不够熟悉,对于模型的设计还有很多不足
    在对于图像数据清洗和处理上,在明知道有错误数据,但是却未来得及处理。

    我相信,随着自己不断地学习,以及条件地改善,这些遗留问题都会被一一解决,自己也能对该类问题更加游刃有余。
    7 心得体会通过这一次,我学习到了很多东西,首先就是将课堂所学用于实践,比如各种超参数调节,过拟合判断与处理,其次是学习到了各种深度学习框架地使用以及各种技巧,更重要的是培养了自己动手解决问题的能力和自信心。当然,在做大作业过程中也遇到了诸多困难,比如模型的设计,怎么才是好的模型呢?无从而知,只有从卷积网络的发展历程可以得知一二,其次就是gpu的限制,复杂模型一跑就是一个下午,很难在进行各种超参数调节。但是总的来说还是收获巨大!学习过程中才发现知识海洋之浩瀚,自己还需要努力学习,争取在这个领域贡献一份自己的力量!
    1 留言 2020-11-25 12:14:48 奖励56点积分
  • 基于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)
    7 留言 2019-12-20 17:48:45 奖励50点积分
  • 枚举并删除系统上PsSetCreateProcessNotifyRoutine回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 PsSetCreateProcessNotifyRoutine 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的进程回调,会存储在一个名为 PspCreateProcessNotifyRoutine 的数组里。 PspCreateProcessNotifyRoutine 可以理解成一个 PVOID 数组,它存储着系统里所有 PsSetCreateProcessNotifyRoutine 进程回调函数地址的加密地址。PspCreateProcessNotifyRoutine数组里的数据是加密的,要经过解密操作才可以获取正确的数据。
    PspCreateProcessNotifyRoutine 数组地址的获取首先,我们需要获取 PspCreateProcessNotifyRoutine 的地址。我们可以借助 WinDbg 帮助我们进行内核调试,下面是我们借助 WinDbg 逆向 Win10 x64 内核函数 PspSetCreateProcessNotifyRoutine 的代码:
    nt!PspSetCreateProcessNotifyRoutine+0x4c:fffff801`54b3b57c 33ff xor edi,edifffff801`54b3b57e 4c8d3dfb0bdfff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff801`5492c180)]
    我们从上面发现,函数中会调用到 PspCreateProcessNotifyRoutine 的地址。所以,在 64 位系统中,我们可以通过扫描内存特征码,从而获取数组PspCreateProcessNotifyRoutine 的 4 字节偏移,再计算出它的地址。在 32 位系统中,通过扫描内存特征码,就可以直接获取 PspCreateProcessNotifyRoutine 的地址了。注意的是:不同系统上,特征码也会不同。下面是我们总结的特征码:




    Win7
    win8.1
    win10




    32 位
    C7450C
    B8
    BB


    64 位
    4C8D35
    4C8D3D
    4C8D3D



    其中,内核函数 PspSetCreateProcessNotifyRoutine 并不是导出函数,所以不能直接获取它的函数地址。使用WinDbg 逆向 PsSetCreateProcessNotifyRoutine 函数的代码如下(Win10 x64 系统):
    nt!PsSetCreateProcessNotifyRoutine:fffff800`042cb3c0 4533c0 xor r8d,r8dfffff800`042cb3c3 e9e8fdffff jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`042cb1b0)
    我们从上面代码发现,内核函数 PsSetCreateProcessNotifyRoutine 里会调用到内核函数 PspSetCreateProcessNotifyRoutine。所以,在 32 位和 64 位系统中,我们可以通过扫描内存特征码,从而获取 PspSetCreateProcessNotifyRoutine 的 4 字节偏移,再计算出它的地址。注意的是:不同系统上,特征码也会不同。下面是我们总结的特征码:




    Win7
    win8.1
    win10




    32 位
    E8
    E8
    E8


    64 位
    E9
    E9
    E9



    要就是说,获取 PspCreateProcessNotifyRoutine 地址可以分成两步:

    首先,通过扫描特征码,从 PsSetCreateProcessNotifyRoutine 函数中获取 PspSetCreateProcessNotifyRoutine 函数的地址
    然后,通过扫描特征码,从 PspSetCreateProcessNotifyRoutine 函数中获取 PspCreateProcessNotifyRoutine 数组的地址

    那么,特征码的确定就变得至关重要了。
    PspCreateProcessNotifyRoutine 里数据的解密我们上面说,PspCreateProcessNotifyRoutine 里的数据是加密的,在 64 位系统和 32 位系统上的加密方式是不相同的,自然解密方式也不同。现在,我们分别介绍 32 位系统和 64 位系统下的解密方式。
    对于 32 位系统来说:PspCreateProcessNotifyRoutine 是一个 4 字节无符号类型的数组,数组大小最大为 8。我们使用 PspCreateProcessNotifyRoutine[i] 表示数组中的值,那么,32 位系统下的解密方式为:

    首先,数组的值 PspCreateProcessNotifyRoutine[i] 位运算“与” 0xFFFFFFF8
    然后,“与”运算之后的结果值再加上 4,结果就是一个存储着回调函数地址的地址

    对于 64 位系统来说:PspCreateProcessNotifyRoutine 是一个 8 字节无符号类型的数组,数组大小最大为 64。我们使用 PspCreateProcessNotifyRoutine[i] 表示数组中的值,那么,64 位系统下的解密方式为:
    数组的值 PspCreateProcessNotifyRoutine[i] 位运算“与” 0xFFFFFFFFFFFFFFF8,结果就是一个存储着回调函数地址的地址。
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 PsSetCreateProcessNotifyRoutine 函数,传入回调函数地址,并设置删除回调函数标志为 TRUE,即可删除回调
    修改 PspCreateProcessNotifyRoutine 数组中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数
    修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作

    编码实现遍历回调// 遍历回调BOOLEAN EnumNotifyRoutine(){ ULONG i = 0; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; // 获取 PspCreateProcessNotifyRoutine 数组地址 pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine(); if (NULL == pPspCreateProcessNotifyRoutineAddress) { DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress); // 获取回调地址并解密#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#endif return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress){ NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutine", status); } return status;}
    获取 PspCreateProcessNotifyRoutine 数组地址// 获取 PspCreateProcessNotifyRoutine 数组地址PVOID GetPspCreateProcessNotifyRoutine(){ PVOID pPspCreateProcessNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pFirstSpecialData[50] = { 0 }; ULONG ulFirstSpecialDataSize = 0; UCHAR pSecondSpecialData[50] = { 0 }; ULONG ulSecondSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D35 pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x35; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // C7450C pSecondSpecialData[0] = 0xC7; pSecondSpecialData[1] = 0x45; pSecondSpecialData[2] = 0x0C; ulSecondSpecialDataSize = 3;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D3D pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // B8 pSecondSpecialData[0] = 0xB8; ulSecondSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D3D pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // BB pSecondSpecialData[0] = 0xBB; ulSecondSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspCreateProcessNotifyRoutineAddress = SearchPspCreateProcessNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize); return pPspCreateProcessNotifyRoutineAddress;}
    根据特征码获取 PspCreateProcessNotifyRoutine 数组地址// 根据特征码获取 PspCreateProcessNotifyRoutine 数组地址PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCteateProcessNotifyRoutine = NULL; PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; // 先获取 PsSetCreateProcessNotifyRoutine 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine"); pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCteateProcessNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateProcessNotifyRoutineAddress; } // 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址 pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine, (PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateProcessNotifyRoutineAddress; } // 获取偏移数据, 并计算地址 lOffset = *(PLONG)pAddress; pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); // 最后, 查找 PspCreateProcessNotifyRoutine 地址 pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateProcessNotifyRoutineAddress; } // 获取地址#ifdef _WIN64 // 64 位先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);#else // 32 位直接获取地址 pPspCreateProcessNotifyRoutineAddress = *(PVOID *)pAddress;#endif return pPspCreateProcessNotifyRoutineAddress;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 PspCreateProcessNotifyRoutine 地址的流程,其中,不同系统的内存特征码是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    而且,不同位数系统中,对 PspCreateProcessNotifyRoutine 数组中的值解密方式也不相同,这个需要区分开来。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    附录Win7 32 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:83fa573b 8bff mov edi,edi83fa573d 55 push ebp83fa573e 8bec mov ebp,esp83fa5740 807d0c00 cmp byte ptr [ebp+0Ch],083fa5744 53 push ebx83fa5745 56 push esi83fa5746 57 push edi83fa5747 0f84fa000000 je nt!PspSetCreateProcessNotifyRoutine+0x10a (83fa5847)nt!PspSetCreateProcessNotifyRoutine+0x12:83fa574d 648b3524010000 mov esi,dword ptr fs:[124h]83fa5754 66ff8e84000000 dec word ptr [esi+84h]83fa575b 33db xor ebx,ebx83fa575d c7450ce032f583 mov dword ptr [ebp+0Ch],offset nt!PspCreateProcessNotifyRoutine (83f532e0)nt!PspSetCreateProcessNotifyRoutine+0x29:83fa5764 ff750c push dword ptr [ebp+0Ch]83fa5767 e894bb0d00 call nt!ExReferenceCallBackBlock (84081300)83fa576c 8bf8 mov edi,eax83fa576e 85ff test edi,edi83fa5770 7439 je nt!PspSetCreateProcessNotifyRoutine+0x6e (83fa57ab)nt!PspSetCreateProcessNotifyRoutine+0x37:83fa5772 8b4f08 mov ecx,dword ptr [edi+8]83fa5775 e8e31ef2ff call nt!ExGetCallBackBlockRoutine (83ec765d)83fa577a 3b4508 cmp eax,dword ptr [ebp+8]83fa577d 7522 jne nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x44:83fa577f 85c9 test ecx,ecx83fa5781 7509 jne nt!PspSetCreateProcessNotifyRoutine+0x4f (83fa578c)nt!PspSetCreateProcessNotifyRoutine+0x48:83fa5783 384d10 cmp byte ptr [ebp+10h],cl83fa5786 7519 jne nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x4d:83fa5788 eb08 jmp nt!PspSetCreateProcessNotifyRoutine+0x55 (83fa5792)nt!PspSetCreateProcessNotifyRoutine+0x4f:83fa578c 807d1000 cmp byte ptr [ebp+10h],083fa5790 740f je nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x55:83fa5792 8b450c mov eax,dword ptr [ebp+0Ch]83fa5795 57 push edi83fa5796 33c9 xor ecx,ecx83fa5798 e8abfeffff call nt!ExCompareExchangeCallBack (83fa5648)83fa579d 84c0 test al,al83fa579f 7547 jne nt!PspSetCreateProcessNotifyRoutine+0xab (83fa57e8)nt!PspSetCreateProcessNotifyRoutine+0x64:83fa57a1 8b450c mov eax,dword ptr [ebp+0Ch]83fa57a4 8bcf mov ecx,edi83fa57a6 e889bc0d00 call nt!ExDereferenceCallBackBlock (84081434)nt!PspSetCreateProcessNotifyRoutine+0x6e:83fa57ab 83450c04 add dword ptr [ebp+0Ch],483fa57af 43 inc ebx83fa57b0 83fb40 cmp ebx,40h83fa57b3 72af jb nt!PspSetCreateProcessNotifyRoutine+0x29 (83fa5764)nt!PspSetCreateProcessNotifyRoutine+0x78:83fa57b5 66ff8684000000 inc word ptr [esi+84h]83fa57bc 0fb78684000000 movzx eax,word ptr [esi+84h]83fa57c3 6685c0 test ax,ax83fa57c6 7516 jne nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x8b:83fa57c8 8d4640 lea eax,[esi+40h]83fa57cb 3900 cmp dword ptr [eax],eax83fa57cd 740f je nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x92:83fa57cf 6683be8600000000 cmp word ptr [esi+86h],083fa57d7 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x9c:83fa57d9 e8a714e9ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!PspSetCreateProcessNotifyRoutine+0xa1:83fa57de b87a0000c0 mov eax,0C000007Ah83fa57e3 e9c5000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0xab:83fa57e8 83c9ff or ecx,0FFFFFFFFh83fa57eb 807d1000 cmp byte ptr [ebp+10h],083fa57ef b8e433f583 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (83f533e4)83fa57f4 7405 je nt!PspSetCreateProcessNotifyRoutine+0xbe (83fa57fb)nt!PspSetCreateProcessNotifyRoutine+0xb9:83fa57f6 b8e033f583 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (83f533e0)nt!PspSetCreateProcessNotifyRoutine+0xbe:83fa57fb f00fc108 lock xadd dword ptr [eax],ecx83fa57ff 8d049de032f583 lea eax,nt!PspCreateProcessNotifyRoutine (83f532e0)[ebx*4]83fa5806 8bcf mov ecx,edi83fa5808 e827bc0d00 call nt!ExDereferenceCallBackBlock (84081434)83fa580d 66ff8684000000 inc word ptr [esi+84h]83fa5814 0fb78684000000 movzx eax,word ptr [esi+84h]83fa581b 6685c0 test ax,ax83fa581e 7516 jne nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xe3:83fa5820 8d4640 lea eax,[esi+40h]83fa5823 3900 cmp dword ptr [eax],eax83fa5825 740f je nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xea:83fa5827 6683be8600000000 cmp word ptr [esi+86h],083fa582f 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xf4:83fa5831 e84f14e9ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!PspSetCreateProcessNotifyRoutine+0xf9:83fa5836 8bcf mov ecx,edi83fa5838 e83a251700 call nt!ExWaitForCallBacks (84117d77)83fa583d 57 push edi83fa583e e8c88a0e00 call nt!AlpcpFreeBuffer (8408e30b)nt!PspSetCreateProcessNotifyRoutine+0x106:83fa5843 33c0 xor eax,eax83fa5845 eb66 jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x10a:83fa5847 807d1000 cmp byte ptr [ebp+10h],083fa584b 7413 je nt!PspSetCreateProcessNotifyRoutine+0x123 (83fa5860)nt!PspSetCreateProcessNotifyRoutine+0x110:83fa584d ff7508 push dword ptr [ebp+8]83fa5850 e82bb0fdff call nt!MmVerifyCallbackFunction (83f80880)83fa5855 85c0 test eax,eax83fa5857 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x123 (83fa5860)nt!PspSetCreateProcessNotifyRoutine+0x11c:83fa5859 b8220000c0 mov eax,0C0000022h83fa585e eb4d jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x123:83fa5860 33c0 xor eax,eax83fa5862 384510 cmp byte ptr [ebp+10h],al83fa5865 0f95c0 setne al83fa5868 50 push eax83fa5869 ff7508 push dword ptr [ebp+8]83fa586c e8a8fdffff call nt!ExAllocateCallBack (83fa5619)83fa5871 8bd8 mov ebx,eax83fa5873 85db test ebx,ebx83fa5875 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x141 (83fa587e)nt!PspSetCreateProcessNotifyRoutine+0x13a:83fa5877 b89a0000c0 mov eax,0C000009Ah83fa587c eb2f jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x141:83fa587e bee032f583 mov esi,offset nt!PspCreateProcessNotifyRoutine (83f532e0)83fa5883 33ff xor edi,edint!PspSetCreateProcessNotifyRoutine+0x148:83fa5885 6a00 push 083fa5887 8bcb mov ecx,ebx83fa5889 8bc6 mov eax,esi83fa588b e8b8fdffff call nt!ExCompareExchangeCallBack (83fa5648)83fa5890 84c0 test al,al83fa5892 7520 jne nt!PspSetCreateProcessNotifyRoutine+0x177 (83fa58b4)nt!PspSetCreateProcessNotifyRoutine+0x157:83fa5894 83c704 add edi,483fa5897 83c604 add esi,483fa589a 81ff00010000 cmp edi,100h83fa58a0 72e3 jb nt!PspSetCreateProcessNotifyRoutine+0x148 (83fa5885)nt!PspSetCreateProcessNotifyRoutine+0x165:83fa58a2 53 push ebx83fa58a3 e8638a0e00 call nt!AlpcpFreeBuffer (8408e30b)83fa58a8 b80d0000c0 mov eax,0C000000Dhnt!PspSetCreateProcessNotifyRoutine+0x170:83fa58ad 5f pop edi83fa58ae 5e pop esi83fa58af 5b pop ebx83fa58b0 5d pop ebp83fa58b1 c20c00 ret 0Chnt!PspSetCreateProcessNotifyRoutine+0x177:83fa58b4 33c9 xor ecx,ecx83fa58b6 41 inc ecx83fa58b7 807d1000 cmp byte ptr [ebp+10h],083fa58bb 7525 jne nt!PspSetCreateProcessNotifyRoutine+0x1a5 (83fa58e2)nt!PspSetCreateProcessNotifyRoutine+0x180:83fa58bd b8e433f583 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (83f533e4)83fa58c2 f00fc108 lock xadd dword ptr [eax],ecx83fa58c6 a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83fa58cb a802 test al,283fa58cd 0f8570ffffff jne nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x196:83fa58d3 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83fa58d8 f00fba2801 lock bts dword ptr [eax],183fa58dd e961ffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x1a5:83fa58e2 b8e033f583 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (83f533e0)83fa58e7 f00fc108 lock xadd dword ptr [eax],ecx83fa58eb a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83fa58f0 a804 test al,483fa58f2 0f854bffffff jne nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x1bb:83fa58f8 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83fa58fd f00fba2802 lock bts dword ptr [eax],283fa5902 e93cffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)
    Win7 64 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:fffff800`042be1b0 48895c2408 mov qword ptr [rsp+8],rbxfffff800`042be1b5 48896c2410 mov qword ptr [rsp+10h],rbpfffff800`042be1ba 4889742418 mov qword ptr [rsp+18h],rsifffff800`042be1bf 57 push rdifffff800`042be1c0 4154 push r12fffff800`042be1c2 4155 push r13fffff800`042be1c4 4156 push r14fffff800`042be1c6 4157 push r15fffff800`042be1c8 4883ec20 sub rsp,20hfffff800`042be1cc 4533e4 xor r12d,r12dfffff800`042be1cf 418ae8 mov bpl,r8bfffff800`042be1d2 4c8be9 mov r13,rcxfffff800`042be1d5 418d5c2401 lea ebx,[r12+1]fffff800`042be1da 413ad4 cmp dl,r12bfffff800`042be1dd 0f840e010000 je nt!PspSetCreateProcessNotifyRoutine+0x141 (fffff800`042be2f1)nt!PspSetCreateProcessNotifyRoutine+0x33:fffff800`042be1e3 65488b3c2588010000 mov rdi,qword ptr gs:[188h]fffff800`042be1ec 83c8ff or eax,0FFFFFFFFhfffff800`042be1ef 660187c4010000 add word ptr [rdi+1C4h],axfffff800`042be1f6 4c8d358395d6ff lea r14,[nt!PspCreateProcessNotifyRoutine (fffff800`04027780)]nt!PspSetCreateProcessNotifyRoutine+0x4d:fffff800`042be1fd 418bc4 mov eax,r12dfffff800`042be200 4d8d3cc6 lea r15,[r14+rax*8]fffff800`042be204 498bcf mov rcx,r15fffff800`042be207 e8f4ecedff call nt!ExReferenceCallBackBlock (fffff800`0419cf00)fffff800`042be20c 33d2 xor edx,edxfffff800`042be20e 488bf0 mov rsi,raxfffff800`042be211 483bc2 cmp rax,rdxfffff800`042be214 743d je nt!PspSetCreateProcessNotifyRoutine+0xa3 (fffff800`042be253)nt!PspSetCreateProcessNotifyRoutine+0x66:fffff800`042be216 488bc8 mov rcx,raxfffff800`042be219 e89eb4c0ff call nt!ExGetCallBackBlockRoutine (fffff800`03ec96bc)fffff800`042be21e 493bc5 cmp rax,r13fffff800`042be221 7523 jne nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x73:fffff800`042be223 48395110 cmp qword ptr [rcx+10h],rdxfffff800`042be227 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x80 (fffff800`042be230)nt!PspSetCreateProcessNotifyRoutine+0x79:fffff800`042be229 403aea cmp bpl,dlfffff800`042be22c 7407 je nt!PspSetCreateProcessNotifyRoutine+0x85 (fffff800`042be235)nt!PspSetCreateProcessNotifyRoutine+0x7e:fffff800`042be22e eb16 jmp nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x80:fffff800`042be230 403aea cmp bpl,dlfffff800`042be233 7411 je nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x85:fffff800`042be235 4c8bc6 mov r8,rsifffff800`042be238 498bcf mov rcx,r15fffff800`042be23b e8100ef5ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`042be240 33c9 xor ecx,ecxfffff800`042be242 3ac1 cmp al,clfffff800`042be244 7540 jne nt!PspSetCreateProcessNotifyRoutine+0xd6 (fffff800`042be286)nt!PspSetCreateProcessNotifyRoutine+0x96:fffff800`042be246 488bd6 mov rdx,rsifffff800`042be249 498bcf mov rcx,r15fffff800`042be24c e8bb2eeeff call nt!ExDereferenceCallBackBlock (fffff800`041a110c)fffff800`042be251 33d2 xor edx,edxnt!PspSetCreateProcessNotifyRoutine+0xa3:fffff800`042be253 4403e3 add r12d,ebxfffff800`042be256 4183fc40 cmp r12d,40hfffff800`042be25a 72a1 jb nt!PspSetCreateProcessNotifyRoutine+0x4d (fffff800`042be1fd)nt!PspSetCreateProcessNotifyRoutine+0xac:fffff800`042be25c 66019fc4010000 add word ptr [rdi+1C4h],bxfffff800`042be263 7517 jne nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xb5:fffff800`042be265 488d4750 lea rax,[rdi+50h]fffff800`042be269 483900 cmp qword ptr [rax],raxfffff800`042be26c 740e je nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xbe:fffff800`042be26e 663997c6010000 cmp word ptr [rdi+1C6h],dxfffff800`042be275 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xc7:fffff800`042be277 e834f5b6ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!PspSetCreateProcessNotifyRoutine+0xcc:fffff800`042be27c b87a0000c0 mov eax,0C000007Ahfffff800`042be281 e916010000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0xd6:fffff800`042be286 403ae9 cmp bpl,clfffff800`042be289 750a jne nt!PspSetCreateProcessNotifyRoutine+0xe5 (fffff800`042be295)nt!PspSetCreateProcessNotifyRoutine+0xdb:fffff800`042be28b f08305f196d6ffff lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`04027984)],0FFFFFFFFhfffff800`042be293 eb08 jmp nt!PspSetCreateProcessNotifyRoutine+0xed (fffff800`042be29d)nt!PspSetCreateProcessNotifyRoutine+0xe5:fffff800`042be295 f08305e396d6ffff lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`04027980)],0FFFFFFFFhnt!PspSetCreateProcessNotifyRoutine+0xed:fffff800`042be29d 418bc4 mov eax,r12dfffff800`042be2a0 488bd6 mov rdx,rsifffff800`042be2a3 498d0cc6 lea rcx,[r14+rax*8]fffff800`042be2a7 e8602eeeff call nt!ExDereferenceCallBackBlock (fffff800`041a110c)fffff800`042be2ac 66019fc4010000 add word ptr [rdi+1C4h],bxfffff800`042be2b3 7519 jne nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x105:fffff800`042be2b5 488d4750 lea rax,[rdi+50h]fffff800`042be2b9 483900 cmp qword ptr [rax],raxfffff800`042be2bc 7410 je nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x10e:fffff800`042be2be 33c0 xor eax,eaxfffff800`042be2c0 663987c6010000 cmp word ptr [rdi+1C6h],axfffff800`042be2c7 7505 jne nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x119:fffff800`042be2c9 e8e2f4b6ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!PspSetCreateProcessNotifyRoutine+0x11e:fffff800`042be2ce 33c0 xor eax,eaxfffff800`042be2d0 f0480fb11e lock cmpxchg qword ptr [rsi],rbxfffff800`042be2d5 740d je nt!PspSetCreateProcessNotifyRoutine+0x134 (fffff800`042be2e4)nt!PspSetCreateProcessNotifyRoutine+0x127:fffff800`042be2d7 483bc3 cmp rax,rbxfffff800`042be2da 7408 je nt!PspSetCreateProcessNotifyRoutine+0x134 (fffff800`042be2e4)nt!PspSetCreateProcessNotifyRoutine+0x12c:fffff800`042be2dc 488bce mov rcx,rsifffff800`042be2df e86cd4b9ff call nt!ExfWaitForRundownProtectionRelease (fffff800`03e5b750)nt!PspSetCreateProcessNotifyRoutine+0x134:fffff800`042be2e4 488bce mov rcx,rsifffff800`042be2e7 e864d7fbff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`042be2ec e9a9000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x141:fffff800`042be2f1 413aec cmp bpl,r12bfffff800`042be2f4 7419 je nt!PspSetCreateProcessNotifyRoutine+0x15f (fffff800`042be30f)nt!PspSetCreateProcessNotifyRoutine+0x146:fffff800`042be2f6 e8b51dfbff call nt!MmVerifyCallbackFunction (fffff800`042700b0)fffff800`042be2fb 413bc4 cmp eax,r12dfffff800`042be2fe 750a jne nt!PspSetCreateProcessNotifyRoutine+0x15a (fffff800`042be30a)nt!PspSetCreateProcessNotifyRoutine+0x150:fffff800`042be300 b8220000c0 mov eax,0C0000022hfffff800`042be305 e992000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x15a:fffff800`042be30a 488bd3 mov rdx,rbxfffff800`042be30d eb03 jmp nt!PspSetCreateProcessNotifyRoutine+0x162 (fffff800`042be312)nt!PspSetCreateProcessNotifyRoutine+0x15f:fffff800`042be30f 498bd4 mov rdx,r12nt!PspSetCreateProcessNotifyRoutine+0x162:fffff800`042be312 498bcd mov rcx,r13fffff800`042be315 e846a8fbff call nt!ExAllocateCallBack (fffff800`04278b60)fffff800`042be31a 488bf0 mov rsi,raxfffff800`042be31d 493bc4 cmp rax,r12fffff800`042be320 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x179 (fffff800`042be329)nt!PspSetCreateProcessNotifyRoutine+0x172:fffff800`042be322 b89a0000c0 mov eax,0C000009Ahfffff800`042be327 eb73 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x179:fffff800`042be329 418bfc mov edi,r12dfffff800`042be32c 4c8d354d94d6ff lea r14,[nt!PspCreateProcessNotifyRoutine (fffff800`04027780)]nt!PspSetCreateProcessNotifyRoutine+0x183:fffff800`042be333 8bc7 mov eax,edifffff800`042be335 4533c0 xor r8d,r8dfffff800`042be338 488bd6 mov rdx,rsifffff800`042be33b 498d0cc6 lea rcx,[r14+rax*8]fffff800`042be33f e80c0df5ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`042be344 413ac4 cmp al,r12bfffff800`042be347 7516 jne nt!PspSetCreateProcessNotifyRoutine+0x1af (fffff800`042be35f)nt!PspSetCreateProcessNotifyRoutine+0x199:fffff800`042be349 03fb add edi,ebxfffff800`042be34b 83ff40 cmp edi,40hfffff800`042be34e 72e3 jb nt!PspSetCreateProcessNotifyRoutine+0x183 (fffff800`042be333)nt!PspSetCreateProcessNotifyRoutine+0x1a0:fffff800`042be350 488bce mov rcx,rsifffff800`042be353 e8f8d6fbff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`042be358 b80d0000c0 mov eax,0C000000Dhfffff800`042be35d eb3d jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x1af:fffff800`042be35f 413aec cmp bpl,r12bfffff800`042be362 751c jne nt!PspSetCreateProcessNotifyRoutine+0x1d0 (fffff800`042be380)nt!PspSetCreateProcessNotifyRoutine+0x1b4:fffff800`042be364 f0011d1996d6ff lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`04027984)],ebxfffff800`042be36b 8b056f91d6ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`042be371 a802 test al,2fffff800`042be373 7525 jne nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1c5:fffff800`042be375 f00fba2d6291d6ff01 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],1fffff800`042be37e eb1a jmp nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1d0:fffff800`042be380 f0011df995d6ff lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`04027980)],ebxfffff800`042be387 8b055391d6ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`042be38d a804 test al,4fffff800`042be38f 7509 jne nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1e1:fffff800`042be391 f00fba2d4691d6ff02 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],2nt!PspSetCreateProcessNotifyRoutine+0x1ea:fffff800`042be39a 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x1ec:fffff800`042be39c 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff800`042be3a1 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff800`042be3a6 488b742460 mov rsi,qword ptr [rsp+60h]fffff800`042be3ab 4883c420 add rsp,20hfffff800`042be3af 415f pop r15fffff800`042be3b1 415e pop r14fffff800`042be3b3 415d pop r13fffff800`042be3b5 415c pop r12fffff800`042be3b7 5f pop rdifffff800`042be3b8 c3 ret
    Win8.1 32 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:819b987e 8bff mov edi,edi819b9880 55 push ebp819b9881 8bec mov ebp,esp819b9883 83ec10 sub esp,10h819b9886 53 push ebx819b9887 8bd9 mov ebx,ecx819b9889 895df4 mov dword ptr [ebp-0Ch],ebx819b988c 56 push esi819b988d 57 push edi819b988e 84d2 test dl,dl819b9890 0f8567020a00 jne nt! ?? ::NNGAKEGL::`string'+0x70493 (81a59afd)nt!PspSetCreateProcessNotifyRoutine+0x18:819b9896 33f6 xor esi,esi819b9898 33ff xor edi,edi819b989a 46 inc esi819b989b 385508 cmp byte ptr [ebp+8],dl819b989e 756e jne nt!PspSetCreateProcessNotifyRoutine+0x90 (819b990e)nt!PspSetCreateProcessNotifyRoutine+0x22:819b98a0 8bd7 mov edx,edint!PspSetCreateProcessNotifyRoutine+0x24:819b98a2 8bcb mov ecx,ebx819b98a4 e8a9000000 call nt!ExAllocateCallBack (819b9952)819b98a9 8bc8 mov ecx,eax819b98ab 894dfc mov dword ptr [ebp-4],ecx819b98ae 85c9 test ecx,ecx819b98b0 0f846b030a00 je nt! ?? ::NNGAKEGL::`string'+0x705b7 (81a59c21)nt!PspSetCreateProcessNotifyRoutine+0x38:819b98b6 b8c8158681 mov eax,offset nt!PspCreateProcessNotifyRoutine (818615c8)819b98bb 8bdf mov ebx,edi819b98bd 8945f8 mov dword ptr [ebp-8],eaxnt!PspSetCreateProcessNotifyRoutine+0x42:819b98c0 8bd1 mov edx,ecx819b98c2 8bc8 mov ecx,eax819b98c4 57 push edi819b98c5 e88e24d9ff call nt!ExCompareExchangeCallBack (8174bd58)819b98ca 84c0 test al,al819b98cc 751c jne nt!PspSetCreateProcessNotifyRoutine+0x6c (819b98ea)nt!PspSetCreateProcessNotifyRoutine+0x50:819b98ce 8b45f8 mov eax,dword ptr [ebp-8]819b98d1 83c304 add ebx,4819b98d4 8b4dfc mov ecx,dword ptr [ebp-4]819b98d7 83c004 add eax,4819b98da 8945f8 mov dword ptr [ebp-8],eax819b98dd 81fb00010000 cmp ebx,100h819b98e3 72db jb nt!PspSetCreateProcessNotifyRoutine+0x42 (819b98c0)nt!PspSetCreateProcessNotifyRoutine+0x67:819b98e5 e941030a00 jmp nt! ?? ::NNGAKEGL::`string'+0x705c1 (81a59c2b)nt!PspSetCreateProcessNotifyRoutine+0x6c:819b98ea 807d0800 cmp byte ptr [ebp+8],0819b98ee 752b jne nt!PspSetCreateProcessNotifyRoutine+0x9d (819b991b)nt!PspSetCreateProcessNotifyRoutine+0x72:819b98f0 b8541cb481 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (81b41c54)819b98f5 f00fc130 lock xadd dword ptr [eax],esi819b98f9 46 inc esi819b98fa a1441cb481 mov eax,dword ptr [nt!PspNotifyEnableMask (81b41c44)]819b98ff a802 test al,2819b9901 7437 je nt!PspSetCreateProcessNotifyRoutine+0xbc (819b993a)nt!PspSetCreateProcessNotifyRoutine+0x85:819b9903 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x87:819b9905 5f pop edi819b9906 5e pop esi819b9907 5b pop ebx819b9908 8be5 mov esp,ebp819b990a 5d pop ebp819b990b c20400 ret 4nt!PspSetCreateProcessNotifyRoutine+0x90:819b990e e86d000000 call nt!MmVerifyCallbackFunction (819b9980)819b9913 85c0 test eax,eax819b9915 742f je nt!PspSetCreateProcessNotifyRoutine+0xc8 (819b9946)nt!PspSetCreateProcessNotifyRoutine+0x99:819b9917 8bd6 mov edx,esi819b9919 eb87 jmp nt!PspSetCreateProcessNotifyRoutine+0x24 (819b98a2)nt!PspSetCreateProcessNotifyRoutine+0x9d:819b991b b8501cb481 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (81b41c50)819b9920 f00fc130 lock xadd dword ptr [eax],esi819b9924 46 inc esi819b9925 a1441cb481 mov eax,dword ptr [nt!PspNotifyEnableMask (81b41c44)]819b992a a804 test al,4819b992c 75d5 jne nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xb0:819b992e b8441cb481 mov eax,offset nt!PspNotifyEnableMask (81b41c44)819b9933 f00fba2802 lock bts dword ptr [eax],2819b9938 ebc9 jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xbc:819b993a b8441cb481 mov eax,offset nt!PspNotifyEnableMask (81b41c44)819b993f f00fba2801 lock bts dword ptr [eax],1819b9944 ebbd jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xc8:819b9946 b8220000c0 mov eax,0C0000022h819b994b ebb8 jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x70493:81a59afd 648b3524010000 mov esi,dword ptr fs:[124h]81a59b04 66ff8e3c010000 dec word ptr [esi+13Ch]81a59b0b 33ff xor edi,edi81a59b0d b8c8158681 mov eax,offset nt!PspCreateProcessNotifyRoutine (818615c8)81a59b12 897df8 mov dword ptr [ebp-8],edi81a59b15 8945fc mov dword ptr [ebp-4],eaxnt! ?? ::NNGAKEGL::`string'+0x704ae:81a59b18 8bc8 mov ecx,eax81a59b1a e85bb2c2ff call nt!ExReferenceCallBackBlock (81684d7a)81a59b1f 8bd8 mov ebx,eax81a59b21 85db test ebx,ebx81a59b23 7426 je nt! ?? ::NNGAKEGL::`string'+0x704e1 (81a59b4b)nt! ?? ::NNGAKEGL::`string'+0x704bb:81a59b25 8bcb mov ecx,ebx81a59b27 e86a67d1ff call nt!ExGetCallBackBlockContext (81770296)81a59b2c 8bd0 mov edx,eax81a59b2e e86d67d1ff call nt!ExGetCallBackBlockRoutine (817702a0)81a59b33 3b45f4 cmp eax,dword ptr [ebp-0Ch]81a59b36 7509 jne nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x704ce:81a59b38 85d2 test edx,edx81a59b3a 7557 jne nt! ?? ::NNGAKEGL::`string'+0x70529 (81a59b93)nt! ?? ::NNGAKEGL::`string'+0x704d2:81a59b3c 385508 cmp byte ptr [ebp+8],dl81a59b3f 7458 je nt! ?? ::NNGAKEGL::`string'+0x7052f (81a59b99)nt! ?? ::NNGAKEGL::`string'+0x704d7:81a59b41 8b4dfc mov ecx,dword ptr [ebp-4]81a59b44 8bd3 mov edx,ebx81a59b46 e8e7b1c2ff call nt!ExDereferenceCallBackBlock (81684d32)nt! ?? ::NNGAKEGL::`string'+0x704e1:81a59b4b 8b4df8 mov ecx,dword ptr [ebp-8]81a59b4e 8b45fc mov eax,dword ptr [ebp-4]81a59b51 41 inc ecx81a59b52 83c004 add eax,481a59b55 894df8 mov dword ptr [ebp-8],ecx81a59b58 8945fc mov dword ptr [ebp-4],eax81a59b5b 83f940 cmp ecx,40h81a59b5e 72b8 jb nt! ?? ::NNGAKEGL::`string'+0x704ae (81a59b18)nt! ?? ::NNGAKEGL::`string'+0x704f6:81a59b60 0fbf863c010000 movsx eax,word ptr [esi+13Ch]81a59b67 40 inc eax81a59b68 6689863c010000 mov word ptr [esi+13Ch],ax81a59b6f 6685c0 test ax,ax81a59b72 7515 jne nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x7050a:81a59b74 8d4670 lea eax,[esi+70h]81a59b77 3900 cmp dword ptr [eax],eax81a59b79 740e je nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x70511:81a59b7b 6639be3e010000 cmp word ptr [esi+13Eh],di81a59b82 7505 jne nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x7051a:81a59b84 e8fb79c8ff call nt!KiCheckForKernelApcDelivery (816e1584)nt! ?? ::NNGAKEGL::`string'+0x7051f:81a59b89 b87a0000c0 mov eax,0C000007Ah81a59b8e e972fdf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x70529:81a59b93 807d0800 cmp byte ptr [ebp+8],081a59b97 74a8 je nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x7052f:81a59b99 8b4dfc mov ecx,dword ptr [ebp-4]81a59b9c 33d2 xor edx,edx81a59b9e 53 push ebx81a59b9f e8b421cfff call nt!ExCompareExchangeCallBack (8174bd58)81a59ba4 84c0 test al,al81a59ba6 7499 je nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x7053e:81a59ba8 83c8ff or eax,0FFFFFFFFh81a59bab b9541cb481 mov ecx,offset nt!PspCreateProcessNotifyRoutineCount (81b41c54)81a59bb0 807d0800 cmp byte ptr [ebp+8],081a59bb4 7405 je nt! ?? ::NNGAKEGL::`string'+0x70551 (81a59bbb)nt! ?? ::NNGAKEGL::`string'+0x7054c:81a59bb6 b9501cb481 mov ecx,offset nt!PspCreateProcessNotifyRoutineExCount (81b41c50)nt! ?? ::NNGAKEGL::`string'+0x70551:81a59bbb f00fc101 lock xadd dword ptr [ecx],eax81a59bbf 8b45f8 mov eax,dword ptr [ebp-8]81a59bc2 8bd3 mov edx,ebx81a59bc4 8d0c85c8158681 lea ecx,nt!PspCreateProcessNotifyRoutine (818615c8)[eax*4]81a59bcb e862b1c2ff call nt!ExDereferenceCallBackBlock (81684d32)81a59bd0 0fbf863c010000 movsx eax,word ptr [esi+13Ch]81a59bd7 40 inc eax81a59bd8 6689863c010000 mov word ptr [esi+13Ch],ax81a59bdf 6685c0 test ax,ax81a59be2 7515 jne nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x7057a:81a59be4 8d4670 lea eax,[esi+70h]81a59be7 3900 cmp dword ptr [eax],eax81a59be9 740e je nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x70581:81a59beb 6639be3e010000 cmp word ptr [esi+13Eh],di81a59bf2 7505 jne nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x7058a:81a59bf4 e88b79c8ff call nt!KiCheckForKernelApcDelivery (816e1584)nt! ?? ::NNGAKEGL::`string'+0x7058f:81a59bf9 33f6 xor esi,esi81a59bfb 33c0 xor eax,eax81a59bfd 46 inc esi81a59bfe 8bce mov ecx,esi81a59c00 f00fb10b lock cmpxchg dword ptr [ebx],ecx81a59c04 85c0 test eax,eax81a59c06 740d je nt! ?? ::NNGAKEGL::`string'+0x705ab (81a59c15)nt! ?? ::NNGAKEGL::`string'+0x7059e:81a59c08 3bc6 cmp eax,esi81a59c0a 7409 je nt! ?? ::NNGAKEGL::`string'+0x705ab (81a59c15)nt! ?? ::NNGAKEGL::`string'+0x705a2:81a59c0c 8bd0 mov edx,eax81a59c0e 8bcb mov ecx,ebx81a59c10 e87d36ccff call nt!ExfWaitForRundownProtectionRelease (8171d292)nt! ?? ::NNGAKEGL::`string'+0x705ab:81a59c15 57 push edi81a59c16 53 push ebx81a59c17 e8f4a3ddff call nt!ExFreePoolWithTag (81834010)81a59c1c e9e2fcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt! ?? ::NNGAKEGL::`string'+0x705b7:81a59c21 b89a0000c0 mov eax,0C000009Ah81a59c26 e9dafcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x705c1:81a59c2b 57 push edi81a59c2c ff75fc push dword ptr [ebp-4]81a59c2f e8dca3ddff call nt!ExFreePoolWithTag (81834010)81a59c34 b80d0000c0 mov eax,0C000000Dh81a59c39 e9c7fcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)
    Win8.1 64 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutineFlow analysis was incomplete, some code may be missingnt!PspSetCreateProcessNotifyRoutine:fffff803`10775bc4 48895c2408 mov qword ptr [rsp+8],rbxfffff803`10775bc9 48896c2410 mov qword ptr [rsp+10h],rbpfffff803`10775bce 4889742418 mov qword ptr [rsp+18h],rsifffff803`10775bd3 57 push rdifffff803`10775bd4 4154 push r12fffff803`10775bd6 4155 push r13fffff803`10775bd8 4156 push r14fffff803`10775bda 4157 push r15fffff803`10775bdc 4883ec20 sub rsp,20hfffff803`10775be0 4533f6 xor r14d,r14dfffff803`10775be3 418ae8 mov bpl,r8bfffff803`10775be6 4c8be1 mov r12,rcxfffff803`10775be9 418d7e01 lea edi,[r14+1]fffff803`10775bed 84d2 test dl,dlfffff803`10775bef 0f85c52e0b00 jne nt! ?? ::NNGAKEGL::`string'+0x79eba (fffff803`10828aba)nt!PspSetCreateProcessNotifyRoutine+0x31:fffff803`10775bf5 4584c0 test r8b,r8bfffff803`10775bf8 7577 jne nt!PspSetCreateProcessNotifyRoutine+0xad (fffff803`10775c71)nt!PspSetCreateProcessNotifyRoutine+0x36:fffff803`10775bfa 418bd6 mov edx,r14dnt!PspSetCreateProcessNotifyRoutine+0x39:fffff803`10775bfd 498bcc mov rcx,r12fffff803`10775c00 e8ab000000 call nt!ExAllocateCallBack (fffff803`10775cb0)fffff803`10775c05 488bf0 mov rsi,raxfffff803`10775c08 4885c0 test rax,raxfffff803`10775c0b 0f84ef2f0b00 je nt! ?? ::NNGAKEGL::`string'+0x7a000 (fffff803`10828c00)nt!PspSetCreateProcessNotifyRoutine+0x4d:fffff803`10775c11 418bde mov ebx,r14dfffff803`10775c14 4c8d3de5b1deff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff803`10560e00)]nt!PspSetCreateProcessNotifyRoutine+0x57:fffff803`10775c1b 8bc3 mov eax,ebxfffff803`10775c1d 4533c0 xor r8d,r8dfffff803`10775c20 488bd6 mov rdx,rsifffff803`10775c23 498d0cc7 lea rcx,[r15+rax*8]fffff803`10775c27 e874b8c3ff call nt!ExCompareExchangeCallBack (fffff803`103b14a0)fffff803`10775c2c 84c0 test al,alfffff803`10775c2e 750c jne nt!PspSetCreateProcessNotifyRoutine+0x78 (fffff803`10775c3c)nt!PspSetCreateProcessNotifyRoutine+0x6c:fffff803`10775c30 03df add ebx,edifffff803`10775c32 83fb40 cmp ebx,40hfffff803`10775c35 72e4 jb nt!PspSetCreateProcessNotifyRoutine+0x57 (fffff803`10775c1b)nt!PspSetCreateProcessNotifyRoutine+0x73:fffff803`10775c37 e9ce2f0b00 jmp nt! ?? ::NNGAKEGL::`string'+0x7a00a (fffff803`10828c0a)nt!PspSetCreateProcessNotifyRoutine+0x78:fffff803`10775c3c 4084ed test bpl,bplfffff803`10775c3f 7545 jne nt!PspSetCreateProcessNotifyRoutine+0xc2 (fffff803`10775c86)nt!PspSetCreateProcessNotifyRoutine+0x7d:fffff803`10775c41 f0013d005a1b00 lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff803`1092b648)],edifffff803`10775c48 8b05ce541b00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`10775c4e a802 test al,2fffff803`10775c50 7450 je nt!PspSetCreateProcessNotifyRoutine+0xde (fffff803`10775ca2)nt!PspSetCreateProcessNotifyRoutine+0x8e:fffff803`10775c52 33c0 xor eax,eaxfffff803`10775c54 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff803`10775c59 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff803`10775c5e 488b742460 mov rsi,qword ptr [rsp+60h]fffff803`10775c63 4883c420 add rsp,20hfffff803`10775c67 415f pop r15fffff803`10775c69 415e pop r14fffff803`10775c6b 415d pop r13fffff803`10775c6d 415c pop r12fffff803`10775c6f 5f pop rdifffff803`10775c70 c3 retnt!PspSetCreateProcessNotifyRoutine+0xad:fffff803`10775c71 e87e000000 call nt!MmVerifyCallbackFunction (fffff803`10775cf4)fffff803`10775c76 85c0 test eax,eaxfffff803`10775c78 0f84782f0b00 je nt! ?? ::NNGAKEGL::`string'+0x79ff6 (fffff803`10828bf6)nt!PspSetCreateProcessNotifyRoutine+0xba:fffff803`10775c7e 488bd7 mov rdx,rdifffff803`10775c81 e977ffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x39 (fffff803`10775bfd)nt!PspSetCreateProcessNotifyRoutine+0xc2:fffff803`10775c86 f0013db7591b00 lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff803`1092b644)],edifffff803`10775c8d 8b0589541b00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`10775c93 a804 test al,4fffff803`10775c95 75bb jne nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)nt!PspSetCreateProcessNotifyRoutine+0xd3:fffff803`10775c97 f00fba2d7c541b0002 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],2fffff803`10775ca0 ebb0 jmp nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)nt!PspSetCreateProcessNotifyRoutine+0xde:fffff803`10775ca2 f00fba2d71541b0001 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],1fffff803`10775cab eba5 jmp nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)
    Win10 32 位 PspSetCreateProcessNotifyRoutinekd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:81e1c9fe 8bff mov edi,edi81e1ca00 55 push ebp81e1ca01 8bec mov ebp,esp81e1ca03 83ec10 sub esp,10h81e1ca06 53 push ebx81e1ca07 8bd9 mov ebx,ecx81e1ca09 895df4 mov dword ptr [ebp-0Ch],ebx81e1ca0c 56 push esi81e1ca0d 57 push edi81e1ca0e 84d2 test dl,dl81e1ca10 0f85d11b0a00 jne nt! ?? ::NNGAKEGL::`string'+0x69bdb (81ebe5e7)nt!PspSetCreateProcessNotifyRoutine+0x18:81e1ca16 33ff xor edi,edi81e1ca18 385508 cmp byte ptr [ebp+8],dl81e1ca1b 7560 jne nt!PspSetCreateProcessNotifyRoutine+0x7f (81e1ca7d)nt!PspSetCreateProcessNotifyRoutine+0x1f:81e1ca1d 8bd7 mov edx,edint!PspSetCreateProcessNotifyRoutine+0x21:81e1ca1f 8bcb mov ecx,ebx81e1ca21 e89a000000 call nt!ExAllocateCallBack (81e1cac0)81e1ca26 8945f8 mov dword ptr [ebp-8],eax81e1ca29 85c0 test eax,eax81e1ca2b 0f84d81c0a00 je nt! ?? ::NNGAKEGL::`string'+0x69cfd (81ebe709)nt!PspSetCreateProcessNotifyRoutine+0x33:81e1ca31 bb30dccb81 mov ebx,offset nt!PspCreateProcessNotifyRoutine (81cbdc30)81e1ca36 8bf7 mov esi,edint!PspSetCreateProcessNotifyRoutine+0x3a:81e1ca38 57 push edi81e1ca39 8bd0 mov edx,eax81e1ca3b 8bcb mov ecx,ebx81e1ca3d e8921dd6ff call nt!ExCompareExchangeCallBack (81b7e7d4)81e1ca42 84c0 test al,al81e1ca44 7516 jne nt!PspSetCreateProcessNotifyRoutine+0x5e (81e1ca5c)nt!PspSetCreateProcessNotifyRoutine+0x48:81e1ca46 8b45f8 mov eax,dword ptr [ebp-8]81e1ca49 83c604 add esi,481e1ca4c 83c304 add ebx,481e1ca4f 81fe00010000 cmp esi,100h81e1ca55 72e1 jb nt!PspSetCreateProcessNotifyRoutine+0x3a (81e1ca38)nt!PspSetCreateProcessNotifyRoutine+0x59:81e1ca57 e9b71c0a00 jmp nt! ?? ::NNGAKEGL::`string'+0x69d07 (81ebe713)nt!PspSetCreateProcessNotifyRoutine+0x5e:81e1ca5c 807d0800 cmp byte ptr [ebp+8],081e1ca60 7529 jne nt!PspSetCreateProcessNotifyRoutine+0x8d (81e1ca8b)nt!PspSetCreateProcessNotifyRoutine+0x64:81e1ca62 f0ff05d8bdff81 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineCount (81ffbdd8)]81e1ca69 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1ca6e a802 test al,281e1ca70 7435 je nt!PspSetCreateProcessNotifyRoutine+0xa9 (81e1caa7)nt!PspSetCreateProcessNotifyRoutine+0x74:81e1ca72 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x76:81e1ca74 5f pop edi81e1ca75 5e pop esi81e1ca76 5b pop ebx81e1ca77 8be5 mov esp,ebp81e1ca79 5d pop ebp81e1ca7a c20400 ret 4nt!PspSetCreateProcessNotifyRoutine+0x7f:81e1ca7d e86c000000 call nt!MmVerifyCallbackFunction (81e1caee)81e1ca82 85c0 test eax,eax81e1ca84 742d je nt!PspSetCreateProcessNotifyRoutine+0xb5 (81e1cab3)nt!PspSetCreateProcessNotifyRoutine+0x88:81e1ca86 33d2 xor edx,edx81e1ca88 42 inc edx81e1ca89 eb94 jmp nt!PspSetCreateProcessNotifyRoutine+0x21 (81e1ca1f)nt!PspSetCreateProcessNotifyRoutine+0x8d:81e1ca8b f0ff05d4bdff81 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineExCount (81ffbdd4)]81e1ca92 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1ca97 a804 test al,481e1ca99 75d7 jne nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0x9d:81e1ca9b b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1caa0 f00fba2802 lock bts dword ptr [eax],281e1caa5 ebcb jmp nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0xa9:81e1caa7 b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1caac f00fba2801 lock bts dword ptr [eax],181e1cab1 ebbf jmp nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0xb5:81e1cab3 b8220000c0 mov eax,0C0000022h81e1cab8 ebba jmp nt!PspSetCreateProcessNotifyRoutine+0x76 (81e1ca74)
    Win10 64 位 PspSetCreateProcessNotifyRoutinekd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:fffff800`a09a4530 48895c2408 mov qword ptr [rsp+8],rbxfffff800`a09a4535 48896c2410 mov qword ptr [rsp+10h],rbpfffff800`a09a453a 4889742418 mov qword ptr [rsp+18h],rsifffff800`a09a453f 57 push rdifffff800`a09a4540 4154 push r12fffff800`a09a4542 4155 push r13fffff800`a09a4544 4156 push r14fffff800`a09a4546 4157 push r15fffff800`a09a4548 4883ec20 sub rsp,20hfffff800`a09a454c 8ada mov bl,dlfffff800`a09a454e 8bf2 mov esi,edxfffff800`a09a4550 d0eb shr bl,1fffff800`a09a4552 4c8be1 mov r12,rcxfffff800`a09a4555 80e301 and bl,1fffff800`a09a4558 f6c201 test dl,1fffff800`a09a455b 0f8559770900 jne nt! ?? ::NNGAKEGL::`string'+0x6752a (fffff800`a0a3bcba)nt!PspSetCreateProcessNotifyRoutine+0x31:fffff800`a09a4561 84db test bl,blfffff800`a09a4563 7573 jne nt!PspSetCreateProcessNotifyRoutine+0xa8 (fffff800`a09a45d8)nt!PspSetCreateProcessNotifyRoutine+0x35:fffff800`a09a4565 488bd6 mov rdx,rsifffff800`a09a4568 498bcc mov rcx,r12fffff800`a09a456b e8a0000000 call nt!ExAllocateCallBack (fffff800`a09a4610)fffff800`a09a4570 488bf0 mov rsi,raxfffff800`a09a4573 4885c0 test rax,raxfffff800`a09a4576 0f8400780900 je nt! ?? ::NNGAKEGL::`string'+0x675ec (fffff800`a0a3bd7c)nt!PspSetCreateProcessNotifyRoutine+0x4c:fffff800`a09a457c 33ff xor edi,edifffff800`a09a457e 4c8d3dfb0bdfff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff800`a0795180)]nt!PspSetCreateProcessNotifyRoutine+0x55:fffff800`a09a4585 498d0cff lea rcx,[r15+rdi*8]fffff800`a09a4589 4533c0 xor r8d,r8dfffff800`a09a458c 488bd6 mov rdx,rsifffff800`a09a458f e8fce9bfff call nt!ExCompareExchangeCallBack (fffff800`a05a2f90)fffff800`a09a4594 84c0 test al,alfffff800`a09a4596 750c jne nt!PspSetCreateProcessNotifyRoutine+0x74 (fffff800`a09a45a4)nt!PspSetCreateProcessNotifyRoutine+0x68:fffff800`a09a4598 ffc7 inc edifffff800`a09a459a 83ff40 cmp edi,40hfffff800`a09a459d 72e6 jb nt!PspSetCreateProcessNotifyRoutine+0x55 (fffff800`a09a4585)nt!PspSetCreateProcessNotifyRoutine+0x6f:fffff800`a09a459f e9e2770900 jmp nt! ?? ::NNGAKEGL::`string'+0x675f6 (fffff800`a0a3bd86)nt!PspSetCreateProcessNotifyRoutine+0x74:fffff800`a09a45a4 84db test bl,blfffff800`a09a45a6 7540 jne nt!PspSetCreateProcessNotifyRoutine+0xb8 (fffff800`a09a45e8)nt!PspSetCreateProcessNotifyRoutine+0x78:fffff800`a09a45a8 f0ff051d481d00 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`a0b78dcc)]fffff800`a09a45af 8b0583441d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a45b5 a802 test al,2fffff800`a09a45b7 744b je nt!PspSetCreateProcessNotifyRoutine+0xd4 (fffff800`a09a4604)nt!PspSetCreateProcessNotifyRoutine+0x89:fffff800`a09a45b9 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x8b:fffff800`a09a45bb 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff800`a09a45c0 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff800`a09a45c5 488b742460 mov rsi,qword ptr [rsp+60h]fffff800`a09a45ca 4883c420 add rsp,20hfffff800`a09a45ce 415f pop r15fffff800`a09a45d0 415e pop r14fffff800`a09a45d2 415d pop r13fffff800`a09a45d4 415c pop r12fffff800`a09a45d6 5f pop rdifffff800`a09a45d7 c3 retnt!PspSetCreateProcessNotifyRoutine+0xa8:fffff800`a09a45d8 e837020000 call nt!MmVerifyCallbackFunction (fffff800`a09a4814)fffff800`a09a45dd 85c0 test eax,eaxfffff800`a09a45df 7584 jne nt!PspSetCreateProcessNotifyRoutine+0x35 (fffff800`a09a4565)nt!PspSetCreateProcessNotifyRoutine+0xb1:fffff800`a09a45e1 b8220000c0 mov eax,0C0000022hfffff800`a09a45e6 ebd3 jmp nt!PspSetCreateProcessNotifyRoutine+0x8b (fffff800`a09a45bb)nt!PspSetCreateProcessNotifyRoutine+0xb8:fffff800`a09a45e8 f0ff05d9471d00 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`a0b78dc8)]fffff800`a09a45ef 8b0543441d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a45f5 a804 test al,4fffff800`a09a45f7 75c0 jne nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)nt!PspSetCreateProcessNotifyRoutine+0xc9:fffff800`a09a45f9 f00fba2d36441d0002 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],2fffff800`a09a4602 ebb5 jmp nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)nt!PspSetCreateProcessNotifyRoutine+0xd4:fffff800`a09a4604 f00fba2d2b441d0001 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],1fffff800`a09a460d ebaa jmp nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)
    Win7 32 位 PsSetCreateProcessNotifyRoutinelkd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:83fa5720 8bff mov edi,edi83fa5722 55 push ebp83fa5723 8bec mov ebp,esp83fa5725 6a00 push 083fa5727 ff750c push dword ptr [ebp+0Ch]83fa572a ff7508 push dword ptr [ebp+8]83fa572d e809000000 call nt!PspSetCreateProcessNotifyRoutine (83fa573b)83fa5732 5d pop ebp83fa5733 c20800 ret 8
    Win7 64 位 PsSetCreateProcessNotifyRoutinelkd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff800`042be3c0 4533c0 xor r8d,r8dfffff800`042be3c3 e9e8fdffff jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`042be1b0)
    Win8.1 32 位 PsSetCreateProcessNotifyRoutinelkd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:811617d2 8bff mov edi,edi811617d4 55 push ebp811617d5 8bec mov ebp,esp811617d7 8a550c mov dl,byte ptr [ebp+0Ch]811617da 8b4d08 mov ecx,dword ptr [ebp+8]811617dd 6a00 push 0811617df e89a000000 call nt!PspSetCreateProcessNotifyRoutine (8116187e)811617e4 5d pop ebp811617e5 c20800 ret 8
    Win8.1 64 位 PsSetCreateProcessNotifyRoutinelkd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff803`10775b00 4533c0 xor r8d,r8dfffff803`10775b03 e9bc000000 jmp nt!PspSetCreateProcessNotifyRoutine (fffff803`10775bc4)
    Win10 32 位 PsSetCreateProcessNotifyRoutinekd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:81e1c8d4 8bff mov edi,edi81e1c8d6 55 push ebp81e1c8d7 8bec mov ebp,esp81e1c8d9 8a550c mov dl,byte ptr [ebp+0Ch]81e1c8dc 8b4d08 mov ecx,dword ptr [ebp+8]81e1c8df 6a00 push 081e1c8e1 e818010000 call nt!PspSetCreateProcessNotifyRoutine (81e1c9fe)81e1c8e6 5d pop ebp81e1c8e7 c20800 ret 8
    Win10 64 位 PsSetCreateProcessNotifyRoutinekd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff800`a09a4460 33c0 xor eax,eaxfffff800`a09a4462 84d2 test dl,dlfffff800`a09a4464 448d4001 lea r8d,[rax+1]fffff800`a09a4468 410f45c0 cmovne eax,r8dfffff800`a09a446c 8bd0 mov edx,eaxfffff800`a09a446e e9bd000000 jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`a09a4530)
    5 留言 2019-03-16 10:05:20 奖励12点积分
  • 数据结构——二叉排序树

    1.定义(BSTTree)二叉排序树或者是一棵空树,或者是具有如下特性的二叉树:

    若它的左子树不空,则左子树上所有结点的值均小于根结点的值
    若它的右子树不空,则右子树上所有结点的值均大于根结点的值
    它的左、右子树也都分别是二叉排序树。

    注:只要有一个结点不满足就不是二叉排序树
    通常,取二叉链表作为二叉排序树的存储结构
    typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; // 左右孩子指针} BiTNode, *BiTree;
    2.二叉排序树的查找算法
    若二叉排序树为空,则查找不成功
    否则:

    若给定值等于根结点的关键字,则查找成功若给定值小于根结点的关键字,则继续在左子树上进行查找若给定值大于根结点的关键字,则 继续在右子树上进行查找

    Status SearchBST (BiTree T, KeyType key, BiTree f, BiTree &p ) { // 在根指针 T 所指二叉排序树中递归地查找其 // 关键字等于 key 的数据元素,若查找成功, // 则返回指针 p 所指该数据元素的结点,并返回 // 函数值为 TRUE; if (!T){ p = f; return FALSE; // 查找不成功 } else if ( EQ(key, T->data.key) ){ p = T; return TRUE;//查找成功 } else if ( LT(key, T->data.key) ){ SearchBST (T->lchild, key, T, p ); // 在左子树中继续查找 } else{ SearchBST (T->rchild, key, T, p ); // 在右子树中继续查找 }} // SearchBST
    3.二叉排序树的插入算法
    根据动态查找表的定义,“插入”操作在查找不成功时才进行
    若二叉排序树为空树,则新插入的结点为新的根结点
    否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到

    4.二叉排序树的删除算法和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性。
    可分三种情况讨论:

    被删除的结点是叶子结点:其双亲结点中相应指针域的值改为“空”
    被删除的结点只有左子树或者只有右子树:其双亲结点的相应指针域的值改为 “指向被删除结点的左子树或右子树”
    被删除的结点既有左子树,也有右子树:以其中序前驱(左子树的最右无右子树的结点)替代之,然后再删除该前驱结点

    结论

    一个无序序列(10,18,3,8,12,2,7,3)可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程
    每次插入的新结点都是二叉排序树的叶子结点,在进行插入操作时,不必移动其它结点,仅需修改某个结点的指针由空变为非空即可。这就相当于在一个有序序列上插入一个元素而没有移动其它元素。这个特性告诉我们,对于需要经常插入和删除记录的有序表采用二叉排序树结构更为合适。

    5.查找性能分析
    值相同的 n 个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同
    不失一般性,假设长度为 n 的序列中有 k 个关键字小于第一个关键字,则必有 n-k-1 个关键字大于第一个关键字,由它构造的二叉排序树平均查找长度是 n 和 k 的函数
    2 留言 2020-10-23 08:51:23 奖励38点积分
  • 编译原理实验之词法分析器、LL(1)语法分析器、LR(1)语法分析器

    一、词法分析器1.1 词法分析器底层部分1.1.1 匹配关键字或标记符自动机1.1.2 匹配无符号数自动机1.1.3 匹配行间注释自动机1.1.4 匹配运算符、分界符自动机1.1.5 匹配字符、字符串自动机1.2 词法分析器前端部分二、LL1语法分析器2.1 first集2.2 follow集2.3 预测分析表2.4 程序设计三、LR1语法分析器3.1 产生式Prod类3.2 项目集Item类3.3 LR类3.4 空串处理3.5 PL0文法测试3.6 for auto & Bug3.7 前端处理一、词法分析器
    这是编译原理的第一个实验,算是热身实验吧,确实很简单,花了一晚上就把词法分析器底层部分写完了,老师比较喜欢图形界面,后来又加了前端,也就是现在看到的效果。实验要求能够匹配出关键字、标记符、运算符、分界符、无符号数,后来我又添加了一部分,现在能匹配出字符/字符串、行间注释。
    1.1 词法分析器底层部分底层部分是用C++写的,大体思路就是,每次从stdin读取出一行,然后从这行的第一个字符开始匹配。匹配完了,读取下一行,行号+1。
    1.1.1 匹配关键字或标记符自动机若当前匹配到的字符i是*字母*,就继续匹配下一个字符,直到下个字符j不是*字母*或者*数字*或者’_‘为止,则截取字符串(i, j),判断这个字符串是不是关键字或者标记符,否则错误处理。如果是标记符,将其存入标记符表中,其在标记符表的位置即为其Pointer。最后输出相关信息。
    1.1.2 匹配无符号数自动机若当前匹配到的字符i是*数字*,就继续匹配下一个字符,直到下个字符j不是*字母*或者*数字*或者’_‘为止,则截取字符串(i, j),判断这个字符串是不是无符号数。如果是无符号数,将其存入常数表中,其在常数表的位置即为其Pointer,若不是无符号数则当错误处理。最后输出相关信息。
    1.1.3 匹配行间注释自动机若当前匹配到的字符i是’/‘并且下一个字符也是’/‘,就继续匹配下一个字符,直到下个字符j不是*空白*(空格或tab)为止,则截取字符串(j, lineEnd),作为注释处理。
    1.1.4 匹配运算符、分界符自动机我将运算符和分界符放到一个optrs表中,若当前匹配到的字符i是optrs的元素,就继续匹配下一个字符,直到下个字符j不是optrs的元素或者运算符类型与字符i不一样或者就是分界符为止,则截取字符串(i, j),判断这个字符串是不是optrs的元素,并确定其类型,其Pointer为该运算符或分界符在optrs的位置,输出相关信息,否则当错误处理。最后输出相关信息。
    1.1.5 匹配字符、字符串自动机若当前匹配到的字符i是"或者',就继续匹配下一个字符,直到下个字符j是字符i为止,则截取字符串(i, j),判断这个字符串是字符还是字符串。如果是的话,将其存入字符或字符串表中,其在相应表的位置即为其Pointer,若不符合则错误处理。最后输出相关信息。
    1.2 词法分析器前端部分前端部分我比较认真,我用html+js+php来实现图形界面,之所以写成网页,是因为我不想写native app,我也没GUI开发环境。在互联网时代,webapp是趋势,谁还写本地客户端啊,况且带个几十M的GUI库实在是麻烦。
    于是我的分析器底层部分设计成输出json格式,然后利用管道将C++程序与php程序进行数据传送。前端只要用js输数据取数据渲染页面即可。
    在这之中发现一个问题,如果*输入文本*一长,渲染效率大大降低,因为我用append方法一个个加元素的。解决方案是最后转换为字符串一次性输出渲染,效率提高了不少。具体可看这个优化片段:优化js运行效率。
    原文地址:
    http://www.netcan666.com/2016/10/07/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%E4%B9%8B%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90%E5%99%A8%E8%AE%BE%E8%AE%A1/
    二、LL1语法分析器
    我写这篇博文写了好几天了,描述比较凌乱,建议大家还是看书吧,或者直接看我程序设计部分。一定要搞懂first集和follow集的求法,不然写程序也会遇到困难的,这里有篇不错的关于求first和follow集的论文,推荐看看:https://www.cs.virginia.edu/~weimer/2008-415/reading/FirstFollowLL.pdf
    LL(1)文法主要是求first集和follow集,个人觉得follow集比较麻烦点,然后就是用这两个集合求预测分析表。
    2.1 first集first集就是求文法符号串α\alphaα的开始符号集合first(α)first(\alpha)first(α),例如有以下文法G(E):

    用例子还是比较好说明的,很容易求出各非终结符的first集。

    这里给出first集的一般描述。

    也就是说,如果非终结符E有产生式TβT\betaTβ,那么它们first(E)=first(T),这个不难理解,因为我(E)能推出你(T),你又能推出它(开始符号,终结符),那我们都能推出它。
    first集的作用是,当用来匹配开头为a的文本时,有产生式A→Xα∣YβA\to X\alpha|Y\betaA→Xα∣Yβ,若a∈Xa\in Xa∈X,则选择候选式A→XαA\to X\alphaA→Xα,若a∈Ya\in Ya∈Y,选择A→YβA\to Y\betaA→Yβ,说了那么多,我只想说,不是用first(A)这个来匹配a,而是用它的候选式first(X)或者first(Y)来匹配。
    用上面的例子来说,匹配(233,因为( ∈\in∈ first(TE’),应该选择E’ →\to→ +TE’ 这个候选式。
    2.2 follow集follow集比较抽象,它用来解决这个问题的,如果非终结符E’的first(E’)含有ε\varepsilonε,那么选择会不确定,比如说G(E),

    很容易求得

    匹配开头为a,因为a ∈\in∈ first(Tb),选择 E →\to→ Tb 产生式,匹配开头为c,因为 c ∈\in∈ first(F),选择 E →\to→ F产生式。然而当我匹配b时,因为b ∉\notin∉ first(Tb) ∧∉\land \notin∧∉ first(F),这时候就不知道选择哪个产生式了,但是因为ε∈\varepsilon \inε∈ first(Tb),且E →\to→ Tb|F ⇒\Rightarrow⇒ (a|ε\varepsilonε)b|F⇒\Rightarrow⇒ ab|b|F,应该选择E →\to→ Tb的,这说明了first集的不足,从而引进follow集。
    给出定义,follow(A)即为非终结符A后面可能接的符号集。
    至于怎么求follow(A),我就直接摘抄PPT的定义吧,然后在说明。
    连续使用下面的规则,直至每个follow不再增大为止。

    对于文法的开始符号S,置#于follow(S)中
    若A→αBβA \to \alpha B\betaA→αBβ是一个产生式,则把first(β)\εfirst(\beta)\backslash {\varepsilon }first(β)\ε加至follow(B)中
    若A→αBA\to \alpha BA→αB是一个产生式,或A→αBβA\to \alpha B\betaA→αBβ是一个产生式而ε∈FIRST(β)\varepsilon\in FIRST(\beta)ε∈FIRST(β),则把follow(A)加至follow(B)中

    第三条规则可以这么理解,因为B后面啥都没,A又能推出B,所以应该把A后面的符号(follow(A))加入follow(B)中。需要注意的是,follow集不含\varepsilon。
    所以要求某个非终结符的follow集,只要在产生式右边中找,然后根据它后面一个符号按照上述规则计算就行了。
    2.3 预测分析表求得first集和follow集后,求分析表就比较容易了。这里简单说下构建方法。
    对文法的每个产生式,执行下面步骤。

    对first(α)first(\alpha)first(α)的每个终结符a,将候选式A→αA\to \alphaA→α加入M[A, a]
    如果ε∈first(α)\varepsilon\in first(\alpha)ε∈first(α),把follow(A)的每个终结符b(包括#),把A→αA\to \alphaA→α加入M[A, b]。

    2.4 程序设计代码的核心部分就是求first集和follow集了,这也是程序的精髓所在。
    首先我约定字符@代表ε\varepsilonε,因为键盘上没那个符号,所以随便找了个合适的符号代替。约定单个大写字母代表非终结符,小写字母或某些符号代表终结符。
    然后设计一个叫做产生式的类Prod,它的构造函数接受一个字符串,字符串描述了一个产生式。然后分割字符串,把它的非终结符存入noTerminal成员中,候选式存入selection集合中。
    接着设计一个叫LL1的类,也就是核心部分了,它提供了求first、follow、分析表等方法。因为first集和follow集可能会求未知表达式的first集和follow集,比如说A->B,B->C,欲求first(A),需求first(B),欲求first(B),需求first(C),从而确定了这两种集合求法只能用递归来求,这也是我所能想到的最简单求法,可以参考我代码。
    然而现在(2016/10/21)补充下,经过反复调教,first集都重写了5次,而follow集是不能用递归来求的,因为有可能出现这种情况:

    求follow(A)需要first(S)和follow(S),递归求follow(S)需要follow(A),然而这样就进入了死递归。所以最后我改写成5个循环搞定。
    求出了first和follow,剩下的就好办了。至于图形界面,和上次一样,套了个php,通过php传json数据到前端,前端输入数据取数据,渲染页面。那颗语法树的画法,通过前序遍历画得。
    原文地址:
    http://www.netcan666.com/2016/10/09/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%E4%B9%8BLL-1-%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90/
    三、LR1语法分析器
    先来吐槽下,据说这个实验是最难的一个实验,当然也是最后的一个实验了。在我们实验室上一届学长只有一个人写出来,可见其难度。
    然而我并不觉得有什么难的,当我上课听懂老师讲LR语法分析的时候,我就疑惑了,难点在哪?学长说难在自动机的构建上,自动机比较难拍,不好用数据结构来描述。这当然给了我巨大信心,回去不到一天把LR分析器核心拍出来了,在没有参考任何代码、龙书,仅看着教科书上的算法来写的。
    3.1 产生式Prod类我来说下具体是怎么实现的吧,用面向对象来写比较好写,绝对比面向过程好写。先来看看我设计的最小的类Prod(为减小篇幅已删除无关紧要的函数)。
    class Prod { // 这里是存放形如X->abc的形式,不存在多个候选式 private: char noTerminal; // 产生式左部非终结符名字 string right; // 产生式右部 set<char> additionalVt; // 附加终结符 friend bool operator == (const Prod &a, const Prod &b) { return a.noTerminal == b.noTerminal && a.right == b.right; } friend bool operator == (const Prod &a, char c) { return a.noTerminal == c; } public: Prod(const char &noTerminal, const string& right, const set<char>& additionalVt): noTerminal(noTerminal), right(right), additionalVt(additionalVt) {}};
    这个类是存放产生式的,存放形如A->Bc,这里的noTerminal就是左边的A,right就是右边的Bc,而additionalVt是LR(1)的项目集的搜索符,长度为1所以叫LR(1)。我重载了==符号,后面用来搜索项目集/文法中是否有这个产生式简直不能再方便,构造函数也能快速构造产生式,无疑为后面项目集中插入产生式提供了方便。
    3.2 项目集Item类class Item { // 项目集 private: vector<Prod> prods; // 项目集产生式 static set<char> Vn; // 非终结符 static set<char> Vt; // 终结符 static set<char> Symbol; // 所有符号 friend bool operator == (const Item &a, const Item &b) { if(a.prods.size() != b.prods.size()) return false; else { for(const auto& p: a.prods) {// 选择a的每个产生式 auto it = find(b.prods.begin(), b.prods.end(), p); if(it == b.prods.end()) // 找不到 return false; else {// 找到的话判断附加终结符是否都相等 if(p.additionalVt != it->additionalVt) return false; } } return true; } } public: void add(const string &prod);};
    项目集Item类中,prods向量存放这个项目集的所有项目(即产生式+搜索符),而Vn/Vt集合存放了所有产生式的非终结符/终结符,Symbol仅仅是Vn/Vt的并集,为后面GOTO函数枚举符号提供了方便,add方法为项目集插入项目。这里最重要的就是重载==符号了,它是判断两个项目集是否相等的关键,判断也很简单,首先判断两个项目集的项目数量是否相等,若相等进一步判断是否所有的项目都相等,这里就展现了前面重载==的优点(当然还需要判断搜索符是否也都相等)。能判断两个项目集是否相等就好办了,后面求项目集规范族插入项目就简单了。
    还需要注意的一点,Prod类已经重载==了,为啥项目集prods用向量来存,而不是直接用集合来存?这样就不用判等了,还能二分查找提高了查询效率。但是我考虑到用集合来存的话,会按照字典序来排,但是这样还要重载<,会打乱产生式的顺序,所以我后面的LR类来存放文法产生式、项目集规范族也是用向量来存(插入的时候判等就是了),而不是集合,以免打乱了顺序。
    3.3 LR类class LR { private: Item G; // 文法G enum actionStat{ ACCEPT=0, SHIFT, REDUCE, }; vector<Item> C; // 项目集规范族 map<pair<int, char>, int> GOTO; // goto数组,项目集<int, int>=char map<pair<int, char>, pair<actionStat, int> > ACTION; // Action数组,Action[(i, a)]=(s|r)j map<char, set<char> > FIRST; // first集 set<char> first(const string &s); // 求first集 vector<char> inStr; // 输入串/栈 vector<int> status; // 状态栈 vector<char> parse; // 分析栈 Item closure(Item I); // 求该项目的闭包 Item Goto(const Item& I, char X); // 求I经过X到达的项目集 void items(); // 求项目集状态机DFA!! public: void add(const string &s); // 添加产生式 void parser(); // LR(1)分析};
    这是最后一个类了,重点说说。G用来存放输入的文法,把它看成一个项目集吧。C是项目集规范族,也就是项目集的集合,用向量来存。actionStat为枚举类型,用于表示ACTION的动作类型。GOTO用于存放自动机DFA上边,边w(i, j)=X,ACTION用于存放动作,Action[(i, X)] = ((s|r)j)|acc,即当状态i遇到终结符X的时候采取的动作,移进/规约/接受。FIRST集合存放各个非终结符的FIRST集合,first方法求集合会记忆化存储到FIRST集合里。接下来就是那三个栈了,还有Closure、Goto、items、parser这几个方法了,书上有我就不细讲了,设计好这几个数据结构,写起来会轻松很多。
    3.4 空串处理那时候我的分析器还不能处理形如A→εA\to \varepsilonA→ε的产生式,书上、指导书给的文法,都没这类产生式。我就想,是不是LR不能处理空串啊?因为LR的DFA构建过程中,如果引出ε\varepsilonε这条边,那就是NFA了,这样还要把它化为DFA,那就很麻烦了。请教了一下李老师,老师说处理A→εA\to \varepsilonA→ε项目的时候,项目直接写成A→A\toA→ .,也就是说求GOTO函数的时候不要把ε\varepsilonε当终结符/非终结符处理,不要引出ε\varepsilonε边。
    恍然大悟,回去修改了下程序,果然能处理这类产生式了。
    3.5 PL0文法测试后来有人给了一组数据,即PL0文法,测试失败。这里给下数据,做了点小修改,因为有些符号和我程序内部符号冲突了,所以只是做了简单的替换。
    A->B,B->CEFHB->HB->CHB->EHB->FHB->CFHB->CEHB->EFHC->cY;D->b=aE->dX;F->GB;G->eb;H->IH->RH->TH->SH->UH->VH->JI->btLJ->fWgK->LQLK->hLL->LOML->ML->-ML->+MM->MPNM->NN->bN->aN->(L)O->+O->-P->*P->/Q->=Q->%Q-><Q->rQ->>Q->sR->pKqHS->mbT->nKoHU->i(X)V->j(Z)W->W;HW->HX->X,bX->bY->Y,DY->DZ->Z,LZ->L
    我调试了一下程序,发现2个比较严重的bug,有一个和程序无关紧要的bug,后面再说,这里先说下其中的一个bug,就是在求first集的bug。
    如果文法产生式有直接左递归的话,那么就会死递归爆栈,所以我对first集做了下简单的修改,就是遇到直接左递归,忽略掉。
    另一个bug也处理好了,PL0测试通过,近300个状态。
    3.6 for auto & Bug这个bug隐藏较深,就是我用auto引用类型引用容器中的元素,然后又在容器中插入元素,那么这个引用就会失效,换成迭代器也没达到预期效果,反而更糟,具体如下。
    #include <iostream>#include <vector>using namespace std;int main() { vector<int> v={1, 2, 3}; for(const auto &i: v) { printf("i=%d\n", i); v.push_back(5); printf("i=%d\n", i); } return 0;}
    只是简单地插入一个元素而已,输出结果却是这样的。
    i=1i=0i=0i=0i=3i=3
    i=0是哪来的。。。这个没找出原因,最后简单的用for(int i=0; ...)来替代处理了。
    现在找到原因了,看了下cppreference.com关于push_back的定义,有这么一句话。

    If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.

    就是说如果vector插入元素的size()大于capacity()的话,所有迭代器、引用都会无效,否则只是最后一个元素的迭代器/引用无效。这个可能和vector内存分配管理有关。
    然后就是打印了下vector的大小,发现刚开始size()==capacity()的,所以一插入元素就会出问题,用reserve()简单的把capacity()调大就没问题了。
    3.7 前端处理核心程序写完了,最后就是把它展现出来,数据格式化下就好了。
    之前在验收LL1实验的时候,老师看到我把语法树都画出来了,就说这个实验(LL1)没必要画语法树,LR1实验能把自动机画出来就好了,然后我就爽快答应了,因为只要核心程序写出来了,那么前端随便你怎么搞都行,这部分重点说下我是怎么画自动机的。
    一开始我就是用mermaid这个JS库,只要给它图的描述,它就能画出来,试了下效果不是很满意。
    继续Google,发现这么一个工具graphviz,它用了一种很简单的DOT语言,只要用DOT语言来描述这个图,它就能画出来。进一步发现这个工具有js移植版本,就试了试,效果不错,就是现在的样子。缺点就是这个移植版本太大了,3.5MB的库,所以第一次加载的时间全都花在下载这个库了,其二就是画那个PL0自动机的时候,会因为内存不足崩溃掉,可能这是JS的机制问题了吧。
    考虑上面2个问题,我写了一个小程序LR_DFA,用它在后台直接输出DOT文件,然后交给graphviz处理导出pdf,能把完整的PL0自动机(画PL0比较耗时)画出来,就是前面贴出来的那个图了。
    原文地址:
    http://www.netcan666.com/2016/10/21/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%E5%AE%9E%E9%AA%8C%E4%B9%8BLR-1-%E5%88%86%E6%9E%90%E5%99%A8%E8%AE%BE%E8%AE%A1/
    1 留言 2020-10-26 10:55:09 奖励45点积分
  • python执行控制台命令与操作剪切板

    Windows系统一 . 执行控制台指令方法例如打开QQ
    1. 使用os库中的system函数
    import osos.system('start D:\\QQ\\QQ.exe')'''如果不加start,打开QQ后控制台窗口不关闭,直到QQ关闭,控制台窗口才关闭 一条os.system()语句执行完成控制台会关闭,所以当执行后续命令需要依赖前面的命令时,将多条命令写到一条 os.system()语句内,多条控制台命令用 && 连接'''
    2. 同样是os库中的
    import os# 返回输出结果os.popen('start D:\\QQ\\QQ.exe')
    二. 操作剪切板使用pyperclip库中的函数
    import pyperclippyperclip.copy(123)pyperclip.copy('添加到剪切板')# 将传入的参数添加到剪切板,参数可以是数字或字符串
    0 留言 2020-10-22 11:12:43 奖励33点积分
  • 通过暴力搜索PID遍历进程并获取进程信息

    背景通常我们在内核中使用 ZwQuerySystemInformation 函数来遍历进程模块并获取进程信息,这种是通过正常的进程遍历方式,所以,有很多 Rootkit 程序会 HOOK 这个 ZwQuerySystemInformation 函数,过滤指定进程,从而实现进程的隐藏。
    本文实现的进程遍历和获取进程信息并不打算使用 ZwQuerySystemInformation 这种方式,而是直接暴力搜索进程的 PID,根据有效的 PID 获取相应进程的信息,从而实现进程的遍历。现在,我就来讲解具体的实现过程和原理,这个程序支持 32 位和 64 位 Win 7 至 Win10 全平台。
    函数介绍PsLookupProcessByProcessId 函数
    PsLookupProcessByProcessId例程接受进程的进程ID,并返回引用的指向EPROCESS结构的进程。
    函数声明
    NTSTATUS PsLookupProcessByProcessId( _In_ HANDLE ProcessId, _Out_ PEPROCESS *Process);
    参数

    ProcessId [in]指定进程的进程ID。Process[out]返回指向ProcessId指定的进程的EPROCESS结构的引用指针。
    返回值

    PsLookupProcessByProcessId在成功时返回STATUS_SUCCESS或适当的NTSTATUS值,例如:STATUS_INVALID_PARAMETER,表示进程 ID 无效。
    备注

    如果对 PsLookupProcessByProcessId 的调用成功,PsLookupProcessByProcessId 会增加Process参数中返回的对象的引用计数。因此,当驱动程序完成使用 Process 参数时,驱动程序必须调用 ObDereferenceObject 来取消引用从 PsLookupProcessByProcessId 例程接收的Process参数。

    IoQueryFileDosDeviceName 函数
    获取文件的 Dos 设备名称。
    NTSTATUS IoQueryFileDosDeviceName( _In_ PFILE_OBJECT FileObject, _Out_ POBJECT_NAME_INFORMATION *ObjectNameInformation);
    参数

    FileObject [in]指向文件的文件对象。ObjectNameInformation [out]返回指向新分配的OBJECT_NAME_INFORMATION结构的指针。 这个结构用MS-DOS设备名称信息成功返回填写。 结构定义如下:

    typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING Name;} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;// 注意:最终通过调用 ExFreePool 来释放此结构。

    返回值

    成功,则返回STATUS_SUCCESS;否则错误。

    实现原理暴力搜索 PID 这个不难理解,即 PID 从 4 开始,步长为 4,一直遍历到 0x1000,因为进程的 PID 总是为 4 的倍数,所以,我们设置遍历的步长为 4。
    然后,我们根据 PID,调用 PsLookupProcessByProcessId 函数获取 EPROCESS 进程结构体,因为 EPROCESS 这个结构体里存储着进程所有的信息,我们可以从这个 EPROCESS 结构体中获取我们想要的信息,例如进程的父进程、进程名、路径、进程双链表链等等。调用完 PsLookupProcessByProcessId 获取 EPROCESS 之后,记得调用 ObDereferenceObject 来释放对象。
    在不同的系统版本中,EPROCESS 这个结构体定义也不相同,所以不建议使用固定的偏移地址来获取进程信息,而应该使用提供的 API 函数从 EPROCESS 结构体中获取。例如:

    获取进程 PID,可以使用 PsGetProcessId 函数。
    获取进程的父进程 PID,可以使用PsGetProcessInheritedFromUniqueProcessId 函数。
    获取进程名称,可以使用 PsGetProcessImageFileName 函数。
    获取进程路径,可以先试用 PsReferenceProcessFilePointer 函数获取文件指针对象,然后再调用 IoQueryFileDosDeviceName 获取 Dos 路径,最后还要记得使用 ObDereferenceObject 函数来释放对象。

    编码实现进程遍历// 遍历进程NTSTATUS EnumProcess(){ NTSTATUS status = STATUS_SUCCESS; ULONG i = 0; PEPROCESS pEProcess = NULL; // 开始遍历 for (i = 4; i < 0x10000; i = i + 4) { status = PsLookupProcessByProcessId((HANDLE)i, &pEProcess); if (NT_SUCCESS(status)) { // 从进程结构中获取进程信息 GetProcessInfo(pEProcess); ObDereferenceObject(pEProcess); } } return status;}
    从进程结构中获取进程信息// 从进程结构中获取进程信息VOID GetProcessInfo(PEPROCESS pEProcess){ NTSTATUS status = STATUS_SUCCESS; HANDLE hParentProcessId = NULL, hProcessId = NULL; PCHAR pszProcessName = NULL; PVOID pFilePoint = NULL; POBJECT_NAME_INFORMATION pObjectNameInfo = NULL; // 获取父进程 PID hParentProcessId = PsGetProcessInheritedFromUniqueProcessId(pEProcess); // 获取进程 PID hProcessId = PsGetProcessId(pEProcess); // 获取进程名称 pszProcessName = PsGetProcessImageFileName(pEProcess); // 获取进程程序路径 status = PsReferenceProcessFilePointer(pEProcess, &pFilePoint); if (NT_SUCCESS(status)) { status = IoQueryFileDosDeviceName(pFilePoint, &pObjectNameInfo); if (NT_SUCCESS(status)) { // 显示 DbgPrint("PEPROCESS=0x%p, PPID=%d, PID=%d, NAME=%s, PATH=%ws\n", pEProcess, hParentProcessId, hProcessId, pszProcessName, pObjectNameInfo->Name.Buffer); } ObDereferenceObject(pFilePoint); } }
    程序测试在 Win7 32 位下,驱动程序正常运行:

    在 Win10 64 位下,驱动程序正常运行:

    总结这个程序不难理解,关键是要注意 EPROCESS 这个函数包含了几乎进程所有的信息,本文只用到了它其中的一小部分。建议大家私下可以使用 WinDbg 查看下系统中 EPROCESS 的定义,同时观察它具体有哪些信息,不同平台系统之间的区别,以此来加深对 EPROCESS 的印象。
    参考参考自《Windows黑客编程技术详解》一书
    2 留言 2019-03-05 22:59:35
  • 编程实现U盘插入自动复制U盘内容到本地

    背景U盘插入计算机后,不用任何操作,程序自动将U盘里的文件都拷贝到本地计算机上。这个功能是我自己开发的“恶魔的结界”系列程序里的一个小功能,至于有什么用,那就看个人的爱好了。在此,只探讨技术,不探讨用途。
    现在,我就对它进行解析,整理成文档,分享给大家。
    实现原理这个程序的实现,可以分成两个部分:

    U盘设备插入的监控,获取U盘盘符
    根据U盘盘符,遍历U盘文件,并进行复制操作

    首先,对U盘设备插入的监控,可以参考我写的 “编程实现监控U盘或者其它移动设备的插入和拔出” 这篇文章,使用方法是对程序添加 WM_DEVICECHANGE 消息处理函数,并根据 DEV_BROADCAST_VOLUME 结构体的 dbcv_unitmask 逻辑单元掩码来计算出插入设备U盘的盘符。
    我们成功获取了U盘盘复制后,也就知道了U盘的路径了。所以,我们使用WIN 32 API 函数 FindFirstFile 和 FindNextFile 从U盘的根目录进行文件遍历,具体的遍历方法解析可以参考本站上其他人写的 “使用FindFirstFile和FindNextFile函数实现文件搜索遍历” 这篇文章。 对于遍历到的文件,我们就调用 CopyFile 函数将它静默拷贝到本地指定的存储路径中。
    这样,经过上述的两步操作,我们就可以实现插入U盘,自动拷贝U盘文件到本地的功能了。
    编码实现U盘插入监控// 监控U盘插入并获取U盘盘符LRESULT CUDiskCopy_TestDlg::OnDeviceChange(WPARAM wParam, LPARAM lParam){ switch (wParam) { // 设备已经插入 case DBT_DEVICEARRIVAL: { PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam; // 逻辑卷 if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype) { // 根据 dbcv_unitmask 计算出设备盘符 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; DWORD dwDriverMask = lpdbv->dbcv_unitmask; DWORD dwTemp = 1; char szDriver[4] = "A:"; for (szDriver[0] = 'A'; szDriver[0] <= 'Z'; szDriver[0]++) { if (0 < (dwTemp & dwDriverMask)) { // 获取设备盘符, 开始执行拷贝, 从目标设备拷贝到本地上 SearchFile(szDriver); } // 左移1位, 接着判断下一个盘符 dwTemp = (dwTemp << 1); } } break; } default: break; } return 0;}
    U盘文件遍历及拷贝// 遍历文件并复制void SearchFile(char *pszDirectory){ // 搜索指定类型文件 DWORD dwBufferSize = 2048; char *pszFileName = NULL; char *pTempSrc = NULL; WIN32_FIND_DATA FileData = { 0 }; BOOL bRet = FALSE; // 申请动态内存 pszFileName = new char[dwBufferSize]; pTempSrc = new char[dwBufferSize]; // 构造搜索文件类型字符串, *.*表示搜索所有文件类型 ::wsprintf(pszFileName, "%s\\*.*", pszDirectory); // 搜索第一个文件 HANDLE hFile = ::FindFirstFile(pszFileName, &FileData); if (INVALID_HANDLE_VALUE != hFile) { do { // 要过滤掉 当前目录"." 和 上一层目录"..", 否则会不断进入死循环遍历 if ('.' == FileData.cFileName[0]) { continue; } // 拼接文件路径 ::wsprintf(pTempSrc, "%s\\%s", pszDirectory, FileData.cFileName); // 判断是否是目录还是文件 if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // 目录, 则继续往下递归遍历文件 SearchFile(pTempSrc); } else { // 文件, 执行复制操作, 复制到本地上 char szNewFile[MAX_PATH] = "C:\\Users\\Desktop\\test\\"; ::lstrcat(szNewFile, FileData.cFileName); ::CopyFile(pTempSrc, szNewFile, FALSE); } // 搜索下一个文件 } while (::FindNextFile(hFile, &FileData)); } // 关闭文件句柄 ::FindClose(hFile); // 释放内存 delete[]pTempSrc; pTempSrc = NULL; delete[]pszFileName; pszFileName = NULL;}
    程序测试我们运行程序后,插入U盘,然后等待一会儿后,我们打开本地上保存U盘拷贝数据的目录,发现成功拷贝U盘里的文件。

    总结为了防止程序在秘密拷贝U盘数据的时候,程序会卡死,所以,可以创建一个多线程,把拷贝文件的操作放到多线程里去执行,这样就不会阻塞主线程了。
    同时,创建本文演示的这个程序还没有对程序的窗口进行隐藏,如果你想要把这个程序做得比较隐蔽的话,可以参考本站上其他人写的 “编程实现MFC程序窗口一运行立马隐藏” 这篇文档,里面有介绍如何一开就隐藏窗口程序。
    参考参考自《Windows黑客编程技术详解》一书
    5 留言 2018-12-20 12:08:31 奖励25点积分
  • EXE加载模拟器直接在内存中加载运行EXE不通过API创建进程运行 精华

    背景在网上搜索了很多病毒木马的分析报告,看了一段时间后,发现还是有很多病毒木马都能够模拟PE加载器,把DLL或者是EXE等PE文件,直接从内存中直接加载到自己的内存中执行,不需要通过API函数去操作,以此躲过一些杀软的检测。
    在看到这些技术的描述后,虽然没有详细的实现思路,但是凭借自己的知识积累,我也大概知道是怎么做了。后来,就自己动手写了这么一个程序,实现了从内存中直接加载并运行EXE,不需要通过API函数创建另一个进程启动该EXE。暂时还没有想清楚这种技术有什么积极的一面,不管了,既然都把程序写出来了,那就当作是对PE结构以及编程水平的一次锻炼吧
    现在,把实现的思路和实现过程,写成文档,分享给大家。
    程序实现原理要想完全理解透彻这个程序的技术,需要对PE文件格式有比较详细的了解才行,起码要了解PE格式的导入表、导出表以及重定位表的具体操作过程。
    这个程序和 “DLL加载模拟器直接在内存中加载DLL不通过API加载” 这篇文章原理是一样的,只是一个是EXE,一个是DLL,本质上都是PE文件加载模拟器。
    EXE加载到内存的过程并运行实现原理
    首先,在EXE文件中,根据PE结构格式获取其加载映像的大小SizeOfImage,并根据SizeOfImage在自己的程序中申请一块可读、可写、可执行的内存,那么这块内存的首地址就是EXE程序的加载基址
    然后,根据EXE中的PE结构格式获取其映像对齐大小SectionAlignment,然后把EXE文件数据按照SectionAlignment对齐大小拷贝到上述申请的可读、可写、可执行的内存中
    接着,根据PE结构的重定位表,重新对重定位表进行修正
    接着,根据PE结构的导入表,加载所需的DLL,并获取导入表导入函数的地址并写入导入表中
    接着,修改EXE的加载基址ImageBase
    最后,根据PE结构获取EXE的入口地址AddressOfEntryPoint,然后跳转到入口地址处继续执行

    这样,EXE就成功加载到程序中并运行起来了。要注意的一个问题就是,并不是所有的EXE都有重定位表,对于没有重定位表的EXE程序,那就不适用于本文介绍的方法。
    编码实现// 模拟PE加载器加载内存EXE文件到进程中// lpData: 内存EXE文件数据的基址// dwSize: 内存EXE文件的内存大小// 返回值: 内存EXE加载到进程的加载基址LPVOID MmRunExe(LPVOID lpData, DWORD dwSize){ LPVOID lpBaseAddress = NULL; // 获取镜像大小 DWORD dwSizeOfImage = GetSizeOfImage(lpData); // 在进程中开辟一个可读、可写、可执行的内存块 lpBaseAddress = ::VirtualAlloc(NULL, dwSizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (NULL == lpBaseAddress) { ShowError("VirtualAlloc"); return NULL; } ::RtlZeroMemory(lpBaseAddress, dwSizeOfImage); // 将内存PE数据按SectionAlignment大小对齐映射到进程内存中 if (FALSE == MmMapFile(lpData, lpBaseAddress)) { ShowError("MmMapFile"); return NULL; } // 修改PE文件重定位表信息 if (FALSE == DoRelocationTable(lpBaseAddress)) { ShowError("DoRelocationTable"); return NULL; } // 填写PE文件导入表信息 if (FALSE == DoImportTable(lpBaseAddress)) { ShowError("DoImportTable"); return NULL; } //修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。 //统一设置成一个属性PAGE_EXECUTE_READWRITE DWORD dwOldProtect = 0; if (FALSE == ::VirtualProtect(lpBaseAddress, dwSizeOfImage, PAGE_EXECUTE_READWRITE, &dwOldProtect)) { ShowError("VirtualProtect"); return NULL; } // 修改PE文件加载基址IMAGE_NT_HEADERS.OptionalHeader.ImageBase if (FALSE == SetImageBase(lpBaseAddress)) { ShowError("SetImageBase"); return NULL; } // 跳转到PE的入口点处执行, 函数地址即为PE文件的入口点IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint if (FALSE == CallExeEntry(lpBaseAddress)) { ShowError("CallExeEntry"); return NULL; } return lpBaseAddress;}
    程序测试在 main 函数中调用上述封装好的函数,加载EXE程序进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ char szFileName[] = "KuaiZip_Setup_2.8.28.8.exe"; // 打开EXE文件并获取EXE文件大小 HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL); if (INVALID_HANDLE_VALUE == hFile) { ShowError("CreateFile"); return 1; } DWORD dwFileSize = GetFileSize(hFile, NULL); // 申请动态内存并读取DLL到内存中 BYTE *pData = new BYTE[dwFileSize]; if (NULL == pData) { ShowError("new"); return 2; } DWORD dwRet = 0; ReadFile(hFile, pData, dwFileSize, &dwRet, NULL); CloseHandle(hFile); // 判断有无重定位表 if (FALSE == IsExistRelocationTable(pData)) { printf("[FALSE] IsExistRelocationTable\n"); system("pause"); return 0; } // 将内存DLL加载到程序中 LPVOID lpBaseAddress = MmRunExe(pData, dwFileSize); if (NULL == lpBaseAddress) { ShowError("MmRunExe"); return 3; } system("pause"); return 0;}
    测试结果:
    运行程序后,成功显示“快压”安装程序的对话框界面,而且还显示了“快压”安装程序正在向 “i.kpzip.com” 使用HTTP发送“GET”数据请求:

    所以,程序测试成功。
    总结这个程序你只要熟悉PE格式结构的话,这个程序理解起来会比较容易。其中,需要特别注意的一点是:并不是所有的EXE都是用于本文介绍的方法。因为,对于那些没有重定位表的EXE程序来说,它们加载运行时的加载基址只能是固定的,因为没有重定位表的缘故,所以无法重定位数据,势必不能成功运行程序。同时,对一些MFC程序也不支持,目前正在改进当中。如果遇到不成功的,那就多换几个有重定位表的EXE试试就好。
    参考参考自《Windows黑客编程技术详解》一书
    9 留言 2019-01-02 09:49:15 奖励25点积分
  • python实现对人脸的实时监测

    使用python实现对人脸的实时监测并将人脸保存到本地文件夹下1、先导入库【可能需要下载一下dlib包,注意一下版本是否对应】
    #导入库import cv2 import dlib import numpy as np2、开始编写#定义脸部特征检测器detector = dlib.getfrontal_face_detector() #得到人脸的正面cap = cv2.VideoCapture(0) #0代表打开摄像头frame_count = 0 #检测的帧数face_count = 0 #每一帧检测的人脸数margin = 0.2 # 边距比例while True:ret, frame = cap.read() #从摄像头获取的视频文件中读取第一帧if (ret != True): #判断摄像头是否正常打开并读取视频print(‘没有捕获到图像,请检查摄像头工作情况!’)breakinput_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) #BGR转为RGBframe_count +=1 #帧数加一img_h,img_w, = np.shape(input_img) #获取图像的尺寸img_h:高,img_w:宽,第三个代表通道detected = detector(frame,1)#对当前帧检测faces = [] #存放当前帧中的脸部数量1代表的是上采样if len(detected) > 0: #当前帧检测到脸部,至少检测到一个 for i, d in enumerate(detected): #enumerate枚举函数,一个个的进行检测 face_count += 1#人脸数增加 # 脸部图像坐标与尺寸,分别表示左上角坐标,右下角坐标,和长宽,加一代表的是: x1, y1, x2, y2, w, h = d.left(), d.top(), d.right() + 1, d.bottom() + 1, d.width(), d.height() # 用边距做调整 #限制最小是0 xw1 = max(int(x1 - margin * w), 0) yw1 = max(int(y1 - margin * h), 0) #img_w-1是:限制最大的就是img-1 xw2 = min(int(x2 + margin * w), img_w - 1) yw2 = min(int(y2 + margin * h), img_h - 1); # 脸部图像坐标 face = frame[yw1:yw2 + 1, xw1:xw2 + 1, :]#加一是为了保存边界 file_name = "./dataset/valid/one/" + str(frame_count) +'_one'+str(i) + '.jpg'#文件路径 cv2.imwrite(file_name,face) #绘制人脸边框 cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)#显示当前帧cv2.imshow("Face Detector", frame) # 按回车键终止视频检测if cv2.waitKey(1)&0xFF == 13: #1代表1ms,13代表回车键,27代表ESC键,ord('q') break3、打印运行结果print(‘已经完成了 {0} 帧检测,保存了 {1} 幅脸部图像’.format(frame_count, face_count))4、关闭摄像头和窗口cap.release()cv2.destroyAllWindows()
    0 留言 2020-10-14 14:36:47 奖励30点积分
  • ECMAScript 6.0 笔记

    ES6let、const……
    解构赋值从对象/数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构
    let [a,b,c]= [1,2,3]let person = {name:"xioaming",age:18}let {name,age} = personlet {name:uname,age:uage}=person//左侧用于匹配,右侧用于赋值变量
    参数赋值初始值function add(a,b,c=10){}//具有默认值的参数值,一般位置要靠后add(1,2)//与结构赋值结合function connect({host="127.0.0.1",username,password,port=3000}){}conncet({ host:'localhost', username:"root", password:"root", port:3306})
    箭头函数……
    this指是函数外最近的那个this,无法被bind/call/apply指定
    剩余参数将一个不定数量的参数表示为一个数组
    function sum(first,...args){ //args为数组}sum(1,2,3,4,5)//剩余参数与解构配合使用let students=['xm','xw','xz']let [s1,...s2] = studentsconsole.log(s1)//'xm'console.log(s2)//['xw','xz']
    扩展运算符(展开语法)将数组或对象转为用逗号分隔的参数序列
    let arr = [1,2,3]console.log(...arr)//应用:合并数组let arr1=[1,2,3]let arr2=[4,5,6]let arr3=[...arr1,...arr2]//arr1.push(...arr2)//应用:将伪数组转为数组let arr = [...arguments]//拓展//构造函数方法Array.fron()转数组let arrayLike={ '0':'a', '1':'b', length:2}let arr2=Array.from(arraylike,[function])//find()找出第一个符合条件的数组成员,如果没有找到返回undefinedlet arr =[ { id:1, name:"xm" }, { id:2, name:"xz" }]let target = arr.find((item,index)=>item.id===2)//{id:1,name:"xm"}//findIndex()找出第一个符合条件的数组成员的位置,如果没找到返回-1let inex = arr.find((item,index)=>item.name==="xz")//1//includes()表示某个数组是否包含给定的值,返回布尔值[1,2,3].includes(2)//true[1,2,3].include(4)//false//startsWith()参数字符串是否在原字符串的头部,返回布尔 let str = "hello world!"str.startsWith('hello')//true//endsWith()参数字符串是否在原字符串的尾部,返回布尔 str.endsWith('!')//true//repeat() 将原字符串重复n次,返回一个新字符串‘x1'.repeat(3) //"x1x1x1"
    symbol数据类型表示独一无二的值
    特点

    值是唯一的,用来解决命名冲突的问题不能与其他数据进行运算定义的对象属性不能使用for…in循环遍历。但可以使用Relect.ownkeys来获取对象的所有键名
    //创建symbollet s = Symbol()let s2 = Symbol("xiaoming")//传入的是一个描述字符串,与结果无关let s3 = Symbol("xiaoming")//与s2不一样s2 === s3 //false//Symbol.for创建let s4 = Symbol.for('xiaoming')let s5 = Symbol.for('xioaming')s4 === s5//true//不能运算//let result = s+100//let result = s>100//let result = s+s//向对象中添加方法up downlet game={}//game.up=function(){}//会有风险,里面可能已经存在let methods={ up:Symbol(), down:Symbol()}game[methods.up]=function(){ console.log("up");}game[methods.down]=function(){ console.log("down");}game[methods.up]()//调用let say = Symbol('say')let youxi = { name:"123", [say]:function(){ console.log('say'); }, [Symbol('do')]:function(){ console.log('do'); },}youxi[say]()//调用/*--------内置Symbol值-------------*///Symbol.hasInstanceclass Person{ static [Symbol.hasInstance](param){ console.log(param)//{a:1} console.log("我被用来检测类型了") return true//控制instanceof的结果 }}let o={a:1}o instanceof Person //Symbol.isConcatSpreadableconst arr=[1,2,3]const arr2=[4,5,6]console.log(arr.concat(arr2));//[1, 2, 3, 4, 5, 6]arr2[Symbol.isConcatSpreadable] = falseconsole.log(arr.concat(arr2));//[1, 2, 3, Array(3)]
    迭代器(Iterator)ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费
    是对象里的一个属性Symbol.iterator
    原生具备iterator接口的数据(可以用for of遍历)

    ArrayArgumentsSetMapStringTypedArrayNodeList
    工作原理
    1. 由Symbol.iterator指向的函数创建一个指针对象,指向当前数据结构的起始位置 2. 第一次调用对象的next方法,指针自动指向数据结构的第一个成员 3. 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员 4. 每调用next方法会返回一个包含value和done属性的对象需要自定义遍历数据的时候,要想到迭代器
    const xiyou=['唐僧','孙悟空','猪八戒','沙僧']let iterator = xiyou[Symbol.iterator]()console.log(iterator.next());//{value: "唐僧", done: false}console.log(iterator.next());//{value: "孙悟空", done: false}console.log(iterator.next());//{value: "猪八戒", done: false}console.log(iterator.next());//{value: "沙僧", done: false}console.log(iterator.next());//{value: undefined, done: true}/*-----迭代器应用,for of遍历对象内数组-------*/const banji = { name: "一班", stus: [ 'xiaoming', 'xiaowang', 'xiaozhang' ], [Symbol.iterator]() { //索引变量 let index = 0 let _this = this return { next() { if (index < _this.stus.length) { const result = { value: _this.stus[index], done: false } //下标自增 index++ return result } else { return { value: undefined, done: true } } } } }}for(s of banji){ console.log(s);}
    生成器生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
    //*的偏向无所谓function * gen(){ //yield函数代码的分割符 yield '一只没有耳朵' yield '一只没有尾巴' yield '真奇怪'}for(let v of gen()){ console.log(v);}//一只没有耳朵// 一只没有尾巴// 真奇怪let iterator=gen()console.log(iterator.next());//{value: "一只没有耳朵", done: false}console.log(iterator.next());//{value: "一只没有尾巴", done: false}console.log(iterator.next());//{value: "真奇怪", done: false}console.log(iterator.next());//{value: undefined, done: true}//参数传递function* gen(arg) { console.log(arg);//AAA let one = yield 111 console.log(one);//BBB yield 222 yield 333}let iterator = gen('AAA')console.log(iterator.next());//{value: 111, done: false}//next()传入的实参将作为上一个yield语句的返回结果console.log(iterator.next('BBB'));//{value: 222, done: false}//实例//1//异步编程 文件操作 网络操作(ajax,request) 数据库操作//1s后控制台输出111 2s后输出222 3s后输出333function one(){ setTimeout(()=>{ console.log(111); console.log(iterator); iterator.next() },1000)}function two(){ setTimeout(()=>{ console.log(222); iterator.next() },2000)}function three(){ setTimeout(()=>{ console.log(333); iterator.next() },3000)}function* gen(){ yield one() yield two() yield three()}let iterator = gen()iterator.next()//2//模拟获取 用户数据 订单数据 商品数据function getUsers(){ setTimeout(()=>{ let data = "用户数据" //调用next方法,并将数据传入 iterator.next(data) },1000)}function getOrders(){ setTimeout(()=>{ let data = "订单数据" iterator.next(data) },1000)}function getGoods(){ setTimeout(()=>{ let data = "商品数据" iterator.next(data) },1000)}function* gen(){ let users = yield getUsers() console.log(users); let orders = yield getOrders() console.log(orders); let goods = yield getGoods() console.log(goods);}let iterator = gen()iterator.next()
    Promiseconst p = new Promise((resolve, reject) => { setTimeout(() => { resolve('用户数据') }, 1000)})const result = p.then(value => { console.log(value);//用户数据 //return //1.非promise类型的属性 // return '123' //2.是promise对象 // return new Promise((resolve,reject)=>{ // resolve('ok') // }) //3.抛出错误 //throw '出错啦!'})console.log(result);//return 非promise类型的属性 // [[PromiseStatus]]: "resolved" // [[PromiseValue]]: "123"//return promise对象 //[[PromiseStatus]]: "resolved" // [[PromiseValue]]: "ok"//return 抛出错误 //[[PromiseStatus]]: "rejected" //[[PromiseValue]]: "出错啦!"//链式调用new Promise((resolve, reject) => { setTimeout(() => { resolve("111") }, 1000)}).then(str => { console.log(str); return new Promise((resolve, reject) => { setTimeout(() => { resolve("222") }, 2000) })}).then(str => { console.log(str); return new Promise((resolve, reject) => { setTimeout(() => { resolve("333") }, 3000) })}).then(str=>{ console.log(str);})
    Set数据结构类似于数组,但是成员的值都是唯一的,没有重复的值
    const set = new Set([1,2,3,4])//数组去重let arr = [1,3,5,1,4,45,14,5]let newArr = [...new Set(arr)]console.log(newArr);//[1, 3, 5, 4, 45, 14]//实例方法//add(value) 添加某个值,返回Set结构本身//delete(value) 删除某个值,返回一个布尔值,表示删除成功//has(value) 返回一个布尔值,表示改制是否为Set成员//clear() 清除所有成员,无返回值//遍历 也有forEach方法const s = new Set([2,3,4,5,6,7])s.forEach(value=>{ console.log(value);})
    Map数据结构类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了iterator接口,所以可以使用拓展运算符和for…of进行遍历。
    Map的属性和方法
    ​ size 返回Map元素个数
    ​ set 增加一个新的元素,返回当前Map
    ​ get 返回键名对象的键值
    ​ has 检测Map中是否包含某个元素,返回boolean值
    ​ clear 清空集合,返回undefined
    0 留言 2020-10-07 10:55:49 奖励22点积分
  • python算法练习题

    冰其淋小店是一种特殊的餐馆

    编写一个名为IceCreamStand的类,让它继承你为完成练习1或练习4而编写的Restaurant类
    这两个版本Restaurant类都可以,挑选你更喜欢的那个即可
    添加一个名为flavors的属性,用于存储一个由各种口味的冰淇淋组成的列表
    编写一个显示这些冰淇淋的方法。创建一IceCreamStand实例,并调用这个方法

    from Restaurant import Restaurantclass IceCreamStand(Restaurant): def __init__(self,restaurant_name,cuisine_type,flavors,number_served = 0): super(IceCreamStand, self).__init__(restaurant_name,cuisine_type,number_served = 0) self.flavor=flavors def getInfo(self): print('这家参观的名字是%s,特色菜是%s,冰欺凌的主要口味有%s'%(self.restaurant_name,self.cuisine_type,self.flavor))IceCreamStand1=IceCreamStand('海底捞','火锅',['火山岩','草莓味','奶油味'])IceCreamStand1.getInfo()
    管理员是一种特殊的用户

    编写一个名为Admin的类,让它继承你为完成练习3或练习5而编写的User类
    添加一个名为privileges的属性,用于存储一个由字符串(如“can add post”、“can delete post”、“can ban user”等)组成的列表
    编写一个名为show_privileges()的方法,它显示管理员的权限,创建一个Admin实例,并调用这个方法

    from user import userclass Admin(user): privileges=['can add post','can delete post','can ban user'] def __int__(self,first_name,last_name,age,sex,phone,login_attempts=0): super(Admin, self).__int__(first_name,last_name,age,sex,phone,login_attempts=0) def show_privileges(self): print('管理员:%s%s有以下权限:'%(self.first_name,self.last_name)) for i in Admin.privileges: print(i)admin1=Admin('su','san',18,'女','19999999999','上海')admin1.show_privileges()
    亡者农药小游戏

    创建三个游戏人物,分别是:

    属性:
    名字:name定位:category血量:Output技能:Skill
    英雄
    铠,战士,血量:1000 技能:极刃风暴王昭君,法师 ,血量:1000 技能:凛冬将至阿轲,刺客,血量:1000 技能:瞬华

    游戏场景,分别:

    偷红buff,释放技能偷到红buff消耗血量300solo战斗,一血,消耗血量500补血,加血200

    class hero(): def __init__(self,name,category,skill,output=1000,score=0): self.name=name self.category=category self.skill=skill self.output=output self.score=score #战斗场景一 :偷红BUFF def red_buff(self): self.output-=300 print('%s%s释放技能偷的红BUFF,消耗血量%d'%(self.category,self.name,self.output)) # 战斗场景二 :solo def solo(self,n=1): self.output -= 500 if self.output<0: print('%s%s送出了一血'%(self.category,self.name)) else: if self.score==0: self.score+=n print('%s%s释放技取得一血,消耗血量500,拿到%d人头'%(self.category, self.name,self.score)) else: self.score += n print('%s%s释放技,消耗血量500,拿到%d人头' % (self.category, self.name,n)) # 战斗场景三 :补血 def add_blood(self): self.output+=200 print('%s%s被辅助加血200,现在的血量为%d'%(self.category, self.name,self.output)) #查看英雄属性 def getInfo(self): if self.output<=0: print('%s%s已死亡等待复活,已取得人头%d'%(self.category, self.name,self.score)) else: print('%s%s已超神,血量还有%d'%(self.category, self.name,self.output)) #实例化对象kai=hero('凯','战士','极寒风暴')kai.red_buff()kai.getInfo()kai.solo()kai.getInfo()kai.add_blood()
    模拟一个简单的银行进行业务办理的类

    类:创建一个银行类
    属性:

    一个属于银行的类属性,用来存储所用银行的开户信息,包含卡号、密码、用户名、余额(外界不能随意访问和修改。开户时要进行卡号验证,查看卡号是否已经存在)每个对象拥有卡号、密码、用户名、余额(外界不能随意访问和更改)
    方法:

    银行类拥有:
    查看本银行的开户总数查看所有用户的个人信息(包含卡号、密码、用户名、余额)
    每个对象拥有:
    实例化对象的时候传入相关参数,初始化对象及类属性取钱(需要卡号和密码验证),通过验证卡号和密码对个人的余额进行操作,如果取钱大于余额,返回余额不足存钱(需要卡号和密码验证),通过验证卡号和密码对个人的余额进行操作,返回操作成功查看个人详细信息(需要卡号密码验证),返回个人的卡号,用户名,余额信息


    class Bank(): #一个属于银行的类属性 __Users = {} #每个对象拥有 卡号、密码、用户名、余额 def __init__(self,CradId,pwd,name,balance): if CradId not in Bank.__Users: Bank.__Users[CradId] = {'pwd':pwd,'Username':name,'Balance':balance} self.__CradId = CradId self.__pwd = pwd self.__name = name self.__balance = balance #查看本银行的开户总数 @classmethod def nums(cls): print('当前用户数:%d'%(len(cls.__Users))) #查看所有用户的个人信息(包含卡号、密码、用户名、余额) @classmethod def get_Users(cls): for key,val in cls.__Users.items(): print('卡号:%s \n 用户名:%s \n密码:%d \n 余额:%d'%(key,val['Username'],val['pwd'],val['Balance'])) print() #验证方法 @staticmethod def check_User(CradId,pwd): if (CradId in Bank.__Users) and (pwd == Bank.__Users[CradId]['pwd'] ): return True else: return False #验证金额 @staticmethod def check_money(money): if isinstance(money,int): return True else: return False # 取钱(需要卡号和密码验证) def q_money(self,CradId,pwd,money): if Bank.check_User(CradId,pwd): #开始取钱 if Bank.check_money(money): if Bank.__Users[CradId]['Balance'] >= money: Bank.__Users[CradId]['Balance'] -= money print('当前卡号%s,当前取款金额%d,当前余额%d'%(CradId,money,Bank.__Users[CradId]['Balance'])) else: print('余额不足') else: print('您输入的金额有误') else: print('卡号或者密码有误') def c_money(self,CradId,pwd,money): if Bank.check_User(CradId,pwd): #开始取钱 if Bank.check_money(money): Bank.__Users[CradId]['Balance'] += money print('当前卡号%s,当前存款金额%d,当前余额%d'%(CradId,money,Bank.__Users[CradId]['Balance'])) else: print('您输入的金额有误') else: print('卡号或者密码有误') #查看个人详细信息(需要卡号密码验证) def getInfo(self,CradId,pwd): if Bank.check_User(CradId, pwd): print('当前卡号%s,当前用户名%s,当前余额%d' % (CradId, Bank.__Users[CradId]['Username'], Bank.__Users[CradId]['Balance'])) else: print('卡号或者密码有误')joe = Bank('1001',111111,'joe',100)joe2 = Bank('1001',111111,'joe',100)Bank.nums()print('_'*50)Bank.get_Users()print('_'*50)joe.c_money('1001',111111,500)print('_'*50)joe.q_money('1001',111111,300)print('_'*50)joe.getInfo('1001',111111)
    0 留言 2020-09-22 10:21:02 奖励26点积分
  • Python 编程里面%、%s 和 % d 代表的意思


    %s,表示格化式一个对象为字符
    %d,整数

    "Hello, %s"%"zhang3" => "Hello, zhang3""%d"%33 => "33""%s:%d"%("ab",3) => "ab:3"

    %字符:标记转换说明符的开始。在%的左侧放置一个字符串(格式化字符串),而右侧则放置希望格式化的值。
    %s表示格式化规则

    1、
    '%s plus %s equals %s' % (1,2,2)Out[29]: '1 plus 2 equals 2'
    2、
    'Price of eggs: $%d' % 42Out[30]: 'Price of eggs: $42'
    3、
    单独使用时取余5%3:
    5%3Out[28]: 2/4
    4、
    LOTTERY_PRE = "LXG_LOT_"LOTTERY_ITEM = LOTTERY_PRE + '%s_ITEM'new_version = "20181007220245756"new_lobbery_item = LOTTERY_ITEM % new_versionprint(new_lobbery_item)输出 LXG_LOT_20181007220245756_ITEM
    1 留言 2020-08-04 19:48:23 奖励16点积分
显示 0 到 15 ,共 15 条
eject