XYCTF RE WP 你真的是大学生吗
在一个运行于DOS环境下的汇编语言程序中,int 21h
的使用是非常频繁的,因为它提供了与操作系统进行交互的主要手段。每当程序需要进行输入输出操作、访问文件、控制屏幕显示、管理内存或是结束程序等操作时,通常都会调用 int 21h
来实现这些功能。
mov ah, 9
: 这里将9送入AH寄存器中,9是DOS中断调用功能号,对应于“输出$-terminated字符串到标准输出(通常是显示器)”的功能。
int 21h
: 最后,通过执行INT 21h指令,程序触发DOS中断,调用之前设置好的功能号9的服务。DOS会读取DX寄存器中的地址作为字符串的起始位置,然后将该地址指向的字符串输出到屏幕上。
其实除了静态分析也可以直接box走起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio. h> int main() { unsigned char data[20 ] = { 0x76 , 0x0E , 0x77 , 0x14 , 0x60 , 0x06 , 0x7D , 0x04 , 0x6B , 0x1E , 0x41 , 0x2A , 0x44 , 0x2B , 0x5C , 0x03 , 0x3B , 0x0B , 0x33 , 0x05 , } for(int i=0 printf("%c" ,data[i] ^ data[i+1 ]) } return 0 }
baby unity https://bbs.kanxue.com/thread-278275.htm
题目附件下载以后发现是il2cpp的脚本处理方式(和平时一般做的mono的不一样)
将几个文件脱upx壳后
用010 editor检查”\baby unity_Data\il2cpp_data\Metadata\global-metadata.dat”文件,发现文件头为AF 1B B1 FA无异常
通过https://github.com/Perfare/Il2CppDumper/下载工具
说明里面提到的命令行格式为:
Il2CppDumper.exe executable-file global-metadata output-directory
用了这个没整起也不知道原因是什么
直接使用下载好的工具里面的Il2CppDumper.exe打开,在下载的题目的路径里面依次选中GameAssembly.dll以及”\baby unity_Data\il2cpp_data\Metadata\global-metadata.dat”(其实本来按照教程应该是libil2cpp.so文件,即 apk的dll,但这个不是apk是exe)然后这个地方的GameAssembly相当于so文件
运行成功
然后就在il2cppdumper的下载目录下面出现Dummydll以及下图
这个是Dummy.dll下面的
使用dnspy 32打开Assembly-Csharp
然后可以看到加密函数名称
用IDA64打开GameAssembly.dll
使用script file 导入文件,依次选择ida py3和script.json
根据最开始上面贴的教程没有选择导入
然后分析界面的函数名称就清晰很多了,直接在函数窗口搜索对应的两个函数,checkflag和encrypt
分析逻辑如下
最后一步即为比较加密后的flag和已知字符串,双击获得该字符串
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import base64 v7_str = "XIcKYJU8Buh:UeV:BKN{U[JvUL??VuZ?CXJ;AX^{Ae]gA[]gUecb@K]ei^22" reversed_v7_bytes = bytes (v7_str, 'utf-8' ) reversed_v7_recovered = bytearray ()for i , byte in enumerate (reversed_v7_bytes): reversed_v7_recovered.append (byte ^ 0 xF) try: v5_decoded = base64.b64decode (reversed_v7_recovered) except Exception as e: print (f"Base64解码时发生错误: {e}" ) v5_decoded = Noneprint ("尝试还原的原始字节数组v5:" , v5_decoded)
XYCTF{389f6900-e12d-4c54-a85d-64a54af9f84c}
DebugMe 题目提示需要debugger,于是查看该apk附件的xml
发现没有debugger,所以在模拟器上使用mt管理器添加调试权限
步骤可参考《安卓逆向这档事》五、1000-7=?&动态调试&Log插桩 - 『移动安全区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
(随便找个地方加到application里面就行了,加中间省事)
形如这样
然后再保存以后再安装,然后可以尝试将修改后的apk导出,用jadx查看xml发现添加成功(有点多此一举,可以直接mt看)
android:debuggable=”true”
(上面这个不行,记下来告诫自己)
设置断点然后开始运行,附上进程即可获得flag
针对这个题,接着去看了下安卓的东西
linux或者安卓下的proc保留的是当前进程下面的进程号,文件句柄之类的
这里提一下proc下面的self,cmdline,environ(当前程序的环境变量,如果flag放在环境变量里面,可以读取这个去找flag),然后非预期也是和这个地方有关
DEX可执行文件,因为后续使用kotlin编写的最后与java是兼容的,所以后续的dex也可以使用jadx进行反编译
jeb可进行动态跟踪
这里复习一下apktool解包命令apktool d
重新打包apktool b
出题人说混淆器用的BlackObfuscator,depth设的高了点,好像有些版本的jadx直接报错了
混淆可以通过jeb去掉,可以去smali改debug的if判定出flag
调试层面也可以通过jadx,在adb路径处直接填写待调试exe的路径即可
ez_cube
建议改下变量名字
可以直接通过该魔方模型进行推算步骤,也可以进行爆破
题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 __int64 sub_7FF629032930 () {int i; char input_steps; int v3; sub_7FF629031384 ((__int64)&unk_7FF6290440A2);for ( i = 0 ; i < 9 ; ++i ) { surface1[i] = &Red; surface2[i] = "Blue" ; surface3[i] = "Green" ; surface4[i] = "Orange" ; surface5[i] = "Yellow" ; surface6[i] = "White" ; } surface2[1 ] = &Red; surface1[1 ] = "Green" ; surface3[1 ] = "Blue" ;while ( 1 ) {do input_steps = getchar ();while ( input_steps == 10 ); switch ( input_steps ) {case 'R' :sub_7FF629031375 ();break ;case 'U' :sub_7FF6290313BB ();break ;case 'r' :sub_7FF629031366 ();break ;case 'u' :sub_7FF62903115E ();break ; } ++steps; v3 = cmp ();if ( v3 == 1 )break ;if ( v3 == 2 )goto LABEL_19; }printf (aGreatYouAreAGo); LABEL_19:system ("pause" );return 0 i64; }
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 from itertools import * red = ['' ] * 9 blue = ['' ] * 9 green = ['' ] * 9 orange = ['' ] * 9 yellow = ['' ] * 9 white = ['' ] * 9 def init_cube(): for i in range(9 ): red[i] = "red" blue[i] = "Blue" green[i] = "Green" orange[i] = "Orange" yellow[i] = "Yellow" white[i] = "White" blue[1 ] = "red" red[1 ] = "Green" green[1 ] = "Blue" def MOVE_R (): v1 = red[2 ] v2 = red[5 ] v3 = red[8 ] red[2 ] = white[2 ] red[5 ] = white[5 ] red[8 ] = white[8 ] white[2 ] = orange[6 ] white[5 ] = orange[3 ] white[8 ] = orange[0 ] orange[0 ] = yellow[8 ] orange[3 ] = yellow[5 ] orange[6 ] = yellow[2 ] yellow[2 ] = v1 yellow[5 ] = v2 yellow[8 ] = v3 v4 = green[1 ] green[1 ] = green[3 ] green[3 ] = green[7 ] green[7 ] = green[5 ] green[5 ] = v4 v5 = green[0 ] green[0 ] = green[6 ] green[6 ] = green[8 ] green[8 ] = green[2 ] green[2 ] = v5 def MOVE_U (): v1 = red[0 ] v2 = red[1 ] v3 = red[2 ] red[0 ] = green[0 ] red[1 ] = green[1 ] red[2 ] = green[2 ] green[0 ] = orange[0 ] green[1 ] = orange[1 ] green[2 ] = orange[2 ] orange[0 ] = blue[0 ] orange[1 ] = blue[1 ] orange[2 ] = blue[2 ] blue[0 ] = v1 blue[1 ] = v2 blue[2 ] = v3 v4 = yellow[1 ] yellow[1 ] = yellow[3 ] yellow[3 ] = yellow[7 ] yellow[7 ] = yellow[5 ] yellow[5 ] = v4 v5 = yellow[0 ] yellow[0 ] = yellow[6 ] yellow[6 ] = yellow[8 ] yellow[8 ] = yellow[2 ] yellow[2 ] = v5 def MOVE_r (): v1 = red[2 ] v2 = red[5 ] v3 = red[8 ] red[2 ] = yellow[2 ] red[5 ] = yellow[5 ] red[8 ] = yellow[8 ] yellow[2 ] = orange[6 ] yellow[5 ] = orange[3 ] yellow[8 ] = orange[0 ] orange[0 ] = white[8 ] orange[3 ] = white[5 ] orange[6 ] = white[2 ] white[2 ] = v1 white[5 ] = v2 white[8 ] = v3 v4 = green[1 ] green[1 ] = green[5 ] green[5 ] = green[7 ] green[7 ] = green[3 ] green[3 ] = v4 v5 = green[0 ] green[0 ] = green[2 ] green[2 ] = green[8 ] green[8 ] = green[6 ] green[6 ] = v5 def MOVE_u (): v1 = red[0 ] v2 = red[1 ] v3 = red[2 ] red[0 ] = blue[0 ] red[1 ] = blue[1 ] red[2 ] = blue[2 ] blue[0 ] = orange[0 ] blue[1 ] = orange[1 ] blue[2 ] = orange[2 ] orange[0 ] = green[0 ] orange[1 ] = green[1 ] orange[2 ] = green[2 ] green[0 ] = v1 green[1 ] = v2 green[2 ] = v3 v4 = yellow[1 ] yellow[1 ] = yellow[5 ] yellow[5 ] = yellow[7 ] yellow[7 ] = yellow[3 ] yellow[3 ] = v4 v5 = yellow[0 ] yellow[0 ] = yellow[2 ] yellow[2 ] = yellow[8 ] yellow[8 ] = yellow[6 ] yellow[6 ] = v5 def Is_right (): Count = 0 for i in range(9 ): if red[i] "red" : Count += 1 if blue[i] "Blue" : Count += 1 if green[i] "Green" : Count += 1 if orange[i] "Orange" : Count += 1 if yellow[i] "Yellow" : Count += 1 if white[i] "White" : Count += 1 #print(Count ) if Count ! 54 : return False return True def main(flag): #print(flag) init_cube() for i in flag: if i "R" : MOVE_R () if i "U" : MOVE_U () if i 'r' : MOVE_r () if i 'u' : MOVE_u () if Is_right (): return flag def get_flag(): table = "RrUu" for string in product(table, repeat=12 ): flag = "" .join(string) ret = main(flag) if ret ! None : print(ret) return get_flag()
ez_rand 之前在某处学了个方法,
该文件使用IDA打开后分析逻辑不知道scanf到哪里去了,使用F5重新分析后获得输入逻辑
v9 v10地址连续
即可以获取最后进行对比的数据v9——29位的flag,包含v10
v4是种子,srand函数和rand函数根据种子来生成随机数,input加密逻辑使用v7 那么首先想到的是爆破种子,来推flag 再看v4,unsigned __int16 v4无符号16位整数型,v4范围为0~2**16-1(65535),结合flag固定格式 XYCTF{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 \#include <stdio.h> \#include <string.h> \#include <stdlib.h> \#include <time.h> int containsXYCTF (const char *str) { if (strstr (str, "XYCTF" ) != NULL ) { return 1 ; } else { return 0 ; } } int main () { unsigned char cipher[29 ] = {0x5D , 0x0C , 0x6C , 0xEA , 0x46 , 0x19 , 0xFC , 0x34 , 0xB2 , 0x62 , 0x23 , 0x07 , 0x62 , 0x22 , 0x6E , 0xFB , 0xB4 , 0xE8 , 0xF2 , 0xA9 , 0x91 , 0x12 , 0x21 , 0x86 , 0xDB , 0x8E , 0xE9 , 0x43 , 0x4D }; unsigned int v7; char flag[29 ] = {0 }; for (unsigned int seed = 0 ; seed < 65536 ; seed++) { srand (seed); for (int i = 0 ; i < 29 ; i++) { v7 = rand (); int num = (int )((unsigned __int64)(2155905153 * v7) >> 32 ); unsigned __int8 data = (unsigned __int8)(v7 + ((num & 0x80000000 ) != 0 ) + (num >> 7 )); flag[i] = cipher[i] ^ data; } if (containsXYCTF (flag)) { printf ("success\n" ); printf ("seed = %d\n" , seed); puts (flag); } } }
给阿姨倒一杯卡布奇诺 茶加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <stdio.h> #define uint32_t unsigned int void decrypt(uint32_t *v, uint32_t *key) { static uint32_t data1 = 0x5F797274 ; static uint32_t data2 = 0x64726168 ; int i; // [rsp+20 h] [rbp-10 h] uint32_t sum; // [rsp+24 h] [rbp-Ch ] uint32_t v1; // [rsp+28 h] [rbp-8 h] uint32_t v0; // [rsp+2 Ch ] [rbp-4 h] sum = 0x6E75316C * 32 ; uint32_t data1_tmp = v[0 ]; uint32_t data2_tmp = v[1 ]; v0 = v[0 ]; v1 = v[1 ]; for (i = 31 ; i >= 0 ; i--) { v1 -= ((v0 >> 5 ) + key[3 ]) ^ (v0 + sum) ^ (key[2 ] + 16 * v0) ^ (sum + i); v0 -= ((v1 >> 5 ) + key[1 ]) ^ (v1 + sum) ^ (key[0 ] + 16 * v1) ^ (sum + i); sum -= 0x6E75316C ; } v[0 ] = v0 ^ data1; v[1 ] = v1 ^ data2; data1 = data1_tmp; data2 = data2_tmp; } int main() { uint32_t key[4 ]; // [rsp+60 h] [rbp-40 h] BYREF uint32_t array[8 ]; // [rsp+70 h] [rbp-30 h] array[0 ] = 0x9B28ED45 ; array[1 ] = 0x145EC6E9 ; array[2 ] = 0x5B27A6C3 ; array[3 ] = 0xE59E75D5 ; array[4 ] = 0xE82C2500 ; array[5 ] = 0xA4211D92 ; array[6 ] = 0xCD8A4B62 ; array[7 ] = 0xA668F440 ; key[0 ] = 0x65766967 ; key[1 ] = 0x756F795F ; key[2 ] = 0x7075635F ; key[3 ] = 0x6165745F ; for (int i = 0 ; i <= 7 ; i += 2 ) { decrypt(array + i, key); } for(int i=0 ; i<32 ; i++) { printf("%c" , ((char*)array)[i]); } return 0 ; } // 133 bffe401d223a02385d90c5f1ca377
trustme 先看main activity发现下图,
代码:
1 public void onClick (View view) { ((TextView) findViewById (C0875R.C0878id.username)).getText ().toString (); if (bytesToHex(RC4(((TextView) findViewById (C0875R.C0878id.password)).getText ().toString ().getBytes (), "XYCTF".getBytes ())).equals ("5 a3c46e0228b444decc7651c8a7ca93ba4cb35a46f7eb589bef4")) { Toast.makeText (this, "成功!", 0 ); } }
经过分析代码逻辑即为使用Android平台的一个onClick点击事件处理函数,当关联到该函数的视图(如按钮)被点击时,此函数会被触发执行,通过findViewById()方法找到与资源ID(C0875R.C0878id.),查找并获取界面上的TextView组件,然后调用getText()方法获取该TextView中的文本内容(username和password),并进一步调用toString()方法将其转换为字符串形式。
发现下面的RC4为标准加密,
XYCTF”为密钥
解密获得用户名为admin
然后发现没有找到password
学习一下
“ProxyApplication”通常是指开发者为了在应用程序启动之初就进行一些全局初始化操作或者代理系统默认的Application类来实现特定功能而自定义的一个Application子类。这样的类会在整个应用程序生命周期内始终存在,且在任何Activity或其他组件启动前就已经初始化完成。“
注意这里面的前两句,是java反射调用
通过这个地方其实可以大差不差猜出来是把shell.apk放到了payload_dex”路径下面
在对应路径可以找到该apk文件
反编译该apk文件
发现执行了SQL查询,即判断应在数据库中存在password数据,当程序运行时输入password和username正确时会得到正确的flag
查看MoveSQLiteUtil类,发现创建了一个File
对象file
,指向预期的数据库文件路径,即上述目录下名为”mydb.db”的SQLite数据库文件(在安卓系统中,db
通常是 Database
(数据库)的缩写,具体指的是 SQLite 数据库文件)
最后使用mt管理器找到该文件,找到flag
.db文件为SQLite数据库文件的标准格式之一
附——
看官方wp发现:其实通过直接看XML可以发现给的入口点和真正的入口点是不一样的,可以因此去判断有壳的存在
android:name="com.swdd.trustme.ProxyApplication"
定义在<application>
标签内,表明ProxyApplication
是应用程序的主Application类。Application类是应用程序的入口点之一,负责初始化应用程序级别的组件、设置全局配置等。这是应用程序启动时第一个加载的类,可以视为一个高层的、全局的入口点。
android:name="com.swdd.tru5tme.MainActivity"
定义在<activity>
标签内,并且设置了android:exported="true"
以及包含了<intent-filter>
标签,这表明MainActivity
是对外公开的、可由系统启动器识别并启动的用户界面入口点。对于大多数应用程序来说,主Activity是用户直接交互的第一个界面,可以说是用户视角的入口点。
adb shell pm list packages -3 命令 adb shell pm list packages -3
是在Android设备上通过ADB(Android Debug Bridge)查询所有已安装的第三方应用程序列表的操作。这里的 -3
参数告诉包管理器(Package Manager,简称pm)只显示第三方应用,即非系统预装的应用。当你执行这条命令时,ADB会连接到你的Android设备(确保你的设备已经开启开发者模式并连接到了电脑),然后列出所有用户安装的应用程序包名。
adb shell dumpsys window | findstr mCurrentFocus 用来查找当前在Android设备上获得焦点的应用窗口的信息。
这个题有两个方法把数据提出来,一个是使用frida
1 frida-dexdump -U -f com .swdd.trustme
使用这个命令然后再用jadx查看提取出来的dex文件,即可以看到真正的MainActivity类
另一个则是因为题目中存在类似解压的操作,那么可以判断应该存在解包的文件,可以直接拖出来
先使用
1 adb shell pm list packages -3
获取所有包名
然后再
1 adb shell dumpsys window | findstr mCurrentFocus
可以获得真正的包名和入口点
得到
1 2 mCurrentFocus=Window{bebbaf6 u0 com.swdd .trustme/com.swdd .tru5tme .MainActivity}
所以可以去data目录里面去找
1 2 3 adb shellcd /data /data /com.swdd.trustme/app_payload_dexls
就可以在/data/data/com.swdd.trustme/app_payload_dex中找到脱壳出来的apk
最后使用adb pull出来
1 adb pull /data/ data/com.swdd.trustme/ app_payload_dex/ shell.apk "D:\t mp"
而解密操作一个似乎是涉及到web的方法,一个是常规的逆向,异或0xFF回去即可,而有个思路很出人意外
因为db数据库文件中一般填充了大量的0x00,而这个打开却发现大多是0xFF,经猜测异或0xFF回去,解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import os,sysf =open ( ./mydb.db ,"rb" ) s=f .read ()f .close ()p =b '' for x in s:p +=chr(x ^0 xff).encode()f =open ( ./mydb.db ,"wb" )f .write (p )f .close ()
strings一下字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 C:\Users\Administrator\Desktop>strings C:\Users\Administrator\Desktop\mydb.db Strings v2.54 - Search for ANSI and Unicode strings in binary images. Copyright (C) 1999 -2021 Mark Russinovich Sysinternals - www.sysinternals.com SQLite format 3 @ .O} Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name ,seq) tableUserUserCREATE TABLE User ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE ,password TEXT NOT NULL indexsqlite_autoindex_User_1User AflagXYCTF{And0r1d_15_V3ryEasy} adminqweradmin flagadmin User
何须相思煮余年 https://defuse.ca/online-x86-assembler.htm(这里放个在线转换汇编的网站)
打开发现是一个txt文本,把数据转换成二进制形式
(这里扔个队友的脚本)
1 2 3 4 5 6 7 8 9 10 hex_data = "0 x55 0 x8b 0 xec 0 x81 0 xec 0 xa8 0x0 0x0 0 x0 0 xa1 0x0 0x40 0x41 0x0 0 x33 0 xc5 0x89 0x45 0 xfc 0 x68 0 x9c 0x0 0x0 0 x0 0 x6a 0 x0 0 x8d 0x85 0x60 0 xff 0 xff 0 xff 0 x50 0 xe8 0 x7a 0 xc 0x0 0x0 0 x83 0 xc4 0 xc 0 xc7 0x85 0x58 0 xff 0 xff 0 xff 0x27 0x0 0x0 0x0 0 xc7 0 x85 0 x5c 0 xff 0 xff 0 xff 0x0 0x0 0x0 0x0 0 xeb 0 xf 0 x8b 0 x8d 0 x5c 0 xff 0 xff 0 xff 0 x83 0 xc1 0x1 0x89 0 x8d 0 x5c 0 xff 0 xff 0 xff 0 x83 0 xbd 0 x5c 0 xff 0 xff 0 xff 0 x27 0 xf 0 x8d 0 xed 0x0 0x0 0 x0 0 x8b 0 x95 0 x5c 0 xff 0 xff 0 xff 0 x81 0 xe2 0x3 0x0 0x0 0x80 0x79 0x5 0 x4a 0 x83 0 xca 0 xfc 0x42 0x85 0 xd2 0x75 0x25 0 x8b 0 x85 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x8c 0x85 0x60 0 xff 0 xff 0 xff 0 x3 0 x8d 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x95 0 x5c 0 xff 0 xff 0 xff 0 x89 0 x8c 0x95 0x60 0 xff 0 xff 0 xff 0 xe9 0 xac 0x0 0x0 0 x0 0 x8b 0 x85 0 x5c 0 xff 0 xff 0 xff 0x25 0x3 0x0 0x0 0x80 0x79 0x5 0x48 0 x83 0 xc8 0 xfc 0x40 0x83 0 xf8 0x1 0x75 0 x22 0 x8b 0 x8d 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x94 0 x8d 0 x60 0 xff 0 xff 0 xff 0 x2b 0 x95 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x85 0 x5c 0 xff 0 xff 0 xff 0x89 0x94 0x85 0x60 0 xff 0 xff 0 xff 0 xeb 0 x73 0 x8b 0 x8d 0 x5c 0 xff 0 xff 0 xff 0 x81 0 xe1 0x3 0x0 0x0 0x80 0x79 0x5 0x49 0x83 0 xc9 0 xfc 0x41 0x83 0 xf9 0x2 0x75 0 x23 0 x8b 0 x95 0 x5c 0 xff 0 xff 0 xff 0 x8b 0x84 0x95 0 x60 0 xff 0 xff 0 xff 0 xf 0 xaf 0 x85 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x8d 0 x5c 0 xff 0 xff 0 xff 0x89 0x84 0 x8d 0 x60 0 xff 0 xff 0 xff 0 xeb 0 x38 0 x8b 0 x95 0 x5c 0 xff 0 xff 0 xff 0 x81 0 xe2 0x3 0x0 0x0 0x80 0x79 0x5 0 x4a 0 x83 0 xca 0 xfc 0x42 0x83 0 xfa 0x3 0x75 0 x20 0 x8b 0 x85 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x8c 0x85 0x60 0 xff 0 xff 0 xff 0 x33 0 x8d 0 x5c 0 xff 0 xff 0 xff 0 x8b 0 x95 0 x5c 0 xff 0 xff 0 xff 0 x89 0 x8c 0x95 0x60 0 xff 0 xff 0 xff 0 xe9 0 xf7 0 xfe 0 xff 0 xff 0 x33 0 xc0 0 x8b 0 x4d 0 xfc 0 x33 0 xcd 0 xe8 0x4 0x0 0x0 0x0 0 x8b 0 xe5 0 x5d 0 xc3" \# 去除空格并将十六进制数据字符串分割成十六进制值的列表 hex_values = hex_data.split() \# 将每个十六进制值转换为相应的整数值 int_values = [int(value, 16 ) for value in hex_values] \# 将整数值列表转换为字节 binary_data = bytes(int_values) \# 将二进制数据写入文件 with open("output", "wb") as f: f.write(binary_data)
将生成的文件拖入IDA,生成汇编指令
把两个call全部nop掉后生成伪代码
逆回去就行
XYCTF{5b3e07567a9034d06851475481507a75}
这里看别人的wp好像还有个思路
贴个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from pwn import * context.arch = 'i386 ' code = b'\x 55 \x 8 b\xec\x 81 \xec\xa8 \x 00 \x 00 \x 00 \xa1 \x 00 \x 40 \x 41 \x 00 \x 33 \xc5 \x 89 \x 45 \xfc\x 68 \x 9 c \x 00 \x 00 \x 00 \x 6 a\x 00 \x 8 d\x 85 \x 60 \xff\xff\xff\x 50 \xe8 \x 7 a\x 0 c \x 00 \x 00 \x 83 \xc4 \x 0 c \xc7 \x 85 \x 58 \xff\xff\xff\x 27 \x 00 \x 00 \x 00 \xc7 \x 85 \x 5 c \xff\xff\xff\x 00 \x 00 \x 00 \x 00 \xeb\x 0 f\x 8 b\x 8 d\x 5 c \xff\xff\xff\x 83 \xc1 \x 01 \x 89 \x 8 d\x 5 c \xff\xff\xff\x 83 \xbd\x 5 c \xff\xff\xff\x 27 \x 0 f\x 8 d\xed\x 00 \x 00 \x 00 \x 8 b\x 95 \x 5 c \xff\xff\xff\x 81 \xe2 \x 03 \x 00 \x 00 \x 80 \x 79 \x 05 \x 4 a\x 83 \x ca\xfc\x 42 \x 85 \xd2 \x 75 \x 25 \x 8 b\x 85 \x 5 c \xff\xff\xff\x 8 b\x 8 c \x 85 \x 60 \xff\x ff\xff\x 03 \x 8 d\x 5 c \xff\xff\xff\x 8 b\x 95 \x 5 c \xff\xff\xff\x 89 \x 8 c \x 95 \x 60 \x ff\xff\xff\xe9 \xac\x 00 \x 00 \x 00 \x 8 b\x 85 \x 5 c \xff\xff\xff\x 25 \x 03 \x 00 \x 00 \x 80 \x 79 \x 05 \x 48 \x 83 \xc8 \xfc\x 40 \x 83 \xf8 \x 01 \x 75 \x 22 \x 8 b\x 8 d\x 5 c \xff\xff\x ff\x 8 b\x 94 \x 8 d\x 60 \xff\xff\xff\x 2 b\x 95 \x 5 c \xff\xff\xff\x 8 b\x 85 \x 5 c \xff\x ff\xff\x 89 \x 94 \x 85 \x 60 \xff\xff\xff\xeb\x 73 \x 8 b\x 8 d\x 5 c \xff\xff\xff\x 81 \x e1 \x 03 \x 00 \x 00 \x 80 \x 79 \x 05 \x 49 \x 83 \xc9 \xfc\x 41 \x 83 \xf9 \x 02 \x 75 \x 23 \x 8 b\x 95 \x 5 c \xff\xff\xff\x 8 b\x 84 \x 95 \x 60 \xff\xff\xff\x 0 f\xaf\x 85 \x 5 c \xff\xff\x ff\x 8 b\x 8 d\x 5 c \xff\xff\xff\x 89 \x 84 \x 8 d\x 60 \xff\xff\xff\xeb\x 38 \x 8 b\x 95 \x 5 c \xff\xff\xff\x 81 \xe2 \x 03 \x 00 \x 00 \x 80 \x 79 \x 05 \x 4 a\x 83 \xca\xfc\x 42 \x 83 \x fa\x 03 \x 75 \x 20 \x 8 b\x 85 \x 5 c \xff\xff\xff\x 8 b\x 8 c \x 85 \x 60 \xff\xff\xff\x 33 \x 8 d\x 5 c \xff\xff\xff\x 8 b\x 95 \x 5 c \xff\xff\xff\x 89 \x 8 c \x 95 \x 60 \xff\xff\xff\x e9 \xf7 \xfe\xff\xff\x 33 \xc0 \x 8 b\x 4 d\xfc\x 33 \xcd\xe8 \x 04 \x 00 \x 00 \x 00 \x 8 b\x e5 \x 5 d\xc3 ' assembly = disasm(code) print(assembly)
砸核桃 先找工具脱个壳
网上能找到的是能脱3.几的版本的
打开以后直接看逻辑写脚本即可
(注意字符类型)
今夕是何年 懒得配了,贴点官方的
1 2 3 main : ELF 64 -bit LSB executable, LoongArch, version 1 (GNU/Linux), statically linked,BuildID [sha1]=d5e0c55633e219eba65de1d09a45173c22668739, for GNU/Linux 5 .19 .0 ,with debug_info, not stripped
可以看到这是一个 LoongArch 架构的 ELF 二进制文件,所以我们需要一个 LoongArch 架构的 环境来运行这个文件。当然,你可以真的买一块 LoongArch 架构的 CPU(如龙芯 3A6000)来 运行这个程序,但是更方便的做法是使用 QEMU,一个可以仿真各种架构的虚拟机程序。 顺便一提,如果你真的选择买一块龙芯 CPU 来运行这个程序的话,你多半会遇到新世界和旧世界 的兼容性问题。这个文件是一个新世界程序,所以需要新世界的操作系统来运行,如果你使用旧 世界的操作系统(如此时此刻的 Loongnix 和 UOS),那么这个程序是无法运行的。 如果你想详细了解这方面,推荐阅读 https://areweloongyet.com/docs/old-and-new-worlds/的相关资料。 QEMU 分为用户态和系统态两种模式。因为 main 是一个静态编译的二进制文件(你可以使用 ldd 得知这一点),你只需要使用用户态就可以完成这道题了。首先,你需要一个 Linux 环境 (只要 x86-64 就可以了),你可以通过搜索引擎或文档来查询如何在你使用的 Linux 发行版上 配置用户态的 QEMU。对于 Kali Linux 来说,你需要安装 qemu-user 软件包,然后只需要给 main 文件加上可执行权限,再使用 ./main 运行就可以得到 flag 了。 当然,你也可以使用系统态的 QEMU 完成这道题,但是我没有试过。如果你有兴趣可以自行尝 试
kali指令如下:
1 2 3 4 5 apt install qemu-user chmod 777 ./* ./main
简爱 IDA打开以后发现没有办法进行调试,在kali上面运行发现
经过搜索发现 LSB relocatable为可重定位文件,经过分析发现是.o文件
在kali上面gcc -o 目标文件名 待执行文件
然后运行发现可以执行了(可调试)
然后发现其实上面两个加密函数并没有起任何作用(最后拿高版本的IDA开,发现最开始做的时候白分析了上面两个加密函数,错付了……)直接相当于howtolove对输入字符进行加密
查看函数
可以考虑先复刻一遍加密逻辑然后在加密的代码里面的时候打印出解密脚本
这个地方注意count1 和 count2 即为将循环结束后flag被++的次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 \ \ \ \ \ \ char box[MAX_BUFFER_SIZE]; char FileNamein[MAX_FILENAME_LENGTH]; char Filenameout[MAX_FILENAME_LENGTH]; int howtolove(char *flag) { int v2[1802]; int v3; int v4; int count1, count2; memset(v2, 0, sizeof(v2)); // Initialize elements of v2 array v2[32] = 2; v2[65] = 2; v2[66] = 4; v2[98] = 2; v2[99] = 5; v2[185] = 2; v2[186] = 2; v2[187] = 1; v2[188] = 1; v2[189] = 1; v2[190] = 1; v2[191] = 1; v2[192] = 1; v2[193] = 1; v2[194] = 1; v2[195] = 1; v2[196] = 1; v2[197] = 1; v2[198] = 1; v2[199] = 1; v2[200] = 1; v2[201] = 1; v2[202] = 1; v2[203] = 1; v2[204] = 1; v2[205] = 1; v2[206] = 1; v2[207] = 1; v2[208] = 1; v2[209] = 1; v2[210] = 1; v2[211] = 1; v2[212] = 1; v2[213] = 1; v2[214] = 1; v2[215] = 1; v2[216] = 1; v2[217] = 1; v2[218] = 1; v2[219] = 1; v2[220] = 1; v2[221] = 1; v2[222] = 1; v2[223] = 1; v2[224] = 1; v2[225] = 1; v2[226] = 1; v2[227] = 1; v2[228] = 1; v2[229] = 2; v2[232] = 2; v2[256] = 2; v2[257] = 5; v2[303] = 1; v2[304] = 1; v2[305] = 1; v2[306] = 1; v2[307] = 2; v2[308] = 5; v2[328] = 1; v2[329] = 1; v2[330] = 1; v2[331] = 1; v2[332] = 1; v2[333] = 1; v2[334] = 1; v2[335] = 1; v2[336] = 1; v2[337] = 1; v2[338] = 1; v2[339] = 1; v2[340] = 1; v2[341] = 1; v2[342] = 2; v2[353] = 2; v2[354] = 5; v2[430] = 2; v2[431] = 2; v2[432] = 5; v2[523] = 2; v2[524] = 5; v2[564] = 2; v2[565] = 5; v2[627] = 2; v2[628] = 1; v2[629] = 1; v2[630] = 1; v2[631] = 1; v2[632] = 1; v2[633] = 1; v2[634] = 1; v2[635] = 1; v2[636] = 1; v2[637] = 1; v2[638] = 1; v2[639] = 1; v2[640] = 1; v2[641] = 1; v2[642] = 1; v2[643] = 1; v2[644] = 1; v2[645] = 1; v2[646] = 1; v2[647] = 2; v2[648] = 4; v2[649] = 1; v2[650] = 1; v2[651] = 1; v2[652] = 1; v2[653] = 2; v2[680] = 2; v2[687] = 2; v2[688] = 4; v2[698] = 2; v2[766] = 2; v2[767] = 5; v2[818] = 2; v2[819] = 1; v2[820] = 2; v2[827] = 2; v2[828] = 5; v2[846] = 2; v2[847] = 5; v2[890] = 2; v2[891] = 1; v2[892] = 1; v2[893] = 1; v2[894] = 1; v2[895] = 1; v2[896] = 1; v2[897] = 1; v2[898] = 1; v2[899] = 1; v2[900] = 1; v2[901] = 1; v2[902] = 1; v2[903] = 1; v2[904] = 1; v2[905] = 1; v2[906] = 1; v2[907] = 1; v2[908] = 1; v2[909] = 1; v2[910] = 1; v2[911] = 1; v2[912] = 1; v2[913] = 1; v2[914] = 1; v2[915] = 1; v2[916] = 1; v2[917] = 1; v2[918] = 1; v2[919] = 1; v2[920] = 1; v2[921] = 1; v2[922] = 1; v2[923] = 1; v2[924] = 1; v2[925] = 1; v2[926] = 1; v2[927] = 1; v2[928] = 1; v2[929] = 1; v2[930] = 1; v2[931] = 1; v2[932] = 1; v2[933] = 2; v2[934] = 5; v2[989] = 2; v2[994] = 2; v2[995] = 1; v2[996] = 1; v2[997] = 1; v2[998] = 1; v2[999] = 1; v2[1000] = 1; v2[1001] = 1; v2[1002] = 1; v2[1003] = 1; v2[1013] = 1; v2[1014] = 1; v2[1015] = 1; v2[1016] = 1; v2[1017] = 1; v2[1018] = 1; v2[1019] = 1; v2[1020] = 1; v2[1021] = 1; v2[1022] = 1; v2[1023] = 1; v2[1024] = 1; v2[1025] = 1; v2[1026] = 1; v2[1027] = 2; v2[1028] = 3; v4 = 0; v3 = 0; while (1) { while (1) { count2 = 0; while (1) { count1 = 0; while (!v2[v3]) { v3++; ++flag[v4]; count1++; } if (count1 != 0) { printf("flag[%d] -= %d;\n" , v4, count1); } if (v2[v3] != 1) break; v3++; count2++; --flag[v4]; } if (count2 != 0) { printf("flag[%d] += %d;\n" , v4, count2); } if (v2[v3] != 2) break; ++v3; ++v4; } if ( v2[v3]==3) break; if ( v2[v3]==4) { flag[v4] = flag[v4] + flag[v4 + 1] - 70; printf("flag[%d] = flag[%d] + 70 - flag[%d];\n" ,v4,v4,v4+1); ++v3; } else if ( v2[v3]==5) { flag[v4] = flag[v4] - flag[v4 + 1] + 70; printf("flag[%d] = flag[%d] - 70 + flag[%d];\n" ,v4,v4,v4+1); ++v3; } } return 0; } int main() { char flag[33] = "flag{Love_is_not_one_sided_Love}" ; howtolove(flag); return 0; }
打印出结果
这个即为解密逻辑,调整一下代码顺序即可
注意一下这种
1 2 3 \#include int main (){ unsigned char flag [] = "flag {Love_is_not_one_sided_ Love }"; flag [28] -= 54; flag [29] -= 4; flag [30] -= 9; flag [30] += 23; flag [28] = flag [28] - 70 + flag [29]; flag [26] -= 42; flag [27] += 42; flag [26] = flag [26] - 70 + flag [27]; flag [25] -= 17; flag [25] = flag [25] - 70 + flag [26]; flag [22] -= 50; flag [23] += 1; flag [24] -= 6; flag [22] = flag [22] - 70 + flag [23]; flag [20] -= 9; flag [21] -= 67; flag [20] = flag [20] + 70 - flag [21]; flag [17] += 4; flag [18] -= 26; flag [19] -= 6; flag [17] = flag [17] + 70 - flag [18]; flag [15] -= 61; flag [16] += 19; flag [15] = flag [15] - 70 + flag [16]; flag [14] -= 39; flag [14] = flag [14] - 70 + flag [15]; flag [13] -= 90; flag [13] = (flag [13]+256)%256; flag [11] -= 75; flag [11] = flag [11] - 70 + flag [12]; flag [9] -= 19; flag [9] += 14; flag [10] -= 10; flag [9] = flag [9] - 70 + flag [10]; flag [8] -= 45; flag [8] += 4; flag [8] = flag [8] - 70 + flag [9]; flag [3] -= 85; flag [5] += 42; flag [6] -= 2; flag [7] -= 23; flag [3] = flag [3] - 70 + flag [4]; flag [2] -= 31; flag [2] = flag [2] + 70 - flag [3]; flag [1] -= 32; flag [0] -= 32; int i = 0; for (;i<32;i ) printf ("%c",flag [i]); return 0; }
ezmath
使用pyinstxtractor进行python的解包,反编译(py文件和题目放同一个路径哈)
可以打开IDA在字符串界面看看,含py字样
找到题目相同名字的pyc文件
sum([flag[23] for _ in range(flag[23])]):
这是一个列表推导式,它创建了一个新列表,其中包含了flag[23]
这个值重复flag[23]
次。也就是说,如果flag[23]
的值是n
,那么这个新列表就会有n
个n
。即为n*n
这一大堆东西整理出来应该是
1 (flag[i] *flag[i] )- (flag[i] *num)
配个方然后发现297412符合,可以直接flag[i]的值
这个题我们拿z3跑出来的
看到群里说Int型是跑不出来的,要用real
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 real型的 from z3 import * # 创建⼀个Z3 求解器 solver = Solver # 定义32 个32 位的变量 flag0 = Real flag1 = Real flag2 = Real flag3 = Real flag4 = Real flag5 = Real flag6 = Real flag7 = Real flag8 = Real flag9 = Real flag10 = Real flag11 = Real flag12 = Real flag13 = Real flag14 = Real flag15 = Real flag16 = Real flag17 = Real flag18 = Real flag19 = Real flag20 = Real flag21 = Real flag22 = Real flag23 = Real flag24 = Real flag25 = Real flag26 = Real flag27 = Real flag28 = Real flag29 = Real flag30 = Real flag31 = Real solver.add + + - + + + - - + - - - + + - + - + + - + - + - + - + + - + - - + - - + - - - + - - - + + - + - + - + + - - - + - + - + + + - + 297412 == 0 )# 检查是否存在解 if solver.check() == sat: # 获取解 model = solver.model() print(model) else: print("No solution")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 Int 型的 from z3.z3 import Int , Solver , sat flag = [Int (f"flag[{i}]" ) for i in range(32 )] solver = Solver () solver.add( flag[23 ] * (flag[23 ]) + flag[12 ] * (flag[12 ]) + flag[1 ] * (flag[1 ]) - flag[24 ] * 222 + flag[22 ] * (flag[22 ]) + flag[31 ] * (flag[31 ]) + flag[26 ] * (flag[26 ]) - flag[9 ] * 178 - flag[29 ] * 232 + flag[17 ] * (flag[17 ]) - flag[23 ] * 150 - flag[6 ] * 226 - flag[7 ] * 110 + flag[19 ] * (flag[19 ]) + flag[2 ] * (flag[2 ]) - flag[0 ] * 176 + flag[10 ] * (flag[10 ]) - flag[12 ] * 198 + flag[24 ] * (flag[24 ]) + flag[9 ] * (flag[9 ]) - flag[3 ] * 168 + flag[8 ] * (flag[8 ]) - flag[2 ] * 134 + flag[14 ] * (flag[14 ]) - flag[13 ] * 170 + flag[4 ] * (flag[4 ]) - flag[10 ] * 142 + flag[27 ] * (flag[27 ]) + flag[15 ] * (flag[15 ]) - flag[15 ] * 224 + flag[16 ] * (flag[16 ]) - flag[11 ] * 230 - flag[1 ] * 178 + flag[28 ] * (flag[28 ]) - flag[5 ] * 246 - flag[17 ] * 168 + flag[30 ] * (flag[30 ]) - flag[21 ] * 220 - flag[22 ] * 212 - flag[16 ] * 232 + flag[25 ] * (flag[25 ]) - flag[4 ] * 140 - flag[31 ] * 250 - flag[28 ] * 150 + flag[11 ] * (flag[11 ]) + flag[13 ] * (flag[13 ]) - flag[14 ] * 234 + flag[7 ] * (flag[7 ]) - flag[8 ] * 174 + flag[3 ] * (flag[3 ]) - flag[25 ] * 242 + flag[29 ] * (flag[29 ]) + flag[5 ] * (flag[5 ]) - flag[30 ] * 142 - flag[26 ] * 170 - flag[19 ] * 176 + flag[0 ] * (flag[0 ]) - flag[27 ] * 168 + flag[20 ] * (flag[20 ]) - flag[20 ] * 212 + flag[21 ] * (flag[21 ]) + flag[6 ] * (flag[6 ]) + 整理得到flag flag[18 ] * (flag[18 ]) - flag[18 ] * 178 +297412 == 0 ) if solver.check() == sat: model = solver.model() print(model) solution = [model.evaluate(flag[i] for i in range(32 ))] print("Solution found:" ) print(solution) for i in range(32 ): print(chr(int(str(model[flag[i]]))), end="" ) else: print("No solution found." )
最后得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 # flag[18] = 89, # flag[6] = 113, # flag[10] = 71, # flag[17] = 84, # flag[5] = 123, # flag[21] = 110, # flag[12] = 99, # flag[20] = 106, # flag[7] = 55, # flag[24] = 111, # flag[2] = 67, # flag[26] = 85, # flag[23] = 75, # flag[1] = 89, # flag[16] = 116, # flag[25] = 121, # flag[30] = 71, # flag[14] = 117, # flag[4] = 70, # flag[11] = 115, # flag[3] = 84, # flag[28] = 75, # flag[9] = 89, # flag[15] = 112, # flag[22] = 106, # flag[8] = 87, # flag[13] = 85, # flag[29] = 116, # flag[31] = 125, # flag[0] = 88, # flag[27] = 84, # flag[19] = 88
easy language https://www.52pojie.cn/thread-1414525-1-1.html IDA7.5支持中文函数命名的办法
这个地方能看到WelcometoXYCTF字符串(string窗口)这个图是IDA7.6开的
图中横线貌似是因为函数名称被过度优化掉了
然后我们试试IDA7.5(还是有点小不习惯)
直接导入插件后好像查看函数窗口没有很明显的效果(不知道为什么)
从字符串交叉引用出主要逻辑处
lpMem
通过调用编辑框::编辑框1_读属性_内容
方法获得一个字符,后续分别进行AES-ECB加密以及base64编码
后面逻辑其实可以直接通过看字符串窗口猜,试几次就出来了
也可以参考这个https://blog.csdn.net/qq_42557115/article/details/138267583