首页
我的邻居
留下你的脚印
Search
1
用PDO得知数据库中某个表的总行数
11,302 阅读
2
积木报表大屏配置动态数据时php api跨域问题的解决
167 阅读
3
layui实现表格分页和总数
157 阅读
4
部署 Memos 接入 QQ 机器人
140 阅读
5
layui弹出提示框并关闭子页刷新父页面
138 阅读
默认分类
PHP
登录
Search
标签搜索
layui
php
api
layuimini
pdo
mysql
积木报表
php跨域
php表格分页
layui表格分页
ruoyi
若依框架
Moxne
累计撰写
14
篇文章
累计收到
2
条评论
首页
栏目
默认分类
PHP
页面
我的邻居
留下你的脚印
搜索到
14
篇与
的结果
2023-05-13
使用 Wireshark 进行嗅探实验
实验环境手机(iPhone)开启热点作为 gateway,让电脑(MacBook)连接热点作为攻击者,平板(iPad)也连接热点作为 victim。让我们来看看,当 victim 上网时,攻击者能看到什么。混杂模式大多数的网卡都支持开启混杂模式(promiscuous mode),只是一般工作在非混杂模式下。在混杂模式下,网卡可以接收所有经过它的数据流,不论目的地是否是它。在一般 Linux 发行版中需要手动开启混杂模式:ip link set eth0 promisc on开启之后,网卡应该就可以支持混杂模式了。在终端使用 ifconfig 查看网卡接口信息,接口的 flags 中包含 PROMISC 就说明目前支持混杂模式,例如:...en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500options=6463<RXCSUM,TXCSUM,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM> ether 1c:91:80:e2:0f:f6 inet6 fe80::1878:5529:853f:6fa1%en0 prefixlen 64 secured scopeid 0xc inet 192.168.86.20 netmask 0xffffff00 broadcast 192.168.86.255 inet6 240e:468:490:e659:402:bca2:58e4:dbae prefixlen 64 autoconf secured inet6 240e:468:490:e659:e445:33c7:a581:43c8 prefixlen 64 autoconf temporary nd6 options=201<PERFORMNUD,DAD> media: autoselect status: active...MacBook 的 macOS 中网络接口命名和一般的设备不同,使用 ifconfig 显示时,en0 才是 Wi-Fi 网卡接口。可以在「系统信息.app」查看,或者直接看当前 active 的接口。安装配置 WiresharkmacOS 下使用 homebrew 安装 Wireshark 时,formula 的版本和 cask 的版本存在 conflict,不能共存。只要安装 cask 的版本(带图形界面的)即可。(从官网下载安装是一样的)brew install --cask wireshark开启后会提示安装 ChmodBPF,否则无法使用。安装之后打开会发现还是提示没有安装,这时候还要给权限才能用:sudo chmod 666 /dev/bpf*给权限之后打开 Wireshark 就可以抓包了。注意 macOS 重启之后这个权限会重置,需要重新设置。ARP 欺骗开启了混杂模式只是保障了我们可以嗅探,但是这时候局域网内的其他主机并不愿意把包发给我们。我们要进行 ARP 欺骗,将自己伪装成一个 gateway,让其他主机乖乖地把包发给我们。我们使用 ettercap 工具进行 ARP 欺骗。Windows 平台建议使用 arpspoof,这个程序更加简单轻量。brew install ettercapsudo ettercap -G # 以图形界面方式运行 Ettercap,注意要 sudo!启动 Ettercap 后,进行 host 扫描或者等待自动探测 host,然后将 host list 中目标主机(即想要欺骗的 victim)加入 target1,将 gateway 加入 target2。注意同一主机在 list 里可能有 IPv4 和 IPv6 两个表项,看 mac 地址可以区分(同一主机 mac 地址相同)。Ettercap 中的 host list如何才能确定这些 IP 谁是谁的呢?对于 victim 的 IP,可以直接在 victim 设备的设置里查看,网络设置中会显示自己被分配到的 IP。有些时候,这个 list 里的 description 会显示设备名称。对于 gateway 的 IP,则可以在任何连接该局域网的设备设置里看到,显示的「网关 IP」即是。这个 list 中的 192.168.86.236 就是我们的 victim。配置好 target 后,点击 MITM menu 里的 ARP poisoning 就可以启动 ARP 欺骗。这时候在 Wireshark 里就能抓到局域网内其他主机的包了。使用 Wireshark 抓包在 Wireshark 里筛选相应 IP 的包,就可以看到与 victim 有关的包。可以这么写查询语句:ip.addr == 192.168.86.236。嗅探到的有关 victim 的所有包可以看到各种协议的流量,TCP、ARP、MDNS、HTTP……观察截图中的 48 号开始的数据包,可以发现这完整演示了 TCP 三次握手的过程:48 号 victim 向服务器 61.187.64.7 发送 SYN=1 的包,结果 49 号重传了(可能因为网络问题没有发送成功),这就是第一次握手;51 号,服务器向 victim 发回了 SYNACK 的包,这就是第二次握手,结果这个包又重传了;53 号则是 victim 最后向服务器发送了 SYN=0 的 ACK 包,这就是第三次握手,TCP 连接建立成功!这个 TCP 连接用于什么目的呢?可以看到接下来对于这个服务器的一个请求就是 HTTP GET,也就是 victim 访问了湖大统一身份认证页面的网页。至此,我们直观地看到了从建立 TCP 连接到发送 HTTP 请求的全过程。还注意到,这个连接由 victim 的 60431 端口和服务器的 80 端口建立。在下面的其他数据包里,victim 的端口各不相同,端口号都大于 60000。看来,发送端的确是任意选择一个不定端口进行发送的。实践:湖大统一身份认证登陆现在还有什么现代网站使用 HTTP 吗?答案是肯定的,比如:湖南大学「统一身份认证」的登录页面。这个页面是完全不支持 HTTPS。那么理论上来说,我是不是可以看到在相同局域网登陆的同学的密码呢?就 TM 你没用 HTTPS 是吧?将前面的一切都配置好之后,在同一个局域网下,在目标设备上登录,筛选 Wireshark 的条件为发自 victim 的 IP 和使用 HTTP 协议,可以写 ip.src == 192.168.86.236 && http。确实看到了目标设备浏览器发出的 HTTP 请求!查看包内容,可以看到表单发送的四个字段,其中赫然包含着这个 password:捕获 victim 登录湖大统一认证的请求然而查看发现,这个内容是加密的。结合之前对湖大登录认证的研究,在发送表单之前会对 password 进行 RSA 加密,加密的公钥是加载网页之初向服务器请求的(每次都不同),而对应的密钥只有服务器后端知道。所以,我们只能截获加密后的内容,没有服务器端的密钥就是没法解密的。看来,虽然没有使用 HTTP,但是湖大统一认证的密码登录还是相对安全的。值得注意的是,统一身份认证的登录页面使用了这种比较精巧的设计,利用非对称加密、每次生成不同的密钥对等来确保我们抓到的包看不到密码(也可能是因为有这样一套 HTTP 下也相对安全的机制,信息化办才不愿意升级 HTTPS),然而大多数的登录表单并不会采用这样复杂的设计,绝大多数还是直接明文发送密码或者密码哈希。如果不使用 HTTPS,确实会有很大的安全性问题。校园网的 AP 隔离马上我们就可以看到「在湖大登录个人门户」是「安全的」的另一个原因:校园网的 AP 隔离。如果两个设备都连在校园网,它们彼此之间是无法访问的,并不像一般的局域网一样。正因如此,校园网内不能联机玩 Minecraft。所以,连接湖大校园网 Wi-Fi 时是无法进行这个实验的,即使进行了 ARP 欺骗,Wireshark 也无法嗅探到局域网其他主机发送的包,只能看到一些无意义的 MDNS 查询:校园网中只能嗅探到一些无意义的 MDNS 查询所以校园网内嗅探是不可行的。HTTPS 下能嗅探吗?如果主机使用 HTTPS 访问网站,我们还能看到其发送的请求吗?仍然使用之前嗅探的实验环境,在 victim 上访问 HTTPS 网站进行登录操作,比如学习通:学习通的登录页面可以看到这个页面是使用 HTTPS 连接的。在这个页面输入账号密码登录,用 Wireshark 能够嗅探到:和学习通服务器建立 HTTPS 连接的过程我们知道 HTTPS 建立在服务器的 443 端口。可以看到,我们抓取到了 victim 和超星学习通服务器建立连接的 TCP 包(也就是发向 443 端口的这一系列包),但是在这之后发的包并没有使用 HTTP 协议(或者 HTTPS 协议?),而是使用了 TLSv1.2 协议。TLS 的全称是 Transport Layer Security,顾名思义是个加密协议。事实上对于这些使用 TLS 加密发送的数据包,我们查看只能看到一堆乱码。比较诡异的是,学习通登录之后自动重定向到了一个 HTTP 页面,所以上图最后几行开始又是 HTTP 协议……不知道这垃圾平台的开发者是怎么想的。如果筛选所有 HTTP 包,就只能看到登录成功、重定向之后 GET 的那些内容,看不到表单登陆 POST 账号密码的内容。筛选 HTTP 包,只能看到 HTTP GET综上所述,HTTPS 实际是「HTTP over TLS/SSL」,也就是用 TLS 这类加密协议传输的 HTTP,所以在 Wireshark 里捕获不到 HTTP 的请求内容,只能看到一个个加密后的 TLS 包。
2023年05月13日
114 阅读
0 评论
0 点赞
2023-05-13
CSAPP 2e Datalab 题解
CSAPP 是 CMU 享誉全球的课程,尤其以其 lab 难度之高著称。这是课程的第一个 lab:datalab,为了让我们熟练掌握计算机中的数据存储而设计。前言:什么是 cheating之前在 B 站上 CSAPP 第一节课,教授就说了在 CMU,作弊的定义是什么,说实话有点 shocked:Cheating: DescriptionCheating: ConsequenceCMU CSAPP 的规定是,完成 lab 的时候不可以上网查资料,只要上网搜索了,不管有没有找结果,搜索的这个过程就算作 cheating(这只是课程实验!)。一个学期有 25 人因为 cheating 受到惩罚,而且后果十分严重。看看我们的 HNU,至今没听说过有谁因为作弊而受到处分……按照这个要求,lab 做得真的非常痛苦。有些题目要想很久还想不出来。而且说实话还是有一两个题目不会做,去网上看了别人的 solution。但是这样做完的 datalab,相比直接上网一通搜索抄来的代码,确实更加有成就感,也对自己的「二进制思维」能力有更大的锻炼。这也正是这几个 lab 的意义所在吧。所以,如果你在做 datalab 而上网搜索看到了这里,在继续看下去之前,不妨再继续独立思考一下吧。本实验的一些坑dlc 这个语法检查器按照 C89/C90(ANSI C)的标准,这个标准规定所有局部变量都要在代码块的开头定义。所以如果在函数中途定义一个局部变量,虽然用 make 可以正常编译(现在的编译器很少还用 ANSI C 标准),但是用 dlc 检查则会提示 parse error 的错误。实验环境必须是旧版本的 Linux 环境(如 Ubuntu 12.04 LTS)。使用较新的发行版可能会导致无法通过 fitsBits 这一关。(目前暂不清楚是编译器还是内核等的问题,据说是本 lab 本身的一个 bug)用 docker 创建运行环境在 x86 的主机上使用 docker 创建 Ubuntu 12.04 LTS 的容器,指定将容器内 /home 目录映射到容器外的目录,然后进入容器,安装相关软件:将容器内 /home 映射到容器外 /root/Lab/HNU-CSAPP/ex2/docker-env/home,这两个目录也可以自己指定docker run -itd --name datalab -v /root/Lab/HNU-CSAPP/ex2/docker-env/home:/home ubuntu:12.04进入容器内的 shelldocker exec -it datalab /bin/bash12.04 年代久远,需要将 sources 里 archive.ubuntu.com 改为 old-reloease.ubuntu.com 才能 apt-get updatesed -i -e 's/archive.ubuntu.com|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list获取软件包列表apt-get update安装本实验必要的软件包apt-get install gcc make gcc-multilib回到容器外,将文件移进指定的目录(/root/Lab/HNU-CSAPP/ex2/docker-env/home),编辑好后再进入容器测试。可以用 tmux 半屏开 vim 写代码,半屏进容器终端测试。注意如果在容器外测试过了,进入容器要 make clean 再重新 make,因为不同环境编译生成的二进制可执行文件可能不兼容。bitAnd用或和非实现与,用直接取反的方法即可:x and y = not ((not x) or (not y))。getByte很显然答案是 (x >> n8) & 0xFF。但是不允许使用乘法,如何获得 n8 呢?答案是直接用加法。8 个 n 相加会超过 ops 数量限制,我们可以先加出 2n、4n。logicalShift要求实现 logical shift,因为默认的 shift 是 arithmetic shift。本质的区别在于负数的 shr,前者左边出来的是 0 而右者出来的是 1。很容易想到可以 and 上一个 keeper,这个 keeper 的值是 0000111...11,右边的 1 就是要保留的那些位。这样可以去掉左边产生的 1。如何得到这个 keeper?显然 keeper = 0x7FFFFFFF >> (n-1)。有两个问题。首先是如何产生 0x7FFFFFFF 这个数字呢?因为游戏规则规定立即数赋值不能超过 2 Byte。答案是 ~(1 << 31)。接下来是如何处理 n = 0 的情况。好在这关允许我们用 ! 运算符,可以实现类似选择赋值的语句。具体来说,我们需要这样一个 flag 变量,当 n 为 0 时它是 0x00000000,当 n 非 0 时它是 0xFFFFFFFF。试试用 ! 运算符,直接对 n 取逻辑非 notn,这样 n 为 0 时我们得到 1,n 非 0 时我们得到 0。很显然,flag = notn - 1。现在我们分别计算出 n 为 0 和不为 0 的两种情况 result1 和 result2,则最后只需要返回 ((!flag) & result1) | (flag & result2)。是不是有点像数电里的「多路复用」呢。这种对 n = 0 特判的方法,在后面的关卡中会多次遇到。最后,这关不能用 - 减法,如何实现 -1 呢?直接用加上 0xFFFFFFFF 就行啦。bitCount(这题有点难想 😨)要求统计二进制中 1 的个数(就是 popcount)。其实对于每一位是 0 是 1 我们都可以通过 and 得知,最难的就是如何累加。累加的部分可以考虑分治,要累加 32 位的结果,我们假设得到了两个 16 位的结果,只考虑这两个如何相加;要计算 16 位的结果只考虑两个 8 位的结果如何相加,以此类推。那么两个 16 位的结果如何相加呢?我们假设两个结果分别存储在 32 位 int 的高 16 位和低 16 位,只要把两个部分取出来,前者右移 16 位相加即可。下面的也类似。除此之外,题目要求使用的立即数大小不超过 0xFF,需要用一些 tricky 的手段获得我们要的立即数,以免超出符号数量限制。可以参阅代码。bang要求计算逻辑反,也就是我们要返回这样一个值,当 x 为全 0 时其为 1,x 任何一位为 1 时其为 0。理所当然地可以想到应该把 x 每一位 or 起来(或者把 ~x 每一位 and 起来),结果放在最低位,得到 1 或 0。然而如果真的取出 32 位每一位进行 or,ops 会超过限制。可以用分治的方法,先将 [0,15] 和 [16,31] 这两段按位处理放入低的一段,再处理 [0,7] 和 [8,15],以此类推,可以在 ops 数量限制内完成。tmin返回最小的 int,就是 1 << 31。fitsBits第一种方法是一开始想的奇怪方法,至少需要用到 19 个 ops,超过题目限制,其实不能通过。要我们返回 0 或 1 表示 x 是否能被 n 位补码表示,实际上就是返回是否 −2�−1≤�≤2�−1−1−2 n−1 ≤x≤2 n−1 −1。也就是 −2�−1≤�<0−2 n−1 ≤x<0 或 0≤�≤2�−1−10≤x≤2 n−1 −1。再变换一下,就是:0≤�+2�−1<2�−10≤x+2 n−1 <2 n−1 或 −2�−1≤�−2�−1≤−1−2 n−1 ≤x−2 n−1 ≤−1。也就是如果 �<0x<0 则要满足 �+2�−1≥0x+2 n−1 ≥0;如果 �≥0x≥0 则要满足 �−2�−1≤−1x−2 n−1 ≤−1。依然根据 x 的符号位选择一个结果返回。第二种方法则是可以通过的正解:根据算数移位的性质。假设给我们一个 n 位补码表示的数,我们直接将其左移至与 32 位 int 符号位对齐,然后再移回去,得到的结果应该是和原来一样的。据此即可判断。⚠️ 这题使用较新版本的环境测试是无法通过的,目前暂不清楚与什么因素有关。在 Ubuntu 12.04 LTS x86_64 中测试可以通过。详见开头「用 docker 创建运行环境」。divpwr2要求返回 �2�2 nx ,向 0 取整。对于正数当然就是 >> n 即可,对于负数就有些复杂,因为 >> 运算默认向下取整,需要进行修正。对于负数,列表可以发现,每个数字在计算之前要加上 2�−12 n −1 的偏移才能得到正确答案。因为出现了 n - 1,所以还要特别考虑 n = 0 的情况。使用 logicalShift 一样的「多路复用」方法即可。negate取负数,就是 (~x)+1,很简单。isPositive根据符号位,可以轻易判断出这个数是否 >= 0。既然本题要求判断 > 0,也就是 0 是特殊情况,依然可以根据前面的方法特殊判断。isLessOrEqual判断 �≤�x≤y,不能直接判断 �−�≥0y−x≥0,因为会 overflow。可以发现如果 x 和 y 同号,肯定不会 overflow。所以只需要对异号的情况进行判断。为了更加清晰,列出一个真值表:x 符号位 y 符号位 返回值1 0 10 1 00 0 y-x 的符号位取反1 1 y-x 的符号位取反依然是个分类讨论(「多路复用」)的思路,根据 x xor y 的值返回答案,如果为 1 答案是 x 的符号位,如果为 0 答案为 y-x 的符号位取反。ilog2要求返回 log2(x) 向下取整。很显然答案就是 x 的二进制中最左边的 1(最高位)在从右往左第几位。首先通过将 x 按位或 x >> k 的手段(k 分别等于 1、2、4……),可以将 x = 0001xxxxx 变为 x = 000111111,也就是最高位之后全都变成 1。然后可以做一遍之前的 bitCount,将结果减去 1 即可。float_neg这部分终于可以用 if 了。只要判断 NaN 的情况原封不动返回,否则修改符号位返回就行。对符号位取反可以对 1<<31 取异或。可以构造两个变量 get_exp 和 get_frac 分别用于和一个数按位与,从而取出这个数中的 exp 或 frac 片段。exp 就是 0xFF << 23,frac 则是 ~exp 再对符号位取反。float_i2f要将提供的 int 值 x 转换为 float。int 和 float 关于负数的概念是截然不同的,所以我们必须对 int 的绝对值进行处理。所以首先要对于 x < 0 的情况设置符号位后直接取相反数。(此时注意特判 tmin 的情况!它没有能由 int 表示的相反数)设置好 sign 后,只要处理 exp 和 frac 即可。假设 x 是个 n 位的二进制数,exp 就等于 n - 1 + bias。frac 就是右边的 n - 1 位(左对齐)。要考虑进位问题。将 frac 被截掉的部分拿出来(最多有 8 位),如果「大于 0x80」或者「等于 0x80 且frac 末位为 1」(round to even 的情况)则要进一位,frac 连续进位。注意到 frac 有可能进位进到 exp 里,但是我们的代码里不用判断,因为 frac 最高位之前就是 exp 最低位。(在这里是不是再次感到了 IEEE 浮点数设计的精妙?)最后注意这题 0 也要特判,因为 0 属于 denomilized。这题写起来很容易超过 ops 数量限制。需要省着点用。float_twice将给出的 float 翻两倍,要分别考虑 nomalized、denomolized 和 special 的情况。nomolized 很简单,直接修改 exp 部分即可。注意可能有 nomolized 进入 infinity 的情况。denomolized 只需要修改 frac 即可。注意可能有从 denomolized 进入 nomolized 的情况,要修改 exp。最后 infinity、NaN 这两种值都原样返回,需要特判。完整代码参考/*bitAnd - x&y using only ~ and |Example: bitAnd(6, 5) = 4Legal ops: ~ |Max ops: 8Rating: 1 */int bitAnd(int x, int y) { return ~((~x) | (~y));}/*getByte - Extract byte n from word xBytes numbered from 0 (LSB) to 3 (MSB)Examples: getByte(0x12345678,1) = 0x56Legal ops: ! ~ & ^ | + << >>Max ops: 6Rating: 2 */int getByte(int x, int n) { int n2 = n + n; int n4 = n2 + n2; int n8 = n4 + n4; return (x >> n8) & 0xFF;}/*logicalShift - shift x to the right by n, using a logical shiftCan assume that 0 <= n <= 31Examples: logicalShift(0x87654321,4) = 0x08765432Legal ops: ! ~ & ^ | + << >>Max ops: 20Rating: 3 */int logicalShift(int x, int n) { int sub1 = ~0; int keeper = ~(1 << 31); int result = (x >> n) & (keeper >> (n + sub1)); int notn = !n; int flag = notn + sub1; return ((~flag) & x) | (flag & result);}/*bitCount - returns count of number of 1's in wordExamples: bitCount(5) = 2, bitCount(7) = 3Legal ops: ! ~ & ^ | + << >>Max ops: 40Rating: 4 */int bitCount(int x) { int ret = x; int mask = 0; // 0x55555555 mask = 0x55 + (0x55 << 8); mask = mask + (mask << 16); ret = (ret & mask) + ((ret>>1) & mask); // 0x33333333 mask = 0x33 + (0x33 << 8); mask = mask + (mask << 16); ret = (ret & mask) + ((ret>>2) & mask); // 0x0f0f0f0f mask = 0x0f + (0x0f << 8) + (0x0f << 16) + (0x0f << 24); ret = (ret & mask) + ((ret>>4) & mask); // 0x00ff00ff mask = 0xff + (0xff << 16); ret = (ret & mask) + ((ret>>8) & mask); // 0x0000ffff mask = 0xff + (0xff << 8); ret = (ret & mask) + ((ret>>16) & mask); return ret;}/*bang - Compute !x without using !Examples: bang(3) = 0, bang(0) = 1Legal ops: ~ & ^ | + << >>Max ops: 12Rating: 4 */int bang(int x) { int ret = ~x; ret = ret & (ret >> 16); ret = ret & (ret >> 8); ret = ret & (ret >> 4); ret = ret & (ret >> 2); ret = ret & (ret >> 1); return ret&1;}/*tmin - return minimum two's complement integerLegal ops: ! ~ & ^ | + << >>Max ops: 4Rating: 1 */int tmin(void) { return 1 << 31;}/*fitsBits - return 1 if x can be represented as ann-bit, two's complement integer.1 <= n <= 32Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1Legal ops: ! ~ & ^ | + << >>Max ops: 15Rating: 2 */int fitsBits(int x, int n) {// int sub1 = ~0;// int get_sign = 1<<31;// int sign = x & get_sign;// int not_sign = !sign;// int power = 1 << (n + sub1);// int sub_power = (~power) + 1;// int result1 = !(((x + power) & get_sign) >> 31);// int result2 = ((x + sub_power) & get_sign) >> 31;// sign = !not_sign;// return (sign & result1) | (not_sign & result2); int subn = (~n) + 1; int to_shl = 32 + subn; int x_fix = (x << to_shl) >> to_shl; int not_ans = x ^ x_fix; return !not_ans;}/*divpwr2 - Compute x/(2^n), for 0 <= n <= 30Round toward zeroExamples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2Legal ops: ! ~ & ^ | + << >>Max ops: 15Rating: 2 */int divpwr2(int x, int n) {// Round to zero is the difficult part int sub1 = ~0; int super2sub1 = (1<<n) + sub1; int notn = !n; int flag = notn + sub1; int result = (x + ((x >> 31) & super2sub1)) >> n; return ((~flag) & x) | (flag & result);}/*negate - return -xExample: negate(1) = -1.Legal ops: ! ~ & ^ | + << >>Max ops: 5Rating: 2 */int negate(int x) { return (~x) + 1;}/*isPositive - return 1 if x > 0, return 0 otherwiseExample: isPositive(-1) = 0.Legal ops: ! ~ & ^ | + << >>Max ops: 8Rating: 3 */int isPositive(int x) { int sub1 = ~0; int notx = !x; int flag = notx + sub1; int result = !(x >> 31); return flag & result; // return ((~flag) & 0) | (flag & result); // the first part isn't necessary}/*isLessOrEqual - if x <= y then return 1, else return 0Example: isLessOrEqual(4,5) = 1.Legal ops: ! ~ & ^ | + << >>Max ops: 24Rating: 3 */int isLessOrEqual(int x, int y) { int xxory = (x >> 31) ^ (y >> 31); int subx = (~x) + 1; int ysubx = y + subx; int result_1 = (x >> 31) & 1; int result_2 = !(ysubx >> 31); return (xxory & result_1) | ((~xxory) & result_2);}/*ilog2 - return floor(log base 2 of x), where x > 0Example: ilog2(16) = 4Legal ops: ! ~ & ^ | + << >>Max ops: 90Rating: 4 */int ilog2(int x) { int sub1 = ~0; int x_fill = x; int ret, mask; x_fill |= x_fill >> 1; x_fill |= x_fill >> 2; x_fill |= x_fill >> 4; x_fill |= x_fill >> 8; x_fill |= x_fill >> 16;// perform bitCount ret = x_fill; mask = 0; // 0x55555555 mask = 0x55 + (0x55 << 8); mask = mask + (mask << 16); ret = (ret & mask) + ((ret>>1) & mask); // 0x33333333 mask = 0x33 + (0x33 << 8); mask = mask + (mask << 16); ret = (ret & mask) + ((ret>>2) & mask); // 0x0f0f0f0f mask = 0x0f + (0x0f << 8) + (0x0f << 16) + (0x0f << 24); ret = (ret & mask) + ((ret>>4) & mask); // 0x00ff00ff mask = 0xff + (0xff << 16); ret = (ret & mask) + ((ret>>8) & mask); // 0x0000ffff mask = 0xff + (0xff << 8); ret = (ret & mask) + ((ret>>16) & mask); return ret + sub1;}/*float_neg - Return bit-level equivalent of expression -f forfloating point argument f.Both the argument and result are passed as unsigned int's, butthey are to be interpreted as the bit-level representations ofsingle-precision floating point values.When argument is NaN, return argument.Legal ops: Any integer/unsigned operations incl. ||, &&. also if, whileMax ops: 10Rating: 2 */unsigned float_neg(unsigned uf) { unsigned change_sign = 1 << 31; unsigned get_exp = 0xFF << 23; unsigned get_frac = (~get_exp) ^ change_sign; if ((uf & get_exp) == get_exp && (uf & get_frac) != 0) return uf; return uf ^ change_sign;}/*float_i2f - Return bit-level equivalent of expression (float) xResult is returned as unsigned int, butit is to be interpreted as the bit-level representation of asingle-precision floating point values.Legal ops: Any integer/unsigned operations incl. ||, &&. also if, whileMax ops: 30Rating: 4 */unsigned float_i2f(int x) { int bias = 127; unsigned get_frac = 0x007fffff; unsigned get_frac_cut = 0x000000ff; unsigned tminf = 0xcf000000; int fix_x, n, nsub1, temp; unsigned sign, exp, frac, frac_cut;// special cases if (x == 0x80000000) return tminf; if (x == 0) return 0;// get abs / get sign fix_x = x; sign = 0; if (fix_x < 0) fix_x = -fix_x, sign = 0x80000000;// get count n = 0; temp = fix_x; while (temp) n++, temp>>=1; nsub1 = n - 1; //printf("count=%d\n",count);// get exp exp = ((nsub1 + bias) << 23);// get frac if (nsub1 <= 23){frac = (fix_x << (23 - nsub1)) & get_frac;} else {frac = (fix_x >> (nsub1 - 23)) & get_frac; frac_cut = (fix_x << (31 - nsub1)) & get_frac_cut; if ((frac_cut > 0x80) || (frac_cut == 0x80 && (frac & 1))) frac++; // carry}return sign + exp + frac;}/*float_twice - Return bit-level equivalent of expression 2*f forfloating point argument f.Both the argument and result are passed as unsigned int's, butthey are to be interpreted as the bit-level representation ofsingle-precision floating point values.When argument is NaN, return argumentLegal ops: Any integer/unsigned operations incl. ||, &&. also if, whileMax ops: 30Rating: 4 */unsigned float_twice(unsigned uf) { unsigned neg_inf = 0xff800000; unsigned pos_inf = 0x7f800000; unsigned get_exp = 0x7f800000; unsigned get_frac = 0x007fffff; unsigned exp = (uf & get_exp) >> 23; unsigned frac = uf & get_frac; unsigned ret = uf; if (exp == 0xff) return uf; if (exp == 0){ // denomolizedif (frac & (1<<23)){ // de -> no ret = ret | (1 << 23); return ret; } else { ret = ret - frac; frac = frac << 1; ret = ret + frac; return ret; }} else { // nomolizedret = ret - (exp << 23); exp++; if (exp > 0xff) { // no -> inf if (uf & (1<<31)) return neg_inf; else return pos_inf; } ret = ret + (exp << 23); return ret;}}
2023年05月13日
130 阅读
0 评论
0 点赞
2023-05-13
部署 Memos 接入 QQ 机器人
前几天部署了开源的浮墨笔记(flomo)替代品:Memos。这个项目已经有了 Android、iOS 客户端、浏览器拓展等等一系列生态。但是对比原版的浮墨笔记,少了 QQ / 微信的接入对于我来说不太方便。Memos 提供了简便的 API,我们可以自己动手,部署一个 QQ 机器人。前置准备服务器,最好是境内的。搭建好的 Memos 实例(我的实例:memos.skywt.cn)。一个可登录的闲置 QQ 账号。运行 go-cqhttp我们的机器人在服务端分为两个组件,这两个组件都要部署在我们的服务器上。go-cqhttp 负责与腾讯 QQ 服务器通信,管理我们的账号登录、消息收发等过程;NoneBot 负责将 go-cqhttp 收到的消息进行处理,控制发送消息的内容,运行我们想要的业务逻辑。首先部署 go-cqhttp。前往 GitHub 上的 release 下载最新适合服务器架构的版本,下载解压之后是一个单文件程序。第一次运行,在提示选择通信方式时,选择「反向 Websocket 通信」,之后程序就会在相同目录下生成配置文件。编辑配置文件,根据提示将 uin 和 password 字段分别改为自己的 QQ 和密码。虽然配置文件中说「密码为空时可以使用扫码登录」,但是经过实测只有在同一网络环境下 QQ 才会允许扫码登录。部署在服务器上扫码是无法登录的,只能使用密码。除此之外,修改文件底部 servers 部分 ws-reverse 中 universal 的地址。我设定的是 ws://127.0.0.1:8989/ws/。这个地址告诉 go-cqhttp,该从哪里与后端的 NoneBot 通信。修改完毕后,再次启动 go-cqhttp,可能会提示需要扫码、滑块验证、手机验证之类,跟着程序的提示进行即可。程序会提示,对于腾讯云的服务器需要修改 DNS 为 114.114.114.114。编辑 /etc/resolv.conf 并修改就行。出现以下消息就表明登录成功:此时可能会出现大量的「Connection Refused」消息,这是因为我们还没启动后端的 NoneBot。忽略他们即可。接下来,要将 go-cqhttp 放到后台运行,开始编写 NoneBot 的主程序。编写运行 NoneBot参阅 NoneBot 的官方指南编写主要的代码,包括 bot.py 和 config.py。在后者中要指定与 go-cqhttp 的 config.yml 中配置一致的 HOST 和 PORT。我的配置分别是 127.0.0.1 和 8989。接下来,我们可以通过编写插件的方式使 QQ 机器人实现 Memo 备忘录的功能。其实直接根据文档里的插件修改一下就可以了:from nonebot import on_command, CommandSessionimport requestsimport jsonplugin_name = 'Memos'plugin_usage = r"""memo [需要添加的 Memo 内容]"""MEMOS_API = 'https://memos.skywt.cn/api/memo?openId='@on_command('memo', aliases=('note'))async def memo(session: CommandSession):memo_text = session.current_arg_text.strip() if not memo_text: await session.send('Memo 内容不能为空!') return ret = await send_memo(memo_text) await session.send(ret) async def send_memo(memo_text: str) -> str:data_to_send = {'content': memo_text} r = requests.post(MEMOS_API, data=json.dumps(data_to_send)) if r.status_code == 200: return '添加成功' else: return '添加失败,请查阅日志'回到 NoneBot 的主目录启动 bot.py 并放在后台,发送消息 memo 这是一条测试笔记,可以看到正确的输出。进入 Memos 可以看到成功添加了一条 memo。聊天界面,机器人回复添加成功参考资料go-cqhttp 使用指南NoneBot 使用指南:文档通俗易懂,强烈推荐
2023年05月13日
140 阅读
0 评论
0 点赞
2023-05-13
欢迎使用 Typecho
如果您看到这篇文章,表示您的 blog 已经安装成功.
2023年05月13日
120 阅读
1 评论
0 点赞
1
2
3