buu-Reverse-[2019红帽杯]childRE

目录

[2019红帽杯]childRE

修饰函数名和函数签名是什么?

对于变换部分的具体分析:


[2019红帽杯]childRE

下载附件,查壳,无壳

在IDA中打开,定位主函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 len; // 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 i; // ecx
  __int64 j; // r9
  __int128 input[2]; // [rsp+20h] [rbp-38h] BYREF

  memset(input, 0, sizeof(input));
  scanf("%s");
  len = -1i64;
  do
    ++len;
  while ( *(input + len) );
  if ( len != 31 )                              // 输入长度判断
  {
    while ( 1 )
      Sleep(1000u);
  }
  v4 = sub_7FF6EDF11280(input);
  v5 = name;                                    // 待转换的函数名
  if ( v4 )
  {
    sub_7FF6EDF115C0(v4[1]);                    // 变换
    sub_7FF6EDF115C0(*(v6 + 16));
    v7 = chr;
    v5[chr] = *v8;
    chr = v7 + 1;                               // i=i+1
  }
  UnDecorateSymbolName(v5, outputString, 0x100u, 0);// 将修饰后的函数名称转换为函数签名,0是转换符号,表示全转换
  v9 = -1i64;                                   // v5=name=修饰后的函数名称
  do
    ++v9;
  while ( outputString[v9] );
  if ( v9 == 62 )
  {
    i = 0;
    j = 0i64;
    do
    {
      if ( a1234567890Qwer[outputString[j] % 23] != a46200860044218[j] )// 确定outputString%23的余数
        _exit(i);
      if ( a1234567890Qwer[outputString[j] / 23] != a55565653255552[j] )// 确定outputString/23的商
        _exit(i * i);
      ++i;
      ++j;
    }
    while ( i < 62 );
    printf("flag{MD5(your input)}\n");
    return 0;
  }
  else
  {
    v10 = sub_7FF6EDF118A0(std::cout);
    std::ostream::operator<<(v10, sub_7FF6EDF11A60);
    return -1;
  }
}

这一部分是分配内存,输入字符串后再对字符串进行长度判断,要求长度为31

 

 这一部分是对输入字符串的变换,后面具体说明

这一部分中UnDecorateSymbolName()函数是将修饰后的函数名转换为函数签名,v5(也就是name)是修饰后的函数名称 ,outputString是转换成的函数签名,0x100u代表长度,0表示全转换

修饰函数名和函数签名是什么?

20世纪70年代以前,一个函数经过编译器编译后,存储在目标文件内的符号与函数名相同。比如定义一个函数func1,在目标函数中的符号名称也为func1。
但是随着汇编编写库的丰富,定义的函数很有可能与库中的函数名冲突。为了解决这一问题,编译器在将源代码编译成目标文件时,会将函数和变量的名字进行修饰,形成符号名,也就是符号修饰/名字修饰(Decorated Name)函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。目的是方便编译过程中,链接器等中间过程识别不同的函数,尤其是在引入重载后,识别函数不能只看函数名,要结合参数、返回值类型来识别。

函数签名包含了一个函数的信息,包括函数名、参数类型、所在类、名称空间及其他信息。
比如:
函数int func(int x)的函数签名为:int func(int)
每个函数签名对应一个修饰后的名字。

 具体怎么修饰:可以参考这几篇文章:

(156条消息) c, c++函数名编译符号修饰符说明_IT超人的博客-CSDN博客

(156条消息) 关于去除PE文件中函数修饰的做法_undecoratesymbolname_clever101的博客-CSDN博客

(156条消息) VS编译器C/C++函数编译后的名字修饰_android编译后函数名_大雄_RE的博客-CSDN博客先有个印象,知道这里有个修饰函数名转函数签名, 往后看,了解程序大致作用后再来了解这部分

这部分就是根据outputString的值做的判断了,第一个if语句可以推断出outputString%23的值,第二个if判断可以推出outputstring/23的值。两者联系可以推出整个outputString的值,上脚本

#include<string.h>
#include<stdlib.h>
int main(){
	char a[]="(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&";
	char b[]="55565653255552225565565555243466334653663544426565555525555222";
	char c[]="1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;ASDFGHJKL:ZXCVBNM<>?zxcvbnm,.";
	int data[100];int cnt,tent;
	int i,j;
	for(i=0;i<62;i++){
		for(j=0;j<100;j++){
			if(c[j]==a[i])
			cnt=j;
			if(c[j]==b[i])
			tent=j;
		}
	printf("%c",tent*23+cnt);	
	}
	
	return 0;
}

 解得:private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

不难看出是个函数签名(其实当时看了半天....)好了,现在我们知道了函数签名outputString字符串的值,结合程序的整体思路:将输入的字符串经过变换后,再传给UnDecorateSymbolName()函数做修饰变换,最后得到outputString;现在我们需要知道函数签名对应的修饰函数,v5即name的值。根据上面推荐的文章进行转换:

 name=?My_Aut0_PWN

 name=?My_Aut0_PWN@R0Pxx

  name=?My_Aut0_PWN@R0Pxx@@AAE

 函数的返回值是chaar*类型即PAD

函数的参数是unsigned char*类型即PAE

name=?My_Aut0_PWN@R0Pxx@@AAEPADPAE

name=?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z

现在,得到了修饰函数的值,再往前推,看这部分怎样进行加密的

动态调试

尽量输入有顺序的31位数,方便观察怎样变换的,F7进入函数查看具体变换,得到如下顺序

 对应下标变换就是

15,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

 逆向解出输入的值

#include<stdio.h>
#include<string.h>
int main(){
	char a[]="?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z";
	int b[31]={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};
	int i,j,cnt=0;
	char c[31];
for(i=0;i<strlen(a);i++){
		c[b[cnt]]=a[i];
		cnt++;
}
for(i=0;i<strlen(a);i++)
printf("%c",c[i]);
	return 0;
}

得到值:Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP  再进行MD5加密就可以得到flag啦

对于变换部分的具体分析:

这个部分应该是二叉树,

是进行了建树操作

 

这部分是后序遍历,即变换操作 ,这样得到最后的序列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值