皮蛋厂的学习日记 | 2022.03.24 Java反射机制 & 流密码系列

admin 2025年6月8日09:44:50评论7 views字数 20253阅读67分30秒阅读模式

皮蛋厂的学习日记系列为山东警察学院网安社成员日常学习分享,希望能与大家共同学习、共同进步~

  • 2020级 Newiy_Gnaw | Java反射机制

    • 0x01 初识反射

    • 0x02 forName()

    • 0x03 newInstance()

    • 0x04 invoke()

    • 0x05 最后

    • 学习参考

  • 2021级 Mu.Chen | 流密码系列

    • 前言

    • LCG

    • RC4

    • 反馈移位寄存器 (FSR)

    • 线性反馈移位寄存器(LFSR)

    • 非线性反馈移位寄存器(NFSR)

WEB

2020 Newiy_Gnaw | Java反射机制

0x01 初识反射

官方Oracle对反射的解释:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.

The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

也就是说,Java本身是一种静态的语言,但可以通过反射机制来动态的创建对象、调用类和对象的方法和属性。

反射机制的核心就是jvm在运行时去创建对象或调用方法/访问属性,它不需要事先知道(写代码或者编译期)对象是谁

java反射的主要功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构建任意一个类的对象
  • 在运行时判断任意一个类所具有的变量和方法(通过反射甚至可以调用private方法)
  • 在运行时调用任意一个对象的方法

反射主要涉及的类(在java.lang.reflect.*;这个包下)

  • java.lang .class:代表一个类型,代表整个类。
  • java.lang.reflect.field: 代表类中的成员变量(静态变量+实例变量)。
  • java.lang.reflect.method: 代表类中的方法。
  • java.lang.reflect.modifier:代表类中的修饰符。
  • java.lang.reflect.constructor:代表类中的构造方法。

最重要的还是在反射里的方法:

  • 获取类的方法:forName
  • 实例化类对象的方法:newInstance
  • 获取函数的方法:getMethod
  • 执行函数的方法:invoke

下面就学习一下这几个方法

0x02 forName()

在学习方法之前先学习一下这个类

java.lang.Class类

Class是用来描述类的类。一个类中有属性,方法,构造器等,不同的类这些元素都不同。如果需要描述这个类,就可以用Class来描述。

Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等

举两个用Class类获取类对象和属性的例子,来自这篇文章

通过Class类获取类对象:

public class ReflectionTest {
 @Test
 public void testClass() {
    Class clazz = null;
  
    //1.得到Class对象
    clazz = Person.class;
  
    System.out.println();  //插入断点。
                           //System.out 标准输出流 与打印机、显示器相关
                           //println=print+line(换行)
 }
}
//小写class表示是一个类类型,大写Class表示这个类的名称

在断点处就可以看到Class对像包含的信息

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

同样,这些属性值是可以获取的 :

public class ReflectionTest {
 @Test
 public void testClass() {
    Class clazz = null;
  
    //1.得到Class对象
    clazz = Person.class;
    //2.返回字段的数组
    Field[] fields = clazz.getDeclaredFields();
  
    System.out.println();  //插入断点
 }
}

查看fields的内容

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

为什么要用Class类获取对象的信息?

  • 有可能这个对象是别人传过来的
  • 有可能没有对象,只有一个全类名

通过反射,可以得到这个类里面的信息

获取java.lang.Class 对象的三种方式

  • 通过类名获取   类名.class   如果你已经加载了某个类,只是想获取到它的 java.lang.Class 对象,那么就直接 拿它的 class 属性即可。这个方法其实不属于反射。
  • 通过对象获取   对象名.getClass()   如果上下文中存在某个类的实例化对象,那么可以直接通过对象.getClass() 来获取它的类
  • 通过全类名获取  Class.forName(全类名)   如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName 来获取

下面重点学习一下forName方法

forName方法

在正常情况下,除了系统类,如果我们想拿到一个类,需要先 import 才能使用。而使用forName就不需要,这样对于我们的攻击者来说就十分有利,我们可以加载任意类。

forName有两个函数重载:

  • Class<?> forName(String name)
  • Class<?> forName(String name, boolean initialize, ClassLoader loader)

第⼀个就是我们最常⻅的获取class的⽅式,其实可以理解为第⼆种⽅式的⼀个封装:

Class.forName(className)
//等于
Class.forName(className, true, currentLoader)

再看一下第二个

Class.forName(className, true, currentLoader)

默认情况下, forName 的第⼀个参数是类名;第⼆个参数表示是否初始化;第三个参数就

ClassLoader

第三个参数:classloader:它就是⼀个“加载器”,告诉Java虚拟机如何加载这个类。Java默认的 ClassLoader 就是根据类名来加载类, 这个类名是类完整路径,如 java.lang.Runtime 。

第二个参数:初始化

读了P牛的文章,似乎这个初始化不是对象的初始化,而是类的初始化。

看一段P牛的代码

public class TrainPrint {

    {
        System.out.printf("Empty block initial %sn"this.getClass());
    }

    static {
        System.out.printf("Static initial %sn", TrainPrint.class);
    }

    public TrainPrint() {
        System.out.printf("Initial %sn"this.getClass());
    }

}

P牛的问题:上述的三个“初始化”⽅法有什么区别,调⽤顺序是什么,在安全上有什么价值?

我用的kali虚拟机运行的

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

类的实例化:首先调用的是 static {} ,其次是 {} ,最后是构造函数。

类的初始化:static {}

注意实力化和初始化是不同的。forName 中的 initialize=true 其实就是告诉Java虚拟机是否执⾏”类初始化“。

补充:获得内部类

我们经常在一些源码里看到,类名的部分包含 替换成 . :https://github.com/alibaba/fastjson/blob/fcc9c2a/src/main/java/com/alibaba/fastjson/parser/ParserConfifig.java#L1038。$ 的作用是查找内部类。

Java的普通类 C1 中支持编写内部类 C2 ,而在编译的时候,会生成两个文件:C1.class 和C1C2") 即可加载这个内部类。

0x03 newInstance()

在获取了类之后,就可以获取类的实例化对象

有两种方法,分别是通过该类的无参构造和有参构造来实例化对象

知识点:获取构造方法

使用的方法:getConstructor() 和 newInstance()

构造方法

先学习一下什么是构造方法

java构造函数,也叫构造方法,是java中一种特殊的函数。函数名与相同,无返回值。

作用:一般用来初始化成员属性和成员方法的,即new对象产生后,就调用了对象了属性和方法。

在现实生活中,很多事物一出现,就天生具有某些属性和行为。比如人一出生,就有年龄、身高、体重、就会哭;汽车一出产,就有颜色、有外观、可以运行等。这些,我们就可以将这些天然的属性和行为定义在构造函数中,当new实例化对象时,也就具有这些属性和方法了,没必要再去重新定义了,从而加快了编程效率。

构造函数是对象一建立就运行,给对象初始化,就包括属性,执行方法中的语句。

而一般函数是对象调用才执行,用".方法名“的方式,给对象添加功能。

一个对象建立,构造函数只运行一次。

而一般函数可以被该对象调用多次。

构造方法的特点:

  • 函数名与类名相同
  • 不用定义返回值类型。(不同于void类型返回值,void是没有具体返回值类型;构造函数是连类型都没有)
  • 不可以写return语句。(返回值类型都没有,也就不需要return语句了)

注:一般函数不能调用构造函数,只有构造函数才能调用构造函数。

举两个例子,一个无参一个有参

e.g.无参构造方法

package javastudy;

public class ConfunDemo {
    public static void main(String[] args) {
        Confun c1=new Confun();            //输出Hello World。new对象一建立,就会调用对应的构造函数Confun(),并执行其中的println语句。
    }
}
class Confun{      
    Confun(){        //定义构造函数,输出Hello World
        System.out.println("Hellow World");
    }
}
//输出Hellow World

e.g.有参构造方法

package javastudy;

public class ConfunDemo4 {
    public static void main(String[] args) {
            PersonDemo s=new PersonDemo("李三",33);        //new对象时,即调用对应的构造函数,并传值。同时,不能new同一个对象多次,否则会报错。
            s.setName("李五");                            //对象建立后,想变更值时,就要用set/get方法,重新设置新的值
            s.setName("阿尔法狗");                        //并可调用对象多次。
            s.print();
    }
}
class PersonDemo{
    private String name;
    private int age;
    PersonDemo(String n,int m){                //建立有参构造函数,用于给两个private变量name、age赋值,同时输出值
        name=n;
        age=m;
        System.out.println("姓名:"+name+"年龄:"+age);
    }
    public void setName(String x){            //set方法,用于再次给name赋值
        name=x;      
    }
    public String getName(){                //get方法,用于获取name的赋值
        return name;
    }
    public void print(){
        System.out.println(name);
    }
}
/**输出:
  *张三
  *3
  */

无参构造

有两种方式;

  • Class.newInstance() ——无参调用
  • Constructor.newInstance()  ——有参调用(因为是有参,放在下一节学习)
**Class.newInstance()  **

P牛讲过,在调用这个方法的时候可能会不成功,原因可能有以下两点

1、你使用的类没有无参构造函数

2、你使用的类构造函数是私有的

这也就变相说明了Class.newInstance()的使用条件

1、只能调用无参的构造函数

2、被调用的构造函数必须是public类型

用P牛举的在payload中最常见的例子( java.lang.Runtime.exec())来理解

这样写是不合适的:

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec", String.class).invoke(clazz.newInstance(), "id");

因为Runtime类的方法是私有的。

所以payload应该是这样:(在最后更详细的学习)

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");

这里用到了getMethod()和invoke()函数(详见后)

getMethod 的作用是通过反射获取一个类的某个特定的公有方法。(注意函数重载)

invoke 的作用是执行方法

有参构造

**Constructor.newInstance() **

比较一下二者的区别

Class.newInstance()只能反射无参的构造器;

Constructor.newInstance()可以反射任何构造器;

Class.newInstance()需要构造器可见(visible);

Constructor.newInstance()可以反射私有构造器;

Class.newInstance()对于捕获或者未捕获的异常均由构造器抛出;

Constructor.newInstance()通常会把抛出的异常封装成InvocationTargetException抛出;

比较一下使用方法:

(Class.newInstance() 在Inside java.lang 包中;Constructor.newInstance() 在 Inside java.lang.reflect 包中 )

Class.newInstance():
Class.forName("HelloWorld").newInstance();
或者
HelloWorld.class.newInstance();

Constructor.newInstance()
HelloWorld.class.getConstructor().newInstance();

就挺好理解了

0x03 getMethod()

Method getMethod(String name, Class<?>... parameterTypes)

--返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

方法后面接收的就是Class类的对象,而如:String.class、int.class这些字节码才是Class类的对象

getMethod 的作用是通过反射获取一个类的某个特定的公有方法。

第一个参数是方法名,第二个参数是该方法的参数类型

如一个函数 int Test(int a, String str);

对应的getMethod方法:

1、getMethod("Test",int.class,String.class);

2、getMethod("Test",new Class[]{int.class,String.class});

使用这个方法时,要注意函数的重载问题,也就是要注意第二个参数传入要获取的函数的参数类型列表

引用一个P牛的例子

用getMethod()获取Runtime.exec方法

Runtime.exec方法有六个重载:

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

如果想用第一个形式,就要使用

getMethod("exec", String.class)来获取 Runtime.exec 方法。

0x04 invoke()

invoke(obj, new Object[]{“args1”, “args2”})

作用是执行方法

第一个参数:

  • 如果这个方法是一个普通方法,那么第一个参数是类对象
  • 如果这个方法是一个静态方法,那么第一个参数是类

第二个是参数列表

P牛的例子很好理解:

我们正常执行方法是 [1].method([2], [3], [4]...) ,其实在反射里就是 method.invoke([1], [2], [3], [4]...) 。

0x05 最后

再看一眼P牛举的那个例子

Class clazz = Class.forName("java.lang.Runtime");

Method execMethod = clazz.getMethod("exec", String.class);

Method getRuntimeMethod = clazz.getMethod("getRuntime");

Object runtime = getRuntimeMethod.invoke(clazz);

execMethod.invoke(runtime, "calc.exe");

这一段代码的最终目的是利用java.lang.Runtime.exec()方法

那么就先来学习一下java.lang.Runtime.exec()这个方法

先学Runtime类

Runtime类是JDK提供的运行时类,该类为Java程序提供了与当前运行环境相连接的一个通道,Java程序可以利用该类对当前的运行环境执行一些简单的操作。

~这个类顾名思义,运行时间嘛~

Runtime类不能使用new关键字创建实例,只能通过Runtime.getRuntime()方法获取实例。

exec()方法的作用就是执行命令

这个方法有很多重载方式,举两个例子

  • Process exec(String command)

    • command:要执行的系统命令,字符串类型
  • Process exec(String[] cmdarray)

    • cmdarray:要执行的系统命令和命令参数,字符串数组类型

举例子:

第一种重载方式:

Runtime.getRuntime().exec("javac hello.java");

第二种重载方式:

String command[]={"javac","hello.java"};

Runtime.getRuntime().exec(command);

就看得懂了

再看一下payload的构造过程:

先用forname()方法实例化java.lang.Runtime类赋值给clazz,然后用getMethod()获取exec方法赋值给execMethod,在获取getRuntime方法赋值给getRuntimeMethod(由于不是公有方法,所以不能使用newInstance(),只能用Runtime.getRuntime()来获取对象),最后用invoke()执行

学习参考

P牛师傅的Java安全漫谈

https://www.cnblogs.com/tech-bird/p/3525336.htmlJava博客园(这个是以类为主线写的)

http://www.hellojava.com/a/78740.html实例化对象

https://www.cnblogs.com/ibelieve618/p/6364541.html java构造函数(方法)

https://www.ucloud.cn/yun/69920.html Constructor.newInstance()

CRYPTO

2021级 Mu.Chen | 流密码系列

前言

之前的学习很不系统,总是看到什么学什么,东一榔头西一棒槌的,做题像是开盲盒,见过的就能做,没见过的就抓瞎。既不知道自己学了多少,也不知道还有多少没学。

周六晚上战队里的师傅再次提起了技能树,我觉得不错,看la佬的博客也是这么写的。根据ctf-Wiki Crypto方面的内容,古典密码之前零零碎碎地学完了,再加上上周见识到的lcg和lfsr都属于流密码,那就从流密码开始。

LCG

线性同余生成器 / 线性同余方法(LCG)

线性同余方法(LCG)是个产生伪随机数的方法。

其是根据线性同余方程

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

来的(很像之前接触过的仿射密码)

其中a,b,m是产生器设定的常数。LCG的最大周期是m,为使其达到最大周期,b和m是互质的。

在题目中,还会接触到一个被命名为seed的东西,也就是公式中的x1

LCG中用到的几个公式

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

公式的证明也不难(仿射密码那里没有放,这里放一下吧)

1、

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

代码实现:

MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1#逆元计算
ani=MMI(a,n)
ouput[1]=(ani*(output[2]-b))%n

2、

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

代码实现:

MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1#逆元计算
a=(output[2]-output[1])*MMI((output[1]-output[0]),n)%n

3、

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

代码实现:

b=(output[2]-a*output[1])%n

4、

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

代码实现:

t[0]=output[1]-output[0]
#……以此类推
m=gcd((t[2]*t[0])-(t[1]*t[1]),(t[3]*t[1])-(t[2]*t[2]))

模板

from functools import reduce
from math import gcd
from Crypto.Util.number import *

def egcd(a, b):
    if a == 0:
        return (b, 01)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

def crack_unknown_increment(states, modulus, multiplier):
    increment = (states[1] - states[0]*multiplier) % modulus
    return modulus, multiplier, increment

def crack_unknown_multiplier(states, modulus):
    multiplier = (states[2] - states[1]) * gmpy2.invert(states[1] - states[0], modulus) % modulus
    return crack_unknown_increment(states, modulus, multiplier)

def crack_unknown_modulus(states):
    diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
    zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
    modulus = abs(reduce(gcd, zeroes))
    return crack_unknown_multiplier(states, modulus)

# N[i+1] = (A*N[i]+B) % M
# A,B,N均未知
sequence = []
modulus, multiplier, increment = crack_unknown_modulus(sequence)
print('A = '+str(multiplier))
print('B = '+str(increment))
print('N = '+str(modulus))

来自https://lazzzaro.github.io

几道题目

from Crypto.Util.number import *
flag = b'Spirit{*****************************}'

plaintext = bytes_to_long(flag)
length = plaintext.bit_length()

a = getPrime(length)
b = getPrime(length)
n = getPrime(length)

seed = plaintext

for i in range(10):
    seed = (a*seed+b)%n
ciphertext = seed

print("a = ",a)
print("b = ",b)
print("n = ",n)
print("c = ",ciphertext)

# a =  59398519837969938359106832224056187683937568250770488082448642852427682484407513407602969
# b =  32787000674666987602016858366912565306237308217749461581158833948068732710645816477126137
# n =  43520375935212094874930431059580037292338304730539718469760580887565958566208139467751467
# c =  8594514452808046357337682911504074858048299513743867887936794439125949418153561841842276

大概是最基础的类型了,已知abn和加密十遍后的x10作为ciphertext,直接利用公式一循环十遍即可

from Crypto.Util.number import *

a =  59398519837969938359106832224056187683937568250770488082448642852427682484407513407602969
b =  32787000674666987602016858366912565306237308217749461581158833948068732710645816477126137
n =  43520375935212094874930431059580037292338304730539718469760580887565958566208139467751467
c =  8594514452808046357337682911504074858048299513743867887936794439125949418153561841842276
MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1#逆元计算
ani=MMI(a,n)

for i in range(10);
    c=(ani*(c-b))%n
print(long_to_bytes(c))
from Crypto.Util.number import *
flag = b'Spirit{****************************************}'

plaintext = bytes_to_long(flag)
length = plaintext.bit_length()

a = getPrime(length)
b = getPrime(length)
n = getPrime(length)

seed = plaintext
output = []
for i in range(10):
    seed = (a*seed+b)%n
    output.append(seed)

print("output = ",output)
# output =  [9997297986272510947766344959498975323136012075787120721424325775003840341552673589487134830298427997676238039214108, 4943092972488023184271739094993470430272327679424224016751930100362045115374960494124801675393555642497051610643836, 6774612894247319645272578624765063875876643849415903973872536662648051668240882405640569448229188596797636795502471, 9334780454901460926052785252362305555845335155501888087843525321238695716687151256717815518958670595053951084051571, 2615136943375677027346821049033296095071476608523371102901038444464314877549948107134114941301290458464611872942706, 11755491858586722647182265446253701221615594136571038555321378377363341368427070357031882725576677912630050307145062, 7752070270905673490804344757589080653234375679657568428025599872155387643476306575613147681330227562712490805492345, 8402957532602451691327737154745340793606649602871190615837661809359377788072256203797817090151599031273142680590748, 2802440081918604590502596146113670094262600952020687184659605307695151120589816943051322503094363578916773414004662, 5627226318035765837286789021891141596394835871645925685252241680021740265826179768429792645576780380635014113687982]

a、b、n都不知道,只有遍历10遍的十个输出值,可以先利用公式四求出n,再利用公式二和三分别求出ab,最后使用ouput[1],求出output[0]

from Crypto.Util.number import *
import gmpy2

s =  [99972979862725109477663449594989753231360120757871207214243257750038403415526735894871348302984279976762380392141084943092972488023184271739094993470430272327679424224016751930100362045115374960494124801675393555642497051610643836677461289424731964527257862476506387587664384941590397387253666264805166824088240564056944822918859679763679550247193347804549014609260527852523623055558453351555018880878435253212386957166871512567178155189586705950539510840515712615136943375677027346821049033296095071476608523371102901038444464314877549948107134114941301290458464611872942706117554918585867226471822654462537012216155941365710385553213783773633413684270703570318827255766779126300503071450627752070270905673490804344757589080653234375679657568428025599872155387643476306575613147681330227562712490805492345840295753260245169132773715474534079360664960287119061583766180935937778807225620379781709015159903127314268059074828024400819186045905025961461136700942626009520206871846596053076951511205898169430513225030943635789167734140046625627226318035765837286789021891141596394835871645925685252241680021740265826179768429792645576780380635014113687982]
#output
t = []
for i in range(9):
    t.append(s[i]-s[i-1])
#10个output中的9个差值t
all_n = []
for i in range(7):
    all_n.append(gmpy2.gcd((t[i+1]*t[i-1]-t[i]*t[i]), (t[i+2]*t[i]-t[i+1]*t[i+1])))
#所有潜在的n
MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1#逆元计算
for n in all_n:
#对所有的n进行检验
    n=abs(n)
    if n==1:
        continue
    a=(s[2]-s[1])*MMI((s[1]-s[0]),n)%n
    ani=MMI(a,n)
    b=(s[1]-a*s[0])%n
    seed = (ani*(s[0]-b))%n

    print(long_to_bytes(seed))
import gmpy2
from Crypto.Util.number import *
import random
import sympy
from secrets import seed,flag
a = getPrime(100)
b = getPrime(100)
n = getPrime(100)
seed = bytes_to_long(seed)
state = seed
result = []
for _ in range(5):
    state = (state * a + b) % n
    result.append(state)
random.seed(seed)
m = bytes_to_long(flag)
p = sympy.prevprime(random.getrandbits(512))
q = sympy.prevprime(random.getrandbits(512))
e = 0x10001
c = pow(m,e,p * q)
print(result)
print(c)
# [597125037895583017711156501405, 593356460994781320992697294966, 724969351853512596427397849525, 178962733761078811068013010935, 540462420568199777821346442296]
# 137731530669620858197072185446977004895808961183893608636211329510394050267946114975982787447691922282540439162221239230419067371002805763768100164411601363600449463422225740498277517163885987308541029932425749529263738151020009189137242648007082868161869422570071610915246177122628185352860430963010059933

纳新赛的题目,跟上题一样,只给了output,分别利用几个公式求出seed后,用seed生成pq,rsa求出m即可

from functools import reduce
from math import gcd
import gmpy2
from Crypto.Util.number import *
import sympy

def egcd(a, b):
    if a == 0:
        return (b, 01)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

def crack_unknown_increment(states, modulus, multiplier):
    increment = (states[1] - states[0]*multiplier) % modulus
    return modulus, multiplier, increment

def crack_unknown_multiplier(states, modulus):
    multiplier = (states[2] - states[1]) * gmpy2.invert(states[1] - states[0], modulus) % modulus
    return crack_unknown_increment(states, modulus, multiplier)

def crack_unknown_modulus(states):
    diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
    zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
    modulus = abs(reduce(gcd, zeroes))
    return crack_unknown_multiplier(states, modulus)

# N[i+1] = (A*N[i]+B) % M
# A,B,N均未知
s = [597125037895583017711156501405593356460994781320992697294966724969351853512596427397849525178962733761078811068013010935540462420568199777821346442296]
modulus, multiplier, increment = crack_unknown_modulus(s)
print('A = '+str(multiplier))
print('B = '+str(increment))
print('N = '+str(modulus))
a = 271379952936509655352239263326
b = 639807583669999596578808532351
n = 963295910539440575070538395403
MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1#逆元计算
ani=MMI(a,n)
seed=(ani*(s[0]-b))%n
print(seed)
print(long_to_bytes(seed))
'''23437534584664428
b'SDPCTql'''

import random
random.seed(seed)
p = sympy.prevprime(random.getrandbits(512))
q = sympy.prevprime(random.getrandbits(512))
n_=p*q
e = 0x10001
c=137731530669620858197072185446977004895808961183893608636211329510394050267946114975982787447691922282540439162221239230419067371002805763768100164411601363600449463422225740498277517163885987308541029932425749529263738151020009189137242648007082868161869422570071610915246177122628185352860430963010059933
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=gmpy2.powmod(c,d,n_)
print(long_to_bytes(m))

exp改自la佬的模板

参考博客:https://blog.csdn.net/superprintf/article/details/108964563

RC4

在密码学中, RC4 (来自Rivest Cipher 4的缩写)是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。RC4是有线等效加密(WEP)中采用的加密算法,也曾经是TLS可采用的算法之一。

加密原理

RC4的加密原理比较简单,包括初始化算法(KSA)和伪随机子密码生成算法(PRGA)两部分

初始化算法

首先生成一个S盒,按升序设置为0到255的值,即S[0]=0,S[1]=1,………,S[255]=255。

同时,生成一个key,内容随便定义,然后将key拓展为256位的,由它本身来补位。

例如:key="bigpowercat",那拓展为256位就是"bigpowercatbigpowercatbig……"(长度为256位)

之后用key来打乱S盒里的值

密钥key实际可用的长度最大为256位,但典型的长度是40-128 位。

s=[0]*256
for i in range(256):  
    s[i]=i
#定义S盒

key=""
k=[0]*256
for i in range(256):
    k[i]=key[(i)mod(len(key)]
#拓展key

j=0
for i in range(256):
    j = (j+s[i]+s[j])%256
    s[i],s[j] = s[j],s[i]
#初始化S盒
伪随机子密码生成算法---流生成

初始化S盒后,就不再使用输入密钥。流生成涉及循环遍历所有元素S[i],并且对每个S[i],根据S当前的配置方案,将S[i],与S中的另一个字节进行交换。到达S[255]之后,继续该过程,从S[0]重新开始。加密时,明文message与现S盒中的值进行异或得到了密文cipher

message="" 
cipher=''
for i in range(len(message))
    i = (i + 1)%256 
    j = (j + s[i])%256
    s[i],s[j] = s[j],s[i]
    t = (s[i] + s[j])%256
    cipher += message[i]^s[t]

由于采用了异或操作,体现了RC4作为对称加密的特点。

反馈移位寄存器 (FSR)

移位寄存器是流密码产生密钥流的一个主要组成部分,一个n级反馈移位寄存器由n个二元存储器与一个反馈函数组成的。

(网图)

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

反馈移位寄存器三要素

1、初始状态

初始状态是由用户确定的

a0,a1,…an−1,此类内容为初态。

2、反馈函数

反馈函数F是n元的布尔函数,其自变量和因变量只能取0或1两个值之一。

因此,在任意时刻,n级的内容构成反馈寄存器的状态,每一状态都对应一个n维的向量,共有2**n种可能的状态

3、输出序列

F函数作为反馈函数,若F是线性的,则称之为线性反馈移位寄存器(LFSR),否则我们称其为1非线性反馈移位寄存器(NFSR)

线性反馈移位寄存器(LFSR)

(还是网图)

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

LFSR的反馈函数就是简单地对移位寄存器中的某些位进行异或,并将异或的结果填充到LFSR的最左端。

如果寄存器的值位{a1,a2,a3,……,an},那么第n+1位的值就可以表示为:

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

对于一个n级的LFSR,其最大周期位2^(n-1)(因为要去掉所有位数都是0的情况),得到的序列被称为最大长度序列m序列

特征多项式

LFSR配置的等效定义是特征多项式。

若线性反馈位移器的反馈函数如下:

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

那么其对应的特征多项式可以表达为:

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

(呜呜呜,线代学的不好,线性变换推导的地方没看明白,只能记个结论)

同时,可以定义其互反多项式为:

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

生成函数

如果已知一个n级线性反馈寄存器的特征多项式,就可以求得该序列的生成函数,其表达式如下:

皮蛋厂的学习日记 | 2022.03.24  Java反射机制 & 流密码系列

非线性反馈移位寄存器(NFSR)

在上边LFSR的部分中,线性是指反馈函数和其对应的特征多项式的系数是常数。特别来讲,他们是布尔常量(0或1)。

而对于非线性反馈移位寄存器(NFSR)来说,系数可以是变量。

原文始发于微信公众号(山警网络空间安全实验室):皮蛋厂的学习日记 | 2022.03.24 Java反射机制 & 流密码系列

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月8日09:44:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   皮蛋厂的学习日记 | 2022.03.24 Java反射机制 & 流密码系列https://cn-sec.com/archives/840249.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息