REVERSE-PRACTICE-JarvisOJ-2

  • DD - Hello
  • APK_500
  • DebugMe
  • FindPass

DD - Hello

macos文件,无壳,ida分析
start函数和sub_100000C90函数没什么作用
主要的逻辑在sub_100000CE0函数,反调试检测和byte_100001040数组的循环变换,最后打印flag

按sub_100000CE0函数的逻辑写脚本,即可得到flag

byte_100001040=[0x41, 0x10, 0x11, 0x11, 0x1B, 0x0A, 0x64, 0x67, 0x6A, 0x68,0x62, 0x68, 0x6E, 0x67, 0x68, 0x6B, 0x62, 0x3D, 0x65, 0x6A,0x6A, 0x3D, 0x68, 0x04, 0x05, 0x08, 0x03, 0x02, 0x02, 0x55,0x08, 0x5D, 0x61, 0x55, 0x0A, 0x5F, 0x0D, 0x5D, 0x61, 0x32,0x17, 0x1D, 0x19, 0x1F, 0x18, 0x20, 0x04, 0x02, 0x12, 0x16,0x1E, 0x54, 0x20, 0x13, 0x14]
start=(0x0000000100000CB0)&0xff
sub_100000C90=(0x0000000100000C90)&0xff
v2 = ((start - sub_100000C90) >> 2) ^ byte_100001040[0]
v1=0
while v1<55:byte_100001040[v1]-=2byte_100001040[v1]^=v2v1+=1v2+=1
print(''.join(chr(byte_100001040[i]) for i in range(1,len(byte_100001040))))
#DDCTF-5943293119a845e9bbdbde5a369c1f50@didichuxing.com

APK_500

apk文件,jadx-gui打开
在com.ctf.test.android_ctf_500_test.MainActivity中看到,静态加载了easy库,输入password后,调用easy库中的helloworld函数,验证输入

ApkToolBox反编译apk文件后,ida打开CTF_500\lib\armeabi-v7a\libeasy.so
没有直接在左侧函数窗找到helloworld函数,来到JNI_OnLoad函数
解出三段字符串,分别为函数名,函数的参数,以及函数所在的类



往下走,sub_1198函数为验证输入的password
对输入的password有以下几步变换:
输入的字符转成了十六进制数
input[i]^=i,输入和其下标异或
input[i]+=1,i∈[0,3],前4个字节加1
input的前7个字节按原来的顺序放到最后7个位置上,其余的字节依次向前移动
变换位置后的input与byte_4004异或
input再转成十六进制数,但是不会填充两位,例如 “0x0c”->“0xc”,有一个0被忽略了
最后input和v50比较

bool __fastcall sub_1198(JNIEnv *a1)
{const char *input; // r4__pid_t v2; // r0int v3; // r3int v4; // r3char *v5; // r2int *v6; // r3int v7; // r0int v8; // r1_BYTE *v9; // lr__pid_t v10; // r0int v11; // r9const char *input_; // r10size_t v13; // r0signed int input_len; // r9size_t v15; // r0const char *v16; // r12int v17; // r3int v18; // t1int v19; // ST0C_4const char *v20; // ST08_4char *v21; // r2signed int v22; // r3char v23; // r1int v24; // r0signed int v25; // r3const char *v26; // r2int v27; // r3char v28; // r0char *v29; // r2char v30; // r1int v31; // r2char *v32; // r3char v33; // t1signed int i; // r3const char *v35; // r6signed int v36; // r9int v37; // t1char *v38; // r6int *v39; // r3int v40; // r0int v41; // r1__pid_t v43; // [sp+0h] [bp-2F0h]int v44; // [sp+0h] [bp-2F0h]char v45; // [sp+18h] [bp-2D8h]char v46; // [sp+1Ah] [bp-2D6h]int v47; // [sp+1Ch] [bp-2D4h]__int16 v48; // [sp+20h] [bp-2D0h]char v49; // [sp+22h] [bp-2CEh]char v50; // [sp+24h] [bp-2CCh]char v51[128]; // [sp+44h] [bp-2ACh]char v52[512]; // [sp+C4h] [bp-22Ch]input = (const char *)((int (*)(void))(*a1)->GetStringUTFChars)();// 读inputv2 = getppid();v3 = 0;v43 = v2;dov52[v3++] = 0;while ( v3 != 64 );v4 = 0;dov51[v4++] = 0;while ( v4 != 32 );v5 = &v50;v6 = &dword_2C6F;do{v7 = *v6;v6 += 2;v8 = *(v6 - 1);*(_DWORD *)v5 = v7;*((_DWORD *)v5 + 1) = v8;v9 = v5 + 8;v5 += 8;}while ( v6 != (int *)&unk_2C7F );*v9 = *(_BYTE *)v6;sub_1064(&v50, 17, 65);                       // /proc/%d/cmdline v47 = 0x9021D19;v48 = 0xD13;v49 = 0x69;sub_1064(&v47, 7, 99);                        // zygote v10 = getppid();snprintf(v51, 32u, &v50, v10, v43);v11 = open(v51, 0);read(v11, v52, 64u);sub_107E(v52);                                // sub_107E函数,大写转小写close(v11);if ( !strstr(v52, (const char *)&v47) )exit(-1);input_ = input;v13 = strlen(input);BYTE2(v47) = 0;v52[0] = 0;input_len = v13;v15 = strlen(input);v16 = (const char *)&unk_2CF7;                // %xv17 = v15;while ( input_ - input < v17 ){v18 = *(unsigned __int8 *)input_++;v19 = v17;v20 = v16;sprintf((char *)&v47, v16, v18);            // %x,输入的字符转成十六进制,不会填充2位strcat(v52, (const char *)&v47);v17 = v19;v16 = v20;}v21 = (char *)input;v22 = 0;while ( v22 < input_len )                     // input[i]^=i{v23 = *v21 ^ v22++;*v21++ = v23;}v24 = sub_10A4();                             // tracerpid 反调试if ( v24 ){v26 = input;v27 = 0;v47 = v24 - v44 + 0x1010101;do                                          // input[i]+=1,0=<i<=3{v28 = *((_BYTE *)&v47 + v27++);*v26++ += v28;}while ( v27 != 4 );}if ( input_len > 7 )                          // input的前7个字节按顺序放到最后的7个位置,其余的依次向前移动,结果放在v51{v25 = 7;do{v29 = &v51[v25];v30 = input[v25++];*(v29 - 7) = v30;}while ( v25 != input_len );v31 = (int)(input - 1);v32 = &v51[input_len - 8];do{v33 = *(_BYTE *)(v31++ + 1);(v32++)[1] = v33;}while ( (const char *)v31 != input + 6 );v51[input_len] = 0;}for ( i = 0; i < input_len; ++i )             // 变换位置后的input与byte_4004异或,再放回inputinput[i] = v51[i] ^ byte_4004[i];v52[0] = 0;v46 = 0;v35 = input;v36 = strlen(input);while ( v35 - input < v36 ){v37 = *(unsigned __int8 *)v35++;sprintf(&v45, (const char *)&unk_2CF7, v37);// %xstrcat(v52, &v45);                          // input字符转成十六进制数放到v52}v38 = &v50;v39 = &dword_2C87;do{v40 = *v39;v39 += 2;v41 = *(v39 - 1);*(_DWORD *)v38 = v40;*((_DWORD *)v38 + 1) = v41;v38 += 8;}while ( v39 != &dword_2CA7 );sub_1064(&v50, 32, 57);                       // v50=ddedd4ea2e7bef168491a6cae2bc660\x00return strcmp(&v50, v52) == 0;                // v50和v52比较
}

需要注意的地方有两点:
1、在解最后要去比较的v50时,只能得到31个可见字符"ddedd4ea2e7bef168491a6cae2bc660",原因是使用"%x"做参数转成十六进制数时,某个字节的1个0被忽略,但是并不知道是哪个字节转十六进制时忽略了0,因此需要爆破0的位置

2、byte_4004数组,在ida中直接可见16个字节

其中dword_4007在程序运行时被赋了新值,交叉引用过去就能看到

写逆运算脚本,打印的字符串中有可读意义的即为flag

#coding:utf-8
from Crypto.Util.number import long_to_bytes
byte_4004=[0x85, 0x8B, 0xEC, 0x83, 0xEC, 0x14, 0x83, 0x8D, 0x0C, 0x01,0x75, 0x5F, 0xC6, 0x45, 0xF3, 0x50]
byte_4004[3]=0x83
byte_4004[4]=0x6c
byte_4004[5]=0x9c
byte_4004[6]=0x83v50="ddedd4ea2e7bef168491a6cae2bc660"#长度为31
flags=[]
for i in range(32):         #爆破被忽略的1个0的位置p=v50[:i]+'0'+v50[i:]flags.append(long_to_bytes(int('0x'+p,16)))
for s in flags:flag=[]for i in range(16):p=ord(s[i])^byte_4004[i]flag.append(p)flag=flag[-7:]+flag[:-7]for i in range(4):flag[i]-=1for i in range(16):flag[i]^=iprint(''.join(chr(i) for i in flag))
#Go0dj06_n1cew0rk

DebugMe

elf文件,无壳,ida分析
main函数,主要逻辑为
命令行输入password,sub_A30函数中有个j_fork,不太明白什么作用
往下走,输入和下标循环异或,进入sub_D90->sub_C14验证输入,第二个参数为0
继续向下,输入和数字1循环异或,进入sub_C14验证输入,第二个参数为7

int __cdecl main(int argc, const char **argv, const char **envp)
{int v3; // r4size_t i; // r4int v5; // r5int v6; // r0int v7; // r0int v8; // r7size_t j; // r4const char *v10; // r0char *input_; // [sp+4h] [bp-A4h]void (__noreturn *v13)(); // [sp+10h] [bp-98h]int v14; // [sp+18h] [bp-90h]char v15; // [sp+20h] [bp-88h]char v16; // [sp+21h] [bp-87h]char v17; // [sp+22h] [bp-86h]char v18; // [sp+24h] [bp-84h]char v19[20]; // [sp+38h] [bp-70h]char v20[64]; // [sp+4Ch] [bp-5Ch]if ( argc != 2 ){j_puts("Usage:\t program_name password");j_exit(0);}input_ = (char *)argv[1];if ( sub_A30(2u, (int)argv) )                 // 不太明白作用{for ( i = 0; i < j_strlen(input_); ++i )input[i] = input_[i] ^ i;                 // input[i]^i}v3 = 0;v13 = sub_D90;                                // check input,主要是sub_C14(input, 0)的调用v14 = 0;j_sigaction(7, (int)&v13, 0);j_sigaction(11, (int)&v13, 0);dov20[v3++] = 0;while ( v3 != 64 );v5 = 0;dov19[v5++] = 0;while ( v5 != 32 );j_memcpy(&v18, &unk_213D, 17u);sub_AE4((int)&v18, 17, 65);                   // /proc/%d/cmdline v15 = 36;v16 = 61;v17 = 83;v6 = sub_AE4((int)&v15, 3, 87);               // sh v7 = j_getppid(v6);j_snprintf(v19, 32, &v18, v7);v8 = j_open(v19, 0);j_read();sub_A0C(v20);                                 // 大写转小写j_close(v8);if ( !j_strstr(v20, &v15) ){for ( j = 0; j < j_strlen(input_); ++j )    // input[i]^1input[j] = input_[j] ^ 1;}if ( !sub_B1C() )                             // 反调试__breakpoint(0);if ( sub_C14(input, 7) )                      // sub_C14(input, 7) 的调用,和上面的的调用比较,第二个参数不同v10 = "you win!\nFlag is your password!";elsev10 = "The password you input is wrong!";j_puts(v10);return 0;
}

进入sub_C14函数,重要的逻辑在switch case语句中按照v3的值,验证输入的内容,LABEL_26的代码,是变换v3的值,实际上v3 = 7 * (v3 + 1) - 11*sub_E14(7 * (v3 + 1), 11)
把sub_E14函数的代码抠出来,编写c程序
两次调用sub_C14函数时,第二个参数(也就是v3的初始值)分别为0和7,于是验证顺序分别为
v3初始为0:0 7 1 3 6 5 9 4 2
v3初始为7:7 1 3 6 5 9 4 2
当v3为2时,sub_C14函数返回1
可以看到,v3初始为7时,比v3初始为0时,前者少验证了1位,其他的验证顺序是一致的

int __fastcall sub_C14(_BYTE *a1, int a2)
{_BYTE *input; // r4int v3; // r7int v4; // r3int v5; // r5int v6; // r0int v7; // r0int v8; // r6int result; // r0int v10; // r1char v11; // [sp+1Ch] [bp-9Ch]char v12; // [sp+28h] [bp-90h]char v13; // [sp+38h] [bp-80h]char v14[20]; // [sp+48h] [bp-70h]char v15[64]; // [sp+5Ch] [bp-5Ch]input = a1;v3 = a2;                                      // v3=a2,第二个参数在两次调用中不同,0和7while ( 2 ){v4 = 0;dov15[v4++] = 0;while ( v4 != 64 );v5 = 0;dov14[v5++] = 0;while ( v5 != 32 );j_memcpy(&v12, &unk_2113, 0xFu);sub_BF8((int)&v12, 15, 107);                // /proc/%d/wchan j_memcpy(&v13, &unk_2122, 0xFu);sub_BF8((int)&v13, 15, 116);                // sys_epoll_wait j_memcpy(&v11, &unk_2131, 0xCu);v6 = sub_BF8((int)&v11, 12, 114);           // ptrace_stop v7 = j_getppid(v6);j_snprintf(v14, 32, &v12, v7);v8 = j_open(v14, 0);j_read();sub_A0C(v15);                               // 大写转小写j_close(v8);if ( j_strstr(v15, &v13) )                  // 反调试return -1;result = -((unsigned int)j_strstr(v15, &v11) >= 1);if ( result == -1 )                         // 反调试return result;switch ( v3 )                               // 验证input内容{case 0:if ( *input == 105 )goto LABEL_26;return 0;case 1:if ( *input != 101 )return 0;goto LABEL_26;case 3:if ( *input != 110 )return 0;goto LABEL_26;case 4:if ( *input != 100 )return 0;goto LABEL_26;case 5:if ( *input != 97 )return 0;goto LABEL_26;case 6:if ( *input != 103 )return 0;goto LABEL_26;case 7:if ( *input != 115 )return 0;goto LABEL_26;case 9:if ( *input == 114 ){LABEL_26:sub_EAC(7 * (v3 + 1), 11);            // 调用sub_EAC函数时第二个参数永不为0,相当于直接调用sub_E14函数++input;v3 = v10;                             // 实际上,v3=7 * (v3 + 1) - 11*sub_E14(7 * (v3 + 1), 11)continue;}return 0;default:return 1;}}
}

由于v3初始为0时,sub_C14函数验证了8位,在调用sub_C14函数前,输入的变换是,input[i]^i
于是写逆运算脚本即可得到8位小写字母,即为flag

#coding:utf-8
#按case 0 7 1 3 6 5 9 4 2 顺序装值
a=[105,115,101,110,103,97,114,100]
print(''.join(chr(a[i]^i)for i in range(len(a))))
#irgmcdtc

FindPass

apk文件,jadx-gui打开
在com.example.findpass.MainActivity中,GetKey方法的逻辑为
获取输入的fkey,已知的ekey做变换,fkey和变换后的ekey比较,验证输入fkey

按照GetKey方法的逻辑写脚本即可得到flag

ekey="Tr43Fla92Ch4n93"
ekey_data=[ord(ekey[i]) for i in range(len(ekey))]
f=open("D:\\ctfdownloadfiles\\src.jpg","rb")
cha=f.read(1024)
f.close()
for i in range(len(ekey)):if ord(cha[ord(ekey[i])])<128:tmp2=ord(cha[ord(ekey[i])])%10else:tmp2 = (-(ord(cha[ord(ekey[i])])&0x7f)) % 10if i%2==1:ekey_data[i]+=tmp2else:ekey_data[i]-=tmp2
print(''.join(chr(i) for i in ekey_data))
#Qv49CmZB2Df4jB-

REVERSE-PRACTICE-JarvisOJ-2相关推荐

  1. linux内核路由反向检查,反向路径过滤——reverse path filter

    反向路径过滤--reverse path filter 一.原理 先介绍个非对称路由的概念 参考<Understanding Linux Network Internals>三十章, 30 ...

  2. java中Collections常用方法总结(包括sort,copy,reverse等)

    1.sort(Collection)方法的使用(含义:对集合进行排序). 例:对已知集合c进行排序public class Practice {public static void main(Stri ...

  3. A Guide To Reverse Tethering

    By Kevin Pocock on February 04, 2013 Using a mobile device such as a smartphone to act as a hotspot ...

  4. picoCTF,Reverse Engineering,逆向类,42/55

    picoCTF,Reverse Engineering,42/55 2019 picoCTF 01.vault-door-training,50分 02.vault-door-1,100分 03.va ...

  5. PAT (Advanced Level) Practice 题解代码 - II (1051-1100)

    PAT PAT (Advanced Level) Practice - II(1051-1100) -------------------------------------------------- ...

  6. (Python) PAT(Basic Level) Practice 刷题笔记(34-66)

    我的代码仅能解题,效率不高也不够简洁,欢迎师傅们提出建议,能让我加以改进. Practice 1036 跟奥巴马一起编程 (15 分) 1037 在霍格沃茨找零钱 (20 分) 1038 统计同成绩学 ...

  7. minty_Brit666‘s python practice no.2

    minty_Brit666 Today's blog is about the practice of the leetcode. And I'll give my own answer in thi ...

  8. 【水一波题解】题解 of University of Central Florida 2020 (Fall) “Practice” Local Programming Contest

    题解 of University of Central Florida 2020 (Fall) "Practice" Local Programming Contest [by_0 ...

  9. 动态规划十大经典案例(Dynamic Programming Practice Problems)

    目录 leetcode 53 最大子序列和(Maximum Value Contiguous Subsequence) leetcode 53 零钱兑换(Making Change) LeetCode ...

  10. 206. Reverse Linked List

    Reverse a singly linked list. 反转单链表 C++(9ms):  迭代 1 /** 2 * Definition for singly-linked list. 3 * s ...

最新文章

  1. 牛客网暑期ACM多校训练营(第一场)J Different Integers
  2. 如何用四个月搞定java?
  3. android 定时打电话教程
  4. SAP Spartacus服务器端渲染模式下的调试方法
  5. mysql求表中年龄同张三,mysql子查询与连接查询
  6. android学习——popupWindow 在指定位置上的显示
  7. maya对象属性_了解每粒子属性和每对象属性
  8. 关于redis说法正确的是_还不会正确使用Redis?这几个技巧让你的程序快如闪电...
  9. Atitit 标记语言ML(Markup Language) v4 目录 1. 标记语言ML Markup Language 1 1.1. 简介 1 2. 置标语言置标语言通常可以分为三类:标识性的
  10. 解决浏览器 fakepath 实现图片上传预览
  11. photoshop快速去掉图片背景颜色(白色背景)
  12. NLP入门干货:手把手教你3种中文规则分词方法
  13. java实现PDF转Word(无水印无页数限制)完全开放
  14. windows按照title开启和关闭进程
  15. python 降低图片分辨率的两种方法
  16. Java基础-简聊类与对象
  17. jupyter notebook / jupyter lab 深色主题下如何设置字体 及 如何设置绘图颜色
  18. 《Linux命令》常用命令-查询、替换、粘贴、复制、剪切、目录、删除、用户等操作。
  19. HTTP1.1协议中文版
  20. HTML5游戏开发技术基础整理

热门文章

  1. c# 第四课 interfaces
  2. C#做的一个加密/解密的类
  3. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol52]43 为AES 对抗侧信道攻击的防御
  4. android studio 无法输入中文,Android Studio 升级到3.0后输入法中文状态下无法选词的终极解决方案...
  5. 某项目的双代号网络图如下所示_2019一级建造师项目管理知识点大全3
  6. binlog日志_【删库跑路】使用Binlog日志恢复误删的MySQL数据
  7. mysql 8.0.12解压版安装教程_mysql 8.0.12 解压版安装教程
  8. 什么端口可以抓LINUX,linux下开启某个端口的方法:可用于SQL
  9. centos7限制cpu使用_CentOS7 CPU隔离配置
  10. js 获取域名_RapidDNS域名查询如何联动Goby