文章目录
006-GUess-The-Number
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))
042-76号-Obfuse-类型
问题细节题 / 汇编代码
-
找不到函数入口点 [ 为什么输入函数在(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<stdio.h>
#include<stdlib.h>
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<windows.h>的数据
还有就是数据的类型一致
DWORD* fun1(DWORD*,int);
对应
v19 = (DWORD *)fun2(&v120[5], 1);
虽然我们
#include<cstdio>
#include<cstdlib>
#include<string>
#include<windows.h>
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 input\n");
exit(0);
}
我们当然要返回 1 了
char __usercall sub_A711F0@<al>(const char *check_1@<esi>)
{
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@<eax>(const char *input@<esi>, 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@<eax>(const char *input@<esi>, 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吗?