REVERSE-PRACTICE-BUUCTF-19

  • [RoarCTF2019]polyre
  • [安洵杯 2019]game
  • [SCTF2019]Strange apk
  • [CFI-CTF 2018]IntroToPE

[RoarCTF2019]polyre

elf文件,无壳,用ida分析
main函数的结构,多重循环,是控制流平坦化,参考:利用符号执行去除控制流平坦化

装好angr,使用脚本deflat.py去除控制流平坦化,脚本取自:deflat


将去除控制流平坦化的文件attachment_recovered拖入ida中分析
main函数还是不好分析,有很多while和do while循环,实际上是虚假控制流,使用脚本去除虚假控制流,在ida的Script command执行

def patch_nop(start,end):for i in range(start,end):PatchByte(i, 0x90)def next_instr(addr):return addr+ItemSize(addr)st = 0x0000000000401117
end = 0x0000000000402144addr = st
while(addr<end):next = next_instr(addr)if "ds:dword_603054" in GetDisasm(addr):while(True):addr = nextnext = next_instr(addr)if "jnz" in GetDisasm(addr):dest = GetOperandValue(addr, 0)PatchByte(addr, 0xe9)PatchByte(addr+5, 0x90) offset = dest - (addr + 5)PatchDword(addr + 1, offset)print("patch bcf: 0x%x"%addr)addr = nextbreakelse:addr = next

去除虚假控制流之后的main函数
主要逻辑为
输入的字符每8个字节为1组,组成1个64位的signed int v4,对v4进行64次循环运算,如果v4为非负,则v4乘以2,相当于左移1位,最高位的0移出,最低位补零,v4必为偶数(如果左移一位后最高位为1,则v4变成了负数值),如果v4为负,则先左移1位,再与0xB0004B7679FA26B3异或,左移一位时,最高位的1被移出了(写逆运算脚本时需要加回来最高位的1),最低位补零,由于3的二进制为0011,异或后v4必为奇数

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{signed __int64 v4; // [rsp+1E0h] [rbp-110h]signed int j; // [rsp+1E8h] [rbp-108h]signed int i; // [rsp+1ECh] [rbp-104h]signed int k; // [rsp+1ECh] [rbp-104h]char s1[48]; // [rsp+1F0h] [rbp-100h]char input[60]; // [rsp+220h] [rbp-D0h]unsigned int v10; // [rsp+25Ch] [rbp-94h]char *v11; // [rsp+260h] [rbp-90h]int v12; // [rsp+26Ch] [rbp-84h]bool v13; // [rsp+272h] [rbp-7Eh]unsigned __int8 v14; // [rsp+273h] [rbp-7Dh]int v15; // [rsp+274h] [rbp-7Ch]char *v16; // [rsp+278h] [rbp-78h]int v17; // [rsp+284h] [rbp-6Ch]int v18; // [rsp+288h] [rbp-68h]bool v19; // [rsp+28Fh] [rbp-61h]char *v20; // [rsp+290h] [rbp-60h]int v21; // [rsp+298h] [rbp-58h]bool v22; // [rsp+29Fh] [rbp-51h]__int64 v23; // [rsp+2A0h] [rbp-50h]bool v24; // [rsp+2AFh] [rbp-41h]__int64 v25; // [rsp+2B0h] [rbp-40h]__int64 v26; // [rsp+2B8h] [rbp-38h]__int64 v27; // [rsp+2C0h] [rbp-30h]__int64 v28; // [rsp+2C8h] [rbp-28h]int v29; // [rsp+2D0h] [rbp-20h]int v30; // [rsp+2D4h] [rbp-1Ch]char *v31; // [rsp+2D8h] [rbp-18h]int v32; // [rsp+2E0h] [rbp-10h]int v33; // [rsp+2E4h] [rbp-Ch]bool v34; // [rsp+2EBh] [rbp-5h]v10 = 0;memset(input, 0, 0x30uLL);memset(s1, 0, 0x30uLL);printf("Input:", 0LL);v11 = input;__isoc99_scanf("%s", input);for ( i = 0; ; ++i ){v12 = i;v13 = i < 64;if ( i >= 64 )break;v14 = input[i];v15 = v14;if ( v14 == '\n' )                          // 换行符'\n'改成字符串结束符'\0'{v16 = &input[i];*v16 = 0;break;}v17 = i + 1;}for ( j = 0; ; ++j ){v18 = j;v19 = j < 6;if ( j >= 6 )                               // 循环6次break;v20 = input;v4 = *(_QWORD *)&input[8 * j];              // 输入的每8个字节一组,组成1个64位的v4,小端序for ( k = 0; ; ++k ){v21 = k;v22 = k < 64;if ( k >= 64 )                            // 循环64次break;v23 = v4;v24 = v4 < 0;if ( v4 >= 0 )                            // 如果v4非负,v4*=2,相当于左移一位,结果必为偶数,如果左移一位后最高位为1,则v4变成了负数{v27 = v4;v28 = 2 * v4;v4 *= 2LL;}else                                      // 如果v4为负,v4乘2后再异或0xB0004B7679FA26B3,相当于先左移一位再异或,结果必为奇数,注意这时最高位的1被移出了{v25 = 2 * v4;v26 = 2 * v4;v4 = 2 * v4 ^ 0xB0004B7679FA26B3LL;}v29 = k;}v30 = 8 * j;v31 = &s1[8 * j];                           // 每组完成64次循环后的值赋给s1*(_QWORD *)v31 = v4;v32 = j + 1;}v33 = memcmp(s1, &unk_402170, 48uLL);         // s1和已知值比较,小端序v34 = v33 != 0;if ( v33 != 0 )puts("Wrong!");elseputs("Correct!");return v10;
}

写逆运算脚本即可得到flag

#coding:utf-8
arr=[0x96, 0x62, 0x53, 0x43, 0x6D, 0xF2, 0x8F, 0xBC, 0x16, 0xEE,0x30, 0x05, 0x78, 0x00, 0x01, 0x52, 0xEC, 0x08, 0x5F, 0x93,0xEA, 0xB5, 0xC0, 0x4D, 0x50, 0xF4, 0x53, 0xD8, 0xAF, 0x90,0x2B, 0x34, 0x81, 0x36, 0x2C, 0xAA, 0xBC, 0x0E, 0x25, 0x8B,0xE4, 0x8A, 0xC6, 0xA2, 0x81, 0x9F, 0x75, 0x55]
enc=[]
for i in range(6):#以小端序的方式读值tmp='0x'for j in range(7,-1,-1):tmp+=hex(arr[i*8+j]).replace('0x','').zfill(2)enc.append(tmp)
flag_data=[]
for i in range(len(enc)):tmp=int(enc[i],16)for j in range(64):#循环64次sign=tmp&1      #判断当前v4的符号 &1后为1即为奇数 为0即为偶数if sign:        #奇数要异或回去tmp^=0xB0004B7679FA26B3tmp//=2          #无论正负都要右移一位回去if sign:        #奇数右移一位后还要补回左移一位时移出的1tmp|=0x8000000000000000flag_data.append(hex(tmp).replace('0x','').replace('L',''))
flag_str=""
for i in range(6):#由于存储方式是小端序 所以需要从后往前的读tmp=flag_data[i]for j in range(len(tmp)-2,-1,-2):num=int('0x'+tmp[j:j+2],16)flag_str+=chr(num)
print(flag_str)
#flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}

[安洵杯 2019]game

elf文件,无壳,ida分析
main函数调用的许多函数都有控制流平坦化混淆,用defalt.py脚本一个一个去平坦化
main函数,获取输入,general_inspection函数利用sudoku做一些运算,不太明白作用,不过影响不大,trace函数完全填充sudoku,动调可得到完全填充的结果,check函数没什么作用,check1函数对输入进行变换,最后进入check3函数验证输入

进入check1函数,对input进行变换,首先input前半部分和后半部分交换,然后input两个一组,两两交换,最后再对input进行变换,input最后的变换结果均为数字字符

check3->check2函数,首先将变换后的input中的数字字符转成普通数字,存储到v11,然后v11顺序地填充到D0g3中元素为0的位置,未完全填充的D0g3也可通过动调得到,最后比较D0g3和sudoku

动调得到未完全填充的D0g3和已完全填充的sudoku后,写逆运算脚本即可得到flag

#coding:utf-8
sudoku=[
1,4,5,3,2,7,6,9,8,
8,3,9,6,5,4,1,2,7,
6,7,2,8,1,9,5,4,3,
4,9,6,1,8,5,3,7,2,
2,1,8,4,7,3,9,5,6,
7,5,3,2,9,6,4,8,1,
3,6,7,5,4,2,8,1,9,
9,8,4,7,6,1,2,3,5,
5,2,1,9,3,8,7,6,4]
D0g3=[
1,0,5,3,2,7,0,0,8,
8,0,9,0,5,0,0,2,0,
0,7,0,0,1,0,5,0,3,
4,9,0,1,0,0,3,0,0,
0,1,0,0,7,0,9,0,6,
7,0,3,2,9,0,4,8,0,
0,6,0,5,4,0,8,0,9,
0,0,4,0,0,1,0,3,0,
0,2,1,0,3,0,7,0,4]
arr=[]
for i in range(81):if D0g3[i]==0:  #input填充到D0g3中元素为0的位置arr.append(sudoku[i])
for i in range(len(arr)):for j in range(32,128):#对input的第三次变换通过爆破来解if arr[i]+48==(j & 0xF3 | ~j & 0xC) - 20:arr[i]=j
for i in range(0,len(arr),2): #两个一组,两两交换arr[i],arr[i+1]=arr[i+1],arr[i]
arr[0:20],arr[20:40]=arr[20:40],arr[0:20]#input前半部分和后半部分交换
flag="".join(chr(i) for i in arr)
print(flag)
#KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J

运行elf,输入,验证正确

[SCTF2019]Strange apk

apk文件,jadx-gui打开
主要逻辑在sctf.hello.c类中
data是apk自带的资源文件,在assets目录下可找到,先对data进行__方法处理,再进行_方法处理

__方法就是读取data文件的字节

_方法是对data文件的字节进行0方法的处理,再将结果写到另一个文件中

0方法,可知是对data文件读取到的字节与"syclover"做循环异或运算,结果写到另一个文件中

脚本:

f= open("D:\\ctfdownloadfiles\\data", "rb")
res= open("D:\\ctfdownloadfiles\\res", "wb")
data=f.read()
s="syclover"
res_data=""
count=0
for c in data:res_data+=chr(ord(c)^ord(s[count%len(s)]))count+=1
res.write(res_data)
res.close()

将输出的文件后缀名改为apk后,再用jadx-gui分析
先看sctf.demo.myapplication.s类,验证输入的长度是否为30,对输入的前12个字符做base64编码后与已知字符串比较,后18个字符赋给data_return

再看sctf.demo.myapplication.t类,key为"syclover"的md5的十六进制摘要,输入的后18个字符与key作为参数传入f.encode方法,返回的字符串与已知字符串比较

再看sctf.demo.myapplication.f类,str为输入的后18个字符,长度s为18,key为"syclover"的md5的十六进制摘要,为"8bfc8af07bca146c937f283b8ec768d4",长度c为32,for循环中,t先append一个输入字符,再append一个key中的字符,由于f<s<c,故t从key中append的字符总是第0个字符,即’8’,同时也知道返回字符串中某字符的下标为偶数时,其即为输入的字符

写脚本即可得到flag

import base64
s=base64.b64decode("c2N0ZntXM2xjMG1l")
res="~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
for i in range(0,len(res),2):s+=res[i]
print(s)
#sctf{W3lc0me~t0_An4r0id-w0rld}

[CFI-CTF 2018]IntroToPE

exe程序,运行后输入password,点击验证
查壳发现时.Net程序,用dnSpy打开
来到ValidatePasswd.verifyPasswd()方法,简单的base64编码验证

解base64即可得到flag

REVERSE-PRACTICE-BUUCTF-19相关推荐

  1. buu Reverse学习记录(19) [GWCTF 2019]pyre

    题目链接:https://buuoj.cn/challenges#[GWCTF%202019]pyre 题目是个.pyc文件 现在python中安装 uncompyle 库 运行命令 uncompyl ...

  2. 金海佳学C++primer 练习9.18/9.19

    读取到deque/list并输出 Practice9.18 #include <iostream> #include <string> #include <vector& ...

  3. 2020上的NLP有哪些研究风向?

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者 | Camel 编辑 | 十.年 来源 | AI科技评论 距离 AAAI 202 ...

  4. java 矩阵转置算法

    工作中用到了行列转置,把这两种情况的算法记下来,以便后用 1.行列数相等的转置 1 /** 2 * @description 矩阵转置 3 * @author oldmonk 4 * @time 20 ...

  5. python字典排序取最值总结

    dic = {"abc":18,"adc":19,"abe":20} # 默认对键排序,从小到大,返回排序后键组成的列表 zidian = ...

  6. 针对多类型数据库,集群数据库的有序GUID

    一.背景 常见的一种数据库设计是使用连续的整数为做主键,当新的数据插入到数据库时,由数据库自动生成.但这种设计不一定适合所有场景. 随着越来越多的使用Nhibernate.EntityFramewor ...

  7. Laravel学习笔记(四)数据库 数据库迁移案例

    创建迁移 首先,让我们创建一个MySql数据库"Laravel_db".接下来打开app/config目录下的database.php文件.请确保default键值是mysql: ...

  8. mysql数据库 常用函数_《MySQL数据库》常用函数整理

    原标题:<MySQL数据库>常用函数整理 以下内容,是我整理出来的比较常用的字符串函数,数值函数,日期函数. 第一类:字符串函数 1.conv(n,from_base,to_base):对 ...

  9. 面试中常见的数据结构与算法题整理,想当架构师,数据结构与算法不过关可不行(数组+字符串,共60题)

    [Java架构师面试网]收集整理了一些Java面试的常见问题,这些问题可能会在你下一次技术面试中遇到.想成为Java架构师,这些都是不可避免也是必须要掌握的哦,对于其他模块的面试题,我后续也将单独分享 ...

  10. Python第三课小节

    Python第三次课堂小结 1.列表元素操作 1.1列表元素的删除 2.列表元素访问与计数 3.浅拷贝/浅复制 4.列表排序 5.random函数产生随机数 6.用于序列操作的常用内置函数 1.列表元 ...

最新文章

  1. R语言union函数计算数据对象(vector、list、dataframe)的并集:union函数计算两个vector向量、dataframe、列表list的并集
  2. web安全之点击劫持攻击(clickjack)
  3. Jsoncpp Compiler、Programming
  4. 【Python金融量化 5- 100 】、五、蒙特卡洛和毛利
  5. python创建虚拟环境jupyter_机器学习中python的有关使用技巧【创建虚拟环境、jupyter的kernel修改】...
  6. anaconda 命令
  7. 面试中遇到过的闭包~
  8. 设计模式之享元(flyweight)模式
  9. 虚函数与纯虚函数区别
  10. VSCode配置PyQt5和designer
  11. 郑大网教育计算机2017,2017郑大计算机操作系统(计算机).docx
  12. sql---如何把sql查询出来的结果当做另一个sql的条件查询,1、语句2、with as
  13. bash linux .ee,Linux下Bash shell学习笔记.md
  14. java day21【缓冲流、转换流、序列化流】
  15. 人生哲理枕边书——你应该知道的165个人生哲理
  16. css表格做日历,CSS 制作有弹性的日历表
  17. 计算机操作系统安全序列详解,操作系统知识梳理
  18. IE 不兼容 justify-content:space-evenly 的解决办法
  19. ArcGIS移动客户端离线地图的几种解决方案
  20. 英语中what的用法

热门文章

  1. treegrid.bootstrap使用说明
  2. python学习---语法
  3. pentaho DI--- Tutorial (spoon)
  4. [dp] LeetCode 62. Unique Paths
  5. java 添加注解_你知道Java中的package-info的作用吗?
  6. python多线程编程_Python 多线程编程
  7. PHP程序中时间戳,php 时间戳常用代码
  8. 801. 二进制中1的个数
  9. mysql to mssql_MysqlToMsSql
  10. linux免密登录_Linux SSH免密钥登录总结