Franklin-Reiter相关消息攻击

news2024/10/6 6:04:36

文章目录

  • 知识导入:
  • 题一
      • 题目描述:
      • 题目分析:
  • 题二
      • 题目描述:
      • 题目分析:
  • 题三
      • 题目描述:
      • 题目分析:
  • 收获与体会:

知识导入:

在这里插入图片描述

  • 总结:
    Franklin-Reiter相关消息攻击(Franklin-Reiter related-message attack):如果两个信息之间存在已知的固定差异,并且在相同的模数n下进行RSA加密,那么就有可能恢复出这两个消息
  • 简单点说就是m和m+a两个明文在相同的e和n下进行加密,那么m就可以破解
如果两条消息之间仅存在已知的固定差异

并且在相同的RSA模N下加密

那么就有可能同时恢复它们

m1 = bytes_to_long(flag)

m2 = a * m1 + b

c1 = pow(m1,e,n)

c2 = pow(a * m1 + b,e,n)

其中c1,c2,e,n都已知,那么m1,m2可被破解

题一

题目描述:

from secret import flag
from Crypto.Util.number import *

m1 = bytes_to_long(flag)
N = getPrime(512)*getPrime(512)
e = 17

c1 = pow(m1, e, N)

a = getRandomNBitInteger(512)
b = getRandomNBitInteger(512)
m2 = a * m1 + b
c2 = pow(m2, e, N)

print(N, a, b, c1, c2, sep="\n")

# 51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741
# 13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967
# 12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170
# 18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142
# 14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720

题目分析:

典型的Franklin-Reiter相关消息攻击,直接上解题代码:

n=51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741
a=13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967
b=12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170
c1=18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142
c2=14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720
e=17

import libnum
def franklinReiter(n,e,c1,c2,a,b):
    R.<X> = Zmod(n)[]
    f1 = X^e - c1
    f2 = (X*a+ b)^e - c2
    # coefficient 0 = -m, which is what we wanted!
    return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0]) # 系数

  # GCD is not implemented for rings over composite modulus in Sage
  # so we do our own implementation. Its the exact same as standard GCD, but with
  # the polynomials monic representation
def compositeModulusGCD(a, b):
    if(b == 0):
        return a.monic()
    else:
        return compositeModulusGCD(b, a % b)

m=franklinReiter(n,e,c1,c2,a,b)
print(libnum.n2s(int(m)))
# flag{a593591a-3749-cc52-0c27-e897fac2c967}

或者(本质一样,看起来更简洁一些):

import libnum
n=51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741
a=13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967
b=12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170
c1=18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142
c2=14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720
e=17
import binascii
def franklinReiter(n,e,c1,c2,a,b):
    PR.<x> = PolynomialRing(Zmod(n))
    g1 = (x)^e - c1
    g2 = (a*x+b)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic() # 
    return -gcd(g1, g2)[0]

m=franklinReiter(n,e,c1,c2,a,b)
print(libnum.n2s(int(m)))
# flag{a593591a-3749-cc52-0c27-e897fac2c967}
  • 其中:
def gcd(g1, g2): 
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic() # 
    return -gcd(g1, g2)[0]
  1. 使用辗转相除法求多项式的最大公因子
  2. 在代数中,一个多项式的首项系数通常被称为该多项式的引导系数(leading coefficient),而将多项式变成首项系数为1的形式被称为将多项式化为首一形式(monic form)
  3. 调用函数g1.monic()将g1转换为首一多项式(monic polynomial),并返回该多项式。
  4. 使用g.monic()[0],则会返回g(x)除以引导系数后得到的多项式的常数项
    比如:g.monic() = x + 32412345
    那么:g.monic()[0] = 32412345

题二

题目描述:

找不到题源,只有图片,将就看吧
在这里插入图片描述

题目分析:

  • 首先来看一下下面这一串
assert(reduce(lambda x,y:x&y,[(i-5)*i+6==0 for i in x]))
 1. [(i-5)*i+6==0 for i in x] 使用列表推导式判断等式是否成立,
    若成立,返回True,否则False,故列表中最终得到的是[True,True]
 2. 为什么是True呢,因为用了assert断言函数,
    如果为真则正常执行,如果为假则报错,过程中断
    给的题目是会正常运行下去的,故该结果应为True,
 3. 由此可以反推出x = [2,3](解方程得到),显然也可以知道下一串中y = [4,11]
 4. reduce函数是应用lambda表达式对列表([True,True])中的每一个元素依次进行异或操作

 5. PS:“lambda x,y:x&y”中的‘x’
        与
        “[(i-5)*i+6==0 for i in x]”中的‘x’
        并不是同一个!!!(如果这里清楚了,那这句话就是真的弄明白了!)
  • 所以推出(解方程相信大家都会)

x = [2,3]
y = [4,11]

  • 接下来看最后一串:
reduce(lambda x,y:x*m+y,x)

这里就不过多说理论方面的了,我直接举个例子吧

x = [1,2,3,4]
reduce(lambda x,y:x+y,x),使用reduce对x进行累积
x = 1,y = 2,==>
x+y = 3 ①
x = ①,y = 3,==>
x+y = 6 ②
x = ②,y = 4,==>
x+y = 10 ③
即 1+2+3+4

最终得到的结果为10
再举个更难一点的例子

x = [1,2,3,4]
reduce(lambda x,y:x*m+y,x),使用reduce对x进行累积
x = 1,y = 2,==>
x*m+y = 1*m+2 ①
x = ①,y = 3,==>
x*m+y = (1*m+2)*m+3 ②
x = ②,y = 4,==>
x*m+y = ((1*m+2)*m+3)*m+4 ③

最终得到的即为式③

  • 由此可以得知题目中
reduce(lambda x,y:x*m+y,x) = 2*m+3
reduce(lambda x,y:x*m+y,y) = 4*m+11
  • 最终得到:
c1 = pow(2*m+3,17,n)
c2 = pow(4*m+11,17,n)
  • 又是典型的Franklin-Reiter相关消息攻击,直接上代码解题
    (没找到题源,将就看图片吧)
    在这里插入图片描述

题三

(NEEPUSec CTF 2021 RSA)

题目描述:

from Crypto.Util.number import *
from sympy import nextprime
import gmpy2
import random

def encode (p1,p2,e):
    not_hint = (p1 + 1) * (p2 + 1)
    S = gmpy2.invert(e, not_hint)
    not_p = S%(p1+1)
    return not_p

flag = b'Neepu{********************}'
flag = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
n = p*q
e = nextprime(random.randint(1,1000))
d = gmpy2.invert(e, (p-1)*(q-1))
c = pow(flag, e, n)
print(c)
print(n)

m = encode(p, q, e)
c1 = pow(m, 7, n)
c2 = pow(m+e, 7, n)
print(c1)
print(c2)

c = 78543767285872349029076059073458316000847341792088805258173041942425687239313215276670106926320359777962661495032475004417723103701253550583245518206305422982968675291500865382213182669036827898932991063338163290845510339896689210314509493839746410486257998875782496654704288722251878269643040214139429715671
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
c1 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892
c2 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119

题目分析:

c1 = pow(m, 7, n)
c2 = pow(m+e, 7, n)

很熟悉的两串,但其中有两个未知数,以上两题都只有一个未知数,所以先要把e给求出来,再使用Franklin-Reiter相关消息攻击求出m。如何求e呢,这里便用到了短填充攻击(Coppersmith’s short-pad attack)【目前本人也没看此攻击手法,等推到了原理再说】,不多说,直接套脚本

def short_pad_attack(c1, c2, e, n):
    PRxy.< x, y > = PolynomialRing(Zmod(n))
    PRx.< xn > = PolynomialRing(Zmod(n))
    PRZZ.< xz, yz > = PolynomialRing(Zmod(n))
    g1 = x ^ e - c1
    g2 = (x + y) ^ e - c2
    q1 = g1.change_ring(PRZZ)
    q2 = g2.change_ring(PRZZ)
    h = q2.resultant(q1)
    h = h.univariate_polynomial()
    h = h.change_ring(PRx).subs(y=xn)
    h = h.monic()
    kbits = n.nbits() // (2 * e * e)
    diff = h.small_roots(X=2 ^ kbits, beta=0.4)[0]  # find root < 2^kbits with factor >= n^0.4
    return diff


def related_message_attack(c1, c2, diff, e, n):
    PRx.< x > = PolynomialRing(Zmod(n))
    g1 = x ^ e - c1
    g2 = (x + diff) ^ e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]


if __name__ == '__main__':
    c2 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892
    c1 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119
    n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
    e = 7
    diff = short_pad_attack(c1, c2, e, n)
    print("difference of two messages is %d" % diff)
    m1 = related_message_attack(c1, c2, diff, e, n)
    print("m1:", m1)
    print("m2:", m1 + diff)
  • 另一种方法求m(直接爆破e):
import binascii
def attack(c1, c2, n, e):
    PR.<x> = PolynomialRing(Zmod(n))
    g1 = (x)^7 - c1
    g2 = (x+e)^7 - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()
    return -gcd(g1, g2)[0]
c1 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892
c2 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543

for e in range(1,1000):
    m = attack(c1, c2, n, e)
    try:
        if pow(m,7,n) == c1:
            print((e,m))
    except:
        pass
#结果:(71, 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859)
#e = 71
#m = 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859
  • 求到m,又m = s % (p+1) = d % (p+1),这不就类似于dp泄露吗,简单推导一下,把加1换成减1即可得到p,q,直接上脚本了
import gmpy2
from Crypto.Util.number import *
e = 71
# m = 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859
c = 78543767285872349029076059073458316000847341792088805258173041942425687239313215276670106926320359777962661495032475004417723103701253550583245518206305422982968675291500865382213182669036827898932991063338163290845510339896689210314509493839746410486257998875782496654704288722251878269643040214139429715671
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
# dp = m ,类似的dp
dp = 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859
for i in range(1,65535):
    p = (dp*e-1)//i-1
    if n%p == 0:
        q = n//p
        d = gmpy2.invert(e, (p - 1) * (q - 1))
        flag = pow(c,d,n)
        print(long_to_bytes(flag))
        break
# Neepu{Have-a-g00d-day12138}

收获与体会:

学到挺多,对相关消息攻击掌握了,对reduce(),lambda()函数理解更透彻了,
学到了dp泄露的另一种变形,嗯,不得不说,收获挺大的

浅记一下:

做完NEEPUSec CTF 2023后,感觉这个比赛的题目质量很不错,于是就找了一下它往年的题,然后就做到了NEEPUSec CTF 2021 RSA这题。一开始看它题目描述感觉很简单,但推来推去也只能得到 m * e = 1 % (p+1),m和e还是不知道,结果就是,啥都没求出来。看完wp后知道原来这里对m,e的求解有具体知识点,于是查找了很多资料,最终把这题给弄懂了。

其实一开始是本着直接套脚本的想法,但突然想到了某位前辈说的一句话 “我们是搞网络安全的,不是找flag的ctfer,别把两者搞反了”。
但其实说完全弄懂这题还是有些勉强,里面有一些小点还是不太懂的,比如求公因子后会得到x-M,其中M即为所求的m,不太明白(挠头),比如短填充攻击(还没找资料看),总之大体步骤和含义是清楚了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/598589.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库管理-第七十九期 儿童节惊魂(20230601)

数据库管理 2023-06-01 第七十九期 儿童节惊魂1 主板挂了&#xff1f;2 时间同步3 数据库参数4 ACFS5 两个错误总结 第七十九期 儿童节惊魂 6月第一天&#xff0c;又是儿童节&#xff0c;加上客户现场来了不少娃&#xff0c;也有一些客户家里有娃去参加活动了&#xff0c;所以…

EMC学习笔记(一)PCB电路板层的设置

EMC学习笔记&#xff08;一&#xff09;PCB电路板层的设置 1.概述2.合理的层数2.1 VCC、GND的层数2.2信号层数 3.单板的性能指标与成本要求4.电源层、地层、信号层的相对位置4.1 VCC\GND平面的阻抗以及电源、地之间的EMC环境问题4.2 VCC、GND作为参考平面&#xff0c;两者的作用…

OneNav Extend网址导航书签系统源码开源版

简介&#xff1a; OneNav Extend 是一款开源免费的书签&#xff08;导航&#xff09;管理程序&#xff0c;使用PHP SQLite 3开发&#xff0c;界面简洁&#xff0c;安装简单&#xff0c;使用方便。 OneNav可帮助你你将浏览器书签集中式管理&#xff0c;解决跨设备、跨平台、跨…

10.Ansible Loops介绍

Ansible Loops是什么&#xff1f;以及实际例子 就是循环语句。让我们看看这个创建Playbook的示例&#xff61;要使用用户模块在系统中创建用户,在本例中, 我们只创建一个用户&#xff61;但是如果我们有多个用户呢? name: Create users hosts: localhost tasks:- user: name g…

城市二次供水设备远程监控解决方案

城市二次供水设备远程监控解决方案 一、项目背景 近年来&#xff0c;随着我国城市日新月异的发展,新建商场和小区高层逐渐的增多&#xff0c;需要二次供水的楼盘也在逐渐增多。二次供水模式成了城市普遍的供水模式&#xff0c;当前普遍采用传统供水方式存在着供水水源、加压供…

Sentinel怎么使用和控制台讲解

Sentinel 基础 官网 1 Github: https://github.com/alibaba/Sentinel 2 快速开始: https://sentinelguard.io/zh-cn/docs/quick-start.html 3 中文: https://github.com/alibaba/Sentinel/wiki/介绍 4 使用手册: https://spring-cloud-alibaba-group.github.io/github-pag…

LNMP搭建过程详解,验证搭建论坛

LNMP搭建过程详解&#xff0c;验证搭建论坛 一、安装Nginx服务1、安装依赖包2、创建运行用户3、编译安装4、优化路径5、添加Nginx 系统服务 二、安装MySQL服务1、安装Mysql环境依赖包2、创建运行用户3、编译安装4、修改mysql配置文件5、更改mysql安装目录和配置文件的属主属组6…

【C/C++】基础知识之输入输出流

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

C#高级控件

大家好&#xff0c;我是华山自控编程朱老师 前几天一个学员在学习C#与高级控件交互时,也不知道高级控件可以用来做什么 。下面我们就详细讲讲C# 和高级控件交互的相关知识。 C#是一种功能丰富的面向对象编程语言&#xff0c;它包含了许多高级控件&#xff0c;如ListView和Tre…

改进YOLOv5系列:结合CVPR2021:多头注意力Efficient Multi-Head Self-Attention

Efficient Multi-Head Self-Attention Efficient注意力介绍代码common代码yaml文件参数结果论文: ResT: An Efficient Transformer for Visual Recognition Efficient注意力介绍 本文提出了一个高效的多尺度视觉变换器,称为ResT,它可以作为图像识别的通用支柱。可以作为图像…

LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS (Paper reading) Edward H, Microsoft, arXiv2021, Cited: 354, Code, Paper 1. 前言 自然语言处理的一个重要范式是在通用领域数据上进行大规模预训练&#xff0c;然后根据特定任务或领域进行适应性训练。随着我们对模…

Hightopo 使用心得(2)- 2D 图纸 GraphView,节点 Node, 连线 Edge,与基本动画 ht.Default.startAnim()

概括来说&#xff0c;用 HT for Web 做可视化主要分为两部分&#xff0c;也就是 2D 和 3D。这两部分需要单独创建。在它们被创建完成后&#xff0c;我们再把它们集成到一起。 HT for Web 的 2D 部分主要是指 ht.graph.GraphView (简称 GraphView&#xff0c;也就是 2D 图纸)。…

匿名管道通信

目录 一、进程通信原理 二、什么是管道 三、创建一个匿名管道 四、fork共享管道的原理 五、管道的特点 六、4中场景 一、进程通信原理 我们知道进程间相互独立&#xff0c;具有独立性。那么我们要实现两个进程之间的通信就需要&#xff0c;让这两个进程看到同一个文件。然…

设计模式-访问者模式

访问者模式 问题背景解决方案&#xff1a;传统方案 访问者模式基本介绍原理UML类图 使用访问者模式解决问题UML类图示例代码运行结果 注意事项和细节 问题背景 我们来制作一台电脑&#xff0c;他的硬件有CPU和磁盘&#xff0c;CPU和磁盘类都有一个常量作为他们各自的数据&…

java企业级信息系统开发学习笔记10 利用MyBatis实现关联查询

文章目录 一、学习目标&#xff08;一&#xff09;针对三张表关联查询&#xff08;二&#xff09;按班级编号查询班级信息&#xff08;三&#xff09;查询全部班级信息 二、创建数据库&#xff08;一&#xff09;创建教师表&#xff08;二&#xff09;创建班级表&#xff08;三…

Linux系统搭建Java的运行环境

目录 JDKTomcatMySQL JDK 对于Linux安装JDK有很多方法~ 这里就掌握最简单的办法—基于yum来进行安装~ yum是“包管理器”&#xff0c;相当于应用商店~ 首先&#xff0c;先搜索一下&#xff0c;看看yum上关于jdk有没有&#xff0c;以及叫啥名字~ 通过 yum list命令&#xff0…

六一亲子嘉年华 | 来迅镭激光过一个五彩缤纷的儿童节!

童年是梦&#xff0c;如七彩的画卷&#xff1b; 童年是诗&#xff0c;如璀璨的星空&#xff1b; 童年是歌&#xff0c;如跳跃的音符&#xff01; 在“六一”儿童节到来之际 为给员工及子女创造一个难忘的亲子时光 迅镭激光开展了六一亲子嘉年华主题活动 让孩子们在迅镭大家庭的…

Minigpt4实战搭建

简介 Minigpt4虽然放出了网页版但是使用后发现网页体验的话&#xff0c;由于并发量比较大&#xff0c;很容易突然卡顿的现象&#xff0c;所以下面我主要讲解一下如何进行本地部署。 之前文章已经介绍过Minigpt4了这里就不重复赘述了&#xff0c;不了解的可以去看看https://bl…

使用python开发“魂斗罗”游戏

使用python开发“魂斗罗”游戏 开发完整的魂斗罗&#xff08;Contra&#xff09;游戏是一个庞大的任务&#xff0c;它涉及到图形渲染、物理碰撞、敌人AI、游戏关卡等多个方面。在这个简短的交互中&#xff0c;我将向你展示一个基本的魂斗罗风格的游戏框架&#xff0c;你可以在此…

结构化文档发布的故事和性能调优

前阵子一个TW朋友跟我抱怨他们的文档发布很慢。正常发布需要一个晚上才能完成发布。中间如果出点错&#xff0c;就得重新发布&#xff0c;那么中间是漫长的等待。 不像MS Word或者InDesign这样所见即所得的软件&#xff0c;结构化文档源文件是XML格式的&#xff0c;就像计算机…