- 006-GUess-The-Number
- 042-76号-Obfuse-类型
- 43-APK-逆向
- 044-Newbie_calculations
- 045-Windows_Reverse2.exe
java环境配置反正就一个安装罢了…其余的真的没什么了
“这里的cmd *** 作”
输入 java -jar 路径 参数
jd-gui-1.6.6.jar
可以打开 .class文件
python解题脚本
a=0x04b64ca12ace755516c178f72d05d7061 b=0xecd44646cfe5994ebeb35bf922e25dba print(hex(a^b))
源码
import java.math.BigInteger; public class guess { static String XOR(String _str_one, String _str_two) {"这里是一个函数" BigInteger i1 = new BigInteger(_str_one, 16);"将字符串0x1234它转化为16机制0x1234" BigInteger i2 = new BigInteger(_str_two, 16); BigInteger res = i1.xor(i2); String result = res.toString(16); return result; } public static void main(String[] args) { int guess_number = 0; int my_num = 349763335; int my_number = 1545686892; int flag = 345736730; if (args.length > 0) { try { guess_number = Integer.parseInt(args[0]); if (my_number / 5 == guess_number) {"这里有一个判断(my_number / 5 == guess_number)我们主动去让他==.算出my_number / 5为多少" String str_one = "4b64ca12ace755516c178f72d05d7061"; String str_two = "ecd44646cfe5994ebeb35bf922e25dba"; "C语言我不会处理这么大的数,用python要简单些" my_num += flag; String answer = XOR(str_one, str_two);"将2者xor" System.out.println("your flag is: " + answer); } else { System.err.println("wrong guess!"); System.exit(1); } } catch (NumberFormatException e) { System.err.println("please enter an integer nexample: java -jar guess 12"); System.exit(1); } } else { System.err.println("wrong guess!"); int num = 1000000; num++; System.exit(1); } } }
知识
- BigInteger i2 = new BigInteger(_str_two, 16);
表示把一个1234 转化为 hex 0x1234 - 打开class文件的 *** 作
“这里的cmd *** 作”
输入 java -jar 路径 参数 - pythob可以很好的处理非常大的数据
- python语法,把数据转16进制
print(hex(a^b))
问题细节题 / 汇编代码
-
找不到函数入口点 [ 为什么输入函数在(start)的上面 ]
-
对那个flag_check函数不知道怎么逆向推flag_
就太多switch到底哪个是退出…
首先找到Getline函数在哪里
为什么找它?
为什么会有Getline?
Linux调试喊你输入Password,你总得找一下哪里输入吧
好比scanf
于是就去找吧
这个你到处窜上传下就知道这个位置与这个函数…我反正没看见,
主要是不知道为什么他会出现在start的上面
call _getline
这个嘛…
反正Ctrl + X 找不到位置
它在函数start的上面,woc
你发现它无法反汇编Tab
mov [esp], eax call _getline test eax, eax mov ebx, eax js short loc_804848F mov eax, [esp+1Ch] mov dword ptr [esp+4], 0 mov [esp], eax call sub_8048580 test eax, eax
于是你就输入,然后进入那个函数
call sub_8048580
你可以发现你可以Tab了
于是就进去呗
多的不讲…谈一下我们要注意的地方,也是要点吧
- 首先是这个switch到底讲的啥
A.
BUff_Input = input[i]; "你的Input是swith的那个case BUtton[(BUff_Input + 64) % 128] = 1; "你的input让特定位置的0变成了1
B.
注意一下返回值类型
_BOOL4 __cdecl sub_8048580(char *input, int i)
说明这个函数的返回值就是一个Button
C.
注意一下Switch说明时候return 到想要返回值
case 106: if ( i != 10 || !BUtton[42] ) return 0;
里面的大多数返回值都是0
说明0不能返回
同时那个 [ IF ]也通常不会满足
否者大家都return 0 了
然后就是这个case,返回的是 1
说明这里就是终点
case 107: return i == 12 && BUtton[43] != 0;
说明有13位flag
另外说明一下
他的每一个
case 119: if ( i != 6 || !BUtton[55] ) return 0; i = 7; break;
BUtton[55] 与 i != 6 都是对应的
因为这个呀
BUff_Input = input[i]; BUtton[(BUff_Input + 64) % 128] = 1;
D.
然后就是它是怎么推动你的检验的
好比
I=0,检验第一位
i=2,检验第3位
case 51: if ( i != 89 || !BUtton[115] ) return 0; i = 90;
你会发现,不满足条件就直接通过赋值跳转到下一位
这也是一个推动
另外就是
case 102: if ( !BUtton[38] || i != 8 && i != 4 ) return 0; goto LABEL_53; ... LABEL_53: ++i; continue;
这里跳转到LABEL后就直接++i,就continue,运行下一次循环了,就光一个自加的 *** 作
如何解题?
Way-!
直接在IDA里面看
0,1,2,3,4,5…12
ase 102: if ( !BUtton[38] || i != 8 && i != 4 ) return 0; goto LABEL_53;
有那些,吧input给手抄下来,可能比较快,因为就13位
里面有个
所以它到底是[4]还是[8]?
都是…4也是它,8也是它
于是就是手写
Way-2
代码暴力破解…还是比较麻烦的…太多case了
下面2个case的脚本看看
#include#include int main() { char BUtton[1024]; for(int i=0;i<13;i++) for(int in=0;in<128;in++) { BUtton[(in + 64) % 128] = 1; if(i==3) if(in=='d') *** if (( i != 3 || !BUtton[36] ) == 0){ printf("flag[%d]='%c'n",i,in); break; } if(i==7) if(in=='e') *** if(( i != 7 || !BUtton[37] )==0){ printf("flag[%d]='%c'n",i,in); break; } } system("pause"); }
结果
flag[3]=‘d’
flag[7]=‘e’
请按任意键继续. . .
我本来就只列了2个case
太麻烦了
Way-3
用Z3吧…个人人为还不如手抄…
但是以后还是要用的,因为不是all的都是13位
小结一下
函数入口点?只有汇编的情况下是要到处看汇编的,然不断F7与F8去看看,函数停在了哪里
后现在可疑点,在去Tab看看
多注意一下函数返回值的类型
还有参数的类型
char
int.
…unsigned int
Dword
一些函数真的靠眼力,去发现他的差异再去突破
43-APK-逆向用Die打开,说是C#.net
于是用dnSpy打开exe,而不是IDA
打开后
cmp="Super Secret Key" data=open('1.exe','r',encoding = 'unicode-escape').read() #新 *** 作之文件字节码编码,exe文件中是unicode编码 length=len(data) flag="CTF{" def search(x,data,length): for i in range(length): if x==data[i]: rusult=i * 1337 % 256; return '%02x' %rusult #新 *** 作之return直接返回print语句 for q in cmp: flag+=search(q,data,length) flag+="}" print(flag) #CTF{7eb67b0bb4427e0b43b40b6042670b55}044-Newbie_calculations
先看一下源码…头大
一位大哥说过
复杂代码本质应该是简洁的,这样才叫出题。
所以认知看吧…
先调试一下…哦豁…卡在以个地方,程序跑不动了
刚开始你会发现下面有很多乱七八糟的函数…函数太丑…你就自己定义一下
注意
函数太丑就自己定义一下
于是一个一个的定义…你会发现就3个函数,是不是很神奇,恰好应征了那一句话
复杂代码本质应该是简洁的,这样才叫出题
我把它定义为
fun1
fun2
fun3
然后你去仔细分析…
会发现这些细节
-
函数没有让你输入任何东西…却一直没有运行到退出…起了怪了…怀疑
-
数据越界
-
数据类型不对
函数明明返回的是指针,而它却用一个Int的类型区存储 -
垃圾代码—这个问题要分析一定的时间
为什么是垃圾代码…因为他与返回值没有一点关系…没有对有用的返回值区做任何的干涩
怎么发现的…IDA的代码高亮还是听yyds的…选中一个代码…然后发现它是被谁使用的
针对 数据越界 与 数据类型不对
要去代码修复…用Y键去修改它的类型
v120[12]–>flag[33]
上面那些 int 一个int * 后面就跟着自动变,神奇呀
然后去分析…发现每个flag[i]都初始化为1,然后去与fun1,2,3亲密接触
源码
int __cdecl main(int argc, const char **argv, const char **envp) { int *v3; // eax int *v4; // eax _DWORD *v5; // eax _DWORD *v6; // eax _DWORD *v7; // eax int *v8; // eax int *v9; // eax int *v10; // eax int *v11; // eax int *v12; // eax _DWORD *v13; // eax _DWORD *v14; // eax _DWORD *v15; // eax int *v16; // eax int *v17; // eax int *v18; // eax int *v19; // eax int *v20; // eax _DWORD *v21; // eax int *v22; // eax int *v23; // eax _DWORD *v24; // eax _DWORD *v25; // eax _DWORD *v26; // eax int *v27; // eax int *v28; // eax int *v29; // eax _DWORD *v30; // eax _DWORD *v31; // eax _DWORD *v32; // eax _DWORD *v33; // eax _DWORD *v34; // eax _DWORD *v35; // eax _DWORD *v36; // eax int *v37; // eax int *v38; // eax int *v39; // eax _DWORD *v40; // eax _DWORD *v41; // eax _DWORD *v42; // eax _DWORD *v43; // eax _DWORD *v44; // eax _DWORD *v45; // eax _DWORD *v46; // eax int *v47; // eax int *v48; // eax int *v49; // eax _DWORD *v50; // eax int *v51; // eax int *v52; // eax _DWORD *v53; // eax _DWORD *v54; // eax _DWORD *v55; // eax _DWORD *v56; // eax _DWORD *v57; // eax _DWORD *v58; // eax _DWORD *v59; // eax _DWORD *v60; // eax int *v61; // eax int *v62; // eax int *v63; // eax _DWORD *v64; // eax _DWORD *v65; // eax _DWORD *v66; // eax _DWORD *v67; // eax _DWORD *v68; // eax _DWORD *v69; // eax _DWORD *v70; // eax _DWORD *v71; // eax _DWORD *v72; // eax _DWORD *v73; // eax _DWORD *v74; // eax _DWORD *v75; // eax _DWORD *v76; // eax _DWORD *v77; // eax _DWORD *v78; // eax int *v79; // eax int *v80; // eax int *v81; // eax _DWORD *v82; // eax int *v83; // eax int *v84; // eax _DWORD *v85; // eax int *v86; // eax int *v87; // eax _DWORD *v88; // eax _DWORD *v89; // eax _DWORD *v90; // eax _DWORD *v91; // eax _DWORD *v92; // eax _DWORD *v93; // eax int *v94; // eax int *v95; // eax int *v96; // eax _DWORD *v97; // eax _DWORD *v98; // eax _DWORD *v99; // eax _DWORD *v100; // eax _DWORD *v101; // eax _DWORD *v102; // eax _DWORD *v103; // eax _DWORD *v104; // eax _DWORD *v105; // eax _DWORD *v106; // eax _DWORD *v107; // eax _DWORD *v108; // eax _DWORD *v109; // eax _DWORD *v110; // eax int *v111; // eax int *v112; // eax int *v113; // eax int v115; // [esp-8h] [ebp-9Ch] int v116; // [esp-4h] [ebp-98h] int v117; // [esp-4h] [ebp-98h] int i; // [esp+4h] [ebp-90h] int j; // [esp+8h] [ebp-8Ch] int flag[32]; // [esp+Ch] [ebp-88h] BYREF int v121; // [esp+8Ch] [ebp-8h] for ( i = 0; i < 32; ++i ) flag[i] = 1; v121 = 0; puts("Your flag is:"); v3 = fun1(flag, 1000000000); // woc...它返回的是指针啊...有对其进行了操作 v4 = fun2(v3, 999999950); fun1(v4, 2); v5 = fun3(&flag[1], 5000000); v6 = fun2(v5, 6666666); v7 = fun3(v6, 1666666); v8 = fun3(v7, 45); v9 = fun1(v8, 2); fun3(v9, 5); v10 = fun1(&flag[2], 1000000000); v11 = fun2(v10, 999999950); v12 = fun1(v11, 2); fun3(v12, 2); v13 = fun3(&flag[3], 55); v14 = fun2(v13, 3); v15 = fun3(v14, 4); fun2(v15, 1); v16 = fun1(&flag[4], 100000000); v17 = fun2(v16, 99999950); v18 = fun1(v17, 2); fun3(v18, 2); v19 = fun2(&flag[5], 1); v20 = fun1(v19, 1000000000); v21 = fun3(v20, 55); fun2(v21, 3); v22 = fun1(&flag[6], 1000000); v23 = fun2(v22, 999975); fun1(v23, 4); v24 = fun3(&flag[7], 55); v25 = fun2(v24, 33); v26 = fun3(v25, 44); fun2(v26, 11); v27 = fun1(&flag[8], 10); v28 = fun2(v27, 5); v29 = fun1(v28, 8); fun3(v29, 9); v30 = fun3(&flag[9], 0); v31 = fun2(v30, 0); v32 = fun3(v31, 11); v33 = fun2(v32, 11); fun3(v33, 53); v34 = fun3(&flag[10], 49); v35 = fun2(v34, 2); v36 = fun3(v35, 4); fun2(v36, 2); v37 = fun1(&flag[11], 1000000); v38 = fun2(v37, 999999); v39 = fun1(v38, 4); fun3(v39, 50); v40 = fun3(&flag[12], 1); v41 = fun3(v40, 1); v42 = fun3(v41, 1); v43 = fun3(v42, 1); v44 = fun3(v43, 1); v45 = fun3(v44, 1); v46 = fun3(v45, 10); fun3(v46, 32); v47 = fun1(&flag[13], 10); v48 = fun2(v47, 5); v49 = fun1(v48, 8); v50 = fun3(v49, 9); fun3(v50, 48); v51 = fun2(&flag[14], 1); v52 = fun1(v51, -294967296); v53 = fun3(v52, 55); fun2(v53, 3); v54 = fun3(&flag[15], 1); v55 = fun3(v54, 2); v56 = fun3(v55, 3); v57 = fun3(v56, 4); v58 = fun3(v57, 5); v59 = fun3(v58, 6); v60 = fun3(v59, 7); fun3(v60, 20); v61 = fun1(&flag[16], 10); v62 = fun2(v61, 5); v63 = fun1(v62, 8); v64 = fun3(v63, 9); fun3(v64, 48); v65 = fun3(&flag[17], 7); v66 = fun3(v65, 6); v67 = fun3(v66, 5); v68 = fun3(v67, 4); v69 = fun3(v68, 3); v70 = fun3(v69, 2); v71 = fun3(v70, 1); fun3(v71, 20); v72 = fun3(&flag[18], 7); v73 = fun3(v72, 2); v74 = fun3(v73, 4); v75 = fun3(v74, 3); v76 = fun3(v75, 6); v77 = fun3(v76, 5); v78 = fun3(v77, 1); fun3(v78, 20); v79 = fun1(&flag[19], 1000000); v80 = fun2(v79, 999999); v81 = fun1(v80, 4); v82 = fun3(v81, 50); fun2(v82, 1); v83 = fun2(&flag[20], 1); v84 = fun1(v83, -294967296); v85 = fun3(v84, 49); fun2(v85, 1); v86 = fun2(&flag[21], 1); v87 = fun1(v86, 1000000000); v88 = fun3(v87, 54); v89 = fun2(v88, 1); v90 = fun3(v89, 1000000000); fun2(v90, 1000000000); v91 = fun3(&flag[22], 49); v92 = fun2(v91, 1); v93 = fun3(v92, 2); fun2(v93, 1); v94 = fun1(&flag[23], 10); v95 = fun2(v94, 5); v96 = fun1(v95, 8); v97 = fun3(v96, 9); fun3(v97, 48); v98 = fun3(&flag[24], 1); v99 = fun3(v98, 3); v100 = fun3(v99, 3); v101 = fun3(v100, 3); v102 = fun3(v101, 6); v103 = fun3(v102, 6); v104 = fun3(v103, 6); fun3(v104, 20); v105 = fun3(&flag[25], 55); v106 = fun2(v105, 33); v107 = fun3(v106, 44); v108 = fun2(v107, 11); fun3(v108, 42); fun3(&flag[26], flag[25]); fun3(&flag[27], flag[12]); v115 = flag[27]; v109 = fun2(&flag[28], 1); v110 = fun3(v109, v115); fun2(v110, 1); v116 = flag[23]; v111 = fun2(&flag[29], 1); v112 = fun1(v111, 1000000); fun3(v112, v116); v117 = flag[27]; v113 = fun3(&flag[30], 1); fun1(v113, v117); fun3(&flag[31], flag[30]); sub_661C7F("CTF{"); for ( j = 0; j < 32; ++j ) sub_661C7F("%c", SLOBYTE(flag[j])); sub_661C7F("}n"); return 0; }
我分析时犯下的错误…
不注意函数的返回值类型…直接认为它是没用的…返回就返回吧…反正对flag无影响…
v83 = fun2(&flag[20], 1); v84 = fun1(v83, -294967296); v85 = fun3(v84, 49); fun2(v85, 1);
然后就错的一塌糊涂…当返回值是指针时…又会对flag *** 作
这样的错误花费了我很多的时间…去无效分析代码
另外一个错误就是数据的类型转化出现问题2
比如f
x时int*
fun&(x,y)
但是fun( DWORD* x,int y)
编译器会报错…因为类型无法转化
然后说一下怎么对fun1,fun2,fun3做处理…
先来个软柿子捏一下
关键的函数
fun3
我还是重新命名一下那些变量…那些变量太丑了,干扰分析
_DWORD *__cdecl fun3(_DWORD *flag, int Mg) { int Mg_; // [esp+Ch] [ebp-18h] int dec; // [esp+10h] [ebp-14h] int Mg_1231; // [esp+18h] [ebp-Ch] int _1231; // [esp+1Ch] [ebp-8h] dec = -1; Mg_ = -1 - Mg + 1; _1231 = 1231; Mg_1231 = Mg + 1231; while ( Mg_ ) { ++_1231; --*flag; --Mg_; --Mg_1231; } while ( dec ) { --Mg_1231; ++*flag; --dec; } ++*flag; return flag; }
然后怎么去知道它的用途…干看我也是啥也不知道的
…就算我去分析不去调试我也看不出来
但是
我们可以先把它拷贝到以一个程序…你自己写一个程序来跑一下它…看看它到底是干嘛的…
发现程序运行的巨慢无比,
很久才出结果…
.你去打印一下中间的数据…发现程序一直在运算…难怪程序老跑不出来…原来还在算…
为什么算这么久?这个跟数据类型有关了
fun1你会发现它有2个循环while
然后去分析一下…垃圾代码有哪些…直接在自己的程序里面去除…他们太消耗程序运算时间了…虽然去掉一个程序还是很慢
去除后
_DWORD *__cdecl fun3(_DWORD flag, int Mg) { char Mg_; // [esp+Ch] [ebp-18h] char dec; // [esp+10h] [ebp-14h] char mg_1231; // [esp+18h] [ebp-Ch] //char _1231; // [esp+1Ch] [ebp-8h] //dec = -1; Mg_ = - Mg ; //"Mg从-的Mg开始--" while ( Mg_ )//"让flag=flag+Mg" { --flag; --Mg_; } " dec从-1开始--" 一下是垃圾代码 while ( dec ) "flag=flag-dec" { ++*flag; --dec; } "flag一直为正数,是不是很奇怪" ++*flag; return flag;*/ printf("%d",flag); }
这么分析呢?垃圾代码的判断你还是懂的…
然后你就魔改一下程序…把所有的int变化为char,可能还涉及unsigned char…见机行事
然后输入数据…因为范围变小了,所以参数要跑的快些
你去多试几组数据
你会发现函数的返回值 fun3(falg,x)
flag=flag+x;
也就是说fun3是一个加法函数…为什么是加法?我看P告诉我的…我之前只知道结果会加1,因为参数一定有一个1
实践告诉的,背后的原理.我也不太知道…我用6进制模拟来了一下
usigned6进制 -3 -2 -1 0 1 2 3 -3 -2 -1 - 1 0 1 2 -3其实他一直在转圈
unsigned 6进制 0 1 2 3 4 5 0 1 2 3 4 5 01 2 3 4 5 其实他也一直在转圈
于是哦你就可以通过小小的举例来验证的猜想是否正确/…
于是这个函数就是一个加法函数
fun2
也是软柿子…
源码
``c
DWORD *cdecl fun2(DWORD *flag, int Mg)
{
int Mg; // [esp+8h] [ebp-10h]
int dec; // [esp+Ch] [ebp-Ch]
int dec; // [esp+14h] [ebp-4h]
int Mul; // [esp+14h] [ebp-4h]
dec = -1;
Mg = -1 - Mg + 1;
dec = -1;
while ( Mg_ )
{
++*flag;
–Mg_;
–dec;
}
Mul = dec * dec;
while ( dec_ )
{
Mul *= 123;
++*flag;
–dec_;
}
++*flag;
return flag;
}
修改后...注意在程序上运行一下 发现它是干什么的 ```c _DWORD *__cdecl fun2(_DWORD *flag, int Mg) { int Mg_; // [esp+8h] [ebp-10h] int dec_; // [esp+Ch] [ebp-Ch] int dec; // [esp+14h] [ebp-4h] int Mul; // [esp+14h] [ebp-4h] // dec_ = -1; Mg_ = -1 - Mg + 1; dec = -1; while ( Mg_ ) { ++*flag; --Mg_; //--dec; } //Mul = dec * dec; while ( dec_ ) { // Mul *= 123; ++*flag; --dec_; } ++*flag; return flag; }
fun1
原来
int *__cdecl fun1(int *flag, int Mg) { int dec; // [esp+Ch] [ebp-1Ch] int flag_1; // [esp+14h] [ebp-14h] int Mg_; // [esp+18h] [ebp-10h] int Mul_2; // [esp+18h] [ebp-10h] int Mul; // [esp+1Ch] [ebp-Ch] int r_fun3; // [esp+20h] [ebp-8h] BYREF flag_1 = *flag; Mg_ = Mg; dec = -1; r_fun3 = 0; Mul = Mg * flag_1; while ( Mg ) { Mul_2 = Mul * flag_1; fun3(&r_fun3, *flag); ++Mul; --Mg; Mg_ = Mul_2 - 1; } while ( dec ) { ++Mul; ++*flag; --dec; --Mg_; } ++*flag; *flag = r_fun3; return flag; }
修改
int *__cdecl fun1(int *flag, int Mg) { int dec; // [esp+Ch] [ebp-1Ch] int flag_; // [esp+14h] [ebp-14h] int Mg_; // [esp+18h] [ebp-10h] int Mul_2; // [esp+18h] [ebp-10h] int Mul; // [esp+1Ch] [ebp-Ch] int result_fun3; // [esp+20h] [ebp-8h] BYREF flag_ = flag; Mg_ = Mg; dec = -1; result_fun3 = 0; Mul = Mg * flag_; while ( Mg ) { fun3(&result_fun3, flag); --Mg; } flag = result_fun3; return flag; }
WP
你自己一个一个的去手算?算死你
写WP遇到的麻烦
IDA对DWORD的翻译是 _DWORD,到时再 IDE里面无法使用
DWORD是#inclde
还有就是数据的类型一致
DWORD* fun1(DWORD*,int);
对应
v19 = (DWORD *)fun2(&v120[5], 1);
虽然我们
#include#include #include #include DWORD* fun1(DWORD*,int); DWORD* fun2(DWORD*,int); DWORD* fun3(DWORD*,int); int main() { DWORD *v3; // eax DWORD *v4; // eax DWORD *v5; // eax DWORD *v6; // eax DWORD *v7; // eax DWORD *v8; // eax DWORD *v9; // eax DWORD *v10; // eax DWORD *v11; // eax DWORD *v12; // eax DWORD *v13; // eax DWORD *v14; // eax DWORD *v15; // eax DWORD *v16; // eax DWORD *v17; // eax DWORD *v18; // eax DWORD *v19; // eax DWORD *v20; // eax DWORD *v21; // eax DWORD *v22; // eax DWORD *v23; // eax DWORD *v24; // eax DWORD *v25; // eax DWORD *v26; // eax DWORD *v27; // eax DWORD *v28; // eax DWORD *v29; // eax DWORD *v30; // eax DWORD *v31; // eax DWORD *v32; // eax DWORD *v33; // eax DWORD *v34; // eax DWORD *v35; // eax DWORD *v36; // eax DWORD *v37; // eax DWORD *v38; // eax DWORD *v39; // eax DWORD *v40; // eax DWORD *v41; // eax DWORD *v42; // eax DWORD *v43; // eax DWORD *v44; // eax DWORD *v45; // eax DWORD *v46; // eax DWORD *v47; // eax DWORD *v48; // eax DWORD *v49; // eax DWORD *v50; // eax DWORD *v51; // eax DWORD *v52; // eax DWORD *v53; // eax DWORD *v54; // eax DWORD *v55; // eax DWORD *v56; // eax DWORD *v57; // eax DWORD *v58; // eax DWORD *v59; // eax DWORD *v60; // eax DWORD *v61; // eax DWORD *v62; // eax DWORD *v63; // eax DWORD *v64; // eax DWORD *v65; // eax DWORD *v66; // eax DWORD *v67; // eax DWORD *v68; // eax DWORD *v69; // eax DWORD *v70; // eax DWORD *v71; // eax DWORD *v72; // eax DWORD *v73; // eax DWORD *v74; // eax DWORD *v75; // eax DWORD *v76; // eax DWORD *v77; // eax DWORD *v78; // eax DWORD *v79; // eax DWORD *v80; // eax DWORD *v81; // eax DWORD *v82; // eax DWORD *v83; // eax DWORD *v84; // eax DWORD *v85; // eax DWORD *v86; // eax DWORD *v87; // eax DWORD *v88; // eax DWORD *v89; // eax DWORD *v90; // eax DWORD *v91; // eax DWORD *v92; // eax DWORD *v93; // eax DWORD *v94; // eax DWORD *v95; // eax DWORD *v96; // eax DWORD *v97; // eax DWORD *v98; // eax DWORD *v99; // eax DWORD *v100; // eax DWORD *v101; // eax DWORD *v102; // eax DWORD *v103; // eax DWORD *v104; // eax DWORD *v105; // eax DWORD *v106; // eax DWORD *v107; // eax DWORD *v108; // eax DWORD *v109; // eax DWORD *v110; // eax DWORD *v111; // eax DWORD *v112; // eax DWORD *v113; // eax int v115; // [esp-8h] [ebp-9Ch] int v116; // [esp-4h] [ebp-98h] int v117; // [esp-4h] [ebp-98h] int i; // [esp+4h] [ebp-90h] int j; // [esp+8h] [ebp-8Ch] DWORD v120[33]; // [esp+Ch] [ebp-88h] BYREF for ( i = 0; i < 32; ++i ) v120[i] = 1; v120[32] = 0; puts("Your flag is:"); v3 = fun1(v120, 1000000000); v4 =fun2(v3, 999999950); fun1(v4, 2); v5 = fun3(&v120[1], 5000000); v6 = (DWORD *)fun2(v5, 6666666); v7 = fun3(v6, 1666666); v8 = fun3(v7, 45); v9 = fun1(v8, 2); fun3(v9, 5); v10 = fun1(&v120[2], 1000000000); v11 = (DWORD *)fun2(v10, 999999950); v12 = fun1(v11, 2); fun3(v12, 2); v13 = fun3(&v120[3], 55); v14 = (DWORD *)fun2(v13, 3); v15 = fun3(v14, 4); fun2(v15, 1); v16 = fun1(&v120[4], 100000000); v17 = (DWORD *)fun2(v16, 99999950); v18 = fun1(v17, 2); fun3(v18, 2); v19 = (DWORD *)fun2(&v120[5], 1); v20 = fun1(v19, 1000000000); v21 = fun3(v20, 55); fun2(v21, 3); v22 = fun1(&v120[6], 1000000); v23 = (DWORD *)fun2(v22, 999975); fun1(v23, 4); v24 = fun3(&v120[7], 55); v25 = (DWORD *)fun2(v24, 33); v26 = fun3(v25, 44); fun2(v26, 11); v27 = fun1(&v120[8], 10); v28 = (DWORD *)fun2(v27, 5); v29 = fun1(v28, 8); fun3(v29, 9); v30 = fun3(&v120[9], 0); v31 = (DWORD *)fun2(v30, 0); v32 = fun3(v31, 11); v33 = (DWORD *)fun2(v32, 11); fun3(v33, 53); v34 = fun3(&v120[10], 49); v35 = (DWORD *)fun2(v34, 2); v36 = fun3(v35, 4); fun2(v36, 2); v37 = fun1(&v120[11], 1000000); v38 = (DWORD *)fun2(v37, 999999); v39 = fun1(v38, 4); fun3(v39, 50); v40 = fun3(&v120[12], 1); v41 = fun3(v40, 1); v42 = fun3(v41, 1); v43 = fun3(v42, 1); v44 = fun3(v43, 1); v45 = fun3(v44, 1); v46 = fun3(v45, 10); fun3(v46, 32); v47 = fun1(&v120[13], 10); v48 = (DWORD *)fun2(v47, 5); v49 = fun1(v48, 8); v50 = fun3(v49, 9); fun3(v50, 48); v51 = (DWORD *)fun2(&v120[14], 1); v52 = fun1(v51, -294967296); v53 = fun3(v52, 55); fun2(v53, 3); v54 = fun3(&v120[15], 1); v55 = fun3(v54, 2); v56 = fun3(v55, 3); v57 = fun3(v56, 4); v58 = fun3(v57, 5); v59 = fun3(v58, 6); v60 = fun3(v59, 7); fun3(v60, 20); v61 = fun1(&v120[16], 10); v62 = (DWORD *)fun2(v61, 5); v63 = fun1(v62, 8); v64 = fun3(v63, 9); fun3(v64, 48); v65 = fun3(&v120[17], 7); v66 = fun3(v65, 6); v67 = fun3(v66, 5); v68 = fun3(v67, 4); v69 = fun3(v68, 3); v70 = fun3(v69, 2); v71 = fun3(v70, 1); fun3(v71, 20); v72 = fun3(&v120[18], 7); v73 = fun3(v72, 2); v74 = fun3(v73, 4); v75 = fun3(v74, 3); v76 = fun3(v75, 6); v77 = fun3(v76, 5); v78 = fun3(v77, 1); fun3(v78, 20); v79 = fun1(&v120[19], 1000000); v80 = (DWORD *)fun2(v79, 999999); v81 = fun1(v80, 4); v82 = fun3(v81, 50); fun2(v82, 1); v83 = (DWORD *)fun2(&v120[20], 1); v84 = fun1(v83, -294967296); v85 = fun3(v84, 49); fun2(v85, 1); v86 = (DWORD *)fun2(&v120[21], 1); v87 = fun1(v86, 1000000000); v88 = fun3(v87, 54); v89 = (DWORD *)fun2(v88, 1); v90 = fun3(v89, 1000000000); fun2(v90, 1000000000); v91 = fun3(&v120[22], 49); v92 = (DWORD *)fun2(v91, 1); v93 = fun3(v92, 2); fun2(v93, 1); v94 = fun1(&v120[23], 10); v95 = (DWORD *)fun2(v94, 5); v96 = fun1(v95, 8); v97 = fun3(v96, 9); fun3(v97, 48); v98 = fun3(&v120[24], 1); v99 = fun3(v98, 3); v100 = fun3(v99, 3); v101 = fun3(v100, 3); v102 = fun3(v101, 6); v103 = fun3(v102, 6); v104 = fun3(v103, 6); fun3(v104, 20); v105 = fun3(&v120[25], 55); v106 = (DWORD *)fun2(v105, 33); v107 = fun3(v106, 44); v108 = (DWORD *)fun2(v107, 11); fun3(v108, 42); fun3(&v120[26], v120[25]); fun3(&v120[27], v120[12]); v115 = v120[27]; v109 = (DWORD *)fun2(&v120[28], 1); v110 = fun3(v109, v115); fun2(v110, 1); v116 = v120[23]; v111 = (DWORD *)fun2(&v120[29], 1); v112 = fun1(v111, 1000000); fun3(v112, v116); v117 = v120[27]; v113 = fun3(&v120[30], 1); fun1(v113, v117); fun3(&v120[31], v120[30]); printf("CTF{"); for ( j = 0; j < 32; ++j ) printf("%c", v120[j]);//SLOBYTE printf("}n"); system("pause"); } DWORD* fun1(DWORD*x,int y) { *x=(*x)*(y); return x; } DWORD* fun2(DWORD*x,int y) { *x=(*x)-(y); return x; } DWORD* fun3(DWORD*x,int y) { *x=(*x)+(y); return x; }
学到了什么
- 一位大哥说过
复杂代码本质应该是简洁的,这样才叫出题。
所以认知看吧…
-
函数太丑就自己定义一下
-
又涉及了代码修复
数据类型要一致才能参与转化…或者小的类型到大的类型
一些比较清晰的代码却不知道它的用的话,可以去IDE里面跑一下…注意转化
-
又遇到了很多垃圾代码…踩了很多得坑
-
哎…多注意一下函数得返回值类型…踩了很多得坑…
045-Windows_Reverse2.exe脱壳软件有,自己下载吧…我寻找半天才找到一个
脱壳后不能调试…
预备知识
- 把数字表示一下
好比78是7与8的组合
那么怎么用7与8来得到78?
xy=x进制+y
eg_1
56(Dec)=510+6=5*10 | 6
eg_2
0xAB= A <<4 | B = A * 16 + B = A << 4 + B - 对Python的类型大概了解一下
计算机读取字符串要去转化一下
string–>Bytes–>二进制
什么是Bytes
b’/e8/b6’
差不多这样子的
我们有char–>str
也有
Byte–>Bytes - 规律吧
字符串A-F的ASCII-55就是16进制对应的10进制
字符串0-9的ASCII-48,48就是’0’的ASCII,这个好理解,对于那个A-F不太好理解 - 对base64的编码与解码要熟悉
题的加密逻辑
好比输入数字字符串ABCD1234(表示16进制)
于是每2个提取出来
AB
A=10
B=11
AB=171
题目把171= 10<<4 | 11
这个东西还是看得懂的,就是一个进制转化
好比89
89=8 * 10 + 9
然会对171–>base64编码
可以看出这里的171就是你输入AB的10进制,
以前我们用base64对可见字符加密,加密后当然也是可见字符
但是这里是对Bytes类型的数字加密,
.
而不是字对String字符串的数字加密
.
也就是说你的输入可能被转为了不可见的数字编码,
.
然后对它加密…如果你解密之后当然可能是乱码.
.
为什么乱码.好比解密出来时178与190,
.
终端显示的时候无法显示178与190,就把178与190当汉字合起来显示
踩到的坑
- 不去看一些数据的地址…很重要
- 不去看一些函数的传参,很重要
- TMD,这里面很多的数据都似乎地址重合…TMD,为什么重合?看汇编与地址呗
- 对python的Bytes类型啥也不知道
代码显示错误,点击去退出来,F5一下就OK
函数一
if ( !check(input) ) { printf("invalid inputn"); exit(0); }
我们当然要返回 1 了
char __usercall sub_A711F0@(const char *check_1@) { int len; // eax int len_; // edx int index; // ecx char BUff; // al len = strlen(check_1); len_ = len; if ( len && len % 2 != 1 ) { index = 0; if ( len <= 0 ) return 1; while ( 1 ) { BUff = check_1[index]; if ( (BUff < '0' || BUff > '9') && (BUff < 'A' || BUff > 'F') ) break; if ( ++index >= len_ ) return 1; } } return 0; }
它的意思就是要求你的输入在0-9,A-F
不就是16进制的数子吗…
继续看主函数
encode(input, (int)check_data); memset(Buffer, 0, sizeof(Buffer)); sprintf(Buffer, "DDCTF{%s}", check_data); if ( !strcmp(Buffer, aDdctfReverse) )
你的输入被encode到了check_data,
check_data装到Buffer,
buffer去与数据strcmp
进入函数
int __usercall encode@(const char *input@ , int check_data) { int len; // edi int index; // edx char result_B_Dec; // bl char in_1; // al char next_value; // al unsigned int half_index; // ecx char result_A_Dec; // [esp+Bh] [ebp-405h] char check_data_[1024]; // [esp+Ch] [ebp-404h] BYREF len = strlen(input); // ADEBDEAEC7BE memset(check_data_, 0, sizeof(check_data_)); index = 0; if ( len > 0 ) { result_B_Dec = result_A_Dec; do { in_1 = input[index]; if ( (unsigned __int8)(in_1 - 48) > 9u ) { // 10 15 if ( (unsigned __int8)(in_1 - 'A') <= 5u ) result_A_Dec = in_1 - 55; // '7' } else { result_A_Dec = input[index] - 48; } // hex-decimal next_value = input[index + 1]; if ( (unsigned __int8)(next_value - 48) > 9u ) { if ( (unsigned __int8)(next_value - 'A') <= 5u ) result_B_Dec = next_value - '7'; } else { result_B_Dec = input[index + 1] - 48; } half_index = (unsigned int)index >> 1; // 上面都是hex-Dec index += 2; check_data_[half_index] = result_B_Dec | (16 * result_A_Dec);// 他把2个数给分别合在一起了 } while ( index < len ); } // ADEBDEAEC7BE return sub_A71000(len / 2, (void *)check_data);// 每2个一位把前面那个*16在|就可以成功拼接 } // AD1EB1E7A2BB1E
这个函数是干嘛的?
把你的数字字符串Straight( ’ 12 ’ ) 转化为 (Dec) 12
很奇怪的是,
int __usercall encode@(const char *input@ , int check_data)
点击check_data,全文没有任何一个地方用到他,于是另外一个函数分析,就更加懵逼了,其实它好像好像本来就没有用,
没关系,不急
你Tab一下看看汇编,去看看到底怎么出入参数的
你的input的地址通过lea指令把地址给了esi
然后去汇编看看
发现check_data_1还是没人用它
于是看看
于是去汇编看看check_data_2是是谁
可以看到check_data_2由esi的东西决定
esi是啥
不就是你的input吗?于是你就明白了嘛
然下面就是对你的Str–>Nummber的转化
do { in_1 = input[index]; if ( (unsigned __int8)(in_1 - 48) > 9u ) { // 10 15 if ( (unsigned __int8)(in_1 - 'A') <= 5u ) result_A_Dec = in_1 - 55; // '7' } else { result_A_Dec = input[index] - 48; } // hex-decimal next_value = input[index + 1]; if ( (unsigned __int8)(next_value - 48) > 9u ) { if ( (unsigned __int8)(next_value - 'A') <= 5u ) result_B_Dec = next_value - '7'; } else { result_B_Dec = input[index + 1] - 48; } half_index = (unsigned int)index >> 1; // 上面都是hex-Dec index += 2; check_data_2[half_index] = result_B_Dec | (16 * result_A_Dec);// 他把2个数给分别合在一起了 } while ( index < len );
因为你是2个个的转化,所以也要2个2个的取
half_index = (unsigned int)index >> 1; // 上面都是hex-Dec index += 2; check_data_2[half_index] = result_B_Dec | (16 * result_A_Dec);// 他把2个数给分别合在一起了
函数的最后才是神奇的爹,这里传进去1/2的len,能够理解,我也之前的数据两两转化,len就减半
这里用到了我们一直没有用到的参数
于是在最后你点击一下return(表示你要看return的汇编,Tab一下)
神奇了
IDA骗我,说好的check_dat_1呢
好吧,这里传进去还是之前不断数据转化的data_data_2,说得通
注意这里把check_data_1给了ecx,后面要用(很重要)
int __cdecl base_64encode(int len_half, void *check_data_1)
你还是会发现.check_data_1仍然没人用(很少)它
可能与上一个的传参类似
点击check_data_1,看伪代码,它确实没怎么用到
仔细看一下
ecx不就是之前的check_data_1吗?
也看一下汇编吧
check_data_1–>ecx–>edi–>cl–>base64加密的中间数据
,继续
于是可以看出来了吧…
于是看到base64加密
do { *(&Mg_0 + inc) = *flag_2; // Mg_0,1,2是连续的,传递0,1,2就达到3 Mg_1_ = Mg_1; ++inc; --len; ++flag_2; if ( inc == 3 ) // 3__要敏感 { r0 = Mg_0 >> 2; r1 = (Mg_1 >> 4) + 16 * (Mg_0 & 3); r2 = (Mg_2 >> 6) + 4 * (Mg_1 & 0xF); r3 = Mg_2 & 0x3F; for ( i = 0; i < 4; ++i ) std::string::operator+=(BUff, table[*(&r0 + i)] ^ 0x76);// 这里有char类型 inc = 0; // 这个循环在遍历你上面的4个加密 } // 这里处理了3->4个 } // 这里就把all的就加密了一遍 while ( len );
falg_2的指针++推动你的数据遍历
上面这3+4个数据有很多地址的连续
Mg_0+0=Mg_0
Mg_0+1=Mg_1
Mg_0+2=Mg_2
对R0,R1,R2,R3,同理可得
那个table是被加密了的,你去把table ^ 0x76打印出来,他就是base64的标准table,一次编码结束就inc = 0,于是再去inc++
我们都知道
base64的加密还有==那种们就是len不是3的倍数的
对于我们这个题,
上半部分 是base64, 判断条件有点多,因为自对3的倍数加密
下半部分还是base64, 但是它的判断条件少了,因为针对非2的倍数余下那 一 部分加密,如果没有余下就不执行下面的语句
模拟一下那个函数.看看打印了什么
取len=8,发现 inc=2,
可以看到当len=8的时候,flag的一些部分还是每加密完,但是len实际上=6,3的倍数下半部分的清空其实可以不管的
为什么取len=8.随便取的
但是下面这个=8,我们应该取len=6
reverse+
取len=6
发现inc =0
反正就那样吧
你把
reverse+
去base64解码
然后要以哦%c会是乱码,因为你的编码数据本来就有可能是不可见的
然后你要把它给Byte的形式打印出来,反正我试了一下,c语言好像不咋个
要用Python的打印Bytes类型,在破解为ehx,再大写,因为你之前本来就是
0-9,A-F,因此要大写
import base64 x = 'reverse+' # 最终结果 x = base64.b64decode(x) print(x) print(x.hex().upper())
输出
b'xadxebxdexaexc7xbe' ADEBDEAEC7BE flag{xxx}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)