title: BUUCTF-RE-0x05
date: 2021-06-06 21:14:44
tags:
BUUCTF刷题记录
期末了,都没办法在网上刷RE的题了。复习ing加油~~~~
[ACTF新生赛2020]rome
题目详情
查壳
没壳,先放入IDA32。然后在F5之后,直接进入字符串搜索,发现正确的地方,然后跳转过去。
int func()
{
int result; // eax
int v1[4]; // [esp+14h] [ebp-44h]
unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
unsigned __int8 v3; // [esp+25h] [ebp-33h]
unsigned __int8 v4; // [esp+26h] [ebp-32h]
unsigned __int8 v5; // [esp+27h] [ebp-31h]
unsigned __int8 v6; // [esp+28h] [ebp-30h]
int v7; // [esp+29h] [ebp-2Fh]
int v8; // [esp+2Dh] [ebp-2Bh]
int v9; // [esp+31h] [ebp-27h]
int v10; // [esp+35h] [ebp-23h]
unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF
strcpy(v12, "Qsw3sj_lz4_Ujw@l");
printf("Please input:");
scanf("%s", &v2);
result = v2;
if ( v2 == 'A' )
{
result = v3;
if ( v3 == 'C' )
{
result = v4;
if ( v4 == 'T' )
{
result = v5;
if ( v5 == 'F' )
{
result = v6;
if ( v6 == '{' )
{
result = v11;
if ( v11 == '}' )
{
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'Z' )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
return result;
++*(_DWORD *)&v12[17];
}
result = printf("You are correct!");
}
}
}
}
}
}
return result;
}
然后粗略读下来还是读懂了,然后就是把这个简单的算法逆向应该就可以了。
flag
v12 ="Qsw3sj_lz4_Ujw@l"
v12list=list(v12)
v12l =[]
for i in range(len(v12)):
v12l.append(ord(v12list[i]))
# v12l = [81,115,119,51,115,106,95,108,122,52,95,85,106,119,64,108]
flag = ''
for k in range(0,16):
for i in range(0,127):
z = i
if i > 64 and i <= 90:
i = (i-51)%26 + 65
if i > 96 and i <= 122:
i = (i-79)%26 + 97
if(i == v12l[k]):
flag += chr(z)
print("flag{"+flag+"}")
# flag{Cae3ar_th4_Gre@t}
flag{Cae3ar_th4_Gre@t}
[FlareOn4]login
题目
下载后解压后发现是个网页。
文本文档没有什么重要的信息。所以我们打开这个,用的是 sublimeText3 打开的,然后看见了这个简单的代码,所以就可以做出了。
login.html 源码
<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>
分析
代码的意思是,将输入方框里面的内容进行替换,然后判断能否等于所比较的内容。而大概的方法是通过13位的替换的方法,只替换字母。
然后在网上可以找到叫做 rot13 的回转13位密码置换的方法。
于是就这个思路写简单的算法。
str ="PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"
flag = ""
string="abcdefghijklmnopqrstuvwxyz"
string2=string.upper()
string+=string2
strlist = list(string)
for i in str:
if i in string:
temp = string.find(i)
if(temp%26-13<0):
temp+=13
else:
temp-=13
flag += strlist[temp]
else:
temp=ord(i)
flag+=i
print("flag{"+flag+"}")
# flag{ClientSideLoginsAreEasy@flare-on.com}
思路就是将每一个在另一个表中去寻找,然后如果求26的余减去13为负,说明原来的时候也是变过的,所以就不需要改变。
flag
flag:
flag{ClientSideLoginsAreEasy@flare-on.com}
小结
感觉这道题很有意思,很简单的一个算法。
像是一个凯撒加密位移了13的密码。
[GUET-CTF2019]re
描述
查壳
发现是upx的压缩壳,而且是elf文件,所以就用upx解压了。
下一步,拖入IDA进行分析一下。
进入主函数看看:
__int64 __fastcall sub_400E28(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // ecx
int v8; // er8
int v9; // er9
__int64 result; // rax
__int64 v11; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v12; // [rsp+28h] [rbp-8h]
v12 = __readfsqword(0x28u);
sub_40F950((unsigned int)"input your flag:", a2, a3, a4, a5, a6, 0LL, 0LL, 0LL, 0LL);
sub_40FA80((unsigned int)"%s", (unsigned int)&v11, v6, v7, v8, v9, v11);
if ( (unsigned int)sub_4009AE(&v11) )
sub_410350("Correct!");
else
sub_410350("Wrong!");
result = 0LL;
if ( __readfsqword(0x28u) != v12 )
sub_443550();
return result;
}
大致可以看出这个逆向挺简单,就是将输入的进行判断一下。
进入判断那个函数
(unsigned int)sub_4009AE(&v11)
_BOOL8 __fastcall sub_4009AE(char *a1)
{
if ( 1629056 * *a1 != 166163712 )
return 0LL;
if ( 6771600 * a1[1] != 731332800 )
return 0LL;
if ( 3682944 * a1[2] != 357245568 )
return 0LL;
if ( 10431000 * a1[3] != 1074393000 )
return 0LL;
if ( 3977328 * a1[4] != 489211344 )
return 0LL;
if ( 5138336 * a1[5] != 518971936 )
return 0LL;
if ( 7532250 * a1[7] != 406741500 )
return 0LL;
if ( 5551632 * a1[8] != 294236496 )
return 0LL;
if ( 3409728 * a1[9] != 177305856 )
return 0LL;
if ( 13013670 * a1[10] != 650683500 )
return 0LL;
if ( 6088797 * a1[11] != 298351053 )
return 0LL;
if ( 7884663 * a1[12] != 386348487 )
return 0LL;
if ( 8944053 * a1[13] != 438258597 )
return 0LL;
if ( 5198490 * a1[14] != 249527520 )
return 0LL;
if ( 4544518 * a1[15] != 445362764 )
return 0LL;
if ( 3645600 * a1[17] != 174988800 )
return 0LL;
if ( 10115280 * a1[16] != 981182160 )
return 0LL;
if ( 9667504 * a1[18] != 493042704 )
return 0LL;
if ( 5364450 * a1[19] != 257493600 )
return 0LL;
if ( 13464540 * a1[20] != 767478780 )
return 0LL;
if ( 5488432 * a1[21] != 312840624 )
return 0LL;
if ( 14479500 * a1[22] != 1404511500 )
return 0LL;
if ( 6451830 * a1[23] != 316139670 )
return 0LL;
if ( 6252576 * a1[24] != 619005024 )
return 0LL;
if ( 7763364 * a1[25] != 372641472 )
return 0LL;
if ( 7327320 * a1[26] != 373693320 )
return 0LL;
if ( 8741520 * a1[27] != 498266640 )
return 0LL;
if ( 8871876 * a1[28] != 452465676 )
return 0LL;
if ( 4086720 * a1[29] != 208422720 )
return 0LL;
if ( 9374400 * a1[30] == 515592000 )
return 5759124 * a1[31] == 719890500;
return 0LL;
}
这个将每一个数值进行算数后进行比较。所以将这些数值取出来,然后逆向就行了。
不过发现少了a1[6] 第七个不见了。
逆向出来的代码:
# [GUET-CTF2019]re
list1 =[ 166163712,731332800,357245568,1074393000,489211344,518971936,406741500,294236496,177305856,
650683500,298351053
,386348487,438258597,249527520,445362764,174988800,981182160,493042704,257493600, 767478780
,312840624, 1404511500 ,316139670,619005024, 372641472,373693320,498266640, 452465676,
208422720,515592000,719890500 ]
list2 =[ 1629056,6771600,3682944,10431000,3977328,5138336,7532250,5551632,3409728,13013670,6088797
,7884663,8944053,5198490,4544518,3645600,10115280,9667504,5364450,13464540,5488432,14479500,
6451830,6252576,7763364,7327320,8741520,8871876,4086720,9374400,5759124 ]
flag=""
list =[]
print(len(list1),len(list2))
for i in range(0,len(list1)):
# if i==6:
# temp='~'
# else:
temp=list1[i]/list2[i]
temp = int(temp)
list.append(temp)
flag+=chr(temp)
print(list)
print(flag)
# flag{e65421110b0a3099a1c039337}
所以flag就是
flag{e?65421110b0a3099a1c039337}
然后就是一个一个试
看了一圈发现自己错的竟然是16和17位的位置换了。。。
所以改下原来的表。
改后flag
# [GUET-CTF2019]re
list1 =[ 166163712,731332800,357245568,1074393000,489211344,518971936,406741500,294236496,177305856,
650683500,298351053 ,386348487,438258597,249527520,445362764,981182160,174988800,493042704,
257493600, 767478780 ,312840624, 1404511500 ,316139670,619005024, 372641472,373693320,
498266640, 452465676, 208422720,515592000,719890500 ]
list2 =[ 1629056,6771600,3682944,10431000,3977328,5138336,7532250,5551632,3409728,13013670,6088797
,7884663,8944053,5198490,4544518,10115280,3645600,9667504,5364450,13464540,5488432,14479500,
6451830,6252576,7763364,7327320,8741520,8871876,4086720,9374400,5759124 ]
flag=""
list =[]
print(len(list1),len(list2))
for i in range(0,len(list1)):
# if i==6:
# temp='~'
# else:
temp=list1[i]/list2[i]
temp = int(temp)
list.append(temp)
flag+=chr(temp)
print(list)
print(flag)
# flag{e165421110ba03099a1c039337} # flag
# flag{e 65421110b0a3099a1c039337}
# flag{e 65421110ba03099a1c039337} 改后
Android模拟器检测常用方法
江城的程序员大叔
分类专栏: 你好,Android 文章标签: android android模拟器 防作弊 虚拟机 模拟器检测
版权
在Android开发过程中,防作弊一直是老生常谈的问题,而模拟器的检测往往是防作弊中的重要一环,接下来有关于模拟器的检测方法,和大家进行一个简单的分享。
1.传统的检测方法。
传统的检测方法主要是对模拟器的IMSI、IDS、默认文件等几个方面进行检测。
(1)默认号码:
private static String[] known_numbers = {"15555215554", "15555215556",
"15555215558", "15555215560", "15555215562", "15555215564",
"15555215566", "15555215568", "15555215570", "15555215572",
"15555215574", "15555215576", "15555215578", "15555215580",
"15555215582", "15555215584"};
(2)默认ID:
private static String[] known_device_ids = {"000000000000000"};
(3)默认IMSI:
private static String[] known_imsi_ids = {"310260000000000"};
(4)默认文件路径:
private static String[] known_files = {
"/system/lib/libc_malloc_debug_qemu.so",
"/sys/qemu_trace",
"/system/bin/qemu-props"};
在得知了这些信息后,只需在运行时进行检测,如果检测结果和默认值吻合,那么检测设备便是模拟器。不过随着防反作弊技术的迭代,现在很多模拟器都可以改变这些值来逃避检测,所以上述传统方法在很多时候未曾达到开发者的预期效果。
2.基于模拟器cpu信息的检测。
成功率相较于传统方法,有了更高的成功率。
cpu信息检测主要是在cpu信息看看是否包含intel、amd等字段,很多模拟器目前对于cpu信息还无法进行模拟。
(1)读取cpu信息:
public static String readCpuInfo() {
String result = "";
try {
String[] args = {"/system/bin/cat", "/proc/cpuinfo"};
ProcessBuilder cmd = new ProcessBuilder(args);
Process process = cmd.start();
StringBuffer sb = new StringBuffer();
String readLine = "";
BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
while ((readLine = responseReader.readLine()) != null) {
sb.append(readLine);
}
responseReader.close();
result = sb.toString().toLowerCase();
} catch (IOException ex) {
}
return result;
}
(2)进行判定:
String cpuInfo = readCpuInfo();
if ((cpuInfo.contains("intel") || cpuInfo.contains("amd"))) {return true;}
类似的还有
String[] blockList = "google_sdk,sdk,sdk_x86,vbox86p".split(",");
原理相同。
3.关键路径检测特定模拟器检测
前面2个方法在很大程度上已经可以鉴定出很多模拟器了,但是对于某些在反防作弊上同样热爱的模拟器,需要特定的检测方法。
bluestacks成功躲避了前两种检测方法,所以在这里给予其VIP的待遇。
以下是总结出来的一些bluestacks的关键路径:
private static String[] known_bluestacks = {"/data/app/com.bluestacks.appmart-1.apk", "/data/app/com.bluestacks.BstCommandProcessor-1.apk",
"/data/app/com.bluestacks.help-1.apk", "/data/app/com.bluestacks.home-1.apk", "/data/app/com.bluestacks.s2p-1.apk",
"/data/app/com.bluestacks.searchapp-1.apk", "/data/bluestacks.prop", "/data/data/com.androVM.vmconfig",
"/data/data/com.bluestacks.accelerometerui", "/data/data/com.bluestacks.appfinder", "/data/data/com.bluestacks.appmart",
"/data/data/com.bluestacks.appsettings", "/data/data/com.bluestacks.BstCommandProcessor", "/data/data/com.bluestacks.bstfolder",
"/data/data/com.bluestacks.help", "/data/data/com.bluestacks.home", "/data/data/com.bluestacks.s2p", "/data/data/com.bluestacks.searchapp",
"/data/data/com.bluestacks.settings", "/data/data/com.bluestacks.setup", "/data/data/com.bluestacks.spotlight", "/mnt/prebundledapps/bluestacks.prop.orig"
};
检测方法:
public static boolean checkBlueStacksFiles() {
for (int i = 0; i < known_bluestacks.length; i++) {
String file_name = known_bluestacks[i];
File qemu_file = new File(file_name);
if (qemu_file.exists()) {
FkLog.e("Result : Find BlueStacks Files!");
return true;
}
}
FkLog.e("Result : Not Find BlueStacks Files!");
return false;
}
这种基于关键路径的检测,便可以成功的检测出bluestacks。
4.模拟器检测新思路
模拟器检测与模拟器反检测都在不断的更新迭代中,无法确保哪一种方法会永垂不朽,在这里分享下新的思路。
电池信息检测
可以从电池的温度和电量等信息入手,检测温度在使用过程中是否一直保持不变、或者是电量一直是固定值并且不是百分之百等等。
亲测可以鉴别出genymotion、bluestacks等主流模拟器。
5.写在最后
其实很多时候在检测模拟器的过程中,都不是只使用某一种固定的方法,一来需要具体问题具体分析,二来也需要用多种方法来综合检测。言而总之,有了十八般武艺才能见招拆招。
ps:如有错误或需要补充的地方,请各位多多指正~
————————————————
版权声明:本文为CSDN博主「江城的程序员大叔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_33150417/article/details/51320228
ISCC2021-Analysis
题目描述
然后下载后IDA去查看。
查壳
发现没有壳,真好。。哈哈哈哈
使用IDA32打开看看。
然后分析如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str[3]; // [esp+11h] [ebp-97h] BYREF
_BYTE v5[57]; // [esp+14h] [ebp-94h] BYREF
int v6; // [esp+4Dh] [ebp-5Bh]
char v7[64]; // [esp+51h] [ebp-57h] BYREF
char v8[7]; // [esp+91h] [ebp-17h] BYREF
int v9; // [esp+98h] [ebp-10h]
int i; // [esp+9Ch] [ebp-Ch]
__main();
v6 = 0;
memset(v5, 0, 4 * (((Str - v5 + 64) & 0xFFFFFFFC) >> 2));
Str[0] = 67;
Str[1] = 223;
Str[2] = 20;
v5[0] = 3;
v5[1] = 13;
v5[2] = 44;
v5[3] = 9;
v5[4] = 1;
v5[5] = 23;
v5[6] = 23;
v5[7] = 8;
v5[8] = 252;
v5[9] = 43;
v5[10] = 250;
v5[11] = 20;
v5[12] = 23;
v5[13] = 249;
v5[14] = 37;
v5[15] = 245;
v5[16] = 34;
v5[17] = 61;
v5[18] = 206;
v5[19] = 24;
v5[20] = 22;
v5[21] = 10;
qmemcpy(v8, "REVERSE", sizeof(v8)); // v8 = REVERSE
v9 = strlen(Str); // v9 = 25
printf(Format);
scanf("%s", v7);
mix(v7, v8, v9);
for ( i = 0; i < v9; ++i )
{
if ( v7[i] != Str[i] ) // v7[i] == Str[i]
{
puts(Buffer); // 错误
return 0;
}
}
puts(aFlag); // 正确
return 0;
}
mix(v7, v8, v9);
int __cdecl mix(char *v7, char *v8, int v9)
{
char v3; // dl
int result; // eax
char v5; // [esp+14h] [ebp-24h]
int n; // [esp+18h] [ebp-20h]
int m; // [esp+1Ch] [ebp-1Ch]
int l; // [esp+20h] [ebp-18h]
size_t k; // [esp+24h] [ebp-14h]
int j; // [esp+28h] [ebp-10h]
int i; // [esp+2Ch] [ebp-Ch]
// 目的:逆向出v7
for ( i = 0; i < v9; ++i ) // v8[7] = RESERVE
// v9 疑似25
v7[i] -= 64;
for ( j = 0; j < v9; ++j )
v7[j] -= v7[j + 1];
for ( k = 0; k < strlen(v8); ++k )
v8[k] %= 64; // 后面的v8都要改变
for ( l = 0; l < v9; ++l )
v7[l] += v8[l % 7];
for ( m = 0; v9 / 2 > m; ++m ) // 翻转
{
v5 = v7[m];
v7[m] = v7[v9 - m - 1];
v7[v9 - m - 1] = v5;
}
for ( n = 0; ; ++n )
{
result = n;
if ( n >= v9 )
break;
if ( (v8[n % 7] & 1) != 0 ) // v8[i] != 0 -> v7[i] +2
// v8[i] == 0 -> v7[i] +1
v3 = v7[n] + 2; // v7[i] +2
else
v3 = v7[n] + 1;
v7[n] = v3;
}
return result;
}
re
然后写出自己的逆向算法
v7A=[67,-33,20,3,13,44,9,1,23,23,8,-4,43,-6,20,23,-7,37,-11,34,61,-50,24,22,10]
# print(len(v7A))
# v7A = [67,223,20,3,13,44,9,1,23,23,8,252,43,250,20,23,249,37,245,34,61,206,24,22,10]
# v8 = "RESERVE"
# v8l = [R,E,S,E,R,V,E]
v8s = "REVERSE"
v8l = list(v8s)
print(v8l)
v8 =[]
print("v81=")
for i in range(len(v8l)):
v8.append(ord(v8l[i])%64);
print(ord(v8l[i]), end=',')
print("v8=",end='')
print(v8)
for i in range(0,25):
if ((v8[i % 7] & 1) != 0):
# v3 = v7A[i] - 2;
v7A[i] -= 2 ;
else:
# v3 = v7A[i] - 1;
v7A[i] -= 1;
print(v7A) ;
v7A.reverse()
v9 = len(v7A)
# for i in range(0,int(v9/2)):
# temp = v7A[i];
# v7A[i] = v7A[v9-i-1]
# v7A[v9-i-1] = temp ;
# print("v7A=",end='')
print(v7A)
for i in range(0,25):
v7A[i] -= v8[i % 7];
for i in range(23,-1,-1):
v7A[i] += v7A[i+1]
flag=""
print(v7A)
for i in range(len(v7A)):
v7A[i] += 64
flag+=chr(v7A[i])
print(v7A)
# [75, 85, 70, 67, 123, 82, 71, 88, 71, 85, 83, 69, 95, 75, 85, 97, 81, 79, 84, 95, 74, 67, 84, 71, 125]
# a="ISCC"
# 73,83,67,67,
print(flag)
# ISCC{REVERSE_IS_NOT_HARD}
Youngter-drive
文件
开始以为是按照题目的提示会有什么解题的方法,然后没有发现。
下载下来是一个EXE的 PE 文件。下载
查壳
传统的查壳步骤。
然后准备使用OD和x64手脱,但是发现好像有点问题。脱不了,因为缺少一个运行库。初步怀疑是文件下载后丢失的。所以就使用UPX脱壳机完成脱壳。
看见是32位的软件,所以就不动态调试了。直接IDA32然后打开。
分析
找了一会儿,然后找到主函数
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
void *v3; // ecx
HANDLE v5; // [esp+D0h] [ebp-14h]
HANDLE hObject; // [esp+DCh] [ebp-8h]
sub_4110FF(v3);
::hObject = CreateMutexW(0, 0, 0);
j_strcpy(Destination, Source);
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
v5 = CreateThread(0, 0, sub_41119F, 0, 0, 0);
CloseHandle(hObject);
CloseHandle(v5);
while ( dword_418008 != -1 )
;
sub_411190();
CloseHandle(::hObject);
return 0;
}
然后发现整个主要是在第一个函数和后面的两个函数来进行处理。
然后有两个线程来进行调用。
然后发现只有29个的样子。
网上的方法
flagpart = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
flagrange = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
flag = ''
for i in range(len(flagpart)):
if i % 2 == 0:
flag += flagpart[i]
else:
if flagpart[i].isupper():
flag += chr(flagrange.find(flagpart[i]) + 96)
else:
flag += chr(flagrange.find(flagpart[i]) + 38)
print("flag{"+flag+"}")
print(len(flag))
然后就继续看WP发现是随便一位都可以,然后看见是E
WP
总结
了解了多线程的实际运用了,然后对于多线程的解法有点懂了。