分类

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

技术文章列表

  • 记c# u3d 之前犯得一点错误


    声明一个数组后,忘记new一个具体大小,导致无法传递值报错
    忘记Array.sort() 可以排序数字,甚至闭门造了个轮子,服了(而且人家还先进一点,我这个简直是shit)
    float[] sortBySpeed(float[] array){ float temp = 0; float[] sortArray=new float[array.Length]; for (int i = 0; i < array.Length; i++) { sortArray[i] = i; } //声明并创造一个序号数组,并在后面的排序中,与速度保持一样的变化; for (int i = 0; i < array.Length; i++) { for (int j = i+1; j < array.Length; j++) { if (array[j]>array[i]) { temp = array[i]; array [i] = array [j]; array [j] = temp; temp = sortArray[i]; sortArray[i] = sortArray[j]; sortArray[j] = temp; } } } return sortArray;}用inistaniate克隆物体,并获取transform.positon,在自动布局组(gridout),如果不延时的话,立即获取会直接获取出生点的坐标导致误差
    预制体的位置不对还可能是没有设置rect的轴心和位置
    如果意外情况(代码挂载脚本),如果开头声明了一个bool值为false的变量,有可能会声明两次变为true(???俺也不知道为什么)
    单例模式的代码尽量简洁一点吧,突然忘了加声明可能会很心伤、
    可以用out,ref声明来在方法中传值和引用(该死,以前学了忘了),不过用多了,代码修改很麻烦,慎重、
    可能要学学画思维导图,设计代码结构有点不合理(三个类打天下,绝了)
    0 留言 2020-08-06 14:25:52 奖励20点积分
  • 基于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框架,不仅仅因为它广泛,而是我希望能够挑战自己。经过这一个周的磨练,我最大的收获除了学到了真正可以应用的知识外,更重要的是学会了项目合作开发的经验。
    0 留言 2020-08-05 15:32:05 奖励46点积分
  • 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
    0 留言 2020-08-04 19:48:23 奖励16点积分
  • 进程挂起再恢复实现进程内存替换

    背景所谓的进程内存替换,就是指将一个进程的内存数据清空,写入任意我们想写入的数据,并更改执行顺序,执行我们写入的数据代码。
    本文介绍的就是这样的编程技术,但是,我们做了一些简化,简化成实现创建一个挂起主线程的进程,在新进程的地址空间内申请一块内存,写入我们的 Shellcode,并更改新进程执行顺序,执行我们的 Shellcode 代码。现在,我就把实现过程和原理整理成文档,分享给大家。
    函数介绍GetThreadContext 函数
    获取指定线程的上下文。64 位应用程序可以使用 Wow64GetThreadContext 函数来检索 WOW64 线程的上下文。
    函数声明
    BOOL WINAPI GetThreadContext( _In_ HANDLE hThread, _Inout_ LPCONTEXT lpContext);
    参数

    hThread [in]要检索其上下文的线程的句柄。 该句柄必须具有对线程的THREAD_GET_CONTEXT访问权限。 有关更多信息,请参阅线程安全和访问权限。WOW64:手柄也必须有THREAD_QUERY_INFORMATION访问权限。lpContext [in,out]指向 CONTEXT 结构的指针,它接收指定线程的适当上下文。 该结构的ContextFlags成员的值指定检索线程的上下文的哪些部分。 CONTEXT结构具有高度的处理器特性。 请参阅WinNT.h头文件,了解该结构的处理器特定定义和任何对齐要求。
    返回值

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

    SetThreadContext 函数
    设置指定线程的上下文。64 位应用程序可以使用 Wow64SetThreadContext 函数设置 WOW64 线程的上下文。
    函数声明
    BOOL WINAPI SetThreadContext( _In_ HANDLE hThread, _In_ const CONTEXT *lpContext);
    参数

    hThread [in]线程的句柄,其上下文将被设置。 该句柄必须具有线程的THREAD_SET_CONTEXT权限。 有关更多信息,请参阅线程安全和访问权限。lpContext [in]指向包含要在指定线程中设置的上下文的CONTEXT结构的指针。 此结构的ContextFlags成员的值指定要设置的线程的上下文的哪些部分。 无法指定的CONTEXT结构中的某些值将默认设置为正确的值。 这包括指定特权处理器模式的CPU状态寄存器中的位,调试寄存器中的全局使能位以及必须由操作系统控制的其他状态。
    返回值

    如果设置了上下文,则返回值为非零。如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

    ResumeThread 函数
    减少线程的暂停计数。 当暂停计数递减到零时,线程的执行被恢复。
    函数声明
    DWORD WINAPI ResumeThread( _In_ HANDLE hThread);
    参数

    hThread [in]
    要重新启动的线程的句柄。
    该句柄必须具有THREAD_SUSPEND_RESUME权限。 有关更多信息,请参阅线程安全和访问权限。

    返回值

    如果函数成功,则返回值是线程先前的挂起计数。如果函数失败,返回值为(DWORD)-1。 要获取扩展错误信息,请调用GetLastError。

    实现原理我们实现进程内存替换,或者说实现进程数据写入,更改执行顺序的原理是:

    首先,使用 CreateProcess 函数创建进程,并且设置创建进程的标志为 CREATE_SUSPENDED,即表示新进程的主线程被挂起。
    然后,使用 VirtualAllocEx 函数在新进程中申请一块可读、可写、可执行的内存,并使用 WriteProcessMemory 函数写入Shellcode 数据。
    接着,使用 GetThreadContext,设置获取标志为 CONTEXT_FULL,即获取新进程中所有的线程上下文。并修改线程上下文的指令指针 EIP 的值,更改主线程的执行顺序。再将修改过的线程上下文设置回主线程中。
    最后,我们调用 ResumeThread 恢复主线程,让进程按照修改后的 EIP 继续运行,执行我们的 Shellcode 代码。

    其中,当使用 CreateProcess 创建进程时,创建标志为 CREATE_SUSPENDED,则表示新进程的主线程被创建为挂起状态,直到使用 ResumeThread 函数恢复主线程,进程才会继续运行。
    其中,要注意的是,在使用 GetThreadContext 获取线程上下文的时候,一定要对 CONTEXT 机构中的 ContextFlags 成员赋值,表示指明要检索线程的上下文的哪些部分,否则会导致程序实现不到想要的效果。我们可以指明 CONTEXT_FULL,表示获取所有的线程上下文信息。
    编码实现// 创建进程并替换进程内存数据, 更改执行顺序BOOL ReplaceProcess(char *pszFilePath, PVOID pReplaceData, DWORD dwReplaceDataSize, DWORD dwRunOffset){ STARTUPINFO si = { 0 }; PROCESS_INFORMATION pi = { 0 }; CONTEXT threadContext = { 0 }; BOOL bRet = FALSE; ::RtlZeroMemory(&si, sizeof(si)); ::RtlZeroMemory(&pi, sizeof(pi)); ::RtlZeroMemory(&threadContext, sizeof(threadContext)); si.cb = sizeof(si); // 创建进程并挂起主线程 bRet = ::CreateProcess(pszFilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); if (FALSE == bRet) { ShowError("CreateProcess"); return FALSE; } // 在替换的进程中申请一块内存 LPVOID lpDestBaseAddr = ::VirtualAllocEx(pi.hProcess, NULL, dwReplaceDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (NULL == lpDestBaseAddr) { ShowError("VirtualAllocEx"); return FALSE; } // 写入替换的数据 bRet = ::WriteProcessMemory(pi.hProcess, lpDestBaseAddr, pReplaceData, dwReplaceDataSize, NULL); if (FALSE == bRet) { ShowError("WriteProcessError"); return FALSE; } // 获取线程上下文 // 注意此处标志,一定要写!!! threadContext.ContextFlags = CONTEXT_FULL; bRet = ::GetThreadContext(pi.hThread, &threadContext); if (FALSE == bRet) { ShowError("GetThreadContext"); return FALSE; } // 修改进程的PE文件的入口地址以及映像大小,先获取原来进程PE结构的加载基址 threadContext.Eip = (DWORD)lpDestBaseAddr + dwRunOffset; // 设置挂起进程的线程上下文 bRet = ::SetThreadContext(pi.hThread, &threadContext); if (FALSE == bRet) { ShowError("SetThreadContext"); return FALSE; } // 恢复挂起的进程的线程 ::ResumeThread(pi.hThread); return TRUE;}
    程序测试我们运行程序,对 520.exe 程序创建挂起进程,并写入 Shellcode,更改进程执行顺序,恢复进程,执行 Shellcode 部分代码:

    总结这个程序在理解原理后,你会发现我们上述的例子并没有替换原来新进程的内存,只是申请了一块新内存,并写入 Shellcode 数据。但是,要替换新进程的内存倒也不难,只要获取了新进程的加载基址,然后根据 PE 格式,获取加载映像大小,并对数据全部置零清空,在写入我们的新数据,这样就实现了替换。但是,只要你理解了本文演示的例子,实现替换操作也不难了。
    如果你仍然不明白,可以多看看 CreateProcess、GetThreadContext、SetThreadContext、ResumeThread 等函数的具体参数说明,理解清楚参数含义就可以了。
    其中,要注意的是,在使用 GetThreadContext 获取线程上下文的时候,一定要对 CONTEXT 机构中的 ContextFlags 成员赋值,表示指明要检索线程的上下文的哪些部分,否则会导致程序实现不到想要的效果。我们可以指明 CONTEXT_FULL,表示获取所有的线程上下文信息。
    参考参考自《Windows黑客编程技术详解》一书
    4 留言 2019-01-22 17:34:57 奖励15点积分
  • 查找并使用PspTerminateThreadByPointer函数强制结束进程可以杀360进程 精华

    背景学习计算机的同学,或多或少都会有一个黑客情节。总是会想成为一个无拘无束的“黑客”,探索计算机世界里技术的边缘,挑战一切规则与界限。其实,正如电影《东邪西毒》里欧阳峰说的:“人都会经历这个阶段,看见一座山,就想知道山后面是什么。我很想告诉ta,可能翻过去山后面,你会发觉没有什么特别,回头看会觉得这边更好”。
    本文要介绍的就是在内核下实现,强制关掉指定进程,甚至可以关闭 360、QQ 等进程。这个技术,虽不能让你成为一名“黑客”,或许可以让你感受一把“黑科技”的瘾。现在,我就把实现过程和原理整理成文档,分享给大家。该程序适用于 32 位和 64 位 Win7 到 Win10 全平台系统。
    实现过程我们知道,线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
    也就是说,当一个进程中的所有线程都被结束的时候,这个进程也就没有了存在的意义,也随之结束了。这,便是我们本文介绍的这种强制杀进程的实现原理,即把进程中的线程都杀掉,从而让进程消亡,实现间接杀进程的效果。
    Windows 提供了一个导出的内核函数 PsTerminateSystemThread 来帮助我们结束线程,所以,类似 360、QQ 等也会对重点监测该函数,防止结束自己的线程。我们通过逆向 PsTerminateSystemThread 函数,可以发现该函数实际上调用了未导出的内核函数 PspTerminateThreadByPointer 来实现的结束线程的操作。所以,我们可以通过查找 PspTerminateThreadByPointer 函数地址,调用直接它来结束线程,就可以绕过绝大部分的进程保护,实现强制杀进程。
    PspTerminateThreadByPointer 的函数声明为:
    NTSTATUS PspTerminateThreadByPointer ( PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate );
    但要注意,PspTerminateThreadByPointer 的函数指针的声明的调用约定:
    // 32 位typedef NTSTATUS(*PSPTERMINATETHREADBYPOINTER_X86) ( PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate );// 64 位typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER_X64) ( PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate );
    其中,PsTerminateSystemThread 里会调用 PspTerminateThreadByPointer 函数。我们使用 WinDbg 逆向 Win8.1 x64 里的 PsTerminateSystemThread 函数,如下所示:
    nt!PsTerminateSystemThread:fffff800`83904518 8bd1 mov edx,ecxfffff800`8390451a 65488b0c2588010000 mov rcx,qword ptr gs:[188h]fffff800`83904523 f7417400080000 test dword ptr [rcx+74h],800hfffff800`8390452a 7408 je nt!PsTerminateSystemThread+0x1c (fffff800`83904534)fffff800`8390452c 41b001 mov r8b,1fffff800`8390452f e978d9fcff jmp nt!PspTerminateThreadByPointer (fffff800`838d1eac)fffff800`83904534 b80d0000c0 mov eax,0C000000Dhfffff800`83904539 c3 ret
    由上面代码可以知道,我们可以通过扫描 PsTerminateSystemThread 内核函数中的特征码,从而获取 PspTerminateThreadByPointer 函数的偏移,再根据偏移计算出该函数的地址。其中,不同系统中的特征码也会不同,下面是我使用 WinDbg 逆向各个系统上总结的特征码的情况:




    Win 7
    win 8.1
    win 10




    32 位
    0xE8
    0xE8
    0xE8


    64 位
    0xE8
    0xE9
    0xE9



    那么,我们强制杀进程的实现原理为:

    首先,根据特征码扫描内存,获取 PspTerminateThreadByPointer 函数地址
    然后,调用 PsLookupProcessByProcessId 函数,根据将要结束进程 ID 获取对应的进程结构对象 EPROCESS
    接着,遍历所有的线程 ID,并调用 PsLookupThreadByThreadId 函数根据线程 ID 获取对应的线程结构 ETHREAD
    然后,调用函数 PsGetThreadProcess 获取线程结构 ETHREAD 对应的进程结构 EPROCESS
    这时,我们可以通过判断该进程是不是我们指定要结束的进程,若是,则调用 PspTerminateThreadByPointer 函数结束线程;否则,继续遍历下一个线程 ID
    重复上述 3、4、5 的操作,直到线程遍历完毕

    这样,我们就可以查杀指定进程的所有线程,线程被结束之后,进程也随之结束。注意的是,当调用 PsLookupProcessByProcessId 和 PsLookupThreadByThreadId 等 LookupXXX 系列函数获取对象的时候,都需要调用 ObDereferenceObject 函数释放对象,否则在某些时候会造成蓝屏。
    编码实现强制结束指定进程// 强制结束指定进程NTSTATUS ForceKillProcess(HANDLE hProcessId){ PVOID pPspTerminateThreadByPointerAddress = NULL; PEPROCESS pEProcess = NULL; PETHREAD pEThread = NULL; PEPROCESS pThreadEProcess = NULL; NTSTATUS status = STATUS_SUCCESS; ULONG i = 0;#ifdef _WIN64 // 64 位 typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);#else // 32 位 typedef NTSTATUS(*PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);#endif // 获取 PspTerminateThreadByPointer 函数地址 pPspTerminateThreadByPointerAddress = GetPspLoadImageNotifyRoutine(); if (NULL == pPspTerminateThreadByPointerAddress) { ShowError("GetPspLoadImageNotifyRoutine", 0); return FALSE; } // 获取结束进程的进程结构对象EPROCESS status = PsLookupProcessByProcessId(hProcessId, &pEProcess); if (!NT_SUCCESS(status)) { ShowError("PsLookupProcessByProcessId", status); return status; } // 遍历所有线程, 并结束所有指定进程的线程 for (i = 4; i < 0x80000; i = i + 4) { status = PsLookupThreadByThreadId((HANDLE)i, &pEThread); if (NT_SUCCESS(status)) { // 获取线程对应的进程结构对象 pThreadEProcess = PsGetThreadProcess(pEThread); // 结束指定进程的线程 if (pEProcess == pThreadEProcess) { ((PSPTERMINATETHREADBYPOINTER)pPspTerminateThreadByPointerAddress)(pEThread, 0, 1); DbgPrint("PspTerminateThreadByPointer Thread:%d\n", i); } // 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏 ObDereferenceObject(pEThread); } } // 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏 ObDereferenceObject(pEProcess); return status;}
    获取 PspTerminateThreadByPointer 函数地址// 获取 PspTerminateThreadByPointer 函数地址PVOID GetPspLoadImageNotifyRoutine(){ PVOID pPspTerminateThreadByPointerAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pSpecialData[50] = { 0 }; ULONG ulSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#else // 32 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // E9 pSpecialData[0] = 0xE9; ulSpecialDataSize = 1;#else // 32 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // E9 pSpecialData[0] = 0xE9; ulSpecialDataSize = 1;#else // 32 位 // E8 pSpecialData[0] = 0xE8; ulSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspTerminateThreadByPointerAddress = SearchPspTerminateThreadByPointer(pSpecialData, ulSpecialDataSize); return pPspTerminateThreadByPointerAddress;}
    根据特征码获取 PspTerminateThreadByPointer 数组地址// 根据特征码获取 PspTerminateThreadByPointer 数组地址PVOID SearchPspTerminateThreadByPointer(PUCHAR pSpecialData, ULONG ulSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsTerminateSystemThread = NULL; PVOID pPspTerminateThreadByPointer = NULL; // 先获取 PsTerminateSystemThread 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsTerminateSystemThread"); pPsTerminateSystemThread = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsTerminateSystemThread) { ShowError("MmGetSystemRoutineAddress", 0); return pPspTerminateThreadByPointer; } // 然后, 查找 PspTerminateThreadByPointer 函数地址 pAddress = SearchMemory(pPsTerminateSystemThread, (PVOID)((PUCHAR)pPsTerminateSystemThread + 0xFF), pSpecialData, ulSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory", 0); return pPspTerminateThreadByPointer; } // 先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspTerminateThreadByPointer = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); return pPspTerminateThreadByPointer;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描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 位系统下,驱动程序正常执行:

    总结这个程序的原理不难理解,关键是如何定位 PspTerminateThreadByPointer 未导出的内核函数在 PsTerminateSystemThread 函数中的位置,要在各个版本系统上进行逆向,以确定内存特征码。
    参考参考自《Windows黑客编程技术详解》一书
    3 留言 2019-02-23 21:08:48 奖励26点积分
  • 基于ssm的酒店管理系统设计与实现

    摘 要传统的酒店管理往往需要酒店管理人花大量时间和精力来处理顾客查询、顾客登记等事务,而错误的查询、繁琐的登记和结账手册、费用的结算错误、空余客房不能及时提供等等问题,可能导致顾客的频繁投诉,从而影响酒店的出租率。这些问题都可以通过计算机辅助系统来解决。随着计算机和信息技术的飞速发展,酒店客房的管理由传统的工作模式逐渐被信息化、网络化的现代工作模式所代替。以住宿为主的酒店假如再延用传统的管理模式,就会增加酒店管理成本和降低工作效率。在酒店客房管理中融入先进的计算机和软件技术,利用酒店客房管理系统进行管理就显得十分有意义。利用酒店客房管理系统进行管理能让管理者及时了解酒店整体情况,便于各种决策,同时也简化了管理的各种复杂操作,提高了酒店的管理效率。本文采用 Javaweb,创建一个适合实际情况的酒店客房管理系统。
    关键词:酒店客房管理;java程序设计;web前端程序设计;数据库模块;统计模块
    ABSTRACTTraditional hotel management often requires hotel managers to spend a lot of time and energy dealing with customer inquiries, customer registration and other matters, while wrong inquiries, cumbersome registration and checkout manuals, fee settlement errors, the lack of timely provision of spare rooms and other issues may lead to frequent customer complaints, thus shadowing the situation. The rental rate of the hotel. These problems can be solved by computer aided system. With the rapid development of computer and information technology, hotel room management has gradually been replaced by the traditional mode of work by the modern mode of information and network. If the traditional hotel management mode is extended, the hotel management cost will be increased and the work efficiency will be reduced. It is very meaningful to integrate advanced computer and software technology into hotel room management and make use of hotel room management system. The use of hotel room management system can enable managers to timely understand the overall situation of the hotel, facilitate decision-making, but also simplify the management of various complex operations, improve the efficiency of hotel management. In this paper, we use Javaweb to create a hotel room management system suitable for the actual situation.
    Key words: Hotel room management; Java programming; web front-end programming; interface module; statistical module
    第一章 绪论酒店客房管理系统是指一种可以提高酒店管理效率的软件或平台,一般包含前台接待、前台收银、客房管家、销售POS、餐饮管理 、娱乐管理、 公关销售、财务查询、电话计费、系统维护、经理查询、工程维修等功能模块。
    1.1 课题研究背景随着经济 的迅速发展 ,酒店业的竞争日趋激烈。酒店业内不得不进一步寻求通过扩大酒店销售、改进服务质量、降低管理成本和提升客户满意度等办法来增强酒店的核心竞争力。其中最有效的手段就是应用 现代化 信息化技术,变革传统意义上的酒店业经营管理模式,跟上时代竞争的步伐。考虑到酒店业务的不断提升和用户需求的日益多样化,尽量满足酒店的个性化需求,同时吸收了同类产品及现有软件系统的优点,力争设计成为一套先进适用的酒店管理软件系统,为顾客提供更加便捷的信息化服务,为酒店管理者、决策者提供准确及时的酒店经营信息,以达到酒店节约经营成本、提高经营质量和经济效益的信息化管理目标。
    1.1.1 酒店客房管理现状在商场如战场,时间就是金钱的当今社会,只有不断提高经营效率、更新管理模式、及时把握企业的经营状况才能提高自身竞争力,才能使自己立于不败之地。随着现代信息技术的普及,越来越多的商家开始采用计算机来管理自己的业务。在应用之余,总希望有好的业务管理软件来帮助他们提高工作效益和管理水平。 
    随着我国旅游业的发展,酒店信息管理系统在此方面的需求相应的更多一些。以前的管理以人工方式处理大量的酒店客户登记、结账及一些管理工作,不可避免的增加了管理的工作量,同时也易造成人为错误,给管理者带来了不必要的麻烦和损失。 
    为了解决上述问题,使酒店客房管理更系统和便捷,准确而高效地开发数据库管理系统,使用户在实际工作中得心应手,就显得尤为重要。而本系统正是在这种时代背景下设计开发的。
    随着计算机和信息技术的飞速发展,传统的 酒店客房管理 模式逐渐被信息化的现代 酒店客房管理 模式所代替。传统的酒店管理往往令管理者花大量的人力和物力以满足各种繁琐的经营活动的需要,例如冗长的登记和结账手续、手工记录所有客房状态、列表统计顾客消费情况等。这种工作模式不但效率低下,且极易出现错误和遗漏,有时甚至会导致严重的经济损失,给酒店的经营带来负面影响。
    1.1.2 课题研究的意义管理信息系统(Management Information System简称MIS)是信息科学的一个分支,是由人、计算机和数据库组成的能进行信息的收集、传递、储存、加工、维护和使用的系统。而酒店计算机管理系统是MIS中的一个重要分支。近年来,随着我国改革开放的发展,国内的酒店业得到了飞速发展。现代酒店作为一个对外来人员的接待场所,是一个城市的窗口。对一个以旅游行业为支柱产业的城市而言,酒店有着举足轻重的作用。作为一种以服务为主的无烟工业,世界各国对此行业的重视程度并不亚于其它工业。酒店在其运行期间,服务水平的高低,直接影响到酒店的形象和声誉,如:服务的安排、调度是否周到;客人的要求是否能很好地得到满足;市场的预测分析是否快捷、准确等。这其中的核心就是对每天大量的信息(客人、费用、房间等)的正确处理和保存。采用计算机这一现代化工具作为管理的辅助手段是必须的。计算机的应用包括OA(办公自动化)、MIS(管理信息系统)、CAD(计算机辅助设计)等,酒店的计算机系统正是典型的MIS应用。而本酒店管理信息系统,是针对酒店的具体业务而开发的,业务管理以酒店的客房管理为核心,为用户提供迅速、高效的服务,减免手工处理的繁琐与误差,及时、准确地反映酒店的工作情况、经营情况,从而提高酒店的服务质量,并配合现代化的酒店管理,获得更好的经济效益。并具有如下几个特点:间接性的,其经济效益不是直接产生的,是通过对人力、物力的节省而带来的,可以堵塞许多漏洞;长期性的,计算机的投资是较大的,是在长期的应用中逐步得到回报的;社会效益,酒店是一个高层次的服务行业,采用计算机可提高服务质量,有良好的社会形象。
    对酒店整个来说,对酒店经营状况起决定作用的是酒店的服务管理水平。如何利用先进的管理手段来提高酒店的管理水平成为酒店业务发展的当务之急。面对信息时代的机遇和挑战,利用科技手段提高酒店的管理无疑是一条行之有效的途径。虽然计算机管理并不是酒店管理走向成功的关键元素,但它可以最大限度地发挥准确、快捷、高效等作用,对酒店的业务管理提供强有力的支持。因此,采用全新的计算机网络和酒店业务管理系统,已成为提高酒店的管理效率,使作业人员与管理系统之间灵活互动,实现流畅的工作流衔接,帮助酒店有效地进行业务管理,释放最大价值。酒店业务管理系统在达到在节省人力资源成本的同时,可以提高业务效率,并能够及时、准确、迅速地满足顾客服务的需求。
    1.2 国内外现状与发展趋势酒店管理系统最开始的时候是在美国,大约在六十年代末,如Ecoo系统,基本实现了酒店管理的给你,如预定、结账、餐厅、客房等模块,由于当时没有PC,所以整个系统都是在集中式的小型机上管理。
    前些年国内的酒店管理系统之所以不成气候,就是因为网络信息化技术不够,从而影响了酒店的业绩。之后,国内外的计算机技术,网络平台,新型技术点不断传入国内。国内的酒店管理系统才开始发展起来。
    现今,酒店的电脑管理和网络技术的日益提升,电脑网络服务日益昌盛。因此,在经济效益上取得了突飞猛进的进展。国家建设部门的规定中已经包含星级酒店的设计方案中必须包含电脑管理系统。这就是网络化的体现。
    1.3 论文结构和内容
    第一章,结合酒店管理系统发展的现状,介绍酒店管理系统发展背景、课题研究意、以及在国内外现状与发展趋势
    第二章,介绍本次项目所运用到的主要、核心技术,以及他们各自的特点
    第三章,利用场景分析等软件工程需求分析方法[9],进行项目各个功能模块的进行需求分析
    第四章,通过E-R图、数据流图等图表与文字来陈述本次总体设计和数据库设计
    第五章,详细叙述本系统各个数据库各个模块的组成及实现结果
    第六章,详细叙述本系统各个功能模块具体的功能以及实现结果

    第二章 相关技术简介2.1 javaJava是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。
    Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。
    Java看起来设计得很像C++,但是为了使语言小和容易熟悉,设计者们把C++语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的。例如,Java不支持go to语句,代之以提供break和continue语句以及异常处理。Java还剔除了C++的操作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。因为Java没有结构,数组和串都是对象,所以不需要指针。Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集,使用户不必为存储管理问题烦恼,能更多的时间和精力花在研发上
    2.2 servlrtServlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
    狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
    最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
    2.3 HTML超文本标记语言(Hyper Text Markup Language),标准通用标记语言下的一个应用。HTML 不是一种编程语言,而是一种标记语言 (markup language),是网页制作所必备的。“超文本”就是指页面内可以包含图片、链接,甚至音乐、程序等非文字元素。超文本标记语言(或超文本标签语言)的结构包括“头”部分、和“主体”部分,其中“头”部提供关于网页的信息,“主体”部分提供网页的具体内容。
    超级文本标记语言是标准通用标记语言下的一个应用,也是一种规范,一种标准,它通过标记符号来标记要显示的网页中的各个部分。网页文件本身是一种文本文件,通过在文本文件中添加标记符,可以告诉浏览器如何显示其中的内容(如:文字如何处理,画面如何安排,图片如何显示等)。浏览器按顺序阅读网页文件,然后根据标记符解释和显示其标记的内容,对书写出错的标记将不指出其错误,且不停止其解释执行过程,编制者只能通过显示效果来分析出错原因和出错部位。但需要注意的是,对于不同的浏览器,对同一标记符可能会有不完全相同的解释,因而可能会有不同的显示效果。
    2.4 JavaScriptJavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。
    在1995年时,由Netscape公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。因为Netscape与Sun合作,Netscape管理层希望它外观看起来像Java,因此取名为JavaScript。但实际上它的语法风格与Self及Scheme较为接近。[1]
    为了取得技术优势,微软推出了JScript,CEnvi推出ScriptEase,与JavaScript同样可在浏览器上运行。为了统一规格,因为JavaScript兼容于ECMA标准,因此也称为ECMAScript。
    2.5 CSS层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。CSS不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。[1]
    CSS 能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。
    2.6 myeclipseMyEclipse企业级工作平台(MyEclipseEnterprise Workbench ,简称MyEclipse)是对EclipseIDE的扩展,利用它我们可以在数据库和JavaEE的开发、发布以及应用程序服务器的整合方面极大的提高工作效率。它是功能丰富的JavaEE集成开发环境,包括了完备的编码、调试、测试和发布功能,完整支持HTML,Struts,JSP,CSS,Javascript,Spring,SQL,Hibernate [1] 。
    MyEclipse 是一个十分优秀的用于开发Java, J2EE的 Eclipse 插件集合,MyEclipse的功能非常强大,支持也十分广泛,尤其是对各种开源产品的支持十分不错。MyEclipse可以支持Java Servlet,AJAX,JSP,JSF,Struts,Spring,Hibernate,EJB3,JDBC数据库链接工具等多项功能。可以说MyEclipse是几乎囊括了目前所有主流开源产品的专属eclipse开发工具。
    第三章 需求分析3.1 系统目标本系统需要满足以下几个系统设计目标:

    实用性原则:真正为酒店工作人员的实际工作服务,按照酒店客房管理工作的实际流程,设计出实用的酒店客房管理系统
    安全性原则:必须为酒店客房提供信息安全的服务,以保证酒店信息的不被泄露
    可操作性原则:本酒店客房管理系统面向的是酒店内工作人员,所以系统操作上要求简单、方便、快捷,便于用户使用
    可扩展性原则:采用开发的标准和接口,便于系统向更大的规模和功能扩展。

    3.2 系统需求根据酒店客房管理系统的理念,此酒店客房管理系统必须满足以下需求:

    具有设置酒店客房类型和房间信息的功能
    能快速、准确地了解酒店的客房状态,以便订房和退房
    提供多种手段查询客房订房信息
    提供修改订房和修改退房功能
    提供简单的酒店工作人员的添加住户和续费房间功能

    3.3 功能需求
    利用系统设置中的登录模块可以进行管理员登录
    客房管理模块主要是对客房进行设置和查询
    预定管理模块主要是对住宿登记、住房时间、房间类型和住户信息
    入住模块主要对客户预定好房间来登记入住开始
    挂账查询模块主要是对挂账和客户结款进行查询、调房登记、续费房间和退宿结账进行管理
    查询统计模块主要是对住宿、退宿进行查询以及对宿费进行提醒
    日结模块主要是对登记预收、客房销售进行报表管理以及对客房销售进行统计
    数据库模块是对客房信息、住户信息和收入信息的统计,不易丢失
    系统维护主要是对数据备份和恢复进行维护

    3.4 系统性能需求为了保证系统能够长期、安全、稳定、可靠、高效的运行,系统应该满足以下的性能需求:
    3.4.1 系统处理的准确性和及时性系统处理的准确性和及时性是系统的必要性能。在系统设计和开发过程中,要充分考虑系统当前和将来可能承受的工作量,使系统的处理能力和响应时间能够满足用户对信息的处理。由于系统的查询功能对于整个系统的功能和性能完成很重要。从系统的多个数据来源来看, 客房信息查询、订房 信息查询、 结算 信息查询,其准确性很大程度上决定了系统的成败。
    因此,在系统开发过程中,系统采用优化的 SQL 语句及安全扩展存储过程来保证系统的准确性和及时性。
    3.4.2 系统的开放性和系统的可扩充性系统在开发过程中,应该充分考虑以后的可扩充性。例如 系统权限和客房信息设置 等模块也会不断的更新和完善。所 有这些 都要求系统提供足够的手段进行功能的调整和扩充。而要实现这一点,应通过系统的开放性来完成,既系统应是一个开放系统,只要符合一定的规范,可以简单的加入和减少系统的模块,配置系统的硬件。通过软件的修补、替换完成系统的升级和更新换代。
    3.4.3 系统的可操作性本酒店客房管理系统面向的用户是酒店内工作人员, 而有些使用人员往往对计算机并不是非常熟悉 ,所以系统操作上要求简单、方便、快捷,便于用户使用。 这就要求系统能够提供良好的用户接口,易用的人机交互界面。
    3.4.4 系统的响应速度系统设计中摒弃大量数据冗余,提出了优化数据库的解决方案,大量使用存储过程,大大提高系统响应时间和速度。系统在日常处理中的响应速度为秒级, 达到实时要求,以及时反馈信息。严格保证操作人员不会因为速度问题而影响工作效率。
    3.4.5 开发技术本系统利用myeclipse、Tomcat编译环境,采用可视化编程,以 SQLsever作为后台数据库。

    计算机及操作系统: Windows10
    开发工具:myeclipse
    运行环境 : javaJDK
    语言: java,HTML,JavaScript 与 SQL 查询语言

    第四章 系统总体功能4.1 系统功能结构
    4.2 系统功能流程图
    第五章 数据库设计5.1 数据库定义数据库名为:db,登录用户名为sa,口令默认为********。该登录用户的服务器脚色是“sysadmin”,具有数据库db的任何权限。
    5.2 数据库表下面数据表中,标记为“*”的是主键,标记为“#”的为外键。其它的缩写为:VC:varchar、NC:nchar、NVC:nvarchar。
    表1、操作人员 usertab




    字段名称
    类型大小

    说明
    举例




    1
    uid
    VC(20)
    ×
    操作者帐号
    4-20位,数字字母


    2
    upwd
    NVC(20)
    ×
    操作者姓名
    2-10位字符或汉字



    “系统管理”用户可以增改操作人员,授予操作权限,以及全部的维护与浏览权限。
    表2、酒店房间 room




    字段名称
    类型大小

    说明
    举例




    1
    roomNumber*
    Char(6)
    ×
    酒店房间编号
    0001,0002


    2
    roomType#
    VC(32)
    ×
    酒店房间类型
    单人间,双人间


    3
    price
    VC(32)
    ×
    酒店房间价格
    200,300



    酒店房间规定了房间号,类型,价格方便查找顾客需要的房间与剩余房间查询。
    表3、酒店房间类型与价格 roomTypeAndPrice




    字段名称
    类型大小

    说明
    举例




    1
    roomType#
    VC(32)
    ×
    酒店房间类型
    单人间,双人间


    2
    price
    Int
    ×
    酒店房间价格
    200,300



    方便查看酒店类型与价格。
    表4、顾客信息表 customers




    字段名称
    类型大小

    说明
    举例




    1
    customerIDCard*
    char(18)
    ×
    顾客身份证号码
    18位身份证号


    2
    customerGender
    Char(4)
    ×
    顾客性别
    女,男


    3
    customerName
    VC(16)
    ×
    顾客姓名
    *


    4
    customerPhoneNumber
    char(11)
    ×
    顾客手机号码
    11位手机号码


    5
    totalAmount
    int

    消费金额
    200,300



    记录顾客信息,并且及时更新。
    表5、入住信息表 orders




    字段名称
    类型大小

    说明
    举例




    1
    orderNumber*
    int
    ×
    订单号
    1,2,3


    2
    orderStatus
    VC(50)
    ×
    订单状态
    预订中,已入住,已退房


    3
    customerIDCard#
    char(18)
    ×
    顾客身份证号
    18位身份证号


    4
    roomNumber#
    char(6)
    ×
    酒店房间编号
    0001,0002


    5
    roomType#
    VC(32)
    ×
    酒店房间类型
    单人间,双人间


    6
    checkInTime
    date
    ×
    入住时间
    2019-7-1


    7
    checkOutTime
    date
    ×
    离店时间
    2019-7-2


    8
    totalMoney
    int
    ×
    需付金额
    200


    9
    orderTime
    date
    ×
    预定时间
    2019-7-1



    记录房间状态。方便预定,退订和更改房间状态。
    表6、房间延期表 timeExtension




    字段名称
    类型大小

    说明
    举例




    1
    operatingID*
    int
    ×
    操作记录号
    1,2,3


    2
    orderNumber
    int
    ×
    操作的订单号
    1,2,3


    3
    oldExpiryDate
    date
    ×
    住房原到期日期
    2019-7-1


    4
    newExpiryDate
    date
    ×
    住房新到期日期
    2017-7-2


    5
    addMoney
    int
    ×
    需要添加的金额
    200,300



    方便操作预定房间过程中出现的问题。
    5.3 视图的定义视图命名格式为View_***。特别指出,在一个视图B依存另个视图A时,必须先创建视图A,然后才创建视图B。此时,在试图命名时必须按视图名称A排序在前。例如:



    序号
    视图名称
    说明




    1
    View_incomeView
    收入视图


    2
    View_roomInfoView
    房间视图


    3
    View_orderview
    订单视图



    第六章 各功能模块6.1 系统总体结构设计如下图所示,整个酒店管理系统包含三个模块:

    bean模块:用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变
    servlet模块:客户端发送请求至服务器;服务器启动并调用 Servlet,Servlet 根据客户端请求生成响应内容并将其传给服务器;服务器将响应返回客户端
    WebRoot模块:是JAVA WEB项目中用来存放JSP,JS,CSS,图片等文件的,其中webroot/WEB-INF用来存放SRC编译好的相关的文件,和需要被保护的JSP文件等










    项目结构1
    项目结构2



    6.2 软件界面设计6.2.1 登录界面设计

    String username = request.getParameter("user");String upwd = request.getParameter("pwd");PrintWriter out = response.getWriter();Connection conn;try { Class.forName(driverName); try{ conn = DriverManager.getConnection(url,user,pwd); String sql = "select *from usertab where uid = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ResultSet rs = ps.executeQuery(); String mima = ""; while(rs.next()){ mima = rs.getString("upwd").trim(); } if(mima.equals(upwd)){ HttpSession session = request.getSession(); session.setAttribute("un", username); out.println("true");
    通过数据库中储存的管理员信息,进行验证并且登录进入酒店管理系统。
    6.2.2 用户预定界面设计

    String sql_customer = "insert customers values('"+customerIDCard+"','"+customerGender+"','"+customerName+"','"+customerPhoneNumber+"',"+price+")";String sql_orders = "insert orders values('"+orderStatus+"','"+customerIDCard+"','"+roomNumber+"','"+roomType+"','"+checkinTime+"','"+checkOutTime+"',"+price+",'"+orderTime+"')";try { Class.forName(driverName); try { conn = DriverManager.getConnection(url,user,pwd); Statement st = conn.createStatement(); st.executeUpdate(sql_customer); st.executeUpdate(sql_orders); System.out.print("插入成功!");
    利用数据库SQL语句进行客户的插入,并且将数据储存到数据库中。
    6.2.3 用户入住界面设计

    String sql_leave = "update orders set orderStatus = '已入住' where orders.customerIDCard = '"+customerIDCard+"'";String sql_query = "select * from orders where customerIDCard = '"+customerIDCard+"'";Connection conn = null;try { Class.forName(driverName); try { conn = DriverManager.getConnection(url,user,pwd); Statement st = conn.createStatement(); st.execute(sql_leave); ResultSet rs = st.executeQuery(sql_query); List<Map> list = new ArrayList<Map>(); while(rs.next()){ String orderNumber = rs.getString("orderNumber"); String orderStatus = rs.getString("orderStatus"); customerIDCard = rs.getString("customerIDCard"); String roomNumber = rs.getString("roomNumber"); String checkInTime = rs.getString("checkInTime"); String checkOutTime = rs.getString("checkOutTime"); String totalMoney = rs.getString("totalMoney"); String orderTime = rs.getString("orderTime"); Map e = new HashMap(); e.put("orderNumber", orderNumber); e.put("orderStatus",orderStatus); e.put("customerIDCard", customerIDCard); e.put("roomNumber",roomNumber); e.put("checkInTime",checkInTime); e.put("checkOutTime",checkOutTime); e.put("totalMoney",totalMoney); e.put("orderTime",orderTime); list.add(e);
    通过SQL语句将预定的顾客状态改为入住,并储存到数据库中。
    6.2.4 用户续费界面设计

    String addDay = request.getParameter("addDay");String sql = "declare @addMoney int,@orderNumber int,@oldExpiryTime date,@newExpiryTime date exec dbo.getPrice '"+roomNumber+"',"+addDay+",@addMoney output,@orderNumber output,@oldExpiryTime output,@newExpiryTime output select @addMoney as addMoney,@orderNumber as orderNumber,@oldExpiryTime as oldExpiryTime,@newExpiryTime as newExpiryTime";Connection conn = null;try { Class.forName(driverName); try { conn = DriverManager.getConnection(url,user,pwd); Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(sql); List<Map> list = new ArrayList<Map>(); while(rs.next()){ String addMoney = rs.getString("addMoney"); String orderNumber = rs.getString("orderNumber"); String oldExpiryTime = rs.getString("oldExpiryTime"); String newExpiryTime = rs.getString("newExpiryTime"); Map e = new HashMap(); e.put("addMoney", addMoney); e.put("orderNumber",orderNumber); e.put("oldExpiryTime",oldExpiryTime); e.put("newExpiryTime",newExpiryTime); list.add(e); }
    6.2.5 用户退房界面设计

    String sql_leave = "update orders set orderStatus = '已退房' where orders.customerIDCard = '"+customerIDCard+"'";String sql_query = "select * from orders where customerIDCard = '"+customerIDCard+"'";Connection conn = null;try { Class.forName(driverName); try { conn = DriverManager.getConnection(url,user,pwd); Statement st = conn.createStatement(); st.execute(sql_leave); ResultSet rs = st.executeQuery(sql_query); List<Map> list = new ArrayList<Map>(); while(rs.next()){ String orderNumber = rs.getString("orderNumber"); String orderStatus = rs.getString("orderStatus"); customerIDCard = rs.getString("customerIDCard"); String roomNumber = rs.getString("roomNumber"); String checkInTime = rs.getString("checkInTime"); String checkOutTime = rs.getString("checkOutTime"); String totalMoney = rs.getString("totalMoney"); String orderTime = rs.getString("orderTime"); Map e = new HashMap(); e.put("orderNumber", orderNumber); e.put("orderStatus",orderStatus); e.put("customerIDCard", customerIDCard); e.put("roomNumber",roomNumber); e.put("checkInTime",checkInTime); e.put("checkOutTime",checkOutTime); e.put("totalMoney",totalMoney); e.put("orderTime",orderTime); list.add(e); }
    6.2.6 收入视图界面设计
    6.2.7 订单视图界面设计
    6.2.8 房间视图界面设计
    6.2.9 续费视图界面设计
    各个视图
    String sql_order = "select * from orderView";//订单视图查询语句String sql_roomInfoView = "select * from roomInfoView";//房间信息视图String sql_timeExtension = "select * from timeExtensionOrdersView";//续费订单视图
    6.2.10 其他各小功能界面设计
    6.3 软件系统编程在编程中,采用的一种Javaweb体系,主要分为3大层次结构,但由于编写方便,本设计在编程另外增加了一个工具层,用于给其他各层提供编写方便,如图4-16为系统编程的3大层次,底层为3大模块,有数据库直接操作,文件直接操作,网络接口直接操作;中层为后台操作层,主要做数据处理和上下层的连接工作,软件工作的核心在于中层的后台操作,界面层主要通过后台提供的数据接口来绘制各种窗口及动画过程。最顶层为界面层,分为5大主界面编程。为了达到软件美观的效果,本设计用到了一些图片美化软件,这些都是很细致的活,美化的图片必需与软件编程时的控件相结合,有时还真是一个个像素点的修正,可以在上面的各图中看出背景与控件的适应性都还不错。
    系统统计总共有60多个java程序,总代码量过万,此次的酒店管理系统编程运用了很多Javaweb新知识,例如servlet,数据库批处理技术,图片缓存技术,等等。
    第七章 调试与结果7.1 初始化系统登录尽管在软件工程科学的方法指导下完成了系统的设计与开发,但是由于软件系统是一个关系紧密且复杂的逻辑系统,因此仅凭严格的设计以及严格的开发流程并不能够完全确保系统不会出现任何缺陷。不存在缺陷的系统是不可能存在的,但是通过软件测试,我们能够尽可能多的在软件系统投入使用前发现目前存在的缺陷并对缺陷进行修复。软件测试与维护这个生命周期是软件生命周期中最长的亦是最重要的,这不仅保证了系统的稳定性以及满足预定的需求,也能够使得软件生命得到延续。因此,通过软件测试对系统进行评估,找到其中隐藏的缺陷并对其加以修复是很有必要的。
    软件测试是为了完善系统质量的技术,通过翻阅书籍,发现大多对软件测试的定义为使用人工或自动的手段来运行某个系统的过程,其目的在于检验它是否满足规定的需求,或是弄清预期结果与实际结果的区别。
    系统登录时,输入数据库中录入的管理人员账号及密码;


    登录成功。
    7.2 住房查询前提:管理人员已经登录。



    测试步骤
    预期结果
    实际结果
    是否通过




    1.进入酒店管理页面 2.进行输入身份证号码入住
    显示入住成功的信息
    显示该身份证号定的房间是已入住状态
    通过


    1. 进入酒店管理页面 2. 选择“订房”
    显示该客户订房成功
    数据库中储存该客户
    通过


    1. 进入酒店管理页面 2. 选择“退订”
    显示该客户退房成功
    数据库中显示该客户已退订
    通过



    参考文献
    [1] 郑人杰,殷人昆,陶永雷.实用软件工程(第二版)[M].北京:清华大学出版社.1997.
    [2] 萨师煊,王珊.数据库系统概论[M].高等教育出版社,2000.7:21-347.
    [3] (美)Bruce Eckel、陈昊鹏、饶若楠等.Java编程思想第3版[M].北京:机械工业出版社.2005.
    [4]候炳辉,刘世峰.信息管理系统[J].信息管理系统分析,2004.5:254-562.
    [5] 彭伟民.基于需求的酒店管理系统的建模与实现.微机发展,2005.10.1-6.
    [6] 薛华成.管理信息系统[M].清华大学出版社,1996.7.2-5.
    [7] 《java核心技术》机械工业出版社(美国)Cay Shorstmann,Gary Cornell著由 叶乃文 翻译。
    [8] 张亚东.酒店企业销售管理信息系统的设计与实现[J].管理信息系统, 2000.9:45249.
    [9] 《java 学习笔记 JDK6.0》清华大学出版社(台湾)良格葛编写。
    3 留言 2020-06-23 12:40:13 奖励66点积分
  • weka数据分类小实验

    一、实验目的
    熟悉weka基本功能和使用方法
    学习对数据集进行分类训练并测试
    比较不同分类算法对本实验测试集预测的准确率

    二、实验环境
    平台:Weka3.8
    数据集:将Weka的data文件夹下默认数据集vote.arff的前20个实例作为测试集,其余实例作为训练集

    三、实验概况
    观察数据集的属性类别以及实例来确定是否进行过滤处理
    利用训练集分别采用C4.5决策树分类器、基于规则的分类器和K最近邻三种算法进行分类训练,并对测试集进行预测
    比较三种分类器对本实验数据集分类模型的优劣

    四、实验内容4.1 观察数据集在weka中打开测试集votetest.arff,观察到数据集共有435个实例,每个实例是一个国会议员的投票信息以及派别,共有17个二元属性,其中一个为类别属性。并且该数据集带有一定的缺失值。国会议员通常按照其党政路线进行投票,本实验通过对议员投票情况(16个属性)对其类别属性进行分类,得到两种派系对政策投票的大致方案。数据集中数据没有与实验无关属性,不进行过滤。

    4.2 使用C4.5决策树算法进行分类训练C4.5决策树算法能够处理具有缺省值的数据,使用信息增益率作为属性选择标准,能对生成树剪枝(参考《数据挖掘与机器学习—WEKA应用技术与实践》)。C4.5在weka中的实现是J48决策树。选择J48进行分类。




    训练结果
    使用C4.5决策树分类器训练数据集(435个实例),得到树形结构如上图所示,共有6个叶子节点。分类模型的准确率为97.2414%,正确分类的实例有423个,Kappa统计量为0.9418,平均绝对误差为0.0519,ROC面积为0.986;混淆矩阵中被错误分类的数据:6个republican被误分为democrat,6个democrat被误分为republican。
    测试:使用测试集进行预测



    预测结果
    预测准确率为95%,ROC面积为0.939,20个实例中有19个预测正确,一个错误。根据混淆矩阵得:一个republican被错误分类到democrat。
    4.3 基于规则的分类器进行分类训练分类模型的规则使用析取范式R=(r1 V r2 V … V rk),规则ri的形式:(Condition)->yi,规则左边是属性测试的合取,右边为预测类别。本实验采用的JRip分类器实现了命题规则学习,重复增量修剪以减少产生错误。(参考《数据挖掘与机器学习—WEKA应用技术与实践》)
    分类训练构建模型



    训练结果
    分类训练得到的规则共有4个。分类模型的准确率为96.5517%,正确分类的实例有420个,Kappa统计量为0.9277,平均绝对误差为0.0615,ROC面积为0.976;混淆矩阵中被错误分类的数据:10个republican被误分为democrat,5个democrat被误分为republican。
    预测



    预测结果
    预测准确率为95%,ROC面积为0.939,20个实例中有19个预测正确,一个错误。根据混淆矩阵得:一个republican被错误分类到democrat。
    4.4 基于K最近邻算法的分类器进行分类训练通过找出与测试样本相近的训练样本,用最近邻的类别标签确定测试样本的类别标签。IBK分类器是一种k-最邻近分类器,可用多种不同搜索算法加快寻找最近邻。(参考《数据挖掘与机器学习—WEKA应用技术与实践》)



    训练结果
    K-最邻近分类模型的准确率为99.7701%,正确分类的实例有434个,Kappa统计量为0.9951,平均绝对误差为0.0049,ROC面积为1.000;混淆矩阵中被错误分类的数据:1个democrat被误分为republican。
    预测



    预测结果:对20个实例的预测全部正确,预测正确率为100%。
    五、实验分析
    比较分析:在本实验中KNN算法相比于C4.5决策树、规则分类具有更好的分类效果。
    六、实验总结根据议员对各个法案的投票,采用KNN算法可以进行更准确的分类,并根据分类模型可以大概率地推测出议员所属党系。一个议员对法案的投票通过与否基本不会偏离所属党派的主要策略。那么一个议员的属性在特征空间里相似的样本中大多属于某一类,则说明其观点大多一致,属于同一党派。KNN算法对这种党派分类与现实符合,从实例具有的全部属性进行判断。而决策树以及规则的分类模型条件划分是对个别属性是否来判断确定,但是对党派的划分条件不应该只根据局部的几个属性进行判断,所以从现实角度看这两种算法也不合适。
    0 留言 2020-07-15 17:21:52 奖励26点积分
  • 【课程笔记】南大软件分析课程11——CFL可达性&IFDS


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/2bd21a34eb8b

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    Infeasible and Realizable Paths——基本概念CFL-Reachablity(IFDS的理论基础,识别Realizable path)Overview of IFDSSupergraph(之前叫iCFG) and Flow FunctionsExploded Supergraph and Tabulation AlgorithmUnderstanding the Distributivity of IFDS
    重点IFDS:Interprocedural,Finite,Distributive,Subset Problem。
    理解CFL-Reachablity,工作机制—括号匹配的过程。
    IFDS的大致原理。
    哪些问题可以利用IFDS,目标问题是否可以利用IFDS,取决于其是否满足可分配性。
    目标:以图可达性分析来进行程序分析,没有了数据流传播的过程。
    1.Infeasible and Realizable Paths——基本概念Infeasible Paths:CFG中实际不会执行到的路径,如不匹配的调用返回边。这种路径可能会影响到程序分析的结果,但静态分析不能完全判定路径是否可达。
    Realizable Paths:跨函数调用产生的返回边和对应的callsite边匹配,这样的path。

    目标:识别Realizable path,避免沿着Unrealizable path来传播数据分析。
    方法:CFL-Reachablity。

    2.CFL-Reachablity(IFDS的理论基础,识别Realizable path)CFL-Reachablity:path连接A和B,或者说A能到达B,当且仅当path所有边的labels连接起来 是context-free language(CFL)中的一个word(或者说经过CFG语法变换之后可以得到该path word)。CFL是编译原理中的概念,遵循CFG语法。
    Context-Free Grammar (CFG):CFG是一个形式化语法,每一个产生的形式都是 S→αS \to \alphaS→α(α\alphaα可以是字符串也可以是空ε\varepsilonε)。EgEgEg,S→aSbS \to aSbS→aSb,S→εS \to \varepsilonS→ε;Context-Free表示不管S出现在哪(不管上下文),S都可以被替换成aSb/εaSb / \varepsilonaSb/ε。
    部分括号匹配问题(Partially Balanced-Parenthesis):利用CFL来解决括号匹配问题,以识别Realizable path。

    部分——有)i一定要有(i,反之有(i不一定要有)i,也即可以调用子函数而不返回。
    标记,调用边——(i;返回边——)i;其他边——e。


    CFL-Reachablity:若path word(所有edge的label连起来组成的单词)可用CFL L(realizable)表示(可进行各种替换),则为Realizable Path。示例如下,(1(2e)2)1(_1(_2e)_2)_1(​1​​(​2​​e)​2​​)​1​​(3就是边的label相连接形成的,绿色是可匹配的部分,realizable可被替换为matched realizable、(i realizable、ε\varepsilonε。语法替换规则如下,这也是一个CFL语言示例:

    利用CFL分析Realizable Path示例:右边显然不是Realizable Path。

    3.Overview of IFDSIFDS含义:Interprocedural,Finite,Distributive,Subset Problem。Interprocedural—全程序分析,Finite—域有限(如live variables、definitions),Distributive—Transfer Function满足f(a∪b)=f(a)∪f(b)f(a \cup b)=f(a) \cup f(b)f(a∪b)=f(a)∪f(b),Subset—子集问题。
    利用图可达性的程序分析框架:采用的操作——Meet-Over-All-Realizable-Paths(MRP),MRPn⊑MOPnMRPn \sqsubseteq MOPnMRPn⊑MOPn。MOP对所有路径进行meet操作,MRP只对realizable path进行meet操作,更准确。
    IFDS步骤:给定程序P,数据流分析问题Q。

    1.构造P的supergraph G∗G^*G​∗​​,根据问题Q定义G∗G^*G​∗​​上每条边的流函数。
    2.构造P的exploded supergraph G♯G ^ \sharpG​♯​​,将流函数转化为Representation relation(分解后变成小子图形式)
    3.问题Q变成图可达性问题(寻找MRP解),对G♯G ^ \sharpG​♯​​采用Tabulation算法。n—程序点,data fact d∈MRPnd \in MRPnd∈MRPn,当且仅当G♯G ^ \sharpG​♯​​中存在一条<Smain,0>→<n,d><Smain, 0> \to <n, d><Smain,0>→<n,d>的 realizable path。


    4.Supergraph(之前叫iCFG) and Flow Functions(1)IFDS步骤一:构造Supergraph说明:之前叫iCFG,给每个node定义transfer function;现在叫做Supergraph,给每个edge定义transfer function。
    Supergraph:G∗=(N∗,E∗)G^*=(N^*, E^*)G​∗​​=(N​∗​​,E​∗​​)。

    G∗G^*G​∗​​包含所有的流图G1G_1G​1​​, G2G_2G​2​​, … (每个函数对应一个流图,本例对应GmainG_{main}G​main​​和GpG_pG​p​​);
    每个流图GpG_pG​p​​都有个开始节点sps_ps​p​​和退出节点epe_pe​p​​;
    每个函数调用包含调用节点CallpCall_pCall​p​​ + 返回节点RetpRet_pRet​p​​。
    函数调用有3类边:

    过程内call-to-return-site边,从Callp→RetpCall_p \to Ret_pCall​p​​→Ret​p​​;
    过程间call-to-start边,从Callp→spCall_p \to s_pCall​p​​→s​p​​(sps_ps​p​​是被调用函数的开头);
    过程间exit-to-return-site边,从ep→Retpe_p \to Ret_pe​p​​→Ret​p​​(epe_pe​p​​是被调用函数的结尾)。



    (2)IFDS步骤一:设计流函数问题Q:假设问题Q是找可能未被初始化的变量,对每个节点n∈N∗n \in N^*n∈N​∗​​,找到执行到n时可能未被初始化的变量集合。
    说明:λ\lambdaλ——lambda 中’.’左侧的S表示输入的集合,右边表示对S的操作。对于未初始化变量问题,遇到var x;指令则 λS.S∪x\lambda S.S \cup {x}λS.S∪x—加入x变量;如遇x:=...;指令则λS.S−x \lambda S.S-{x}λS.S−x—去掉x;遇到无关指令则λS.S \lambda S.SλS.S—不变。
    示例:

    call-to-callee把与callee直接相关信息传递进去,如用形参替换实参;
    exit-to-return边把形参相关信息剔除;
    call-to-return-site只传递局部变量,排除全局变量g,降低误报。全局变量已经传入到被调用函数进行处理了,全局变量是否被初始化取决于被调用函数。


    5.Exploded Supergraph and Tabulation Algorithm(1)IFDS步骤二:构造exploded supergraphExploded Supergraph G♯G ^ \sharpG​♯​​:将trans func转换成边的关系representation relations(graph),每个流函数变成了有2(D+1)个节点,边数最多(D+1)2,D表示dataflow facts元素个数(如待分析的变量个数)。G∗G^*G​∗​​中每个结点n被分解成D+1个结点,每条边n1→n2n_1 \to n_2n​1​​→n​2​​被分解成representation relation。
    representation relation:用Rf表示,流函数-f。Rf⊆(D∪0)×(D∪0)R_f \subseteq (D \cup 0) \times (D \cup 0)R​f​​⊆(D∪0)×(D∪0)。RfR_fR​f​​规则如下:

    0→00 \to 00→0始终有一条边;
    0→d10 \to d_10→d​1​​,y∈f(∅)y \in f( \varnothing)y∈f(∅) 若没有任何输入也能得到y,则加上该边;
    d1→d2d_1 \to d_2d​1​​→d​2​​,y可以从x得到,但不能从0得到,则加上该边;
    还有一条,di→did_i \to d_id​i​​→d​i​​,与did_id​i​​无关时自己连自己,保持可达性。

    示例:
    (1)输入S是什么输出就是什么,1/3;
    (2)无论什么输入,都输出{a},1/2;
    (3)b是无条件产生,所以0→b,a不能往下传了,b已经从0可达了就不用加b→b,c不受影响,也即无论有关a和b的事实之前是什么样,都不再重要;
    (4)b通过a得到所以a→b,不影响a、c的传递。注意,这里的值不是说变量在程序中真正的值是多少,而是说有关此变量的数据流事实的值是什么,如a的值可以为被初始化了和未被初始化两种,对应的集合即不包括和包括a。

    问题:为什么需要0→00 \to 00→0的边?以往数据流分析中,确定程序点(结点)p是否包含data fact a,是看a是否在OUT[p]中;IFDS中,是看<smain,0><s_{main}, 0><s​main​​,0>是否能到达<p, a>。如果没有0→00 \to 00→0的边,则无法完全连通,所以0→00 \to 00→0又称为Glue Edge。
    构建G♯G^ \sharpG​♯​​示例:最后能从<smain,0>→<emain,g><s_{main}, 0> \to <e_{main}, g><s​main​​,0>→<e​main​​,g>(要通过realizable paths),则emaine_{main}e​main​​点的g是可能未初始化的。emaine_{main}e​main​​处的x和nPrint(a,g)n_{Print(a,g)}n​Print(a,g)​​处的g都是初始化过的,因为从smains_{main}s​main​​不可达(不能通过non-realizable paths——绿色线)。



    (2)IFDS步骤三:Tabulation算法——判断是否可达说明:实心圈表示从<smain,0><s_{main}, 0><s​main​​,0>通过realizable paths可达,空心圈表示不可达。
    目标:给定exploded supergraph G♯G^ \sharpG​♯​​,Tabulation算法通过寻找从<smain,0><s_{main}, 0><s​main​​,0>的所有realizable paths来确定MRP解。也即n—程序点,data fact d∈MRPnd \in MRPnd∈MRPn,当且仅当G♯G^ \sharpG​♯​​中存在一条<Smain,0>→<n,d><S_{main}, 0> \to <n, d><S​main​​,0>→<n,d>的realizable path。
    Tabulation算法:复杂度是O(ED3)O(ED^3)O(ED​3​​),E—supergraph的边数,D是域中待分析元素的个数。主要工作:括号匹配+路径探索。主要就是处理调用边、返回边、总结边,将间接可达的两结点直接连起来,每个结点用Xn存储当前所有可达的data fact。

    calledProc: 把函数调用结点(call)和代表被调用函数名关联上
    returnSite: 把call结点和return结点连起来
    callers: 把函数名映射到call结点所形成的集合关联
    procOf: 把函数结点和它的函数主体关联


    Tabulation算法工作原理:假设只关注1个data fact,p’被p和p’’同时调用。

    处理括号匹配:每次处理到返回点ep′e_{p^{\prime}}e​p​′​​​​时,开始括号匹配(call-to-return匹配),找到调用点(Callp,Callp′′)(Callp, Call{p^{\prime \prime}})(Callp,Callp​′′​​)和相应的返回点(Retp,Retp′′)(Retp, Ret{p^{\prime \prime}})(Retp,Retp​′′​​)。
    处理总结边——SummaryEdge:总结边—<Call,dm>→<Ret,dn><Call,d_m> \to <Ret,d_n><Call,d​m​​>→<Ret,d​n​​>,表示dmd_md​m​​通过调用p′p^{\prime}p​′​​能到达pnp_np​n​​,要避免重复处理ppp和p′′p^{\prime \prime}p​′′​​中调用同一函数p′p^{\prime}p​′​​(优化)。


    Tabulation算法优点:传统的worklist算法是利用了queue的特性,每次循环只考虑与被改变值结点的相关结点。论文中用于解决图可达问题的Tabulation 算法是基于worklist的动态规划算法,比传统worklist算法考虑interprocedure问题更精确也更省时。
    6.Understanding the Distributivity of IFDS问题:不能用IFDS进行常量传播分析、指针分析。
    原因:由IFDS的框架决定,一次只能处理1个变量。例如,表示若x和y都存在则…,无法表示这种关系。不满足F(x^y)=F(x)^F(y)。
    总结:给定语句S,如果输出取决于多个输入的data fact,则该分析不具备可分配性,不能用IFDS表达。IFDS中,每个data fact(圆圈)与其传播(边)都可以各自处理,且不影响最终结果。
    指针分析:箭头表示变量是否指向new T,但由于缺乏别名信息alias(x,y) / alias(x.f,y.f),导致分析结果错误。归根结底,要想在IFDS中获取别名信息alias(x,y),需要考虑多个输入data fact(x、y),所以不能用IFDS。

    参考用求解图内节点是否可达的算法来解决IFDS问题
    0 留言 2020-07-22 10:12:37 奖励30点积分
  • 【课程笔记】南大软件分析课程10——基于Datalog的程序分析


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/a8930401dee9

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    MotivationDatalog介绍Datalog实现指针分析Datalog实现污点分析
    重点Datalog语法,如何利用Datalog实现指针分析和污点分析。
    本节课内容讲到了很多数据逻辑方面的应用,单上数理逻辑会觉得理论性太强,单上这节课的应用知识又觉得理论上不够严谨,总之算是一种互补。
    1.Motivation内容:了解命令式 vs 声明式语言,对比两种语言实现指针分析算法的优劣。
    // 问题:从一群人中挑出成年人。// 命令式语言(Imperative):详细的命令机器怎么(How)去处理一件事情以达到你想要的结果(What)。如JavaSet<Person> selectAdults(Set<Person> persons) { Set<Person> result = new HashSet<>(); for (Person person : persons) if (person.getAge() >= 18) result.add(person); return result; }// 声明式语言(Declarative):只告诉你想要的结果(What),机器自己摸索过程(How)。如SQL,代码更简洁SELECT * FROM Persons WHERE Age >= 18;
    命令式语言—PTA:若采用命令式实现指针分析算法,实现复杂。需考虑worklist数据结构,是数组list还是链表,是先进先出还是先进后出;如何表示指针集合,hash集还是bit向量;如何关联PFG节点和指针;如何处理相关语句中的变量。

    声明式语言—PTA:如何用声明式语言实现PTA?优点是简洁、可读性强、易于实现,例如Datalog。缺点是不方便表达复杂逻辑(Eg,for all全部满足)、不能控制性能。
    2.Datalog介绍Datalog(Data + Logic):是声明式逻辑编程语言,可读性强,最初用于数据库。现在可用于程序分析、大数据、云计算。特点—没有副作用、没有控制流、没有函数、非图灵完备(精简了许多功能)。
    (1)Data(谓词、原子)谓词Predicate:看作一系列陈述的集合,陈述某事情是不是事实(真假)。如Age,表示一些人的年龄。
    事实fact:特定值的组合。Eg,(“Xiaoming”, 18)。

    原子Atom:P(X1, X2, ... , Xn)。P表示谓词名,Xi表示参数(又叫term,可以是变量或常量)。Eg,Age("Xiaoming", 18) == true ;Age("Alan", 23) == false。
    (2)Logic(Rule)Rule:表示逻辑推导规则,若Body都为true,则Head为true。H <- B1, B2, ... ,Bn。H是Head,Bi是Body。 Eg,Adult(person) <- Age(person, age), age >= 18。
    Rule要求:规则中的值要有限,如A(x) <- B(y), x > y;规则不能有悖论,如A(x) <- B(x), !A(x)。
    Datalog中逻辑或:A或B都可推导出C,可写成C<-A. C<-B或者C<-A;B。
    Datalog中逻辑非:!B(...)。
    (3)Datalog谓词分类
    EDB(extensional database)外延数据库:谓词需预先定义,关系不可变,可被当做输入。
    IDB(intensional database)内涵数据库:谓词是根据规则建立的,关系是根据规则推导的,可被看作是是输出。

    说明:H <- B1, B2, ... ,Bn,H只能是IDB,Bi可以是EDB或IDB。
    递归性:Datalog支持递归,也即能够推导出自身。Eg,Reach(from, to) <- Edge(from, to);Reach(from, to) <- Reach(from, node), Edge(node, to)。
    (4)Datalog程序运行Datalog程序运行:输入EDB+rules到Datalog引擎,输出IDB。常用Datalog引擎——LogicBlox, Soufflé, XSB, Datomic, Flora-2。
    Datalog程序性质:单调性、终止性。
    3.Datalog实现指针分析(1)概念EDB:程序句法上可获得的指针相关信息。如New / Assign / Store / Load语句。V-变量,F-域,O-对象。

    New(x: V,o: O) <- i: x = new T()
    Assign(x : V, y : V) <- x=y
    Store(x : V, f : F, y : V) <- x.f = y
    Load(y : V, x : V, f : F) <- y = x.f

    IDB:指针分析结果。

    VarPointsTo(v: V, o : O) ,如VarPointsTo(x,oi)表示oi ∈ 𝑝𝑡(𝑥)
    FieldPointsTo(oi : O, f: V, oj : O) ,如FieldsPointsTo(𝑜i, 𝑓, 𝑜j)表示𝑜j ∈ 𝑝𝑡(𝑜i.𝑓)

    Rules:指针分析规则(与之前相同)。先分析上下文不敏感。

    (2)上下文不敏感PTA示例
    步骤:其实指令处理顺序不固定。

    首先将EDB(指令)表示成表格数据形式。
    处理New指令
    处理Assign指令
    处理Store指令
    处理Load指令

    (3)上下文敏感—全程序指针分析call指令规则:S—指令,M—方法。共3条rule。
    1.首先找到调用的目标函数m,传递this指针。

    2.传递参数

    3.传返回值

    全程序指针分析:引入程序入口函数m。

    4.Datalog实现污点分析EDB谓词-输入:

    Source(m : M) ——产生污点源的函数
    Sink(m : M) ——sink函数
    Taint(l : S, t : T) ——关联某callsite l和它产生的污点数据t

    IDB谓词-输出:

    TaintFlow(t : T, m : M) ——表示污点数据t会流向sink函数m
    规则:处理source和sink函数。

    课后问题
    有的调用图有多个main入口方法,咋办?将多个入口函数都加入到EntryMethod(m)即可。
    有没有datalog和传统结合的做法如chord(java+Datalog实现)
    0 留言 2020-07-21 15:56:32 奖励30点积分
  • 【课程笔记】南大软件分析课程9——污点分析


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/f43218636968

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    信息流安全保密性和完整性显示流和隐藏信道-Explicit Flows and Covert Channels污点分析
    重点显示流和隐藏信道,使用污点分析来检测信息流漏洞。
    1.信息流安全访问控制:关注信息访问。
    信息流安全:关注信息传播。
    信息流:x->y表示x的值流向y。
    信息等级:对不同变量进行分级,即安全等级,H-高密级,L-低密级。
    安全策略:非干涉策略,高密级变量H的信息不能影响(流向)低密级变量L。
    2.保密性和完整性保密性—信息泄露,读保护;完整性—信息篡改,写保护。
    完整性错误类型:命令注入、SQL注入、XSS攻击、… 。都属于注入错误。
    完整性更宽泛的定义:准确性、完整性、一致性。准确性表示关键数据不被不可信数据破坏;完整性表示系统存储了所有的数据;一致性表示发送的数据和接收的数据是一致的。
    3.显示流和隐藏信道-Explicit Flows and Covert Channels显示流:直接的数值传递。由于显示流能泄露更多信息,所以本课程关注显示流的信息泄露。
    隐式信息流—侧信道:程序可能会以一些意想不到的方式泄露数据。
    // Eg1 隐式流if (secret_H < 0) public_L = 1; else Public_L = 0;// Eg2 终止信道while(secret_H < 0) { ... };// Eg3 时间信道if (secret_H < 0) for (int i = 0; i< 1000000; ++i) { ... };// Eg4 异常if (secret_H < 0) throw new Exception("...");// Eg5 如果访问数组越界,则可以推断secret可以为负数int sa_H[] = getSecretArray();sa_H[secret_H] = 0;
    covert channels:信道指的是传递信息的机制,原本目的不是为了传递信息的信道。
    4.污点分析说明:使用最广的信息流分析技术,需将程序数据分为两类,把感兴趣的数据标记为污点数据。
    (1)概念Sources & Sink:Sources是污点数据的源,一般是有些函数的返回值,如read();Sink是特定的程序点,某些敏感函数。
    保密性:Source是秘密数据,sink是泄露点,信息泄露漏洞。
    x = getPassword(); // source y = x;log(y); // sink
    完整性:Source是不可信数据,Sink是关键计算,注入漏洞。
    x = readInput(); // source cmd = "..." + x; execute(cmd); // sink
    (2)污点分析定义:关注的是,污点数据是否能流向sink点。或者说,sink点处的指针指向哪些污点数据。
    TA/PTA对比:污点分析与指针分析,一个是污点数据的流向,一个是抽象对象的流向。可把污点数据看作是对象,source看作allocation-site,借助指针分析来实现污点分析。
    标记:tit_it​i​​表示调用点i返回的污点数据,指针集就包含普通对象 + 污点数据。

    输入:source(返回污点数据的函数),sink(违反安全规则的函数)
    输出:TaintFlows—<tit_it​i​​, m>,表示tit_it​i​​这个污点数据会流向m函数,污点数据和sink函数这个pair的集合就是TaintFlows。
    规则:主要规则不变,关键是Sources和Sink调用的处理。

    Sources:对于产生污点源的函数调用m,将返回值标记为污点值tlt_lt​l​​,并更新接收变量r的指向。
    Sink:对于Sink函数调用m,若所传参数的指向集包含污点tjt_jt​j​​,则将<tj,m><t_j, m><t​j​​,m>加入TaintFlows。



    示例:第3行产生新对象o11o_{11}o​11​​的同时,产生的污点数据t3t_3t​3​​;最终指针分析发现,t3t_3t​3​​会流向sink函数log()。

    问答:实现分析器用到:分析Java用Soot/WALA;分析C++用LLVM;有的用Datalog实现分析器(如DOOP分析框架)。
    隐藏信道的论文:Implicit Flows: Can’t Live With ‘Em, Can’t Live Without ‘Em
    LLVM指针分析工具:SVF
    0 留言 2020-07-21 14:23:35 奖励30点积分
  • 【课程笔记】南大软件分析课程8——指针分析-上下文敏感


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/5ab79839f686

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    介绍Context Sensitive Pointer Analysis:RulesContext Sensitive Pointer Analysis:AlgorithmsContext Sensitivity Variants—上下文的选取
    重点
    上下文敏感指针分析的完整算法(一般其他教程中很少涉及到)。上下文敏感概念,堆对象的上下文敏感表示,上下文敏感指针分析的规则。上下文的三种选择,以及效率、准确度的对比。
    1.上下文不敏感的问题说明:上下文敏感分析是对指针分析的准确性提升最有效的技术。
    (1)问题
    问题:上下文不敏感时,分析常量传播这个问题,由于没有明确调用id()的上下文,会把不同的调用混合在一起,对id函数内的变量n只有一种表示(没有对局部变量进行区分),导致n指向的对象集合增大,将i识别为非常量NAC。实际上,x.get()的值只来自于One()对象,i应该是常量1。
    解决:根据调用的上下文(主要有3种:如根据调用点所在的行数——call-site sensitivity)来区分局部变量。
    (2)上下文敏感分析概念:

    call-site sensitivity (call-string):根据调用点位置的不同来区分上下文,3:id(n1) / 4:id(n2)。
    Cloning-Based Context Sensitivity:每种上下文对应一个节点,标记调用者行数。克隆多少数据,后面会讨论。



    Context-Sensitive Heap:面向对象程序(如Java)会频繁修改堆对象,称为heap-insensitive。所以不仅要给变量加上下文,也要给堆抽象加上下文,称为heap context(本课程是基于allocate-site来进行堆抽象的)。
    堆抽象上下文示例:

    堆抽象上下文不敏感:如果不区分8 X x = new X();调用的堆抽象的上下文,导致只有1个o8.f,把两个上下文调用产生的o8.f指向集合都合并了,得出了o8.f的错误指向的结果。
    堆抽象上下文敏感:用不同的调用者来区分堆抽象,如3:o8、4:o8是不同的堆抽象。所以说,既要根据上下文的不同来区分局部变量,也要区分堆抽象,例如:3:p是给变量加上下文,3:o8是给堆抽象加上下文。
    2.Context Sensitive Pointer Analysis:Rules标记:根据调用者的行数来区分不同上下文,只要区分了函数、变量、堆对象,就能够区分实例域、上下文敏感的指针(变量+对象域)。C—上下文(暂时用调用点的行数表示),O—对象,F—对象中的域。

    规则:跟之前区别不大,只是增加了个上下文标记,注意load表示和之前有区别。

    call指令规则:

    上下文对于Dispatch(oi, k)(找目标函数)没有影响,根据oi指向和函数签名k找到目标函数。select(c, l, c’:oi, m)根据调用时的信息来给调用目标函数选择上下文(c是调用者的上下文,l是调用者的行号,c’:oi是x对象的指向集合,m是目标函数),ct表示目标函数的上下文(后面会将如何Select如何选择上下文)。c是可以累积的,一连串的调用,上下文将用一连串的行数来表示。
    传递this变量:ct:mthisc^t:m_{this}c​t​​:m​this​​是目标函数ct:mc^t:mc​t​​:m的this变量
    传递参数:ct:mpjc^t:m_{pj}c​t​​:m​pj​​是目标函数ct:mc^t:mc​t​​:m的第j个形参。
    传递返回值:ct:mretc^t:m_{ret}c​t​​:m​ret​​是目标函数ct:mc^t:mc​t​​:m的返回值


    3.Context Sensitive Pointer Analysis:Algorithms区别:和过程间指针分析相比,仍然分为两个过程,分别是构造PFG和根据PFG传递指向信息。主要区别是添加了上下文。
    PFG构造:边添加规则和之前一样,Assign、Store、Load、Call,Call需要加参数传递、返回值传递的边。

    符号:

    S:可达语句的集合(就是RM中的语句)Sm:函数m中的语句RM:可达函数的集合CG:调用图的边
    算法:被调用函数的上下文暂时用ct表示,之后会解释Select()函数。

    先处理New、Assign指令。AddReachable(c:m)只多了上下文。遍历WL,Propagate()和原来相同。处理Store、Load指令,AddEdge()只多了上下文。处理Call指令,ProcessCall(),多了一行ct=Select(c,l,c’:oi,m),在找到调用目标函数之后,需选择被调用的函数的上下文。
    4.Context Sensitivity Variants—上下文的选取上下文的选取主要采用3类:

    Call-Site SensitivityObject SensitivityType Sensitivity…
    说明:Select(c,l,c’:oi,m),c—调用者上下文,l—调用者,c’:oi—接收对象(含堆的上下文信息)。
    (1)Call-Site Sensitivity原理:又称为k-call-site sensitivity / k-CFA,不断添加调用行号。1991年Olin Shivers提出。

    Select(c,l,c’:oi,m) = (l’,…,l’’, l)

    问题:如果函数调用自身,导致无限递归,如何限制上下文长度?
    解决:k-limiting Context Abstraction。只取最后k个上下文,通常取k<=3。例如,函数的上下文通常取2,堆上下文通常取1。
    示例:采用1-Call-Site。
    interface Number { int get(); }class One implements Number { public int get() { return 1; }}class Two implements Number { public int get() { return 2; }}1 class C {2 static void main() {3 C c = new C();4 c.m();5 }67 Number id(Number n) {8 return n;9 }10 void m() {11 Number n1,n2,x,y;12 n1 = new One();13 n2 = new Two();14 x = this.id(n1);15 y = this.id(n2);16 x.get();17 }18 }



    WL
    正处理
    PFG
    指针集
    RM
    CG
    处理语句
    算法语句




    1




    {[]:C.main()}

    3
    AddReachable(mentry)—加入RM


    2
    [<[]:c, {o3}>]





    3
    AddReachable(mentry)—处理New


    3
    []
    <[]:c, {o3}>

    pt([]:c) ={o3};



    While开头,Propagate()—遍历WL更新指针


    4
    [⟨[4]:C.mthis, {o3}⟩]





    4
    ProcessCall()—this指针加入WL


    5
    [⟨[4]:C.mthis, {o3}⟩]




    {[ ]:4 → [4]:C.m()};

    ProcessCall()——函数加入CG


    6
    [⟨[4]:C.mthis, {o3}⟩,⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]

    没有参数/返回值

    {[]:C.main(), [4]:C.m()}

    12,13
    ProcessCall():AddReachable(m)处理m函数中的New


    7
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]
    ⟨[4]:C.mthis, {o3}⟩

    pt([]:c) ={o3};pt([4]:C.mthis)={o3};



    While开头,Propagate()—遍历WL更新指针


    8
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]






    ProcessCall():处理m中的this调用


    9
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]





    14
    ProcessCall():Select(c,l,c’:oi)选择上下文ct=[14]


    10
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]



    {[]:C.main(), [4]:C.m(),[14]:C.id(Number)}
    {[ ]:4 → [4]:C.m();[4]:14 → [14]:C.id(Number)};

    ProcessCall():AddReachable([14]:C.id(Number))


    11
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]

    [4]:n1→[14]:n→[4]:x;




    ProcessCall():AddEdge()参数边/返回值边


    12
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]

    [4]:n1→[14]:n→[4]:x;[4]:n2→[15]:n→[4]:y;

    {[]:C.main(), [4]:C.m(),[14]:C.id(Number),[15]:C.id(Number)}
    {[ ]:4 → [4]:C.m();[4]:14 → [14]:C.id(Number),[4]:15 → [15]:C.id(Number)};
    15
    ProcessCall()同理


    13
    []
    [⟨[4]:n1, {o12⟩,⟨[4]:n2, {o13⟩]





    While开头—遍历WL更新指针


    14
    []





    16
    While开头,ProcessCall()—处理x.get()



    上下文不敏感vs上下文敏感(1-Call-Site):

    (2)Object Sensitivity原理:针对面向对象语言,用receiver object来表示上下文。对比1层的调用点敏感和对象敏感,时间和准确性上对象敏感显然更优,这是由面向对象语言的特点所确定的。

    Select(c,l,c’:oi,m) = [oj, … , ok, oi] (c’ = [oj, … , ok])

    示例:选取1-object,最终pt(x)=o3pt(x)=o_3pt(x)=o​3​​。

    对比:对比1-Call-Site和1-object上下文,在这个示例中1-object明显更准确。原因是面向对象语言的特性,多态性产生很多继承链,一层一层调用子对象,其中最关键的是receiver object,receiver object决定了调用者的根源。本例有多层调用,若采用2-Call-Site就不会出错。


    示例2:在本示例中,1-Call-Site明显更准确。因为同一个receiver object用不同参数多次调用了子函数,导致局部变量无法区分。

    结论:所以理论上,对象敏感与callsite敏感的准确度无法比较。但是对于面向对象语言,对象敏感的准确度要优于callsite敏感。
    (3)Type Sensitivity原理:牺牲精度,提高速度。基于创建点所在的类型,是基于对象敏感粗粒度的抽象,精度较低。

    Select(c,l,c’:oi,m) = [𝑡′,…,𝑡′′,InType(𝑜𝑖)] 其中𝑐′ = [𝑡′, … , 𝑡′′]


    (4)总体对比精度:object > type > call-site
    效率:type > object > call-site
    本课老师提出选择上下文的方法,对代码的特点有针对性的选择上下文方法,见A Principled Approach to Selective Context Sensitivity for Pointer Analysis。厉害了!
    0 留言 2020-07-19 16:50:53 奖励30点积分
  • 【课程笔记】南大软件分析课程7——指针分析基础


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/5cbc5bb5c4da

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    指针分析规则如何实现指针分析指针分析算法指针分析如何处理函数调用(过程间指针分析)
    重点理解指针分析的规则、指针流图PFG、指针分析算法。
    理解指针分析调用函数的规则、过程间指针分析算法、实时调用图构建。
    1.指针分析规则首先分析前4种语句:New / Assign / Store / Load。
    指针分析的域和相应的记法:变量/函数/对象/实例域/指针,用pt表示程序中的指向关系(映射)。

    规则:采用推导形式,横线上面是条件,横线下面是结论。

    New:创建对象,将new T()对应的对象oi加入到x的指针集。
    Assign:将y的指针集加入到x对应的指针集。
    Store:让oi的field指向oj。
    Load:Store的反操作。


    2.如何实现指针分析算法要求:全程序指针分析,要容易理解和实现。
    本质:在指针(变量/域)之间传递指向信息。Andersen-style分析(很普遍)——很多solving system把指针分析看作是一种包含关系,eg,x = y,x包含y。
    问题:当一个指针的指向集发生变化,必须更新与它相关的其他指针。如何表示这种传递关系?PFG。
    PFG:用指针流图PFG来表示指针之间的关系,PFG是有向图。

    Nodes:Pointer = V U (O x F) 节点n表示一个变量或抽象对象的域。Edges:Pointer X Pointer 边x -> y 表示指针x指向的对象may会流入指针y。
    Edges添加规则:根据程序语句 + 对应的规则。

    示例:

    PTA步骤:

    构造PFG(根据以上示例,PFG也受指向关系影响)
    根据PFG传播指向信息

    3.指针分析算法(1)过程内PTA算法
    符号:

    S:程序语句的集合。WL:Work list,待合并的指针信息,二元组的集合,<指针n,指向的对象集合pts>。pts将被加入到n的指向集pt(n)中。PFG:指针流图。
    步骤:对每种语句都是基于第1小节的规则来实现。

    对S中所有类似New x = new T()的语句,将<x, {oi}>加入到WL。
    对S中所有类似Assign x = y的语句,调用AddEdge()将y -> x加入到PFG,<x, pt(y)>加入到WL(传播指向信息)。
    遍历WL,取一个元素<n, pts>,除去pts中与pt(n)重复的对象得到Δ\DeltaΔ,调用Propagate(n,Δ\DeltaΔ)将Δ\DeltaΔ加入到pt(n),且取出PFG中所有n指向的边n->s,将<s, pts>加入到WL(根据PFG将指向信息传递给同名指针)。
    如果n表示一个变量x(x跟Store/Load指令相关),对Δ\DeltaΔ中的每个对象oi。对S中所有类似 Store x.f = y 的语句,调用AddEdge()将y -> oi.f加入到PFG,<oi.f, pt(y)>加入到WL(传播指向信息);对S中所有类似Load y = x.f的语句,调用AddEdge()将oi.f -> y加入到PFG,<y, pt(oi.f)>加入到WL(传播指向信息)。

    问题:

    为什么要去重?避免冗余,英文叫做Differential propagation差异传播。
    指针集用什么数据结构存储?混合集 Hibra-set,集合元素小于16个用hash set,大于16个用big-rector 位存储。
    开源项目有哪些?Soot、WALA、Chord。

    (2)示例1 b = new C(); 2 a = b;3 c = new C(); 4 c.f = a;5 d = c;6 c.f = d; 7 e = d.f;

    4.指针分析如何处理函数调用构造调用图技术对比:

    CHA:基于声明类型,不精确,引入错误的调用边和指针关系。
    指针分析:基于pt(a),即a指向的类型,更精确,构造更准的CG并对指针分析有正反馈(所以过程间指针分析和CG构造同时进行,很复杂)。

    void foo(A a) { // pt(a) = ??? ... b = a.bar(); // pt(b) = ??? 把a的指向分析清楚了,就能确定a.bar()到底调用哪个对象的bar()函数,那么b的指向也明确了。 ... }
    (1)调用语句规则call语句规则:主要分为4步。


    找目标函数m:Dispatch(oi, k)——找出pt(x),也即oi类型对象中的k函数。
    receiver object:把x指向的对象(pt(x))传到m函数的this变量,即mthism_{this}m​this​​。
    传参数:pt(aj), 1<=j<=n 传给m函数,即p(mpj)p(m{pj})p(mpj), 1<=j<=n。建立PFG边,a1−>ma_1->ma​1​​−>m{p1},…,an->m{pn}。
    传返回值:pt(mret)pt(m{ret})pt(mret)传给pt(r)。建立PFG边,r<−mr<-mr<−m{ret}。

    问题:为什么PFG中不添加x−>mthisx->m{this}x−>mthis边?因为mmm{this}只和自己这个对象相关,而可能有pt(x)={new A, new B, new C},指定对象的x只流向对应的对象,是无法跨对象传递的。
    (2)过程间PTA算法问题:由于指针分析和CG构造互相影响,所以每次迭代只分析可达的函数和语句。然后不断发现和分析新的可达函数。
    可达示例:

    算法:黄色背景的代码是和过程内分析不同的地方。

    符号:

    mentry:入口main函数
    Sm:函数m中的语句
    S:可达语句的集合(就是RM中的语句)
    RM:可达函数的集合
    CG:调用图的边

    步骤:基于调用规则来实现。

    首先调用AddReachable(mentry)AddReachable(m^{entry})AddReachable(m​entry​​),将入口函数mentrym^{entry}m​entry​​的语句加到S中。处理New x = new T()语句,把<x, {oi}>加入到WL;处理Assign x = y语句,调用AddEdge(y, x)加入边到PFG。
    跟过程内指针分析一样,遍历WL,取一个元素<n, pts>,除去pts中与pt(n)重复的对象得到Δ\DeltaΔ,调用Propagate(n,Δ\DeltaΔ)将Δ\DeltaΔ加入到pt(n),且取出PFG中所有n指向的边n->s,将<s, pts>加入到WL(根据PFG将指向信息传递给同名指针)。
    如果n表示一个变量x(x跟Store/Load指令相关),对Δ\DeltaΔ中的每个对象oi。对S中所有类似 Store x.f = y 的语句,调用AddEdge()将y -> oi.f加入到PFG,<oi.f, pt(y)>加入到WL(传播指向信息);对S中所有类似 Load y = x.f 的语句,调用AddEdge()将oi.f -> y加入到PFG,<y, pt(oi.f)>加入到WL(传播指向信息)。
    最后调用ProcessCall(x, oi),处理与x相关的call指令。取出S中类似r = x.k(a1,...,an)的调用语句L,首先调用Dispatch(oi, k)解出调用的目标函数m,把<mthis, {oi}>加入到WL(传递接收对象,上下文敏感分析将用到),将L->m这条调用边加入到CG;调用AddReachable(m)将新的语句加入到S,并处理New/Assign语句;调用AddEdge()将实参->形参、返回值->r边加入到PFG(传递参数、返回值),并将<形参,pt(实参)>、<r,pt(返回值)>加入到WL。

    问题:为什么ProcessCall(x, oi)中,要判断L->m这条边是否已经加入到CG?因为x可能指向多个对象,就会多次处理L这个调用指令,可能x中别的对象oj早就已经将这条边加入进去了。
    (3)示例class A { static void main(){ A a = new A(); A b = new B(); A c = b.foo(a); } A foo(Ax){...}}class B extends A { A foo(A y) { A r=newA(); return r; }}




    WL
    正处理
    PFG
    指针集
    RM
    CG
    语句
    算法语句




    1
    []

    {}

    {}
    {}

    初始化


    2
    []



    {A.main()}

    1,2
    AddReachable(mentry)


    3
    [<a,{o3}>, <b,{o4}>]





    3,4



    4
    [<b,{o4}>]
    <a,{o3}>

    pt(a)={o3};



    while开头


    5
    []
    <b,{o4}>

    pt(b)={o4}



    while开头


    6
    []





    5
    ProcessCall(b, o4)


    7
    [<B.foothis, {o4}>]




    {5->B.foo(A)}

    m=Dispatch(o4, foo())=B.foo();添加到调用图


    8
    [<B.foothis, {o4}>, <r, o11>]



    {A.main(), B.foo()}


    AddReachable(B.foo());添加到可达函数


    9
    [<B.foothis, {o4}>, <r, o11>, <y, {o3}>]

    {a->y, r->c}




    AddEdge();添加参数边、返回值边


    10
    [<r, o11>, <y, {o3}>]
    <B.foothis, {o4}>

    pt(B.foothis)={o4};



    while开头,B.foothis没有调用任何函数


    11
    [<y, {o3}>, <c, {o11}>]
    <r, o11>

    pt(r)={o11};



    while开头


    12

    <y, {o3}>, <c, {o11}>

    pt(y)={o3};pt(c)={o11}



    while开头



    如果是CHA的话,CG={5->B.foo(A), 5->A.foo(A)},错误识别为调用边。
    结果:

    问题:没有入口函数的?如对库函数处理,生成调用库函数的程序。
    0 留言 2020-07-19 11:55:31 奖励30点积分
  • 【课程笔记】南大软件分析课程6——指针分析介绍


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/9d15edf2604e

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    Motivation指针分析介绍影响指针分析的关键要素分析哪些语句
    重点什么是指针分析?影响指针分析的关键因素是什么?指针分析要分析哪些指令?
    1.Motivation指针分析必要性

    2.指针分析目标:分析程序指针可以指向哪些内存。对于Java等面向对象语言,主要分析指针指向哪个对象。
    说明:指针分析属于may analysis,分析的结果是某指针所有可能指向哪些对象,是个over-approximation集合。
    示例:面向对象语言中的指针指向问题。对于setB()函数,this指向new A(),因为是调用者是a.setB();setB()中的b是x传过来的,所以b指向new B(),A.b指向 new B()。

    区别:

    指针分析:分析指针所有可能指向的对象。
    别名分析:分析两个指针是否指向相同的对象,可通过指针分析来推导得到。

    应用:基本信息(别名分析/调用图),编译优化(嵌入虚拟调用),漏洞(空指针),安全分析(信息流)。
    3.影响指针分析的关键要素指标:精度(precision)& 效率(efficiency)。
    影响因素:本课程,我们主要分析分配点的堆抽象技术、上下文敏感/不敏感、流不敏感、全程序分析。

    (1)堆抽象(内存建模)问题:程序动态执行时,堆对象个数理论上是无穷无尽的,但静态分析无法处理这个问题。所以为保证指针分析可以终止,我们采用堆抽象技术,将无穷的具体对象抽象成有限的抽象对象。也即,将有共性的对象抽象成1个静态对象,从而限制静态分析对象的个数。
    // 示例for (...) { A a = new A();}
    技术概览

    我们只学习Allocation-Site技术,最常见也最常被使用。
    Allocation-Site原理:将动态对象抽象成它们的创建点(Allocation-Site),来表示在该点创建的所有动态对象。Allocation-Site个数是有限的。
    示例:循环创建了3个对象,我们用O2来抽象表示这3个动态对象。

    (2)上下文敏感 Context Sensitivity问题:考虑是否区分不同call-site对同一函数的调用。

    Context-sensitive:根据某函数调用上下文的不同,多次分析同一函数。
    Context-insensitive:每个函数只分析一次。


    (3)流敏感 Flow Sensitivity问题:考虑语句顺序(控制流)的影响 vs 把程序当做无序语句的集合。
    方法:流敏感会在每个程序点都保存一份指针指向关系映射,而流不敏感则对整个程序保存一份指向关系映射。
    说明:目前流敏感对Java提升不大,不过在C中很有效,本课程分析的是Java,所以重点讨论流不敏感技术。
    指针分析示例:

    (4)分析范围 Analysis Scope问题:分析程序的哪一部分?

    Whole-program 全程序:分析全程序的指向关系。
    Demand-driven 需求驱动:只分析影响特定域的指针的指向关系。

    4.分析哪些语句问题:哪些语句会影响指针指向,那就只分析这些语句。
    Java指针类型:

    Lacal variable: x
    Static field:C.f (有时称为全局变量)——不分析
    Instance field: x.f (对象的field)
    Array element: array[i] ——不分析,因为静态分析无法确定下标,所以将array中所有成员映射到一个field中,等价于Instance field,所以不重复分析。如下图所示:


    影响指针指向的语句:
    1. New: x = new T()2. Assign:x = y3. Store: x.f = y4. Load: y = x.f5. Call: r = x.k(a,...) - Static call: C.foo() - Special call: super.foo() / x.<init>() / this.privateFoo() - **Virtual call**:x.foo()
    复杂的内存访问可以通过引入临时变量,转化为三地址代码:
    x.f.g.h = y;// 转化为t1 = x.f;t2 = t1.g;t2.h = y;
    0 留言 2020-07-17 11:33:24 奖励30点积分
  • 【课程笔记】南大软件分析课程5——过程间分析


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/2d14c0ae41cd

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    目录
    Motivation调用图构建过程间控制流分析过程间数据流分析
    重点:学习如何利用类层级分析来构建调用图;过程间控制流/数据流分析;过程间的常量传播。
    1.Motivation问题:过程内的分析未考虑函数调用,导致分析不精确。
    过程间分析:Inter-procedural Analysis,考虑函数调用,又称为全程序分析(Whole Program Analysis),需要构建调用图,加入Call edges和Return edges。
    2.调用图构建(1)调用图定义:本质是调用边的集合,从调用点(call-sites)到目标函数(target methods / callees)的边。
    示例:

    应用:是所有过程间分析(跨函数分析)的基础,程序优化,程序理解,程序调试。
    (2)面向对象语言的调用图构造(Java)代表性算法:从上往下精度变高,速度变慢,重点分析第1、4个算法。

    Class hierarchy analysis(CHA)Rapid type analysis(RTA)Variable type analysis(VTA)Pointer analysis(k-CFA)
    Java调用分类:

    Method Dispatch:最难的是Virtual call,其中关键步骤是Method Dispatch,就是找到最终调用的实际函数。
    virtual call在程序运行时才能得到,基于2个要素得到:

    reciever object的具体类型:c
    调用点的函数签名:m。(通过signature可以唯一确定一个函数)

    signature = 函数所在的类 + 函数名 + 描述符描述符 = 返回类型 + 参数类型
    简记为C.foo(P, Q, R)


    (3)Method Dispatch(virtual call)定义:用Dispatch(c, m)来模拟动态Method Dispatch过程,c表示reciever object,m表示函数签名。

    解释:若该类的非抽象方法(实际可执行的函数主体)中包含和m相同名字、传递/返回参数的m‘,则直接返回;否则到c的父类中找。
    示例:

    (4)Class Hirarchy Analysis (CHA) 类层级分析目的:根据每个virtual call 的 receiver varible 的声明类型来求解所有可能调用的目标函数。如 A a = ... ; a.foo(); 这个a就是receiver varible,声明类型就是A。假定a可以指向A以及A所有子类对象,CHA的过程就是从A和子类中去找目标函数。
    算法:Resolve(cs)——利用CHA算法找到调用点所有可能的调用目标。

    算法示例:

    算法应用:

    错误:以上b.foo()的调用目标 C.foo()和D.foo()是错误的,因为已经指定了是B类型,所以b.foo()根本不会调用C、D的foo()。因为CHA只考虑声明类型,也就是B,导致准确度下降。多态性就是说,父类可以引用子类的对象,如B b=new C()。
    优缺点:CHA优点是速度快,只考虑声明类型,忽略数据流和控制流;缺点是准确度低。
    总结:本类中有同名函数就在本类和子类找,没有就从父类找,接着找父类的子类中的同名函数(CHA分析)。
    (5)利用CHA构造调用图算法:遍历每个函数中的每个调用指令,调用CHA的Resolve()找到对应的目标函数和调用边,函数+调用边=调用图。

    示例:

    3.过程间控制流分析定义:过程间控制流图ICFG = CFG + (Call edges + Return edges)。

    Call edges:连接调用点和目标函数入口Return edges:从return语句连到Return site(Call site后面一条语句)
    示例:

    4.过程间数据流分析说明:对ICFG进行数据流分析,没有标准的一套算法。
    对比:

    常量传播数据流分析:

    Node transfer:与过程内分析相同,对每个调用点,将等号左边部分去掉。Call edge transfer:传参Return edge transfer:传返回值
    常量传播示例:

    说明:黄色背景边必须有,从b = addOne(a)到c=b-3,a通过此边传递,b通过addOne()传递。若a也通过addOne()传递,会额外消耗系统资源。
    0 留言 2020-07-15 10:12:26 奖励30点积分
  • 【课程笔记】南大软件分析课程4——数据流分析基础


    最近在看“静态分析”技术相关的文章,看到这个系列的笔记和视频教程,感觉介绍得很好,通俗易懂,而且还比较详细,故转载分享,同时也备份保留下,方便自己今后阅读。(PS:建议大家一边看笔记,一边看视频,加深理解)原作者:bsauce原文链接:https://www.jianshu.com/p/d314b316b332

    首先非常感谢南京大学李樾和谭添老师的无私分享,之前学习程序分析是看的北大熊英飞老师的ppt,但是很多地方没看懂,正如李樾老师所说的那样,熊英飞老师的授课涵盖非常广,不听课只看ppt的话,理解起来还是很有难度的。但李樾老师的视频就讲解的非常易懂,示例都是精心挑选的,所以墙裂推荐。
    推送门:南大课件 南大视频课程 北大课件

    关于这一节zcc的笔记已经够完美了,我就直接在他基础上记录了。
    目录
    迭代算法-另一个角度偏序(Partial Order)上下界(Upper and Lower Bounds)格(Lattice),半格(Semilattice),全格和格点积(Complete and Product Lattice)数据流分析框架(via Lattice)单调性与不动点定理(Monotonicity and Fixed Point Theorem)迭代算法转化为不动点理论从lattice的角度看may/must分析分配性(Distributivity)和MOP常量传播Worklist算法
    重点上节课是介绍了3种数据流分析迭代算法,本节课将从数学理论的角度来讨论数据流分析,加深对数据流分析算法的理解。
    1.迭代算法-另一个角度本质:常见的数据流迭代算法,目的是通过迭代计算,最终得到一个稳定的不变的解。
    (1)理论定义1:给定有k个节点(基本块)的CFG,迭代算法就是在每次迭代时,更新每个节点n的OUT[n]。
    定义2:设数据流分析的值域是V,可定义一个k-元组:(OUT[n1],OUT[n2],..,OUT[nk])(OUT[n_1],OUT[n_2], .. , OUT[n_k])(OUT[n​1​​],OUT[n​2​​],..,OUT[n​k​​])是集合 (V1×V2..×Vk)(V_1 \times V_2 .. \times V_k)(V​1​​×V​2​​..×V​k​​) (幂集,记为VkV_kV​k​​)的一个元素,表示每次迭代后k个节点整体的值。
    定义3:每一次迭代可看作是Vk映射到新的Vk,通过转换规则和控制流来映射,记作函数F:Vk→VkF:V_k \rightarrow V_kF:V​k​​→V​k​​。
    迭代算法本质:通过不断迭代,直到相邻两次迭代的k-元组值一样,算法结束。
    (2)图示
    不动点:当Xi = F(Xi)时,就是不动点。
    问题:

    迭代算法是否一定会停止(到达不动点)?迭代算法如果会终止,会得到几个解(几个不动点)?迭代几次会得到解(到达不动点)?
    2.偏序(Partial Order)定义:给定偏序集(P,⊑)(P, \sqsubseteq)(P,⊑),⊑\sqsubseteq⊑是集合PPP上的二元关系,若满足以下性质则为偏序集:

    自反性Reflexivity:∀x∈P, x⊑x
    对称性Antisymmetry:∀x,y∈P, x⊑y∧y⊑x ⇒ x=y
    传递性Transitivity:∀x,y∈P, x⊑y ∧ y⊑z ⇒ x⊑z

    例子:

    P是整数集,⊑\sqsubseteq⊑表示≤\leq≤,是偏序集;若⊑\sqsubseteq⊑表示<,则显然不是偏序集
    P是英文单词集合,⊑\sqsubseteq⊑表示子串关系(可以存在两个元素不具有偏序关系,不可比性),是偏序集

    3.上下界(Upper and Lower Bounds)(1)定义定义:给定偏序集(P, \sqsubseteq),且有P的子集S⊆P:

    ∀x∈S, x⊑u, 其中u∈P,则u是子集S的上界 (注意,u并不一定属于S集)
    ∀x∈S, l⊑x, 其中l∈P,则l是S的下界

    最小上界:least upper bound(lub 或者称为join),用⊔S表示。上确界?
    定义:对于子集S的任何一个上界u,均有⊔S⊑u。
    最大下界:greatest lower bound(glb 或者称为meet),用⊓S表示。下确界?
    定义:对于子集S的任何一个下界l,均有l⊑⊓S。
    (2)示例若S只包含两个元素,a、b(S = {a, b})那么上界可以表示为a⊔b,下界可以表示为a⊓b。

    (3)特性
    并非每个偏序集都有上下确界


    如果存在上下确界,则是唯一的
    利用传递性和反证法即可证明。
    4.格(Lattice),(半格)Semilattice,全格,格点积(Complete and Product Lattice)都是基于上下确界来定义的。
    (1)格定义:给定一个偏序集(P,⊑),∀a,b∈P,如果存在a⊔b和a⊓b,那么就称该偏序集为格。偏序集中的任意两个元素构成的集合均存在最小上界和最大下界,那么该偏序集就是格。
    例子:

    (S, ⊑)中S是整数子集,⊑\sqsubseteq⊑是≤\leq≤,是格点
    (S, ⊑)中S是英文单词集,⊑\sqsubseteq⊑表示子串关系,不是格点,因为单词pin和sin就没有上确界
    (S, ⊑)中S是{a, b, c}的幂集,⊑\sqsubseteq⊑表示⊆\subseteq⊆子集,是格点

    (2)半格定义:给定一个偏序集(P,⊑),∀a,b∈P:

    当且仅当a⊔b存在(上确界),该偏序集叫做 join semilatice
    当且仅当a⊓b存在(下确界),该偏序集叫做 meet semilatice

    (3)全格定义:对于格点 (S, ⊑\sqsubseteq⊑) (前提是格点)的任意子集S,⊔S上确界和⊓S下确界都存在,则为全格complete lattice。
    例子:

    P是整数集,⊑\sqsubseteq⊑是≤\leq≤,不是全格,因为P的子集正整数集没有上确界
    (S, ⊑)中S是{a, b, c}的幂集,⊑\sqsubseteq⊑表示⊆\subseteq⊆子集,是全格

    符号:⊤=⊔P\top = \sqcup P⊤=⊔P,叫做top;⊥=⊓P\perp = \sqcap P⊥=⊓P,叫做bottom。
    性质:有穷的格点必然是complete lattice。全格一定有穷吗? 不一定,如实数界[0, 1]。
    (4)格点积定义:给定一组格,L1=(P1,⊑1)L_1=(P_1, \sqsubseteq 1)L​1​​=(P​1​​,⊑1),L2=(P2,⊑2)L_2=(P_2, \sqsubseteq 2)L​2​​=(P​2​​,⊑2),.. ,Ln=(Pn,⊑n)L_n=(P_n, \sqsubseteq n)L​n​​=(P​n​​,⊑n),都有上确界⊔i\sqcup i⊔i和下确界⊓i\sqcap i⊓i,则定义格点积 Ln=(P,⊑)Ln = (P, \sqsubseteq)Ln=(P,⊑):
    P=P1×..×PnP = P_1 \times .. \times P_nP=P​1​​×..×P​n​​(x1,..xn)⊑(y1,..yn)⇔(x1⊑y1)∧..∧(xn⊑yn)(x_1, .. x_n) \sqsubseteq (y_1, .. y_n) \Leftrightarrow (x_1 \sqsubseteq y_1) \wedge .. \wedge (x_n \sqsubseteq y_n)(x​1​​,..x​n​​)⊑(y​1​​,..y​n​​)⇔(x​1​​⊑y​1​​)∧..∧(x​n​​⊑y​n​​)(x1,..xn)⊔(y1,..yn)=(x1⊔y1,..,xn⊔yn)(x_1, .. x_n) \sqcup (y_1, .. y_n) = (x1 \sqcup y_1, .., x_n \sqcup y_n)(x​1​​,..x​n​​)⊔(y​1​​,..y​n​​)=(x1⊔y​1​​,..,x​n​​⊔y​n​​)(x1,..xn)⊓(y1,..yn)=(x1⊓y1,..,xn⊓yn)(x_1, .. x_n) \sqcap (y_1, .. y_n) = (x_1 \sqcap y_1, .., x_n \sqcap y_n)(x​1​​,..x​n​​)⊓(y​1​​,..y​n​​)=(x​1​​⊓y​1​​,..,x​n​​⊓y​n​​)性质:格点积也是格点;格点都是全格,则格点积也是全格。
    5.数据流分析框架(via Lattice)数据流分析框架(D, L, F) :

    D—方向
    L—格点(值域V,meet ⊓\sqcap⊓ 或 join ⊔\sqcup⊔ 操作)
    F—转换规则V→VV \rightarrow VV→V。

    数据流分析可以看做是迭代算法对格点 利用转换规则和 meet/join操作。
    6.单调性与不动点定理(Monotonicity and Fixed Point Theorem)目标问题:迭代算法一定会停止(到达不动点)吗?
    (1)单调性定义:函数f:L→Lf: L \rightarrow Lf:L→L,满足∀x,y∈L,x⊑y⇒f(x)⊑f(y),则为单调的。
    (2)不动点理论定义:给定一个完全lattice(L,⊑),如果f:L→L是单调的,并且L有限
    那么我们能得到最小不动点,通过迭代:f(⊥),f(f(⊥)),…,fk(⊥)直到找到最小的一个不动点。
    同理 我们能得到最大不动点,通过迭代:f(⊤),f(f(⊤)),…,fk(⊤)直到找到最大的一个不动点。
    (3)证明不动点的存在性;
    最小不动点证明。
    7.迭代算法转化为不动点理论问题:我们如何在理论上证明迭代算法有解、有最优解、何时到达不动点?那就是将迭代算法转化为不动点理论。因为不动点理论已经证明了,单调、有限的完全lattice,存在不动点,且从⊤开始能找到最大不动点,从⊥开始能找到最小不动点。
    目标:证明迭代算法是一个完全lattice(L,⊑)lattice(L, \sqsubseteq)lattice(L,⊑),是有限的,单调的。

    (1)完全lattice证明根据第5小节,迭代算法每个节点(基本块)的值域相当于一个lattice,每次迭代的k个基本块的值域就是一个k-元组。k-元组可看作lattice积,根据格点积性质:若Lk中每一个lattice都是完全的,则Lk也是完全的。
    (2)L是有限的迭代算法中,值域是0/1,是有限的,则lattice有限,则Lk也有限。
    (3)F是单调的函数F:BB中转换函数fi:L → L + BB分支之间的控制流影响(汇聚是join ⊔\sqcup⊔ / meet ⊓\sqcap⊓ 操作,分叉是拷贝操作)。

    转换函数:BB的gen、kill是固定的,值域一旦变成1,就不会变回0,显然单调。
    join/meet操作:L × L → L 。证明:∀x,y,z∈L,且有x⊑y需要证明x⊔z⊑y⊔z。

    总结:迭代算法是完全lattice,且是有限、单调的,所以一定有解、有最优解。
    (4)算法何时到达不动点?定义:lattice高度—从lattice的top到bottom之间最长的路径。

    最坏情况迭代次数:设有n个块,每次迭代只有1个BB的OUT/IN值的其中1位发生变化(则从top→bottom这1位都变化),则最多迭 (n × h) 次。
    8.从lattice的角度看may/must分析说明:may 和 must 分析算法都是从不安全到安全(是否安全取决于safe-aprroximate过程),从准确到不准确。

    (1)may分析以 Reaching Definitions分析为例:

    从⊥\perp⊥开始,⊥\perp⊥表示所有定义都不可达,是不安全的结果(因为这个分析的应用目的是为了查错,查看变量是否需要初始化。首先在Entry中给每个变量一个假定义,标记所有变量为都为未初始化状态,⊥\perp⊥表示所有的假定义都无法到达,说明所有变量在中间都进行了赋值,那就不需要对任何变量进行初始化,这是不安全的,可能导致未初始化错误)。
    ⊤\top⊤表示所有Entry中的假定义都可达,从查错角度来说,需要对每个变量都进行初始化,非常安全!但是这句话没有用,我都要初始化的话还做这个分析干嘛?
    Truth:表明最准确的验证结果,假设{a,c}是truth,那么包括其以上的都是safe的,以下的都是unsafe,就是上图的阴影和非阴影。
    从⊥\perp⊥到⊤\top⊤,得到的最小不动点最准确,离Truth最近。上面还有多个不动点,越往上越不准。


    (2)must分析以available expressions分析为例:

    从⊤\top⊤开始,表示所有表达式可用。如果用在表达式计算优化中,那么有很多已经被重定义的表达式也被优化了(实际上不能被优化),那么该优化就是错误的,不安全!
    ⊥\perp⊥表示没有表达式可用,都不需要优化,很安全!但没有用。
    从⊤\top⊤到⊥\perp⊥,就是从不安全到安全,存在一个Truth,代表准确的结果。
    从⊤\top⊤到⊥\perp⊥,达到一个最大不动点,离truth最近的最优解。

    迭代算法转化到lattice上,may/must分析分别初始化为最小值⊥\perp⊥和最大值⊤\top⊤,最后求最小上界/最大下界。
    9.分配性(Distributivity)和MOP目的:MOP(meet-over-all-paths)衡量迭代算法的精度。
    (1)概念定义:最终将所有的路径一起来进行join/meet操作。
    路径P = 在cfg图上从entry到基本块si的一条路径(P = Entry → s1 → s2 → … → s~i )。
    路径P上的转移函数Fp:该路径上所有语句的转移函数的组合fs1,fs2,… ,fsi-1,从而构成FP。
    MOP:从entry到si所有路径的FP的meet操作。本质—求这些值的最小上界/最大下界。

    MOP准确性:有些路径不会被执行,所以不准确;若路径包含循环,或者路径爆炸,所以实操性不高,只能作为理论的一种衡量方式。
    (2)MOP vs 迭代算法
    对于以上的CFG,抽象出itter和MOP公式。
    证明:

    根据最小上界的定义,有x⊑x⊔y和 y⊑x⊔y。
    由于转换函数是单调的,则有F(x)⊑F(x⊔y)和F(y)⊑F(x⊔y),所以F(x⊔y)就是F(x)和F(y)的上界。
    根据定义,F(x)⊔F(y)是F(x)和F(y)的最小上界。
    所以F(x)⊔F(y)⊑F(x⊔y)。

    结论:所以,MOP更准确。若F满足分配律,则迭代算法和MOP精确度一样 F(x⊔y)=F(x)⊔F(y)。一般,对于控制流的join/meet,是进行集合的交或并操作,则满足分配律。
    10.常量传播 (constant propagation)问题描述:在程序点p处的变量x,判断x是否一定指向常量值。
    类别:must分析,因为要考虑经过p点所有路径上,x的值必须都一样,才算作一定指向常量。
    表示:CFG每个节点的OUT是pair(x, v)的集合,表示变量x是否指向常数v。
    数据流分析框架(D, L, F)(1)D:forward更直观
    (2)L:lattice

    变量值域:所有实数。must分析,所以⊤\top⊤是UNDEF未定义(unsafe),⊥\perp⊥是NAC非常量(safe)。
    meet操作:must分析, ⊓\sqcap⊓。在每个路径汇聚点PC,对流入的所有变量进行meet操作,但并非常见的交和并,所以不满足分配律。

    NAC⊓v=NACNAC \sqcap v = NACNAC⊓v=NAC
    UNDEF⊓v=vUNDEF \sqcap v = vUNDEF⊓v=v 未初始化的变量不是我们分析的目标
    c⊓v=?c⊓c=c c1⊓c2=NACc \sqcap v = ? c \sqcap c = c \space c1 \sqcap c2 =NACc⊓v=?c⊓c=c c1⊓c2=NAC

    (3)F转换函数
    OUT[s] = gen U (IN[s] - {(x, _})
    输出 = BB中新被赋值的 U 输入 - BB中相关变量值已经不是f常量的部分。
    对所有的赋值语句进行分析(不是赋值语句则不管,用val(x)表示x指向的值):

    (4)性质:不满足分配律

    可以发现,MOP更准确。F(X⊓Y)⊑F(X)⊓F(Y)F(X \sqcap Y) \sqsubseteq F(X) \sqcap F(Y)F(X⊓Y)⊑F(X)⊓F(Y),但是是单调的。
    11.Worklist算法本质:对迭代算法进行优化,采用队列来存储需要处理的基本块,减少大量的冗余的计算。
    0 留言 2020-07-13 22:12:43 奖励30点积分
显示 0 到 15 ,共 15 条
eject