前言
由于hust的sb课程设置,所以只有时间打第一周。快五一了终于有空闲时间了,把所有的reverse题目给复现一下
第一周
reverse
喵喵喵的flag碎了一地
考点:ida的功能,没有值得学习的地方
聪明的信使
考点:shift+f12 +凯撒加密
DebugMe
考点:调试apk;jeb远程附加
一般来说,Android 根据如下的顺序来判断一个应用是否可以被调试
- 检测 boot.img 中的 ro.debuggable 是否为 1,为 1 的话,手机中的任何应用均可以调试。
- 否则,检测对应应用中 AndroidManifest.xml 中 <application> 元素中是否包含了 android:debuggable=“true”,如果有的话,才会开启调试支持。
自然,我们也就有两种方法来使得一个应用可以被调试
- 将一个 apk 文件解包,在 <application> 元素中添加 android:debuggable=“true”,然后重打包,签名。
- 修改 boot.img 中的 ro.debuggable 为 1。
一般来说,因为前者需要我们每次都需要修改应用
ro.debuggable
的值可以根据如下命令来查询
adb shell getprop ro.debuggable
你是真的大学生吗?
考点:16位DOS下的汇编
enc = [0x76, 0x0e, 0x77, 0x14, 0x60, 0x06, 0x7d, 0x04, 0x6b, 0x1e,
0x41, 0x2a, 0x44, 0x2b, 0x5c, 0x03, 0x3b, 0x0b, 0x33, 0x05]
dst = [0x00] * 20
for i in range(19):
dst[i] = enc[i] ^ enc[i + 1]
dst[19] = enc[19] ^ enc[0]
print("".join([chr(i) for i in dst]))
Trustme
非预期解:sql注入,万能密码直接出
预期解:先用jadx
看看mainactivity
,解出一个提示,账号是admin
;你看到的其实是假逻辑,真正的主函数被加固到dex里,可以从ProxyApplication
中找到classes.dex
的加密逻辑,即异或255然后写回apk;然后发现是从本地mydb.db
中查询;直接拿到这个文件直接strings
就能找到flag
baby unity
考点:il2cpp-unity逆向
利用il2cppdumper利用gm.dat把gameassembly.dll给dump出来;然后ida打开,恢复符号,利用cs文件找到函数的主逻辑,发现是个简单的异或
enc='XIcKYJU8Buh:UeV:BKN{U[JvUL??VuZ?CXJ;AX^{Ae]gA[]gUecb@K]ei^22'
enc_2=''
for i in enc:
enc_2+=chr(ord(i)^0xF)
print(enc_2)
import base64
print(base64.b64decode(enc_2).decode('utf-8'))
misc
熊博士
atbash cipher
ez_隐写
伪加密,watermark,图片宽高爆破
有个压缩包,图片伪加密,先修复,把图片搞出来,然后修复宽高,得到提示:密码是开赛日
然后解压加密压缩包:名字是watermark,就是盲水印
利用吾爱破解论坛的watermark软件可以得到flag
game
谷歌识图
zzl的护理小课堂
F12查看源码,发现是本地验证
有两种方式:
1.在这段代码前打个断点,然后控制台修改score=101
2.用burp改包,直接把服务端返回的 score改为101再返回本地浏览器
zip神之套
压缩包密码爆破,明文攻击
用ida打开给的exe文件,搜索字符串,给了个hint:密码是xyctf????????ftcyx
用这种方式爆破
然后有两个zip,其中一个没加密,一个加密了,flag藏在加密的压缩包,然后ARPCH明文攻击
tcpl
risk-v架构程序,用qemu搭个riscv的虚拟机然后运行就可以出
网络迷踪
wireshark 跟踪tcp流量
随便翻翻,发现一段flag文本
随波逐流一把嗦 xxencode
XYCTF{fake_flag}
真正的flag格式:XYCTF{靶机ip地址_nmap扫描出的靶机开放的端口(由大到小排列 中间用_进行连接)_获
取靶机shell使用的漏洞的CVE编号}
例:XYCTF{1.1.1.1_888_88_8_CVE-2009-3103}
那就一步一步分析nmap的流量特征
回[SYN,ACK]的那三个TCP流量所展示的端口就是开放端口 135,139,445 然后因为是受害者回应攻击者,所以靶机IP为回应IP,即 192.168.204.133
CVE:SMB以及 IPC有关 的windows漏洞有关 直接搜索关键字 定位到 # Microsoft Windows Server - Code Execution (MS08-067)对应的CVE为CVE-2008-4250
crypto
factor
这道题目还是挺不错的,我当时卡了挺久,实际上你同构想一想把当做p与当做q就能利用wienerattack
import gmpy2
import hashlib
from Crypto.Util.number import *
import sympy as sp
# p = getPrime(512)
# q = getPrime(512)
# d = getPrime(512)
# e = gmpy2.invert(d, (p**3 - 1) * (q**3 - 1))
# print(e)
# print(p * q)
e=172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163
pq=99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793
# x=sp.symbols('x')
# while True:
# a=e//k*n
# equation=sp.Eq(x**2+(n+1)*x+n**2-n+1-a,0)
# solutions = sp.solveset(equation, x, domain=sp.Naturals)
# if solutions:
# print("解",solutions)
# break
# k+=1
import gmpy2
# numerator(n):分子, denominator(d):分母
def t_cf(n, d): # 将分数 x/y 转为连分数的形式
res = []
while d:
res.append(n // d)
n, d = d, n % d
return res
def cf(sub_res): # 得到渐进分数的分母和分子
n, d = 1, 0
for i in sub_res[::-1]: # 从后面往前循环
d, n = n, i * n + d
return d, n
def list_fraction(x, y): # 列出每个渐进分数
res = t_cf(x, y)
res = list(map(cf, (res[0:i] for i in range(1, len(res))))) # 将连分数的结果逐一截取以求渐进分数
return res
def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q(解二元一次方程)
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2
def wienerAttack(e, n):
for (d, k) in list_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e * d - 1) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi = (e * d - 1) // k # 这个结果就是 φ(n)
px, qy = get_pq(1, n - phi + 1, n)
if px * qy == n:
p, q = abs(int(px)), abs(int(qy)) # 可能会得到两个负数,负负得正未尝不会出现
flag = "XYCTF{" + hashlib.md5(str(gmpy2.iroot(p,3)[0] + gmpy2.iroot(q,3)[0]).encode()).hexdigest() + "}"
print(flag)
d = gmpy2.invert(e, (p - 1) * (q - 1)) # 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
print(p)
print(q)
return d
print("求解d失败")
d=wienerAttack(e,pq**3)
print(d)
#flag = "XYCTF{" + hashlib.md5(str(p + q).encode()).hexdigest() + "}"
happy to solve1
异或的性质 p,q非常接近分解pq
from Crypto.Util.number import *
import sympy
import gmpy2
n=24852206647750545040640868093921252282805229864862413863025873203291042799096787789288461426555716785288286492530194901130042940279109598071958012303179823645151637759103558737126271435636657767272703908384802528366090871653024192321398785017073393201385586868836278447340624427705360349350604325533927890879
c=14767985399473111932544176852718061186100743117407141435994374261886396781040934632110608219482140465671269958180849886097491653105939368395716596413352563005027867546585191103214650790884720729601171517615620202183534021987618146862260558624458833387692782722514796407503120297235224234298891794056695442287
# # from secrets import flag
#
#
def get_happy_prime():
p = getPrime(512)
q = sympy.nextprime(p ^ ((1 << 512) - 1))
return p, q
p,q=get_happy_prime()
print(p, '\n', q)
e = 65537
t1=1<<512
p = (2 ** 512 + gmpy2.iroot((2 ** 512) ** 2 - 4 * n, 2)[0]) // 2
p = int(p)
while n % p != 0:
p = gmpy2.next_prime(p)
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
signin
from Crypto.Util.number import *
from tqdm import *
import gmpy2
flag = b'XYCTF{'
flag = bytes_to_long(flag)
leak = bin(int(flag))
#Padding 不足514位补0
while 1:
leak += "0"
if len(leak) == 514:
break
#高位和低位交换位置
def swap_bits(input_str):
input_list = list(input_str[2:])
length = len(input_list)
for i in range(length // 2):
temp = input_list[i]
input_list[i] = input_list[length - 1 - i]
input_list[length - 1 - i] = temp
return ''.join(input_list)
input_str = leak
result = swap_bits(input_str)
a = result
def custom_add(input_str):
input_list = list(input_str)
length = len(input_list)
for i in range(length):
input_list[i] = str((int(input_list[i]) + i + 1) % 10)
result = ''.join(input_list)
return result
def reverse_custom_add(result_str):
input_list = list(result_str)
length = len(input_list)
for i in range(length):
input_list[i] = str((int(input_list[i]) - i - 1) % 10)
input_str = ''.join(input_list)
return input_str
input_str = a
result = custom_add(input_str)
b = result
print(b)
b=12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567891134567780012455678012234677801133467889112356678911245567891223457790013346788912235667801134456899122355788001235578891124566799013445778012235578800123467889112456678001245567991223557880012455788901334677800134456789122355788001235578890133566790013445778012235577900133467889112356678001245578801233467789112355779912234577990233556780113
re_b=reverse_custom_add(str(b))
print(re_b)
sp_re_b=swap_bits('0b'+re_b)
print(sp_re_b)
sp_re_b='1011000010110010100001101010100010001100111101101100010001100010011100100110101001110010011001100110100001101000010110100110000001100100110010000110110001011010011010001100010001110010011100000101101001110010011000100111001001101000010110100110110001101100011001100111001001101000011000001100100001100010011100101100110001110000110001001111101'
r_leak=int(sp_re_b,2)
print(r_leak)
print(long_to_bytes(r_leak))
BabyRSAMAX
题目:
from Crypto.Util.number import *
from gmpy2 import *
from random import choice
flag = b'XYCTF{******}'
e = '?'
def getBabyPrime(nbits):
while True:
p = 1
while p.bit_length() <= nbits:
p *= choice(sieve_base)
if isPrime(p+1):
return p+1
p = getBabyPrime(512)
q = getBabyPrime(512)
n = p*q
gift1 = (pow(p,e,n)-pow(q,e,n)) % n
gift2 = pow(p+q,e,n)
t = 65537
x = bytes_to_long(e)
y = pow(x, t, n)
m = bytes_to_long(flag)
c = powmod(m, e, n)
print(f'n = {n}')
print(f'gift1 = {gift1}')
print(f'gift2 = {gift2}')
print(f'c = {c}')
print(f'y = {y}')
'''
n = 39332423872740210783246069030855946244104982381157166843977599780233911183158560901377359925435092326653303964261550158658551518626014048783435245471536959844874036516931542444719549997971482644905523459407775392702211086149279473784796202020281909706723380472571862792003687423791576530085747716706475220532321
gift1 = 4549402444746338327349007235818187793950285105091726167573552412678416759694660166956782755631447271662108564084382098562999950228708300902201571583419116299932264478381197034402338481872937576172197202519770782458343606060544694608852844228400457232100904217062914047342663534138668490328400022651816597367310
gift2 = 111061215998959709920736448050860427855012026815376672067601244053580566359594802604251992986382187891022583247997994146019970445247509119719411310760491983876636264003942870756402328634092146799825005835867245563420135253048223898334460067523975023732153230791136870324302259127159852763634051238811969161011462
c = 16938927825234407267026017561045490265698491840814929432152839745035946118743714566623315033802681009017695526374397370343984360997903165842591414203197184946588470355728984912522040744691974819630118163976259246941579063687857994193309554129816268931672391946592680578681270693589911021465752454315629283033043
y = 1813650001270967709841306491297716908969425248888510985109381881270362755031385564927869313112540534780853966341044526856705589020295048473305762088786992446350060024881117741041260391405962817182674421715239197211274668450947666394594121764333794138308442124114744892164155894256326961605137479286082964520217
'''
由二项式定理
则
那么
然后就没什么思路了
其实这个题跟第一个等式没什么关系
利用B光滑数算法可以求出p,q
N =
39332423872740210783246069030855946244104982381157166843977599780233911183158560901377359925435092326653303964261550158658551518626014048783435245471536959844874036516931542444719549997971482644905523459407775392702211086149279473784796202020281909706723380472571862792003687423791576530085747716706475220532321
a=2
n=2
from gmpy2 import *
while True:
a = pow(a, n, N)
res = gcd(a-1, N)
if res != 1 and res != N:
q = N // res
print("n=",n)
print("p=",res)
print("q=",q)
break
n += 1
'''
n= 97241
p=
2364384004775215979229504451537962651990724045771831909531148051705228759045517
80358338769440558816351105253794964040981919231484098097671084895302287425479
q=
1663537893730573521952685751683977503626438222012535089410528359454206249832164
56266478176579651490080696973849607356408696043718492499993062863415424578199
'''
t=65537
p=23643840047752159792295044515379626519907240457718319095311480517052287590455 1780358338769440558816351105253794964040981919231484098097671084895302287425479
q=16635378937305735219526857516839775036264382220125350894105283594542062498321
6456266478176579651490080696973849607356408696043718492499993062863415424578199
N=39332423872740210783246069030855946244104982381157166843977599780233911183158
5609013773599254350923266533039642615501586585515186260140487834352454715369598
4487403651693154244471954999797148264490552345940777539270221108614927947378479
6202020281909706723380472571862792003687423791576530085747716706475220532321
y=18136500012709677098413064912977169089694252488885109851093818812703627550313
8556492786931311254053478085396634104452685670558902029504847330576208878699244
6350060024881117741041260391405962817182674421715239197211274668450947666394594
121764333794138308442124114744892164155894256326961605137479286082964520217
from gmpy2 import *
from libnum import n2s
phi = (p-1)*(q-1)
d=invert(t,phi)
print(n2s(int(pow(y,d,N))))
这个应该是非预期解
复现
Reverse
ezunity
其实与babyunity思路一致:
这里有两种想法:一是修复gm.dat;二是利用il2cpp-bridge-frida进行hook
不会trace导致卡了,浪费很多时间
trace真是个好东西
先说思路一(修复gm.dat):把第一排改成AF 1B B1 FA 1D 00 00 00 00 01 00 00,删几个字符,让后面接着C0 B2 然后把下面的内容对齐 直接dumper导入,导入成功!然后沿用babyunity的方法直接静态分析即可
再说思路二(用frida进行hook)
记frida-il2cpp-bridge的使用-CSDN博客
这个教程讲的比较浅,frida还是需要深入学习,确实hook很方便
搭好环境,写个脚本,跟踪这个程序
import "frida-il2cpp-bridge";
Il2Cpp.perform(() => {
console.log("Unity version: " + Il2Cpp.unityVersion);
let ass = Il2Cpp.domain.assembly("Assembly-CSharp");
Il2Cpp.trace(true).assemblies(ass).and().attach()
// const Assembly = Il2Cpp.domain.assembly("Assembly-CSharp").image;
const SystemString = Il2Cpp.corlib.class("System.String");
Il2Cpp.trace(true).classes(SystemString)
.classes(SystemString)
.and()
.attach();
// console.log("Assembly-CSharp: " + Assembly)
// console.log("System.String: " + SystemString);
});
显然是AES加密
e language
IDA7.5支持中文函数命名的办法 - 『逆向资源区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
fjqisba/E-Decompiler: 用来辅助分析易语言程序的IDA插件 (github.com)
易语言逆向,又成脚本小子了 找到隐藏按钮的函数
动调提取key,再提密文
62146
然而是个假flag
综合多个wp,事实上这道题的情况是有一个反调试,如果检测到debugger的存在,就会给函数假数据和假key,其实这道题abctf已经给了提示了,我们就要想办法绕过这个反调
思路一是找到这个反调函数然后nop掉
这道题的反调试比较简单,直接附加就可以绕过
然后重复以上的过程获得
key:welcometoxyctf!!
enc:RZy/zVEWMFxaCbzChAg8x26XZYr51rNVnM+zBoBp3gya93L9QQXpFRin1JE33vyx
from Crypto.Cipher import AES
import base64
enc=b'RZy/zVEWMFxaCbzChAg8x26XZYr51rNVnM+zBoBp3gya93L9QQXpFRin1JE33vyx'
enc_b64decode=base64.b64decode(enc.decode('utf-8'))
print(enc_b64decode)
key=b'welcometoxyctf!!'
cipher=AES.new(key,AES.MODE_ECB)
print(cipher.decrypt(enc_b64decode))
#b'XYCTF{y0u_@r3_v3ry_g00d_a7_E_l@ngu@ge}\n\n\n\n\n\n\n\n\n\n'
ezmath
py程序直接解包反编译一条龙
需要注意的是:用的python版本最好跟需要反编译的pyc版本一致,否则可能导致少部分信息缺失
# uncompyle6 version 3.9.1
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.11.5 | packaged by Anaconda, Inc. | (main, Sep 11 2023, 13:26:23) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: ezmath.py
flag = [ord(i) for i in input("flag:")]
if len(flag) == 32:
if (
sum([flag[23] for _ in range(flag[23])])
+ sum([flag[12] for _ in range(flag[12])])
+ sum([flag[1] for _ in range(flag[1])])
- sum([flag[24] for _ in range(222)])
+ sum([flag[22] for _ in range(flag[22])])
+ sum([flag[31] for _ in range(flag[31])])
+ sum([flag[26] for _ in range(flag[26])])
- sum([flag[9] for _ in range(178)])
- sum([flag[29] for _ in range(232)])
+ sum([flag[17] for _ in range(flag[17])])
- sum([flag[23] for _ in range(150)])
- sum([flag[6] for _ in range(226)])
- sum([flag[7] for _ in range(110)])
+ sum([flag[19] for _ in range(flag[19])])
+ sum([flag[2] for _ in range(flag[2])])
- sum([flag[0] for _ in range(176)])
+ sum([flag[10] for _ in range(flag[10])])
- sum([flag[12] for _ in range(198)])
+ sum([flag[24] for _ in range(flag[24])])
+ sum([flag[9] for _ in range(flag[9])])
- sum([flag[3] for _ in range(168)])
+ sum([flag[8] for _ in range(flag[8])])
- sum([flag[2] for _ in range(134)])
+ sum([flag[14] for _ in range(flag[14])])
- sum([flag[13] for _ in range(170)])
+ sum([flag[4] for _ in range(flag[4])])
- sum([flag[10] for _ in range(142)])
+ sum([flag[27] for _ in range(flag[27])])
+ sum([flag[15] for _ in range(flag[15])])
- sum([flag[15] for _ in range(224)])
+ sum([flag[16] for _ in range(flag[16])])
- sum([flag[11] for _ in range(230)])
- sum([flag[1] for _ in range(178)])
+ sum([flag[28] for _ in range(flag[28])])
- sum([flag[5] for _ in range(246)])
- sum([flag[17] for _ in range(168)])
+ sum([flag[30] for _ in range(flag[30])])
- sum([flag[21] for _ in range(220)])
- sum([flag[22] for _ in range(212)])
- sum([flag[16] for _ in range(232)])
+ sum([flag[25] for _ in range(flag[25])])
- sum([flag[4] for _ in range(140)])
- sum([flag[31] for _ in range(250)])
- sum([flag[28] for _ in range(150)])
+ sum([flag[11] for _ in range(flag[11])])
+ sum([flag[13] for _ in range(flag[13])])
- sum([flag[14] for _ in range(234)])
+ sum([flag[7] for _ in range(flag[7])])
- sum([flag[8] for _ in range(174)])
+ sum([flag[3] for _ in range(flag[3])])
- sum([flag[25] for _ in range(242)])
+ sum([flag[29] for _ in range(flag[29])])
+ sum([flag[5] for _ in range(flag[5])])
- sum([flag[30] for _ in range(142)])
- sum([flag[26] for _ in range(170)])
- sum([flag[19] for _ in range(176)])
+ sum([flag[0] for _ in range(flag[0])])
- sum([flag[27] for _ in range(168)])
+ sum([flag[20] for _ in range(flag[20])])
- sum([flag[20] for _ in range(212)])
+ sum([flag[21] for _ in range(flag[21])])
+ sum([flag[6] for _ in range(flag[6])])
+ sum([flag[18] for _ in range(flag[18])])
- sum([flag[18] for _ in range(178)])
+ 297412
== 0
):
print("yes")
有佬用z3解
from z3 import *
# 创建⼀个Z3求解器
solver = Solver()
# 定义32个32位的变量
flag0 = Real('flag0')
flag1 = Real('flag1')
flag2 = Real('flag2')
flag3 = Real('flag3')
flag4 = Real('flag4')
flag5 = Real('flag5')
flag6 = Real('flag6')
flag7 = Real('flag7')
flag8 = Real('flag8')
flag9 = Real('flag9')
flag10 = Real('flag10')
flag11 = Real('flag11')
flag12 = Real('flag12')
flag13 = Real('flag13')
flag14 = Real('flag14')
flag15 = Real('flag15')
flag16 = Real('flag16')
flag17 = Real('flag17')
flag18 = Real('flag18')
flag19 = Real('flag19')
flag20 = Real('flag20')
flag21 = Real('flag21')
flag22 = Real('flag22')
flag23 = Real('flag23')
flag24 = Real('flag24')
flag25 = Real('flag25')
flag26 = Real('flag26')
flag27 = Real('flag27')
flag28 = Real('flag28')
flag29 = Real('flag29')
flag30 = Real('flag30')
flag31 = Real('flag31')
solver.add((flag23 * flag23) + (flag12 * flag12) + (flag1 * flag1) -
(222 * flag24) + (flag22 * flag22) + (flag31 * flag31) + (flag26 *
flag26) - (178 * flag9) - (232 * flag29) + (
flag17 * flag17) - (150 *
flag23) - (226 * flag6) - (110 * flag7) + (flag19 * flag19) + (flag2 *
flag2) - (
176 * flag0) + (flag10 * flag10) - (198 * flag12) + (flag24 *
flag24) + (flag9 * flag9) - (
168 * flag3) + (flag8 * flag8) - (134 *
flag2) + (flag14 * flag14) - (170 * flag13) + (
flag4 * flag4) - (142 *
flag10) + (flag27 * flag27) + (flag15 * flag15) - (224 * flag15) +
(flag16 * flag16) - (230 * flag11) - (178 * flag1) + (flag28 * flag28) -
(246 * flag5) - (168 * flag17) + (flag30 * flag30) - (220 * flag21) -
(212 * flag22) - (232 * flag16) + (flag25 * flag25) - (140 * flag4) -
(250 * flag31) - (150 * flag28) + (flag11 * flag11) + (flag13 * flag13) - (234 * flag14) + (
flag7 * flag7) - (174 * flag8) + (flag3 * flag3) -
(242 * flag25) + (flag29 * flag29) + (flag5 * flag5) - (142 * flag30) -
(170 * flag26) - (176 * flag19) + (flag0 * flag0) - (168 * flag27) +
(flag20 * flag20) - (212 * flag20) + (flag21 * flag21) + (flag6 * flag6)
+ (flag18 * flag18) - (178 * flag18) + 297412 == 0)
# 检查是否存在解
# if solver.check() == sat:
# # 获取解
# model = solver.model()
if solver.check()==sat:
model=solver.model()
print(model)
list=[88, 89, 67, 84, 70, 123, 113, 55, 87, 89, 71, 115, 99, 85, 117, 112, 116, 84, 89, 88, 106, 110, 106, 75, 111, 121, 85, 84, 75, 116, 71, 125]
for i in list:
print(chr(i),end='')
#XYCTF{q7WYGscUuptTYXjnjKoyUTKtG}
else:
print("No solution")
有点迷惑z3的变量设置了,因为用Int会跑很久,Real几秒出
所以该如何声明什么类型的变量,如何判断?
思路二是观察然后化简
if len(flag) == 32 and (
(flag[0] * (flag[0] - 176)) +
(flag[1] * (flag[1] - 178)) +
(flag[2] * (flag[2] - 134)) +
(flag[3] * (flag[3] - 168)) +
(flag[4] * (flag[4] - 140)) +
(flag[5] * (flag[5] - 246)) +
(flag[6] * (flag[6] - 226)) +
(flag[7] * (flag[7] - 110)) +
(flag[8] * (flag[8] - 174)) +
(flag[9] * (flag[9] - 178)) +
(flag[10] * (flag[10] - 142)) +
(flag[11] * (flag[11] - 230)) +
(flag[12] * (flag[12] - 198)) +
(flag[13] * (flag[13] - 170)) +
(flag[14] * (flag[14] - 234)) +
(flag[15] * (flag[15] - 224)) +
(flag[16] * (flag[16] - 232)) +
(flag[17] * (flag[17] - 168)) +
(flag[18] * (flag[18] - 178)) +
(flag[19] * (flag[19] - 176)) +
(flag[20] * (flag[20] - 212)) +
(flag[21] * (flag[21] - 220)) +
(flag[22] * (flag[22] - 212)) +
(flag[23] * (flag[23] - 150)) +
(flag[24] * (flag[24] - 222)) +
(flag[25] * (flag[25] - 242)) +
(flag[26] * (flag[26] - 170)) +
(flag[27] * (flag[27] - 168)) +
(flag[28] * (flag[28] - 150)) +
(flag[29] * (flag[29] - 232)) +
(flag[30] * (flag[30] - 142)) +
(flag[31] * (flag[31] - 250)) + 297412 == 0
通过观察可以发现,每一行后面减的数除以二就是flag
ez_cube
是一个魔方程序,先阅读一个类似的程序获得灵感
用C语言写一个数字版的3阶魔方_简单c语言程序判断3*3洛术魔方-CSDN博客
所以只能逆如何把cube还原,是利用CFOP公式
魔方求解器 (rubiks-cube-solver.com)
也可以用angr一把嗦,或者自己写脚本爆破
借鉴了一下dl的脚本
from itertools import *
red = [''] * 9
blue = [''] * 9
green = [''] * 9
orange = [''] * 9
yellow = [''] * 9
white = [''] * 9
def init_cube():
for i in range(9):
red[i] = "red"
blue[i] = "blue"
green[i] = "green"
orange[i] = "orange"
yellow[i] = "yellow"
white[i] = "white"
blue[1] = "red"
red[1] = "green"
green[1] = ("blue")
def MOVE_R():
v1 = red[2]
v2 = red[5]
v3 = red[8]
red[2] = white[2]
red[5] = white[5]
red[8] = white[8]
white[2] = orange[6]
white[5] = orange[3]
white[8] = orange[0]
orange[0] = yellow[8]
orange[3] = yellow[5]
orange[6] = yellow[2]
yellow[2] = v1
yellow[5] = v2
yellow[8] = v3
v4 = green[1]
green[1] = green[3]
green[3] = green[7]
green[7] = green[5]
green[5] = v4
v5 = green[0]
green[0] = green[6]
green[6] = green[8]
green[8] = green[2]
green[2] = v5
def MOVE_U():
v1 = red[0]
v2 = red[1]
v3 = red[2]
red[0] = green[0]
red[1] = green[1]
red[2] = green[2]
green[0] = orange[0]
green[1] = orange[1]
green[2] = orange[2]
orange[0] = blue[0]
orange[1] = blue[1]
orange[2] = blue[2]
blue[0] = v1
blue[1] = v2
blue[2] = v3
v4 = yellow[1]
yellow[1] = yellow[3]
yellow[3] = yellow[7]
yellow[7] = yellow[5]
yellow[5] = v4
v5 = yellow[0]
yellow[0] = yellow[6]
yellow[6] = yellow[8]
yellow[8] = yellow[2]
yellow[2] = v5
def MOVE_r():
v1 = red[2]
v2 = red[5]
v3 = red[8]
red[2] = yellow[2]
red[5] = yellow[5]
red[8] = yellow[8]
yellow[2] = orange[6]
yellow[5] = orange[3]
yellow[8] = orange[0]
orange[0] = white[8]
orange[3] = white[5]
orange[6] = white[2]
white[2] = v1
white[5] = v2
white[8] = v3
v4 = green[1]
green[1] = green[5]
green[5] = green[7]
green[7] = green[3]
green[3] = v4
v5 = green[0]
green[0] = green[2]
green[2] = green[8]
green[8] = green[6]
green[6] = v5
def MOVE_u():
v1 = red[0]
v2 = red[1]
v3 = red[2]
red[0] = blue[0]
red[1] = blue[1]
red[2] = blue[2]
blue[0] = orange[0]
blue[1] = orange[1]
blue[2] = orange[2]
orange[0] = green[0]
orange[1] = green[1]
orange[2] = green[2]
green[0] = v1
green[1] = v2
green[2] = v3
v4 = yellow[1]
yellow[1] = yellow[5]
yellow[5] = yellow[7]
yellow[7] = yellow[3]
yellow[3] = v4
v5 = yellow[0]
yellow[0] = yellow[2]
yellow[2] = yellow[8]
yellow[8] = yellow[6]
yellow[6] = v5
def is_right():
flag=1
for i in range(9):
if red[i]!="red":
flag=0
if blue[i]!="blue":
flag=0
if green[i]!="green":
flag=0
if orange[i]!="orange":
flag=0
if yellow[i]!="yellow":
flag=0
if white[i]!="white":
flag=0
return flag
def main(flag):
print(flag)
init_cube()
for i in flag:
if i=="R":
MOVE_R()
if i=="U":
MOVE_U()
if i=="r":
MOVE_r()
if i=="u":
MOVE_u()
if is_right():
return flag
def get_flag():
table="RrUu"
for string in product(table,repeat=12):
flag="".join(string)
ret=main(flag)
if ret !=None:
print(ret)
return
get_flag()
#RuRURURuruRR
砸核桃
ASP壳,用od脱一下壳
[原创]新手 NsPacK3.7 脱壳记录-加壳脱壳-看雪-安全社区|安全招聘|kanxue.com
4.2 寻找OEP
经过修改后的 PE 文件已经可以正常识别了,同样也显示出相应的编译信息与壳的信息,NsPacK V3.7 中文名称为北斗壳,是国内人编写的压缩壳。既然是手工对其脱壳,自然第一步就是寻找 OEP(原始入口点)。
ollydbg 载入后入口点处为:pushfd,pushad,call XXXX
OEP 的特征如下:popad,popfd,jmp OEP
我采用了搜索popfd打断点 不断F9到OEP的位置
然后按照教程fixdump得到主函数(现在对windowsPE结构不太懂,不了解为什么要按照这个步骤来,以后有时间进行学习
发现是个简单的异或
flag=""
key=[0x74, 0x68, 0x69, 0x73, 0x5F, 0x69, 0x73, 0x5F, 0x6E, 0x6F, 0x74, 0x5F, 0x66, 0x6C, 0x61, 0x67]
enc=[0x00000012, 0x00000004, 0x00000008, 0x00000014, 0x00000024, 0x0000005C, 0x0000004A, 0x0000003D, 0x00000056, 0x0000000A, 0x00000010, 0x00000067, 0x00000000, 0x00000041, 0x00000000, 0x00000001, 0x00000046, 0x0000005A, 0x00000044, 0x00000042, 0x0000006E, 0x0000000C, 0x00000044, 0x00000072, 0x0000000C, 0x0000000D, 0x00000040, 0x0000003E, 0x0000004B, 0x0000005F, 0x00000002, 0x00000001, 0x0000004C, 0x0000005E, 0x0000005B, 0x00000017, 0x0000006E, 0x0000000C, 0x00000016, 0x00000068, 0x0000005B, 0x00000012, 0x00000000, 0x00000000, 0x00000048]
print(len(enc))
for i in range(42):
flag+=chr(enc[i]^key[i%16])
print(flag)
# flag{59b8ed8f-af22-11e7-bb4a-3cf862d1ee75}
ez_enc
scanf(&input, input_);
for ( i = 0; i < (int)(j_strlen(input_) - 1); ++i )
input_[i] = aImouto[i % 6] ^ (input_[i + 1] + (unsigned __int8)input_[i] % 20);
for ( j = 0; j < (int)j_strlen(input_); ++j )
{
if ( input_[j] != byte_14001E008[j] )
{
printf("Wrong");
return 0;
}
}
printf("Right,but where is my Imouto?\n");
摘录关键逻辑
逆向解的话有两个思路,⼀是通过倒序解 Str[i] ,但 Str[i] 取了模存在信息丢失,很难解)需要爆破;⼆是通过正序解 Str[i+1] ,这个需要知道 Str[0] 就能递推。经过 ⼿动解前⼏个得出开头为 flag{ ,因此 Str[0] 就是 f
思路一的exp;
target = [0x27, 0x24, 0x17, 0x0B, 0x50, 0x03, 0xC8, 0x0C, 0x1F, 0x17, 0x36,
0x55, 0xCB, 0x2D, 0xE9, 0x32, 0x0E, 0x11, 0x26, 0x02, 0x0C, 0x07, 0xFC,
0x27, 0x3D, 0x2D, 0xED, 0x35, 0x59, 0xEB, 0x3C, 0x3E, 0xE4, 0x7D ]
aImouto =[0x49, 0x4D, 0x6F, 0x75, 0x74, 0x6F]
flag = [0]*len(target)
flag[len(target)-1] = target[len(target)-1]
print(flag)
def checkflag(idx,result):
if(idx==-1):
print("flag:爆破成功!")
for i in range(len(result)):
print(chr(result[i]),end="")
print("")
result
for ch1 in range(128):
if target[idx]==(aImouto[idx%6]^(result[idx+1]+ch1%20)):
result[idx]=ch1
checkflag(idx-1,result)
checkflag(idx-1,result)
if(ch1==128 and result[idx]==0):
return
checkflag(32,flag)
思路二的exp
enc=[0x27, 0x24, 0x17, 0x0B, 0x50, 0x03, 0xC8, 0x0C, 0x1F, 0x17, 0x36, 0x55, 0xCB, 0x2D, 0xE9, 0x32, 0x0E, 0x11, 0x26, 0x02, 0x0C, 0x07, 0xFC, 0x27, 0x3D, 0x2D, 0xED, 0x35, 0x59, 0xEB, 0x3C, 0x3E, 0xE4, 0x7D]
flag=""
# """ 正向逻辑:
# for i in range(len(input)-1)
# enc[i]=Imouto[i%6]^(input[i+1]+input[i]%20)
# enc[len(input)-1]=input[len(input)-1]
# chr(0x7D)='}'
# 那么我们就可以逆向求解
# from string import printable
# input 都是可读字符
key='IMouto'
flag=[0]*len(enc)
flag[0]=ord('f')
for i in range(0,len(enc)-1):
flag[i+1]=((enc[i] ^ ord(key[i % 6])) - flag[i] % 20) & 0xff
# flag[i+1]=((enc[i]-flag[i]%20)^ord(key[i%6]))&0xff
for i in flag:
print(chr(i),end="" )
#减法优先级高于异或
z3的解法
from prism import *
key = 'IMouto
last = [0x27, 0x24, 0x17, 0x0B, 0x50, 0x03, 0xC8, 0x0C,
0x1F, 0x17, 0x36, 0x55, 0xCB, 0x2D, 0xE9, 0x32,
0x0E, 0x11, 0x26, 0x02, 0x0C, 0x07, 0xFC, 0x27,
0x3D, 0x2D, 0xED, 0x35, 0x59, 0xEB, 0x3C, 0x3E,
0xE4, 0x7D]
out,flag = zini(34)
f = Solver()
for i in range(33):
f.add(last[i]==(ord(key[i%6])^out[i+1]+out[i] % 20))
isflag(f,flag)
zcheck(f,flag)
# flag{!_r3ea11y_w4nt_@_cu7e_s1$ter}
findme
die
查看一下三个文件的信息,发现文件4可以打开
可以分析出:首先进行rc4_init
初始化一个512数组
然后打开Doraemon3
文件读取文件流,然后逐字符进行rc4加密,并且以sbox[idx%512]
作为伪随机数种子获得随机数,写入随机大小的垃圾数组得到filestream1
。而题目只给了我们1,2没有给3,我们要先由文件1逆推得到3
- 思路1:看了师傅们的wp,由于rc4是流加密,s[box]加密解密不变,因此加密和解密的伪随机数生成的也是一一样的,rc4解密好处理,关键是垃圾数据的去除。事实上只要把fputc给hook成fgetc就完美还原了文件解密的过程
如何patch?
patch fputc的字节码为:FF 15 0B B0 00 00,
由于fgetc只需要1个参数且第一个参数存入rcx
fputc函数的调用第一个参数存入rcx,第二个参数存入的是rdx
patch后需要把rdx改为rcx然后nop掉中间将第一个参数传入rcx的指令就可以把第二个参数传入rcx当中并且正常运行了
因此要修改这四处地方,更换输入输出
-
思路2:照着加密逻辑,写正向脚本,按照第一种思路把输入输出流给颠倒一下获得解密脚本,函数主体直接copy反编译的源码,然后稍微修改一下
#include <stdio.h> #include <string.h> const char *Str = "Find_Doraemon"; unsigned char BOX[512]; void init_box() { char *v0; // rdi __int64 i; // rcx char v3[32]; // [rsp+0h] [rbp-20h] BYREF char v4; // [rsp+20h] [rbp+0h] BYREF unsigned int v5; // [rsp+24h] [rbp+4h] char v6; // [rsp+44h] [rbp+24h] char v7[532]; // [rsp+70h] [rbp+50h] BYREF unsigned int j; // [rsp+284h] [rbp+264h] int v9; // [rsp+2A4h] [rbp+284h] int v10; // [rsp+2C4h] [rbp+2A4h] v5 = strlen(Str); memset(v7, 0, 0x200); for (j = 0; j < 0x200; ++j) { v6 = j; BOX[j] = -(char)j; v7[j] = Str[j % v5]; } v9 = 0; v10 = 0; while (v9 < 512) { v10 = ((unsigned __int8)v7[v9] + BOX[v9] + v10) % 512; unsigned char tmp = BOX[v9]; BOX[v9] = BOX[v10]; BOX[v10] = tmp; ++v9; } } int main() { size_t v3; // rax int v5; // eax FILE *fout; // [rsp+28h] [rbp+8h] FILE *fin; // [rsp+48h] [rbp+28h] int v8; // [rsp+64h] [rbp+44h] int v9; // [rsp+84h] [rbp+64h] int v10; // [rsp+A4h] [rbp+84h] int v11; // [rsp+C4h] [rbp+A4h] unsigned __int8 v12; // [rsp+104h] [rbp+E4h] unsigned __int8 v13; // [rsp+144h] [rbp+124h] int i; // [rsp+164h] [rbp+144h] init_box(); v8 = 0; v10 = 0; v11 = 0; fout = fopen("Doraemon3", "wb"); fin = fopen("Doraemon1", "rb"); while (!feof(fin)) { v10 = (v10 + 1) % 512; v11 = (BOX[v10] + v11) % 512; unsigned char tmp = BOX[v10]; BOX[v10] = BOX[v11]; BOX[v11] = tmp; v13 = BOX[(unsigned __int8)((BOX[v11] + BOX[v10]) % 512)]; v12 = v13 ^ fgetc(fin); fputc(v12, fout); srand(BOX[v8 % 512]); v9 = rand() % 4; for (i = 0; i < v9; ++i) { v5 = rand(); fgetc(fin); } ++v8; } fclose(fout); fclose(fin); return 0; }
最后获得3是个解密程序,用于解密2直接执行即可
用010editor打开发现GIF89a是gif头,修改拓展名然后打开即可
今夕是何年
与TCPL类似,用qemu搭建龙芯环境即可
win11 使用 QEMU 配置龙芯 3A5000 虚拟环境_qemu 龙芯-CSDN博客
熟悉qemu的用法吧
What’s this
拖进die,发现是lua编译
Lua Decompiler (metaworm.site)
找个在线网站反编译一下
或者找在线工具
viruscamp/luadec: Lua Decompiler for lua 5.1 , 5.2 and 5.3 (github.com)
魔术数字51可判断版本为lua5.1
反编译的lua脚本经过混淆了,
抓取关键逻辑
Xor = function(num1, num2)
-- function num : 0_109
local tmp1 = num1
local tmp2 = num2
local str = ""
repeat
repeat
local s1 = tmp1 % 2
local s2 = tmp2 % 2
if s1 == s2 then
str = "0" .. str
else
str = "1" .. str
end
tmp1 = (math.modf)(tmp1 / 2)
tmp2 = (math.modf)(tmp2 / 2)
until tmp1 == 0
until tmp2 == 0
return tonumber(str, 2)
end
value = ""
output = ""
i = 1
while 1 do
local temp = (string.byte)(flag, i)
temp = (string.char)(Xor(temp, 8) % 256)
value = value .. temp
i = i + 1
if (string.len)(flag) < i then
break
end
end
do
for _ = 1, 1000 do
x = 3
y = x * 3
z = y / 4
w = z - 5
if w == 0 then
print("This line will never be executed")
end
end
for i = 1, (string.len)(flag) do
temp = (string.byte)(value, i)
temp = (string.char)(temp + 3)
output = output .. temp
end
result = output:rep(10)
invalid_list = {1, 2, 3}
for _ = 1, 20 do
(table.insert)(invalid_list, 4)
end
for _ = 1, 50 do
result = result .. "A"
;
(table.insert)(invalid_list, 4)
end
for i = 1, (string.len)(output) do
temp = (string.byte)(output, i)
temp = (string.char)(temp - 1)
end
for _ = 1, 30 do
result = result .. (string.lower)(output)
end
for _ = 1, 950 do
x = 3
y = x * 3
z = y / 4
w = z - 5
if w == 0 then
print("This line will never be executed")
end
end
for _ = 1, 50 do
x = -1
y = x * 4
z = y / 2
w = z - 3
if w == 0 then
print("This line will also never be executed")
end
end
require("base64")
obfuscated_output = to_base64(output)
obfuscated_output = (string.reverse)(obfuscated_output)
obfuscated_output = (string.gsub)(obfuscated_output, "g", "3")
obfuscated_output = (string.gsub)(obfuscated_output, "H", "4")
obfuscated_output = (string.gsub)(obfuscated_output, "W", "6")
invalid_variable = obfuscated_output:rep(5)
if obfuscated_output == "==AeuFEcwxGPuJ0PBNzbC16ctFnPB5DPzI0bwx6bu9GQ2F1XOR1U" then
print("You get the flag.")
else
print("F**k!")
end
end
flag 每字节与8异或,然后加3,转为base64,逆置,然后进行字符替换写一个脚本逆过来即可
import base64
enc="==AeuFEcwxGPuJ0PBNzbC16ctFnPB5DPzI0bwx6bu9GQ2F1XOR1U"
newenc=enc.replace("3","g").replace("4","H").replace("6","W")
newenc=newenc[::-1]
print(newenc)
newenc2=base64.b64decode(newenc)
print(newenc2)
for i in newenc2:
print(chr(((i-3)^0x8)&0xff),end="")
# U1ROX1F2QG9ubWxwb0IzPD5BPnFtcW1CbzNBP0JuPGxwcEFueA==
# b'STN_Qv@onmlpoB3<>A>qmqmBo3A?Bn<lppAnx'
# XYCTF{5dcbaed781363fbfb7d8647c1aee6c}
ezrand
可以看出随机数的seed与时间有关,获得的随机数再与输入的flag进行一些列操作获得密文,但是rand_number是unsigned int8类型,我们可以对seed进行爆破
rand 函数在api-ms-win-crt-utility-l1-1-0 )总是返回一个范围是 16 位的整数。 为确保爆破用的 rand 同样是来自 api-ms-win-crt-utility-l1-1-0 的
坑:这里的伪代码是有问题的,这⾥并⾮直接调⽤srand(time64(0)) ⽽是中间⽤了个 movzx ecx,ax; 实际上就是保留了低16位 即真正的随机数种⼦是time64(0)&0xf
#include <stdio.h>
#include <stdlib.h>
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int
#define u64 unsigned long long
u8 ida_chars[] =
{
0x5D, 0x0C, 0x6C, 0xEA, 0x46, 0x19, 0xFC, 0x34, 0xB2, 0x62,
0x23, 0x07, 0x62, 0x22, 0x6E, 0xFB, 0xB4, 0xE8, 0xF2, 0xA9,
0x91, 0x12, 0x21, 0x86, 0xDB, 0x8E, 0xE9, 0x43, 0x4D };
const char* tmpl = "XYCTF{XXXXXXXXXXXXXXXXXXXXXX}";
int main() {
for (u32 i = 0; i <= 0xFFFF; i++) {
u32 flag = 1;
srand(i);
for (int j = 0; j < 29; j++) {
u32 eax = rand(); // 16 bits
u32 r8d = eax;
u64 mul = (u64)eax * (u64)0x80808081;
u32 edx = (mul >> 32) >> 7;
u32 ecx;
// always 0:
// ecx = edx >> 31;
// edx += ecx;
ecx = edx * 0xFF;
r8d -= ecx;
r8d ^= ida_chars[j];
if (j < 6 || j == 28) {
if (r8d != tmpl[j]) {
flag = 0;
break;
}
}
// or:
// if (r8d < 32 || r8d >= 127) {
// flag = 0;
// break;
// }
}
if (flag) {
printf("Seed: %u\t", i);
srand(i);
for (int j = 0; j < 29; j++) {
u32 eax = rand();
u32 r8d = eax;
u64 mul = (u64)eax * (u64)0x80808081;
u32 edx = (mul >> 32) >> 7;
u32 ecx;
ecx = edx * 0xFF;
r8d -= ecx;
r8d ^= ida_chars[j];
printf("%c", r8d);
}
printf("\n");
}
}
}
给阿姨倒一杯卡布奇诺
魔改tea加密,稍微修改一下tea的模版即可
#include <stdio.h>
#define uint32_t unsigned int
void decrypt(uint32_t* v, uint32_t* key)
{
static uint32_t data1 = 0x5F797274;
static uint32_t data2 = 0x64726168;
int i; // [rsp+20h] [rbp-10h]
uint32_t sum; // [rsp+24h] [rbp-Ch]
uint32_t v1; // [rsp+28h] [rbp-8h]
uint32_t v0; // [rsp+2Ch] [rbp-4h]
sum = 0x6E75316C * 32;
uint32_t data1_tmp = v[0];
uint32_t data2_tmp = v[1];
v0 = v[0];
v1 = v[1];
for (i = 31; i >= 0; i--)
{
v1 -= ((v0 >> 5) + key[3]) ^ (v0 + sum) ^ (key[2] + 16 * v0) ^ (sum + i);
v0 -= ((v1 >> 5) + key[1]) ^ (v1 + sum) ^ (key[0] + 16 * v1) ^ (sum + i);
sum -= 0x6E75316C;
}
v[0] = v0 ^ data1;
v[1] = v1 ^ data2;
data1 = data1_tmp;
data2 = data2_tmp;
}
int main()
{
uint32_t key[4]; // [rsp+60h] [rbp-40h] BYREF
uint32_t array[8]; // [rsp+70h] [rbp-30h]
array[0] = 0x9B28ED45;
array[1] = 0x145EC6E9;
array[2] = 0x5B27A6C3;
array[3] = 0xE59E75D5;
array[4] = 0xE82C2500;
array[5] = 0xA4211D92;
array[6] = 0xCD8A4B62;
array[7] = 0xA668F440;
key[0] = 0x65766967;
key[1] = 0x756F795F;
key[2] = 0x7075635F;
key[3] = 0x6165745F;
for (int i = 0; i <= 7; i += 2)
{
decrypt(array + i, key);
}
for (int i = 0; i < 32; i++)
{
printf("%c", ((char*)array)[i]);
}
return 0;
}
馒头
数据结构哈夫曼树的构建和解码
//大概的正向流程
typedef struct
{
int data;
int weight;
int parent;
int lch;
int rch;
} htNode;
typedef htNode *huffmanTree;
int initHuffmanTree(huffmanTree *HT)
{
*HT = (huffmanTree)malloc(960);
for (int i = 1; i <= 47; ++i)
{
(*HT)[i].parent = (*HT)[i].lch = (*HT)[i].rch = -1;
}
puts("please input flag:");
for (int i_0 = 1; i_0 <= 24; ++i_0)
{
(*HT)[i_0].data = i_0;
(*HT)[i_0].weight = getchar();
}
return 1;
}
void creatHuffmanTree(huffmanTree *HT, int n)
{
int j; // [rsp+8h] [rbp-18h]
int rnode; // [rsp+Ch] [rbp-14h]
int min2; // [rsp+10h] [rbp-10h]
int lnode; // [rsp+14h] [rbp-Ch]
int min1; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]
if (n > 1)
{
for (i = n + 1; i < 2 * n; ++i)
{
min1 = 0x7FFF;
lnode = -1;
min2 = 0x7FFF;
rnode = -1;
for (j = 1; i > j; ++j) // 找到两个最小的节点
{
if (min1 > (*HT)[j].weight && (*HT)[j].parent == -1)
{
min2 = min1;
rnode = lnode;
min1 = (*HT)[j].weight;
lnode = j;
}
else if (min2 > (*HT)[j].weight && (*HT)[j].parent == -1)
{
min2 = (*HT)[j].weight;
rnode = j;
}
}
(*HT)[lnode].parent = (*HT)[rnode].parent = i;
(*HT)[i].lch = lnode;
(*HT)[i].rch = rnode;
(*HT)[i].weight = (*HT)[lnode].weight + (*HT)[rnode].weight;
}
}
}
int ans1[58] = { 2270, 917, 446, 217, 106, 51, 20, 15, 17, 229, 114, 16, 11, 471, 233, 116, 14, 13, 238, 118, 12, 7, 1353, 557, 248, 123, 6, 24, 309, 137, 67, 3, 5, 172, 84, 4, 1, 796, 383, 186, 89, 2, 8, 197, 97, 48, 23, 10, 21, 413, 203, 101, 22, 9, 210, 104, 19, 18 };
int check_flag(huffmanTree HT, int i)
{
static int index = 0;
if (i <= 24) // 对于叶子,只比对下标
{
if (HT[i].data != ans1[index++])
return 0;
}
else
{
if (HT[i].weight != ans1[index++])
return 0;
if (HT[HT[i].lch].data <= 24 && HT[HT[i].lch].data > 0)
{
if (HT[HT[i].lch].weight != ans1[index++])
return 0;
}
}
if (HT[i].lch <= 0)
return 1;
return check_flag(HT, HT[i].lch) && check_flag(HT, HT[i].rch);
}
由于每个叶子节点权值不会小于32,手画一下huffmantree就可以了