[[ ]]
代码混淆(smc和花指令)
自修改代码(Self-Modifying Code, SMC)
SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果。
SMC的实现方式有很多种,可以通过修改PE文件的Section Header、使用API Hook实现代码加密和解密、使用VMProtect等第三方加密工具等。
这里我们把函数的代码提取出来然后进行加密解密.
有点lazy,懒得总结了,一般可以把被加密的那一段代码解密或者动态调试就能找到我们想要的,之前学过的不是很想看了,当一下lazydog jump掉吧
花指令
简介
利用花指令可以增大代码的阅读难度,并且使得工具无法轻易的反编译,也极大增加了静态分析的难度
原理
反编译器这种并没有想象中的智慧,他们都是利用的线性反编译.从机器码挨个读取内容,这就会导致有时候我们插入一些不被使用的垃圾代码比如这个代码被跳转的时候,他仍然会错误的编译他导致后续无法成功解析出来,当然常用的ida其实是 利用的递归下降 ,遇到call这种跳转的时候就会直接到call的分支进行分析
当然遇到递归下降这种形式我们可以利用一些条件判断的跳转来绕过或者是一些其他方法我们
阅读此文
前几天开始尝试学习逆向,这两天差不多看了汇编基础的只是,尽力摆脱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" , 0 Ah#定义字 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 异或
阅读此文
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, PolynomialRingfrom Crypto.Cipher import AESfrom hashlib import md5import 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;
如下构造
阅读此文
打的时候看到这么多解,人都有点自闭,不过在当天晚上也算是做完了。不算很难,就俩道题
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 legendrefrom secret import flagm = 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 tqdmn = 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" )
阅读此文
前言:
近期开始想认真的学一学一种编程语言,之前学习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!!!" ) }
阅读此文
湾区杯的密码题虽然很像之前在楚惠杯遇到的题目,但是明显约束更少,而且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)=n f(n-1)$
$f(n-1)=(n-1)f(n-2)$
以此类推到,n-1。.可以发现这其实比较像小学二年级学过的数列。
$f(3)=3 f(2)$ ......
然后计算f(1)...,f(2),...f(n)
接下来我们用经典的扩展欧几里得算法作为示例
扩展欧几里得是基于,$gcd(a,b)=gcd(b,r)$ 其中 $r=a(mod~b)$
肯定有$ax+b y=gcd(a,b)$ ,那么有
$bx_1+(a-qb)y_1=gcd(a,b)$
$a y_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 }
阅读此文
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_getsourceprint (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 ; /* 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 ; /* 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 ; /* 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
然鹅这样是拉去不了镜像的,一个很好的解决方法是,
在clashverge里面可以调整。
之后就可以拉去了
阅读此文