2

easy_rust

rust环境搭建:https://blog.csdn.net/yao_hou/article/details/124292061

rust逆向,main函数

image

image

re_rust可以看作一个类(实际rust没有类),有mainbase64encode方法,在base64encode里面还有trans_a4b64_to_char方法

image

image

将三个字节转换为4个字节,没有魔改

image

image

easy_go

开始并不会直接跳转到main函数,但是函数栏可以发现

image

image

c  0xDB,0x9E,0xB7,0x9A,0x91,0xCA,0xA1,0x6B,0x97,0xC1,0x74,0xB3,0x90,0x0,0x0,0x0

a  0xD3,0x75,0x9B,0xF9,0xA3,0x87,0xED,0x93,0x8D,0xDD,0x77,0xED,0x67,0x0,0x0,0x0

b  0xB7,0x9C,0x79,0x43,0x9B,0xAF,0x94,0xE4,0x94,0x71,0xEC,0xEA,0x8E,0x0,0x0,0x0

c + a * v3 == b

所有的运算都是针对 8位无符号整数(uint8,即 byte 类型) 进行的。Go 语言(以及大多数底层语言)在处理 byte 类型时会自动对 256 取模,不会发生溢出。

所以实际上:

(((a * v3) % 256) + c ) % 256 == b

逆向求解就要将取模显式表达出来

a = [0xD3, 0x75, 0x9B, 0xF9, 0xA3, 0x87, 0xED, 0x93, 0x8D, 0xDD, 0x77, 0xED, 0x67]
b = [0xB7, 0x9C, 0x79, 0x43, 0x9B, 0xAF, 0x94, 0xE4, 0x94, 0x71, 0xEC, 0xEA, 0x8E]
c = [0xDB, 0x9E, 0xB7, 0x9A, 0x91, 0xCA, 0xA1, 0x6B, 0x97, 0xC1, 0x74, 0xB3, 0x90]

for i in range(0, len(a)):
    temp = (b[i] - c[i]) % 256
    inv_a = pow(a[i], -1, 256)#计算 a[i] 在模256下的乘法逆元
    print(chr((temp * inv_a) % 256), end='')

乘法逆元

在模运算中,不能直接做除法,而是用乘法逆元代替除法:

若要求 (b / a) mod m,等价于求 b * inv(a) mod m

a * x ≡ 1 mod m x 就是 a 在模 m 下的逆元

easy_csharp

easy_csharp写的exe在die里面是无法识别的,只能分析出c/c++

image

有壳,使用了类似 .NET Reactor 的保护工具,且资源节(.rsrc)被压缩

可以用de4dot去,生成easy_csharp-cleaned.exe

再用dnspy或ilspy打开就能看到函数

在面向对象中,类的函数(方法)就是成员,所以搜索main函数时,在dnspy里面是成员,il2spy是method

image

image

des加密然后是base64编码

des解密需要key和iv(static),这在dnspy里面更容易找到

image

image

通过dnSpy对原程序进行修改

这是wp提到的解法

image

加密函数是smethod_1,还有一个smethod_0,猜测后者是解密函数

那么就可以修改代码

image

在main函数右键-编辑方法(上图是修改之后的)

将内容改为下面的就可以了

		Console.WriteLine("Input ciphertext:");
		string decryptedFlag = Class1.smethod_0(Console.ReadLine(), Class1.string_0, Class1.string_1);
		Console.WriteLine("Decrypted Flag: " + decryptedFlag);

这里在我的电脑上是有这一个问题

image

只能用Version=5.0.0.0的dotnet运行,但是程序是32位的,所以只能用x86,而默认的是x64

所以我就只有这样指定"C:\Program Files (x86)\dotnet\dotnet.exe" .\easy_csharp-cleaned.exe

这时候输入密文就行了

image

easy_python

import easy_python
print(dir(easy_python))
print(help(easy_python))

看到函数和用法

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'base64', 'hashlib', 'os', 'rc4', 'sys', 'time']
Help on module easy_python:

NAME
    easy_python

FILE
    e:\nss\easy_python.pyc

CLASSES
    rc4

    class rc4
     |  Methods defined here:
     |
     |  __init__(self, public_key=None)
     |
     |  decode(self, string)
     |
     |  docrypt(self, string)
     |
     |  encode(self, string)


None

pycdas可以输出字节码,难看但是能够找到密文,并且知道是用了rc4和base64

        '__main__'
        'zBhzAVLG6XTu2w0H'
        'Input your flag:'
        'oxurpmahzeM2kHKblmTkWlLpb2i5jXKrBN/uf3+Xn5n0lYKMJA=='
        'True!'
        'False!'
# -*- coding: utf-8 -*-
import easy_python

rc4_obj = easy_python.rc4("zBhzAVLG6XTu2w0H")

encrypted_str = 'oxurpmahzeM2kHKblmTkWlLpb2i5jXKrBN/uf3+Xn5n0lYKMJA=='

decrypted_str = rc4_obj.decode(encrypted_str)

print(decrypted_str)

第一行就是因为版本原因会有如下报错

  File "E:\NSS\temp.py", line 1
SyntaxError: Non-ASCII character '\xe5' in file E:\NSS\temp.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

这个错误是 Python 2 特有的编码问题。Python 2 默认使用 ASCII 编码,当代码中出现非 ASCII 字符(如中文)时,需要在在 Python 2 文件的第一行或第二行添加编码声明

easy_mfc

在运行的时候提醒缺少dll,需要下载Microsoft Visual C++ 2013 Redistributable Package ,因为该程序是32位的,所以Microsoft Visual C++ 2013 Redistributable Package就要下载x86的

还是用spy++查看,xspy分析

image

image

wp的科普:

ON_COMMAND是MFC提供的宏,实现命令消息(如菜单、工具栏的选项消息)的消息响应函数的注册。使用方法为ON_COMMAND(消息ID, 响应函数名).注册了响应函数之后,一旦主窗口接收到该命令消息,程序就会调用我们提供的消息响应函数进行处理

所以就看ON_COMMAND对应的函数,偏移为0x001880

image

image

就是一个异或

数组这样分

image

也不知道为什么最后两位会分开

#include <stdio.h>
#include <string.h>

int main(){
	int a[]={0xBC,0x9D,0x8C,0x92,0x40,0x47,0x86,0x21,0xF5,0xAC,0x8F,0xFD,0x68,0xE4,0xE9,0x3A,0xC0,0x66,0xB3,0x64,0x7E,0x79,0xD3,0x22,0x31,0xF8};
	int b[]={0xD2,0xAC,0xEE,0xFD,0x2F,0x2C,0xFD,0x79,0xC5,0xDE,0xD0,0x85,0x58,0xB6,0xB6,0xB,0xF5,0x39,0xC0,0x54,0x21,0x1F,0xE2,0x54,0x2,0x85};
	for(int i=0;i<30;i++){
		printf("%c",a[i] ^ b[i]);
	}
	return 0;
}

BUU crackMe

用户名:'welcomebeijing',要找出密码

从后往前看

image

在main函数里面看到判断

image

下面的函数可以确定v16数组是什么

image

所以v16是dbappsec

可以验证一下

#include <stdio.h>
#include <string.h>

int main() {
	int a=0;
	a|=4;
	a|=0x14;
	a|=0x84;
	a|=0x114;
	a|=0x380;
	a|=0xa04;
	a|=0x2310;
	a|=0x8a10;
	printf("%d",a);
	return 0;
}

image

对于上面这个函数关键是异或,可以看到将byte_E06050存到cl,这是ecx的低八位,所以数组的值是存到ecx里面

用ollydbg动调,密码随便

看到偏移是1B3E

image

手动提取吧

byte:0x2A,0xD7,0x92,0xE9,0x53,0xE2,0xC4,0xCD

异或回去就是密码了

#include <stdio.h>
#include <string.h>

int main() {
	int a[]={0x2A,0xD7,0x92,0xE9,0x53,0xE2,0xC4,0xCD};
	char b[]="dbappsec";
	for(int i=0;i<8;i++){
		printf("%x",a[i] ^ b[i]);
	}
}

UIUC2024 Summarize

找到六个九位数,以hex格式输出

image

关键函数

像这种运算的函数看起来非常抽象

image

所以可以将代码复制出来自己试一下

sub_40163D为例

#include <stdio.h>
#include <string.h>

int func1(unsigned int a1, unsigned int a2)
{
	unsigned int v5; // [rsp+10h] [rbp-18h]
	char v6; // [rsp+14h] [rbp-14h]
	unsigned int v7; // [rsp+18h] [rbp-10h]
	unsigned int v8; // [rsp+1Ch] [rbp-Ch]
	__int64 v9; // [rsp+20h] [rbp-8h]
	
	v9 = 0;
	v5 = 0;
	v6 = 0;
	while ( a1 || a2 )
	{
		v7 = a1 & 1;
		v8 = a2 & 1;
		a1 >>= 1;
		a2 >>= 1;
		v9 += (v5 ^ v8 ^ v7) << v6;
		v5 = v5 & v7 | v8 & v7 | v5 & v8;
		++v6;
	}
	return (v5 << v6) + v9;
}

int main() {
	int a=func1(3,2);
	printf("%d",a);
	return 0;
}

所以可以判断这是加法函数

详细解释:

这个函数实现了一个二进制加法器,模拟了硬件全加器的行为

a1 和 a2: 输入的两个要相加的无符号整数
v5: 进位标志 (carry)
v6: 当前处理的位数 (从最低位开始)
v7: a1 的当前最低位
v8: a2 的当前最低位
v9: 存储累加结果的变量

算法流程
初始化:
所有变量初始化为0
循环处理每一位:
只要 a1 或 a2 还有未处理的位就继续循环
每次迭代处理最低位:
v7 = a1 & 1: 获取 a1 的最低位
v8 = a2 & 1: 获取 a2 的最低位
a1 >>= 1 和 a2 >>= 1: 右移一位,准备处理下一位
计算当前位和与进位:
当前位和: v5 ^ v8 ^ v7 (三个值的异或)
将当前位和左移到正确位置并累加到结果: v9 += (v5 ^ v8 ^ v7) << v6
计算新的进位: v5 = v5 & v7 | v8 & v7 | v5 & v8 (三个中有两个或三个为1则进位)
递增位数计数器:
v6++ 准备处理下一位
处理最终进位:
循环结束后,如果还有进位,将其左移到正确位置并加到结果中: (v5 << v6) + v9

其他也是一样的

image

那么就可以用z3来解决

其中sub函数

image

加的是一个负数,所以是减法

from z3 import *

s = Solver()  # 创建了一个 Solver 对象 s
a1 = [BitVec('a%d' % i,32) for i in range(6)]  # 变量

for i in range(6):
    s.add(And(0x5F5E100 <= a1[i], a1[i] < 0x3B9AC9FF))
s.add(((a1[0] - a1[1]) + a1[2])%0x10AE961 == 4139449)
s.add((a1[0] + a1[1]) % 0x1093A1D == 9166034)
s.add(((3*a1[0]) - (2*a1[1])) % (a1[0] ^ a1[3]) == 556569677)
s.add(((a1[1] & (a1[2] + a1[0]))% 0x6E22) == 12734)
s.add((a1[1] + a1[3]) % a1[0] == 540591164)
s.add((a1[2] ^ (a1[3] + a1[5]))% 0x1CE628 == 1279714)
s.add((a1[4] - a1[5]) % 0x1172502 == 17026895)
s.add((a1[4] + a1[5]) % 0x2E16F83 == 23769303)

if s.check() == sat: #检查当前添加的所有约束条件是否存在满足的解,sat为满足
    m = s.model()
    # 按顺序打印所有变量的值
    result = [m[a1[i]].as_long() for i in range(6)]
    print(''.join([hex(x)[2:] for x in result]))

不建议像这样组合起来,还是分步搞好一些,因为由于运算优先级的问题检查了好几遍

输出2a142dd72e87fa9c1456a32d1bc4f77739975e5fcf5c6c0

flag:uiuctf{2a142dd72e87fa9c1456a32d1bc4f77739975e5fcf5c6c0}

posted @ 2025-07-13 20:25  zzz222666  阅读(14)  评论(0)    收藏  举报