hardsharp (.NET 逆向)
使用exeinfo发现是 C# .NET文件
直接用 dnSpy 打开
找到主函数
private static void Main(string[] args)
{
AesClass aesClass = new AesClass();
string text = "";
string strB = "1Umgm5LG6lNPyRCd0LktJhJtyBN7ivpq+EKGmTAcXUM+0ikYZL4h4QTHGqH/3Wh0";
byte[] array = new byte[]
{
81,
82,
87,
81,
82,
87,
68,
92,
94,
86,
93,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18,
18
};
Console.WriteLine("Welcome to nepnep csharp test! plz input the magical code:");
string text2 = Console.ReadLine();
if (text2.Length != 37)
{
Console.WriteLine("Nope!");
Console.ReadKey();
return;
}
if (text2.Substring(0, 4) != "Nep{" || text2[36] != '}')
{
Console.WriteLine("Nope!");
Console.ReadKey();
return;
}
for (int i = 0; i < 32; i++)
{
text += Convert.ToChar((int)(array[i] ^ 51)).ToString();
}
if (string.Compare(aesClass.AesEncrypt(text2, text), strB) == 0)
{
Console.WriteLine("wow, you pass it!");
Console.ReadKey();
return;
}
Console.WriteLine("Nope!");
Console.ReadKey();
}
这才是真正的签到题吧…
发现是 AES 加密
进入 AesEncrypt 函数看一眼
public string AesEncrypt(string str, string key)
{
if (string.IsNullOrEmpty(str))
{
return null;
}
byte[] bytes = Encoding.UTF8.GetBytes(str);
byte[] array = new RijndaelManaged
{
Key = Encoding.UTF8.GetBytes(key),
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
}.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);
return Convert.ToBase64String(array, 0, array.Length);
}
发现使用了 ECB 模式,那么直接上网找个 python 脚本计算就行
array = [81, 82, 87, 81, 82, 87, 68, 92, 94, 86, 93, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18]
key = ''
for i in array:
key += chr(i ^ 51)
print (f'key: {key}')
print (f'key length: {len(key)}')
import base64
from Crypto.Cipher import AES
message = '1Umgm5LG6lNPyRCd0LktJhJtyBN7ivpq+EKGmTAcXUM+0ikYZL4h4QTHGqH/3Wh0'
encrypt_data = message
cipher = AES.new(key)
result2 = base64.b64decode(encrypt_data)
a = cipher.decrypt(result2)
a = a.decode('utf-8','ignore')
a = a.rstrip('\n')
a = a.rstrip('\t')
a = a.rstrip('\r')
a = a.replace('\x06','')
print('\n','data:',a)
得到结果:
key: badbadwomen!!!!!!!!!!!!!!!!!!!!!
key length: 32
data: Nep{up_up_down_down_B_a_b_A_Nep_nep~}
二十六进制 (c / c++ 逆向)
先用exeinfo打开,发现是无壳32位c++代码。
用ida打开,找到有用的字符串,定位关键代码
void __noreturn sub_4010A0()
{
__int64 v0; // rax
char Dst; // [esp+0h] [ebp-108h]
memset(&Dst, 0, 0xFFu);
dword_403378 = (int)malloc(8u);
Memory = (void *)dword_403378;
*(_DWORD *)(dword_403378 + 4) = 0;
sub_401020("plz input right num:\n", Dst);
sub_401060("%s", &Dst, 32);
v0 = atoi64(&Dst);
sub_401120(v0);
}
输入一个数字,将其传入 sub_401120
函数。
进入这个函数
void __cdecl __noreturn sub_401120(__int64 a1)
{
signed __int64 v1; // rdi
int v2; // eax
int v3; // edx
char v4; // cl
v1 = __PAIR__(a1, HIDWORD(a1));
if ( a1 )
{
do
{
v2 = sub_401F00(__PAIR__(v1, HIDWORD(v1)), 0x1Au, 0);
LODWORD(v1) = v3;
v4 = byte_402194[HIDWORD(v1) - 26 * v2]; // 2163qwe)(*&^%489$!057@#><A
HIDWORD(v1) = v2;
sub_401160(v4 ^ 7);
}
while ( v1 );
}
sub_401190();
}
根据题目信息的提示,这是把输入数字转换成为26进制,随后进行一个异或运算,然后存到一个地址里。
进入 sub_401190
函数
void __noreturn sub_401190()
{
_DWORD *v0; // esi
int v1; // ecx
_DWORD *v2; // ebx
unsigned int v3; // edi
unsigned int v4; // esi
char *v5; // ecx
char v6; // dl
int v7; // eax
int v8; // edi
void *v9; // eax
int v10; // [esp+10h] [ebp-4h]
v0 = Memory;
v1 = 0;
v10 = 0;
v2 = Memory;
v3 = strlen(aFb726);
if ( v3 )
{
v4 = 0;
do
{
if ( !v2 )
break;
v5 = &aFb726[v4];
v6 = *(_BYTE *)v2;
v7 = v10 + 1;
v2 = (_DWORD *)v2[1];
++v4;
if ( v6 != *v5 )
v7 = v10;
v1 = v7;
v10 = v7; // 判断某内存种对应位置是否与 aFb726 字符串相同,相同就在长度上加 1
}
while ( v4 < v3 );
v0 = Memory;
}
if ( v0 )
{
v8 = v1;
do
{
v9 = (void *)v0[1];
dword_403378 = (int)v0;
Memory = v9;
free(v0);
v0 = Memory;
--v8;
}
while ( Memory ); // 释放内存
}
else
{
v8 = -1;
}
if ( v10 != strlen(aFb726) ) // 根据之间计算的相同字母数量,判断两个字符串是否相同
{
puts("flag is Error!!!");
exit(v8);
}
puts("flag is Right!!!, please md5('Nep{you_input_num}') submit th4 flag");
system("pause");
exit(v8);
}
所以只需要将 aFb726 字符串进行反向计算即可
>>> flag = 'Fb72>&6'
>>> str = '2163qwe)(*&^%489$!057@#><A'
>>> num = []
>>> for c in flag:
... num.append(str.index(chr(ord(c) ^ 7)))
...
>>> num
[25, 6, 18, 19, 15, 17, 1]
>>> sum = 0
>>> for i in num[::-1]:
... sum *= 26
... sum += i
...
>>> sum
518100101
运行程序进行测试,得到
plz input right num:
518100101
flag is Right!!!, please md5('Nep{you_input_num}') submit th4 flag
请按任意键继续. . .
根据题目信息,计算一下数字的32位小写md5(一开始算的是 Nep{51…01} 这个字符串的md5,发现不对,所以算数字的试了一下)
得到flag
Nep{967fa25cbea166ded43127f141cff31a}
password (安卓逆向)
这道题做了好久都没做出来,结果发现是把base64的 ‘+/’ 记反了…
首先用 jeb
打开,找到 com.nepnep.app
中的 MainActivity
public class MainActivity extends AppCompatActivity {
public Encrypt en;
static {
System.loadLibrary("native-lib");
}
public MainActivity() {
super();
this.en = new Encrypt();
}
protected void onCreate(Bundle arg4) {
super.onCreate(arg4);
this.setContentView(0x7F0B001C); // activity_main
this.findViewById(0x7F080057).setOnClickListener(new View$OnClickListener(this.findViewById(0x7F0800B7), this.findViewById(0x7F0800F2)) { // btn, key, password
public void onClick(View arg4) {
if(MainActivity.this.verify(this.val$key.getText().toString()) == 0) {
System.out.println(this.val$key.getText().toString());
Toast.makeText(MainActivity.this, "key错误!", 0).show();
}
else if(MainActivity.this.en.file(this.val$passwd.getText().toString().getBytes(), this.val$key.getText().toString())) {
Toast.makeText(MainActivity.this, "明文正确,快去解压缩包获取flag吧!", 0).show();
}
else {
Toast.makeText(MainActivity.this, "明文错误!", 0).show();
}
}
});
}
public native int verify(String arg1) {
}
}
这段代码是读入 key
和 password
,然后先判断 key
是否正确,如果正确的话判断 password
是否正确
那么肯定先破解 key
,发现 verify
是外部函数
找到 native-lib.so
文件,拖入ida,找到函数名里搜索 java
或 verify
之类的,找到这个判断函数
v4 = (char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0LL);
v5 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1344LL))(a1, v3);
memset(&s, 0, 0x3E8uLL);
sub_710(v4, (__int64)&s, v5 - 3 * (((unsigned __int64)('UUUV' * v5) >> 63) + ((unsigned __int64)('UUUV' * v5) >> 32)));
sub_820(&s, (__int64)&v22); // s='th1s_1s_k3ya!!!!'
if ( v5 <= 0 )
goto LABEL_10;
v6 = 0;
while ( 1 ) // base64
{
v11 = v6;
if ( v5 >= 3 )
{
*((_BYTE *)&v15 + v6) = aAbcdefghijklmn[*((char *)&v22 + v6)];// abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ
*((_BYTE *)&v15 + v6 + 1) = aAbcdefghijklmn[*((char *)&v22 + v6 + 1)];
*((_BYTE *)&v15 + v6 + 2) = aAbcdefghijklmn[*((char *)&v22 + v6 + 2)];
v7 = aAbcdefghijklmn[*((char *)&v22 + v6 + 3)];
v6 += 4;
*((_BYTE *)&v15 + v11 + 3) = v7;
goto LABEL_4;
}
if ( v5 == 1 )
break;
if ( v5 == 2 )
{
*((_BYTE *)&v15 + v6) = aAbcdefghijklmn[*((char *)&v22 + v6)];
*((_BYTE *)&v15 + v6 + 1) = aAbcdefghijklmn[*((char *)&v22 + v6 + 1)];
*((_BYTE *)&v15 + v6 + 2) = aAbcdefghijklmn[*((char *)&v22 + v6 + 2)];
*((_BYTE *)&v15 + v6 + 3) = '=';
goto LABEL_10;
}
LABEL_4:
v10 = __OFADD__(-3, v5);
v8 = v5 == 3;
v9 = v5 - 3 < 0;
v5 -= 3;
if ( (unsigned __int8)(v9 ^ v10) | v8 )
goto LABEL_10;
}
*((_BYTE *)&v15 + v6) = aAbcdefghijklmn[*((char *)&v22 + v6)];
*((_BYTE *)&v15 + v6 + 1) = aAbcdefghijklmn[*((char *)&v22 + v6 + 1)];
*(_WORD *)((char *)&v15 + v6 + 2) = '==';
LABEL_10:
(*(void (__fastcall **)(__int64, __int64, char *))(*(_QWORD *)a1 + 1360LL))(a1, v3, v4);
v12 = _mm_movemask_epi8(
_mm_and_si128(
_mm_cmpeq_epi8(_mm_load_si128((const __m128i *)&v15), (__m128i)xmmword_BE0),
_mm_cmpeq_epi8(_mm_loadu_si128((const __m128i *)((char *)&v15 + 9)), (__m128i)xmmword_BD0)));// 3g6L2PWL2PXFmR+7ise7iq==
__android_log_print(4LL, "nepnep", "%s", &v15);
result = 0LL;
if ( v12 == 0xFFFF )
{
__android_log_print(4LL, "nepnep", "key is true!", v13);
result = 1LL;
}
return result;
我们的目的肯定是让 v12 = 0FFFF
虽然对于 mm 之类的指令不太清楚,但大概的意思应该还是两个字符串相等的,那么就让 (const __m128i *)&v15), (__m128i)xmmword_BE0)
和 ((char *)&v15 + 9)), (__m128i)xmmword_BD0))
两两相等即可
在字符串中找到这两个,就能得到 3g6L2PWL2PXFmR+7ise7iq==
,这个看起来是一个 base64,但解出来是乱码,所以还是得往前看。上面一大段从 while
开始的代码,显然就是 base64 的最后一步,点开字符串,发现这个不是标准的 base64。那么根据这个字符串反向求解 base64 就可以了。
实际操作时,继续阅读了 sub_820
和 sub_710
两个函数。只看 sub_820
的话看不太懂,建议先看 sub_710
,发现 sub_710
中,将一个字符串的每个字符根据 ascii 数值直接拆开到了八个 char 上,每个 char 存 '0'
或 '1'
( 0x30
和 0x31
)。到了 sub_820
函数,则每六个进行合并,每一位运算看起来很奇怪,但如果用 0x30
和 0x31
带入会发现就是很简单的将 0 和1 重新组合起来。就是做了一个 base64 运算。
于是写个代码反向求解就可以了
import string
from libnum import s2n
import base64
en_key = '7+RmFXP2LWP2L6g3'[::-1] + '==qi7esi'[::-1]
true_base = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
fake_base = string.ascii_lowercase + string.digits + '+/' + string.ascii_uppercase
key = ''
for c in en_key:
if c == '=':
key += c
continue
key += true_base[fake_base.index(c)]
key = base64.b64decode(key)
print(key)
得到 key: th1s_1s_k3y!!!!!
这里我因为打错了base的字符串,卡了整整一天(
接下来就可以去求明文密码了
继续阅读 java 代码
public class Encrypt {
public Encrypt() {
super();
}
public void en1(int[] arg6, String arg7, int arg8) {
int v0 = 0x100;
byte[] v1 = new byte[v0];
byte[] v7 = arg7.getBytes();
int v2 = 0;
int v3;
for(v3 = 0; v3 < v0; ++v3) {
arg6[v3] = 0x100 - v3; // 256-1降序
v1[v3] = v7[v3 % arg8]; // 用密钥填满v1
}
int v7_1 = 0;
while(v2 < v0) {
v7_1 = (arg6[v2] + v7_1 + v1[v2]) % 0x100;
arg8 = arg6[v2];
arg6[v2] = arg6[v7_1];
arg6[v7_1] = arg8;
++v2;
}
}
public void en2(int[] arg7, byte[] arg8, int arg9) {
int v0 = 0;
int v1 = 0;
int v2 = 0;
while(v0 < arg9) {
v1 = (v1 + 1) % 0x100;
v2 = ((arg7[v1] & 0xFF) + v2) % 0x100;
int v3 = arg7[v1];
arg7[v1] = arg7[v2];
arg7[v2] = v3;
arg8[v0] = ((byte)(arg8[v0] ^ arg7[((arg7[v1] & 0xFF) + (arg7[v2] & 0xFF)) % 0x100]));
++v0;
}
}
public boolean file(byte[] arg6, String arg7) { // passwd, key = 'th1s_1s_k3ya!!!!'
int[] v0 = new int[0x100];
int v1 = 17;
int[] v2 = new int[]{0x8B, 0xD2, 0xD9, 0x5D, 0x95, 0xFF, 0x7E, 0x5F, 0x29, 0x56, 0x12, 0xB9, 0xEF, 0xEC, 0x8B, 0xD0, 0x45};
this.en1(v0, arg7, arg7.length());
this.en2(v0, arg6, arg6.length);
if(arg6.length != v1) {
return 0;
}
int v7;
for(v7 = 0; v7 < v1; ++v7) {
if((arg6[v7] & 0xFF) != v2[v7]) {
return 0;
}
}
return 1;
}
}
懂的话就会发现这个就是一个s盒从 0-255
变成 256-1
的 RC4
密码
不懂的话直接把代码复制出来,把 file
函数改写成 main
函数,反向求解一下就行(因为仔细观察就可以发现 en2
只对明文做了个异或运算,且异或的对象和明文本身没有关系),这里当密码学的题来做其实就好了。
这是我复制后改写的 java 代码
public class Main {
public static void en1(int[] arg6, String arg7, int arg8) {
int v0 = 0x100;
byte[] v1 = new byte[v0];
byte[] v7 = arg7.getBytes();
int v2 = 0;
int v3;
for(v3 = 0; v3 < v0; ++v3) {
arg6[v3] = 0x100 - v3; // 256-1降序
v1[v3] = v7[v3 % arg8]; // 用密钥填满v1
}
int v7_1 = 0;
while(v2 < v0) {
v7_1 = (arg6[v2] + v7_1 + v1[v2]) % 0x100;
arg8 = arg6[v2];
arg6[v2] = arg6[v7_1];
arg6[v7_1] = arg8;
++v2;
}
}
public static void en2(int[] arg7, int[] arg8, int arg9) {
int v0 = 0;
int v1 = 0;
int v2 = 0;
while(v0 < arg9) {
v1 = (v1 + 1) % 0x100;
v2 = ((arg7[v1] & 0xFF) + v2) % 0x100;
int v3 = arg7[v1];
arg7[v1] = arg7[v2];
arg7[v2] = v3;
arg8[v0] = ((byte)(arg8[v0] ^ arg7[((arg7[v1] & 0xFF) + (arg7[v2] & 0xFF)) % 0x100]));
++v0;
}
}
public static void main(String[] args) {
String arg7 = "th1s_1s_k3y!!!!!";
int[] arg6 = new int[]{0x8B, 0xD2, 0xD9, 0x5D, 0x95, 0xFF, 0x7E, 0x5F, 0x29, 0x56, 0x12, 0xB9, 0xEF, 0xEC, 0x8B, 0xD0, 0x45};
int[] v0 = new int[0x100];
int v1 = 17;
int[] v2 = new int[]{};
en1(v0, arg7, arg7.length());
en2(v0, arg6, arg6.length);
for (int i = 0; i < arg6.length; i++) {
System.out.print((char)(arg6[i] & 0xff));
}
}
}
当然也可以跟在第一段 python 后面继续写
import string
from libnum import s2n
import base64
en_key = '7+RmFXP2LWP2L6g3'[::-1] + '==qi7esi'[::-1]
true_base = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
fake_base = string.ascii_lowercase + string.digits + '+/' + string.ascii_uppercase
key = ''
for c in en_key:
if c == '=':
key += c
continue
key += true_base[fake_base.index(c)]
key = base64.b64decode(key)
print(key)
# th1s_1s_k3y!!!!!
ciphertext = [0x8B, 0xD2, 0xD9, 0x5D, 0x95, 0xFF, 0x7E, 0x5F, 0x29, 0x56, 0x12, 0xB9, 0xEF, 0xEC, 0x8B, 0xD0, 0x45]
def en1(key, len_key):
res = []
v0 = 0x100
v1 = [0] * v0
v7 = [c for c in key]
v2 = 0
for v3 in range(v0):
res.append(0x100 - v3)
v1[v3] = v7[v3 % len_key]
v7_1 = 0
while v2 < v0:
v7_1 = (res[v2] + v7_1 + v1[v2]) % 0x100
len_key = res[v2]
res[v2] = res[v7_1]
res[v7_1] = len_key
v2 += 1
return res
def en2(res_key, text, len_text):
v0 = 0
v1 = 0
v2 = 0
# text = bytes(text)
while v0 < len_text:
v1 = (v1 + 1) % 0x100
v2 = ((res_key[v1] & 0xFF) + v2) % 0x100
v3 = res_key[v1]
res_key[v1] = res_key[v2]
res_key[v2] = v3
text[v0] = ((text[v0] ^ res_key[((res_key[v1] & 0xFF) + (res_key[v2] & 0xFF)) % 0x100]))
v0 += 1
return text
list_key = en1(key[:], len(key))
plaintext = en2(list_key[:], ciphertext[:], len(ciphertext))
print (plaintext)
flag = ''
for c in plaintext:
flag += chr(c)
print (flag)
这样就拿到密码了 Y0uG3tTheP4ssw0rd
然后将 apk 解压一下(用的 bandizip,可以直接解压),在目录下面可以找到 \assets\flag.zip
解压即可拿到 flag
easy_mips (mips 逆向)
这是一道mips的题,用ida无法反汇编,爬了
先用ida的字符串搜索找到了关键代码在 tty_write
函数里,并且调用了 encry
函数和 init
函数,但因为没学过mips,也就没法继续分析了
于是第一次尝试使用 Ghidra
用 Ghidra 打开二进制文件
找到关键函数 tty_write
void tty_write(char *param_1,char *param_2)
{
int __fd;
size_t __n;
int iVar1;
EVP_PKEY_CTX aEStack56 [20];
undefined4 local_24;
undefined4 local_20;
undefined4 local_1c;
undefined4 local_18;
undefined4 local_14;
char local_10;
int local_c;
local_c = __stack_chk_guard;
local_24 = 0x666c6167;
local_20 = 0x7b69745f;
local_1c = 0x69735f5f;
local_18 = 0x5f5f6661;
local_14 = 0x6b657d0a;
local_10 = '\0';
__fd = open(param_1,2);
if (__fd < 0) {
printf("cannot open");
/* WARNING: Subroutine does not return */
exit(-1);
}
__n = strlen(param_2);
write(__fd,param_2,__n);
read(__fd,aEStack56,0x13);
encry(aEStack56);
iVar1 = strcmp((char *)aEStack56,(char *)&local_24);
if (iVar1 == 0) {
write(__fd,"you get the fake flag\n",0x16);
}
else {
iVar1 = strcmp((char *)aEStack56,"3_isjA0UeQZcNa\\`\\Vf");
if (iVar1 != 0) {
puts("you_don\'t_get_the_flag");
goto LAB_00400b48;
}
write(__fd,"good_job!\n",10);
}
close(__fd);
LAB_00400b48:
if (local_c != __stack_chk_guard) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
之前在 ida 中发现 local_24
是一个fake flag,就不管了
发现调用了一个 encry
函数,进入这个函数
void encry(EVP_PKEY_CTX *param_1)
{
char cVar1;
int local_10;
init(param_1);
cVar1 = '\x05';
local_10 = 0;
while (local_10 < 0x13) {
param_1[local_10] = (EVP_PKEY_CTX)((char)param_1[local_10] - cVar1);
cVar1 = cVar1 + '\x01';
local_10 = local_10 + 1;
}
return;
}
发现先调用了 init
函数,然后对每一位做了个减法
查看 init
函数
int init(EVP_PKEY_CTX *ctx)
{
int iVar1;
char extraout_var;
char extraout_var_00;
char extraout_var_01;
int local_20;
iVar1 = __stack_chk_guard;
srand(0x1c5e);
rand();
rand();
rand();
*ctx = (EVP_PKEY_CTX)((byte)*ctx ^ extraout_var + 0x32U);
ctx[5] = (EVP_PKEY_CTX)((byte)ctx[5] ^ extraout_var_00 - 0x39U);
ctx[6] = (EVP_PKEY_CTX)((byte)ctx[6] ^ extraout_var_01 + 0x30U);
local_20 = 7;
while (local_20 < 0xd) {
local_20 = local_20 + 1;
}
if (iVar1 != __stack_chk_guard) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return __stack_chk_guard;
}
这里自动分析出现了问题,不知道 extraout_var
和 rand
有什么关系
于是只能去找对应的汇编慢慢读
根据 Ghidra 里代码与汇编的映射关系,找到了 extraout_var
的位置
00400d00 80 43 00 00 lb v1,0x0 (v0)
00400d04 8f c2 00 24 lw v0,local_1c (s8)
00400d08 30 42 00 ff andi v0,v0,0xff
00400d0c 24 42 00 32 addiu v0,v0,0x32
00400d10 30 42 00 ff andi v0,v0,0xff
00400d14 7c 02 14 20 seb v0,v0
00400d18 00 62 10 26 xor v0,v1,v0
00400d1c 7c 02 1c 20 seb v1,v0
00400d20 8f c2 00 1c lw v0,local_24 (s8)
00400d24 a0 43 00 00 sb v1,0x0 (v0)
找到 addiu
说明 v0
对应的就是 extraout_var
,发现是从 local_1c
处取出的
往上找 local_1c
00400c80 8f dc 00 10 lw gp,local_30 (s8)
00400c84 8f 82 80 44 lw v0,-0x7fbc (gp)=>->rand = 004010e0
00400c88 00 40 c8 25 or t9,v0,zero
00400c8c 03 20 f8 09 jalr t9=>rand int rand(void)
00400c90 00 00 00 00 _nop
00400c94 8f dc 00 10 lw gp,local_30 (s8)
00400c98 00 40 18 25 or v1,v0,zero
00400c9c 83 c2 00 31 lb v0,local_10 +0x1 (s8)
00400ca0 00 62 10 26 xor v0,v1,v0
00400ca4 00 02 16 03 sra v0,v0,0x18
00400ca8 af c2 00 24 sw v0,local_1c (s8)
从后往前看,最后是将 v0 存到 local_1c 中,倒数第二步是将 v0 右移 0x18 位(这里问了一下会mips的大佬…),虽然没法完美分析出 rand 的结果存到了哪里,但是可以猜测最后右移了 0x18 位并存到了 local_1c
中。
于是就可以尝试编写代码,破解flag了。
首先计算几个 rand
的结果
#include <stdio.h>
#include <stdlib.h>
int main(){
srand(0x1c5e);
char a[20];
a[0] = '8';
printf("%x\n", rand());
printf("%x\n", rand());
printf("%x\n", rand());
return 0;
}
运行得到
./test_rand
446aef60
5de30bb4
27445d71
随后写个反向的算法
encry = b'3_isjA0UeQZcNa\\`\\Vf'
flag_1 = []
t = 5
for i in encry:
flag_1.append(i + t)
t += 1
print (flag_1)
rand = [0x44 + 0x32, 0,0,0,0,0x5d - 0x39, 0x27 + 0x30] + [0] * 15
flag = ''
for i in range(len(flag_1)):
flag += chr(flag_1[i] ^ (rand[i] & 0xff))
print (flag)
得到最终的flag
[56, 101, 112, 123, 115, 75, 59, 97, 114, 95, 105, 115, 95, 115, 111, 116, 113, 108, 125]
Nep{solar_is_sotql}
worrrrms (go语言逆向)
只知道用了SM4算法加密,不会go语言,具体的看不懂,爬了。
总结
这个比赛的前五道题其实就是各个方向的入门题目,有相应的工具并且能够看懂伪代码就可以做了,只需要进行静态分析。做go语言的时候发现go的函数调用太奇怪了,而且有各种指针,不愧是最安全的语言。
至于后面的几道题,由于解的人太少,也就没有去看,看完wp再补。