int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
_QWORD *v4; // rax
const CHAR *v5; // r11
__int64 v6; // r10
int v7; // r9d
const CHAR *v8; // r10
__int64 v9; // rcx
__int64 *v10; // rax
unsigned int v12; // ecx
__int64 v13; // r9
__int128 flag[2]; // [rsp+20h] [rbp-38h] BYREF
memset(flag, 0, sizeof(flag));
scanf("%s");
v3 = -1i64;
do
++v3;
while ( *((_BYTE *)flag + v3) );
if ( v3 != 31 )
{
while ( 1 )
Sleep(0x3E8u); // len(flag)=31
}
v4 = sub_7FF766461280(flag);
v5 = name;
if ( v4 )
{
sub_7FF7664615C0((unsigned __int8 *)v4[1]);
sub_7FF7664615C0(*(unsigned __int8 **)(v6 + 16));
v7 = dword_7FF7664657E0;
v5[dword_7FF7664657E0] = *v8;
dword_7FF7664657E0 = v7 + 1;
}
UnDecorateSymbolName(v5, outputString, 0x100u, 0);// 将已装饰的符号名v5
// 转换为未装饰的符号名outputstring
v9 = -1i64;
do
++v9;
while ( outputString[v9] );
if ( v9 == 62 ) // len(outputString)=62
{
v12 = 0;
v13 = 0i64;
do
{
if ( a1234567890Qwer[outputString[v13] % 23] != str2[v13] )
_exit(v12);
if ( a1234567890Qwer[outputString[v13] / 23] != str1[v13] )
_exit(v12 * v12);
++v12;
++v13;
}
while ( v12 < 62 );
print("flag{MD5(your input)}\n");
return 0;
}
else
{
v10 = sub_7FF7664618A0(std::cout);
std::ostream::operator<<(v10, sub_7FF766461A60);
return -1;
}
}
大致只能分析出这些了。
看别人wp:
输入长度31的字符串---进行置换运算---取消修饰函数名---将未修饰函数名的商和余数与字符串比较
第一步:
根据 a/b=c......d 可以求出outputString
str1='55565653255552225565565555243466334653663544426565555525555222'
str2='(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&'
s='''1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;',27h,'ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,.'''
p=''
for i in range(62):
p+=chr(s.index(str2[i])+s.index(str1[i])*23)
print(p)
# private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
然后就是看那个UnDecorateSymbolName()函数
c, c++函数名编译符号修饰符说明_c函数 前缀修饰-CSDN博客
buu [2019红帽杯]childRE wr_2019红帽杯分数-CSDN博客
有点难懂。
根据 private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
那么 v5 = ?My_Aut0_PWN@R0Pxx@@AAE
也可以通过写一个函数来得到
#include <iostream>
class R0Pxx {
public:
R0Pxx() {
My_Aut0_PWN((unsigned char*)"hello");
}
private:
char* __thiscall My_Aut0_PWN(unsigned char*);
};
char* __thiscall R0Pxx::My_Aut0_PWN(unsigned char*) {
std::cout << __FUNCDNAME__ << std::endl;
return 0;
}
int main()
{
R0Pxx A;
system("PAUSE");
return 0;
}
第二步:置换运算
发现是result给name的赋值,故找result的值怎么来的
用result来解的:
a = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z"
b = 0x50, 0x51, 0x48, 0x52, 0x53, 0x49, 0x44, 0x54, 0x55, 0x4a, 0x56, 0x57, 0x4b, 0x45, 0x42, 0x58, 0x59, 0x4c, 0x5a, 0x5b, 0x4d, 0x46, 0x5c, 0x5d, 0x4e, 0x5e, 0x5f, 0x4f, 0x47, 0x43, 0x41
c = list(a)
for i in range(0,len(a)):
c[b[i]-0x41] = a[i]
a = ''.join(c)
print(a)
/*
我传入的v14=“abcdefghijklmnopqrstuvwxyz12345”,第一次的时候它给name[0]赋值成了p,对应我输入的字符串的下标是15,我调试了多次,发现每次都是这样,而且最后的name里存放的是打乱了顺序的输入的字符串,动调它给name赋值的过程,记录下对应的下标
最后得到的赋值顺序下标是[15, 16, 7, 17, 18, 8, 3, 19, 20, 9, 21, 22, 10, 4, 1, 23, 24, 11, 25, 26, 12, 5, 27, 28, 13, 29, 30, 14, 6, 2, 0]
直接说动态调试发现是固定的位置变换,直接把索引提取出来了
到这里程序逻辑就清楚了,首先输入长度为31的字符串,然后根据赋值下标的顺序打乱数据得到v2。
逆算一下输入的字符串
原文链接:https://blog.csdn.net/mcmuyanga/article/details/115575264
*/
这个动调的思路需要学学
import hashlib
ops = '?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'
result = [''] * 31
index = [15, 16, 7, 17, 18, 8, 3, 19, 20, 9, 21, 22, 10, 4, 1, 23, 24, 11, 25, 26, 12, 5, 27, 28, 13, 29, 30, 14, 6, 2, 0]
for i in range(len(ops)):
result[index[i]] += ops[i]
ss = ''.join(i for i in result)
print(ss)
print(hashlib.md5(ss.encode('utf-8')).hexdigest())
#63b148e750fed3a33419168ac58083f5
又细翻了一下别人wp:
_QWORD *__fastcall sub_7FF7F4FE1280(_BYTE *a1)
{
_QWORD *v2; // r12
_QWORD *v3; // rax
__int64 v4; // rdx
__int64 v5; // r8
__int64 v6; // rcx
char v7; // bl
__int64 v8; // rdi
__int64 v9; // rax
bool v10; // zf
__int64 v11; // r8
int v12; // esi
__int64 v13; // rdi
__int64 v14; // rdx
__int64 v15; // r8
__int64 v16; // rcx
char v17; // bl
__int64 v18; // r14
__int64 v19; // rcx
__int64 v20; // rdx
__int64 v21; // r8
__int64 v22; // rcx
char v23; // bl
__int64 v24; // r14
__int64 v25; // rcx
_QWORD *v27; // [rsp+20h] [rbp-40h]
_QWORD *v28; // [rsp+20h] [rbp-40h]
__int128 v29; // [rsp+28h] [rbp-38h] BYREF
__int128 v30; // [rsp+38h] [rbp-28h]
__int64 v31; // [rsp+48h] [rbp-18h]
v2 = operator new(0x18ui64);
*(_BYTE *)v2 = 0;
v2[1] = 0i64;
v2[2] = 0i64;
v29 = 0i64;
v30 = 0ui64;
v31 = 0i64;
v3 = operator new(0x10ui64);
v3[1] = 0i64;
*(_QWORD *)&v29 = v3;
*v3 = &v29;
v4 = *((_QWORD *)&v30 + 1);
v5 = v31;
v6 = v30;
if ( (((_BYTE)v31 + BYTE8(v30)) & 1) == 0 && (unsigned __int64)v30 <= (unsigned __int64)(v31 + 2) >> 1 )
{
sub_7FF7F4FE1AE0(&v29);
v5 = v31;
v4 = *((_QWORD *)&v30 + 1);
v6 = v30;
}
*((_QWORD *)&v30 + 1) = (2 * v6 - 1) & v4;
v7 = BYTE8(v30) + v5;
v8 = 8 * (((unsigned __int64)(*((_QWORD *)&v30 + 1) + v5) >> 1) & (v6 - 1));
v9 = *((_QWORD *)&v29 + 1);
if ( !*(_QWORD *)(v8 + *((_QWORD *)&v29 + 1)) )
{
*(_QWORD *)(v8 + *((_QWORD *)&v29 + 1)) = operator new(0x10ui64);
v9 = *((_QWORD *)&v29 + 1);
}
*(_QWORD *)(*(_QWORD *)(v8 + v9) + 8i64 * (v7 & 1)) = v2;
v10 = v31 == -1;
v11 = ++v31;
v12 = 1;
if ( !v10 )
{
do
{
v13 = *(_QWORD *)(*(_QWORD *)(*((_QWORD *)&v29 + 1) + 8 * ((*((_QWORD *)&v30 + 1) >> 1) & (v30 - 1)))
+ 8i64 * (BYTE8(v30) & 1));
v31 = v11 - 1;
if ( v11 == 1 )
*((_QWORD *)&v30 + 1) = 0i64;
else
++*((_QWORD *)&v30 + 1);
*(_BYTE *)v13 = *a1++;
if ( v12 >= 31 )
{
v11 = v31;
}
else
{
v27 = operator new(0x18ui64);
*(_BYTE *)v27 = 0;
v27[1] = 0i64;
v27[2] = 0i64;
*(_QWORD *)(v13 + 8) = v27;
v14 = *((_QWORD *)&v30 + 1);
v15 = v31;
v16 = v30;
if ( (((_BYTE)v31 + BYTE8(v30)) & 1) == 0 && (unsigned __int64)v30 <= (unsigned __int64)(v31 + 2) >> 1 )
{
sub_7FF7F4FE1AE0(&v29);
v15 = v31;
v14 = *((_QWORD *)&v30 + 1);
v16 = v30;
}
*((_QWORD *)&v30 + 1) = (2 * v16 - 1) & v14;
v17 = BYTE8(v30) + v15;
v18 = 8 * (((unsigned __int64)(*((_QWORD *)&v30 + 1) + v15) >> 1) & (v16 - 1));
v19 = *((_QWORD *)&v29 + 1);
if ( !*(_QWORD *)(v18 + *((_QWORD *)&v29 + 1)) )
{
*(_QWORD *)(v18 + *((_QWORD *)&v29 + 1)) = operator new(0x10ui64);
v19 = *((_QWORD *)&v29 + 1);
}
*(_QWORD *)(*(_QWORD *)(v18 + v19) + 8i64 * (v17 & 1)) = *(_QWORD *)(v13 + 8);
v11 = ++v31;
if ( ++v12 < 31 )
{
v28 = operator new(0x18ui64);
*(_BYTE *)v28 = 0;
v28[1] = 0i64;
v28[2] = 0i64;
*(_QWORD *)(v13 + 16) = v28;
v20 = *((_QWORD *)&v30 + 1);
v21 = v31;
v22 = v30;
if ( (((_BYTE)v31 + BYTE8(v30)) & 1) == 0 && (unsigned __int64)v30 <= (unsigned __int64)(v31 + 2) >> 1 )
{
sub_7FF7F4FE1AE0(&v29);
v21 = v31;
v20 = *((_QWORD *)&v30 + 1);
v22 = v30;
}
*((_QWORD *)&v30 + 1) = (2 * v22 - 1) & v20;
v23 = BYTE8(v30) + v21;
v24 = 8 * (((unsigned __int64)(*((_QWORD *)&v30 + 1) + v21) >> 1) & (v22 - 1));
v25 = *((_QWORD *)&v29 + 1);
if ( !*(_QWORD *)(v24 + *((_QWORD *)&v29 + 1)) )
{
*(_QWORD *)(v24 + *((_QWORD *)&v29 + 1)) = operator new(0x10ui64);
v25 = *((_QWORD *)&v29 + 1);
}
*(_QWORD *)(*(_QWORD *)(v24 + v25) + 8i64 * (v23 & 1)) = *(_QWORD *)(v13 + 16);
v11 = ++v31;
++v12;
}
}
}
while ( v11 );
}
sub_7FF7F4FE17D0(&v29);
return v2;
}
注意传入的是byte,操作也是 input+8 , input+16
__int128 input[2]; // [rsp+20h] [rbp-38h] BYREF 16*8=128
这是个创建二叉树的函数
动态调试会发现,开辟的地址处有三个数据,第一个是我们输入的字符,第二个是左孩子的地址,第三个是右孩子的地址。
可以识别出是二叉树结构体。
发现后面题都喜欢动调让猜,需要点脑洞,还有一些数据结构的