wubba lubba dub dub.
post @ 2026-01-06

[[]]

代码混淆(smc和花指令)

自修改代码(Self-Modifying Code, SMC)

SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果。
SMC的实现方式有很多种,可以通过修改PE文件的Section Header、使用API Hook实现代码加密和解密、使用VMProtect等第三方加密工具等。 这里我们把函数的代码提取出来然后进行加密解密.

  • 有点lazy,懒得总结了,一般可以把被加密的那一段代码解密或者动态调试就能找到我们想要的,之前学过的不是很想看了,当一下lazydog jump掉吧

花指令

  • 简介 利用花指令可以增大代码的阅读难度,并且使得工具无法轻易的反编译,也极大增加了静态分析的难度

原理

反编译器这种并没有想象中的智慧,他们都是利用的线性反编译.从机器码挨个读取内容,这就会导致有时候我们插入一些不被使用的垃圾代码比如这个代码被跳转的时候,他仍然会错误的编译他导致后续无法成功解析出来,当然常用的ida其实是 利用的递归下降,遇到call这种跳转的时候就会直接到call的分支进行分析 当然遇到递归下降这种形式我们可以利用一些条件判断的跳转来绕过或者是一些其他方法我们

阅读此文
post @ 2025-12-19

前几天开始尝试学习逆向,这两天差不多看了汇编基础的只是,尽力摆脱ida只会f5的状态.

逆向入门-汇编基础

基础解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
;hello.asm
section .data#用于初始化声明数据
message db "hello,world", 0Ah#定义字
msg_len equ $-message#
section .text# 用于存放程序指令
global _start
_start:
mov rax,1
mov rdi,1
mov rsi,message
mov rdx,msg_len
syscall
mov rax,60
mov rdi,0
syscall

这是最基础的输出hello-world的汇编代码,这里对其做出解释 可以看出分成了三段,data初始化全局变量,bss段存放未初始化的全局变量,最后test段存放指令 现在来看看test段,global作为全局声明让_start能被其他链接器调用

计算机基础

程序

静态的程序

当一个程序还是可执行文件时,他是一个静态的实体包含

阅读此文

feistel和spn结构的差分分析

前言

以下内容仅仅作为个人对差分分析的理解,或许具备一定的参考但是不多哈哈哈,如果发现问题师傅可以私信交流一下

差分分析介绍

引入

==为什么要在分组密码之中差分==

我们在通常的加密解密过程中,存在一些场景密钥是会被复用的.有时我们能获得一个加密装置,通过控制输入M得到输出C.如果是以最朴素的方式 $C=M\oplus Key$ 那么我们控制输入m1和得到输出c1就能得到$Key=m_{1}\oplus c_{1}$ 为了防止这种事情的发生我们在分组密码中引入了多个一些线性和非线性的操作..让我们在每轮次线性和非线性的操作中无法轻易的得到key利用明文和密文. 或许只有一轮次这种操作,如图他的线性操作都可以逆,并且S盒的非线性操作都是6bit一组你可以轻松爆破,但是一旦轮次增加,就不可能利用单独的爆破对破解效率有足够的提升.

]

因为中间每一轮次都会有key的影响,或是在s盒前或是在S盒后进行与key 异或

阅读此文
post @ 2025-11-19

RCTF2025

suanp0ly

这做了三个题,不过就只能把其中一个做出来,可惜,实力有限.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from sage.all import GF, ZZ, sample, gcd, PolynomialRing

from Crypto.Cipher import AES

from hashlib import md5

import os



r, d = 16381, 41

R.<x> = PolynomialRing(GF(2))

S.<X> = R.quo(x^r - 1)



def suan_p01y(nt, db):

return sum(x^i for i in set(sample(range(db+1), nt)))



while True:

t = [suan_p01y(d, r//3) for _ in range(2)]

if gcd(t[0], t[1]) != 1:

print(f'11')

continue

h = [ti * X for ti in t]

if h[0]:

break



hint=h[1]/h[0]

with open("output.txt", "w") as f:

f.write(f"hint = {h[1]/h[0]}\n")




f.write(

AES.new(

key=md5(str(h[0]).encode()).digest(),

nonce=b"suanp01y",

mode=AES.MODE_CTR

).encrypt(os.environ.get("FLAG", "RCTF{fake_flag}").encode()).hex()

)

看到别人基本AI梭了,不过我是没梭出来哈哈哈,不过做法其实就是拿rational_reconstruction做就行了,了解少了不知道这个工具,不过我就直接没看这个题的复现了,最近事情比较多先.知道了咋做就行这个

reparping

rust代码看不懂一点,给了让ai审了一下代码,发现就是输出会给你c1,c2,c3.等待输入是否和c1,c2,c3一样.通过构造c1',c2',c3'不同但是仍能返回正确的key就能求解出flag.唯一做出来的一道,感谢suan没让我zero let c2_prime = c2 + g1_gen;

let c3_prime = c3 + h1;

let c1_prime = c1 * pk; 如下构造

阅读此文

双线性配对补充+陇剑杯-rsa.io复现

双线性配对

  • 双线性配对在最初的学习密码学的时候已经接触过,不过当时不是很能理解一些东西,现在重新写一下顺便进行总结。

双线性映射

  • 线性映射

    这里做简单的解释:线性映射

    给定域$F$ 上的线性空间$V$和$U$。如果有映射$f:V->U$满足: 对于任意向量$v_1,v_2∈V,标量c_1,c_2∈F$都有

    $f(c_1v_1+c_2v_2)=c_1f(x_1)+c_2f(x_2)$

    则称作$f$是V到U的一个线性映射$(linear .~mp)$ 也叫做线性变换

    上述满足的条件也可以叫做加性与齐加性

    $f(u+v)=f(u+v)$ $f(cu)=c[f(u)]$

双线性映射:

双线性性意味着

对于给定域$k$的向量空间 ,$X,Y,Z$ 或者交换环的模$X,Y,Z$,有

$f:X\times Y\longrightarrow Z$ 这样的映射

$f$是双线性映射,指$f$关于x,y都是线性的。也就是说,若固定$y∈Y,x∈X$

则映射$f(x,y)$ 是$X$到Z的线性映射。固定X同理

weil配对

  • 在很久之前我写的里面写过,这里做些补充,懒TT。weil的配对的是基于

    设$P,Q∈E[m]$,也就是说P和Q是群E上阶为m的点。$f_p$和$f_Q$是群E上的有理函数

    ,满足

    $div(f_p)=m[P]-m[O],divf(_{Q})=m[Q]-m[O]$

    则$P$和$Q$的weil Pairing的定义为

    $e_m(P,Q)=\frac{f_P{Q+S}/f_P(S)}{f_Q(P-S)/f_Q(-S)}$

    式里面的S是E中的任意元素,且$S\notin {Q,P,Q,-Q,-P}$ 。而且$e_m(P,Q)的值不依赖于f_p.f_Q,S$的选择

    满足双线性的性质和特征这里不多做赘述。之前的文章已经说过。

阅读此文
post @ 2025-10-19
  • 打的时候看到这么多解,人都有点自闭,不过在当天晚上也算是做完了。不算很难,就俩道题

random

题目如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from Crypto.Util.number import *
from gmpy2 import legendre
from secret import flag

m = bytes_to_long(flag)

p, q = getPrime(256), getPrime(256)
n = p * q

x = getRandomRange(0, n)
while legendre(x, p) != -1 or legendre(x, q) != -1:
x = getRandomRange(0, n)
def encrypt(msg , n, x):
y = getRandomRange(0, n)
enc = []
while msg:
bit = msg & 1
msg >>= 1
enc.append((pow(y, 2) * pow(x, bit)) % n)
y += getRandomRange(1, 2**48)
return enc

with open('output.txt', 'w') as file:
file.write(f"n = {n}\n")
file.write(f"x = {x}\n")
file.write(f"enc = {encrypt(m, n, x)}\n")


本题看似是一个Goldwasser-Micali 公钥加密系统,不过有个漏洞这个y很小。 所以我们可以利用 x_inv来判断来获得可能的y^2,我们假设第一个bit是1求出低一个y后利用能否求出正确的 getRandomRange(1, 2**48) 。可以求出下一个的是否是0或1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from sage.all import *

from Crypto.Util.number import *

from tqdm import tqdm

n = 7494062703769887703668081866565274579333132167014632821313548612356114287792191446305040778987677683423969025588609780586722302041792065732461222120206217
x = 176589407974509728660827257088337183280645510941533692756437851307282715528543669659120503474533510718918949406280646936944629296899342927397139761503564
enc = []
x_inv=inverse(x,n)
P.<d> = PolynomialRing(Zmod(n))
y0=enc[0]*x_inv % n
f="1"
for c in tqdm(enc[1:]):
A=c*x_inv -y0% n

poly=(A-d**2)**2-4*d**2*y0

roots=poly.small_roots(epsilon=1/20)

if roots:

f="1"+f

else:

f="0"+f

print(f)
flag_int = int(f, 2)
print(long_to_bytes(flag_int))

lfsr

先看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from Crypto.Util.number import *  
from random import *
from os import urandom
from secret import flag


flag = flag.strip(b"DASCTF{").strip(b"}")
m = bytes_to_long(flag)
LENGTH = m.bit_length()


class LFSR():
def __init__(self,length):
self.seed = urandom(32)
self.length = length
seed(self.seed)

def next(self):
return getrandbits(self.length)

def rotate(msg,l=1):
temp = bin(msg)[2:].zfill(LENGTH)
return int(temp[l:] + temp[:l],2)


lfsr = LFSR(LENGTH)
c = []
l = []
for i in range(200):
temp = lfsr.next()
c.append(temp ^ m)
l.append(bin(temp)[2:].count("1"))
m = rotate(m)

with open("output.txt","w") as f:
f.write("LENGTH = " + str(LENGTH) + "\n")
f.write("c = " + str(c) + "\n")
f.write("l = " + str(l) + "\n")
阅读此文
post @ 2025-09-24

前言:

近期开始想认真的学一学一种编程语言,之前学习C和JAVA,Cpp都如同过水浮萍,希望这次能坚持下去。仅以此记录我的go语言学习过程。

go的优越性

性能:go的通道可以使得并发变得很方便。go能自己维护线程池,利用Goroutine可以看作一个轻量级的线程。在go中你不需要去写复杂的进程线程协程,你只需要掌握goroutine的使用,就能简单(也许)的提高代码效率

  • 或许你已经知道了线程进程之间的关系,但是goroutine这个单词其实是取自Coroutines,协程,程序员自己实现的轻量级线程

goroutine

how_to_use

  • 我们使用go关键字调用goroutine, 在下面我给出一个示例并可以体现出goroutine的一些作用。 普通函数
1
2
3
4
5
6
7
8
9
10
11
12
13
package main  

import (
"fmt"
)

func hi() {
fmt.Println("hi here we go")
}
func main() {
hi()
fmt.Println("Its Man!!!")
}
阅读此文
post @ 2025-09-23
  • 前言

湾区杯的密码题虽然很像之前在楚惠杯遇到的题目,但是明显约束更少,而且z3工具虽然是专为线性约束诞生,但是不适合用来做多解。还是得学一下动态规划等等算法,希望以后不当脚本小子,TT

递归

  • 所有的编程课程都会给你将这个,个人有一些脚本小子,没怎么使用过这方面的。 递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。

  • 在最开始的时候我一般把递归理解称一种套娃。现在我也是这么想的,但是在长时间的学习会发现,如果只是对一个东西的大致概念有理解,看得懂也就是顶天了,想要实操,必须对其有一定的深入理解。

  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main  

import "fmt"

func rcn(n int) int {
if n == 0 || n == 1 {
return 1
} else {
a := n * rcn(n-1)
return a
}
}
func main() {
a := rcn(5)
fmt.Println(a)

}

通过调试我们可以发现,递归是把函数先一次次的运算到底部界限,再一步步的倒退回去。具体可以参考一下堆的先进后出。 ![[Pasted image 20250918002534.png]] 在这里打两个断点,调试过程中你会发现, n....1变化之后再继续弹出a的, $f(n)=n*(n-1)(n-2)...1$ $f(n)=nf(n-1)$ $f(n-1)=(n-1)f(n-2)$ 以此类推到,n-1。.可以发现这其实比较像小学二年级学过的数列。 $f(3)=3f(2)$ ...... 然后计算f(1)...,f(2),...f(n) 接下来我们用经典的扩展欧几里得算法作为示例

  • 先推理出递归的大致结构

扩展欧几里得是基于,$gcd(a,b)=gcd(b,r)$ 其中 $r=a(mod~b)$
肯定有$ax+by=gcd(a,b)$ ,那么有 $bx_1+(a-qb)y_1=gcd(a,b)$ $ay_1+b(x_1-qy_1)$ =gcd(a,b) 所以$x=y_1,y=x_1-qy_1$ 我们可以先一步步递归到最后一步,再倒回去得到x,y。

1
2
3
4
5
6
7
8
9
func extend_gcd(a, b int) (int, int, int) {  
if b == 0 {
return a, 1, 0
}
gcd, x1, y1 := extend_gcd(b, a%b)
x := y1
y := x1 - (a/b)*y1
return gcd, x, y
}
阅读此文
post @ 2025-08-23

JQCTFone-line-crypto

1
assert __import__('re').fullmatch(br'flag\{[!-z]{11}\}',flag:=os.getenvb(b'FLAG')) and [is_prime(int(flag.hex(),16)^^int(input('🌌 '))) for _ in range(7^7)]

这是题目源码。这道题侧重点在于利用is_Prime函数的特性。

1
2
from sage.misc.sageinspect import sage_getsource
print(sage_getsource(Integer.is_prime))

查看sage的这个函数

发现是基于pari_is_prime的,查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
long
BPSW_psp(GEN N)
{
pari_sp av;
if (typ(N) != t_INT) pari_err_TYPE("BPSW_psp",N);
if (signe(N) <= 0) return 0;
if (lgefint(N) == 3) return uisprime(uel(N,2));
if (!mod2(N)) return 0;
#ifdef LONG_IS_64BIT
/* 16294579238595022365 = 3*5*7*11*13*17*19*23*29*31*37*41*43*47*53
* 7145393598349078859 = 59*61*67*71*73*79*83*89*97*101 */
if (!iu_coprime(N, 16294579238595022365UL) ||
!iu_coprime(N, 7145393598349078859UL)) return 0;
#else
/* 4127218095 = 3*5*7*11*13*17*19*23*37
* 3948078067 = 29*31*41*43*47*53
* 4269855901 = 59*83*89*97*101
* 1673450759 = 61*67*71*73*79 */
if (!iu_coprime(N, 4127218095UL) ||
!iu_coprime(N, 3948078067UL) ||
!iu_coprime(N, 1673450759UL) ||
!iu_coprime(N, 4269855901UL)) return 0;
#endif
/* no prime divisor < 103 */
av = avma;
return gc_long(av, is2psp(N) && islucaspsp(N));
}

明显的,如果输入的N的有小于103的素因子,那么就不会进入真正的素数检测,而是直接return 0。

阅读此文

docker 代理冲突问题的解决

docker的配置在网上已经有很多,大多数用的是配置镜像的方法,但是有的盆友可能和我一样发现不开代理登录不上去docker--desktop,登录上去之后呢,拉去镜像会出现这个问题。

1
2
3
docker pull nginx
Using default tag: latest
Error response from daemon: failed to resolve reference "docker.io/library/nginx:latest": failed to do request: Head "https://hub-mirror.c.163.com/v2/library/nginx/manifests/latest?ns=docker.io": proxyconnect tcp: dial tcp 127.0.0.1:7897: connect: connection refused

因为用clash-verge这样的软件进入代理之后,会自动设置

1
2
3
HTTP_PROXY=127.0.0.1:7897

HTTP_PROXYs=127.0.0.1:7897

然鹅这样是拉去不了镜像的,一个很好的解决方法是,

  • 关掉系统代理,开启虚拟网卡模式,这样的坏处可能就是下东西卡了点。

    image-20250808221124189

在clashverge里面可以调整。

之后就可以拉去了

阅读此文
⬆︎TOP