2019 re 强网先锋
一个简单的base64,签到题
解码即可:flag{mafakuailaiqiandaob}
2019 re 设备固件
题目给了一个完整的文件系统镜像和一个elf文件,根据readme中所述使用以下命令模拟运行:
1 | sudo qemu-system-mipsel -M malta -hda openwrt-malta-le-root.ext4 -kernel openwrt-malta-le-vmlinux.elf -nographic -append "root=/dev/sda console=tty50" |
而后在/bin中找到目标二进制文件hello:
运行:
但是这个二进制文件在系统里面,没办法拿出来用ida查看,先想想办法把hello文件提取出来
采用在宿主机上挂载 ext4 镜像的方法:
1 | # 1. 创建一个目录作为挂载点sudo mkdir /mnt/openwrt-root |
而后镜像文件的所有内容都暴露在 /mnt/openwrt-root 目录下
找到hello文件完成提取
首先使用file命令分析:
32位mips架构
放入ida内分析:
可以看到首先将输入的用户名放入v7的前40个字节中,密码放入后40个字节中,而后进入函数sub_400D48进行判断,该函数主要有两个部分,分别是函数sub_400C6C有用于验证用户名是否正确,函数sub_400CC4用于验证密码是否正确
先看函数sub_400C6C
很容易看出来该用户名就是直接确定了dd772
只有dd772才能令最终v1==0
而后看函数sub_400CC4:
主要逻辑在sub_400800,该函数主要是模拟虚拟机执行,有以下操作:
| 操作码 | 符号 | num1 | num2 | 行为 |
|---|---|---|---|---|
| 0x01 | add | 寄存器 | 寄存器 | num1+=num2 |
| 0x02 | sub | 寄存器 | 寄存器 | num1-=num2 |
| 0x03 | cmp | 寄存器 | 寄存器 | ZF=(num1==num2?1:2) |
| 0x04 | jmp | 立即数 | - | IP+=num1 |
| 0x05 | mov | 寄存器 | 寄存器 | num1=num2 |
| 0x06 | - | 寄存器 | - | num1=IP |
| 0x07 | - | 寄存器 | - | IP=num1 |
| 0x08 | - | 寄存器 | 立即数 | num1=nmu2 |
| 0x09 | ret | - | - | 返回 0 |
| 0x0A | load | 寄存器 | 寄存器 | num1=tran_pass[num2] |
| 0x0B | load | 寄存器 | 寄存器 | num1=const_ARR[num2] |
| 0x0C | xor | 寄存器 | 寄存器 | num1^=num2 |
| 0x0D | imul | 寄存器 | 寄存器 | num1*=num2 |
| 0x0E | DIV | 寄存器 | 寄存器 | num1/=num2 |
| 0x0F | shl | 寄存器 | 寄存器 | num1<<=num2 |
| 0x10 | shr | 寄存器 | 寄存器 | num1>>=num2 |
| 0xFF | ret | - | - | 返回 1 |
然后指令就是地址4110c0 所指向的内容:
即:
| 地址 | 数据 | 指令 |
|---|---|---|
| 004110c0 | 08 00 00 00 20 00 | mov R0, 0x20 |
| 004110c6 | 08 00 01 00 00 00 | mov R1, 0x00 |
| 004110cc | 08 00 02 00 01 00 | mov R2, 0x01 |
| 004110d2 | 03 00 01 00 00 00 | cmp R1, R0 |
| 004110d8 | 04 01 16 00 00 00 | jz +0x16 |
| 004110de | 08 00 0b 00 00 00 | mov RB, 0x00 |
| 004110e4 | 08 00 0c 00 00 00 | mov RC, 0x00 |
| 004110ea | 01 00 0c 00 0b 00 | add RC, RB |
| 004110f0 | 03 00 0b 00 01 00 | cmp RB, R1 |
| 004110f6 | 01 00 0b 00 02 00 | add RB, R2 |
| 004110fc | 04 02 fc ff 00 00 | jnz -0x04 |
| 00411102 | 08 00 03 00 08 00 | mov R3, 0x08 |
| 00411108 | 08 00 04 00 06 00 | mov R4, 0x06 |
| 0041110e | 08 00 09 00 10 00 | mov R9, 0x10 |
| 411114 | 0f 00 09 00 03 00 | shl R9, R3 |
| 0041111a | 08 00 0a 00 24 00 | mov Ra, 0x24 |
| 411120 | 01 00 09 00 0a 00 | add R9, Ra |
| 411126 | 01 00 09 00 0c 00 | add R9, RB |
| 0041112c | 0a 00 05 00 01 00 | LOAD R5, R1 //_pass |
| 411132 | 0d 00 05 00 09 00 | imul R5, R9 |
| 411138 | 05 00 06 00 05 00 | mov R6, R5 |
| 0041113e | 10 00 06 00 04 00 | shr R6, R4 |
| 411144 | 0b 00 07 00 01 00 | LOAD R7, R1 //_const |
| 0041114a | 03 00 06 00 07 00 | cmp R6, R7 |
| 411150 | 01 00 01 00 02 00 | add R1, R2 |
| 411156 | 04 01 e9 ff 00 00 | jz -0x17 |
| 0041115c | 04 02 01 00 00 00 | jnz +1 |
| 411162 | ff 00 00 00 00 00 | ret 1 |
| 411168 | 09 00 00 00 00 00 | ret 0 |
| 0041116e | ff 00 00 00 00 00 | ret 1 |
最后推出算式const_ARR[i]=(((0x10<<8)+0x24+ (i) )*tran_pass[i])>>6
但是这种直接分析太麻烦,也可以使用动态调试(推荐使用)
很容易就能看出算法为const_ARR[i] = (input[i] * out) >> 6,其中out的初值为0x1024,out += 1+i
爆破一下password:
1 | flagenc = [0x00000C5B, 0x00000CDD, 0x00000D1F, 0x000018C0, 0x000018C6, 0x00000C26, 0x00000E72, 0x00000DF7, 0x000019B1, 0x00000D41, 0x00000D08, 0x0000191C, 0x00000CD9, 0x00000EB1, 0x00000CEE, 0x00001A78, 0x00000D8B, 0x00000D99, 0x00000D64, 0x00000CED, 0x000019F8, 0x00000E61, 0x00001A7F, 0x00001AE7, 0x00000F26, 0x00001B34, 0x00001AD0, 0x00000D7C, 0x00000FC9, 0x00000E7E, 0x00001C0E, 0x00001BAE] |
所以用户名就是dd772,密码就是134bb097e43b292f4431b6cd8db194db
得到flag
2019 re Justre
A1BAAAAAAAAAAAAAAAAAAAAAAA
前8个A1BAAAAA为一组 xmm5中有4个
第二个8个,先判断是否为数字,不是的话判断是否为大写字母,是数字下面的标点以及大写字母的话v14=16*(该字符-7), 是大写字母下面的字符的话把v14=0,是数字前面的符号的话v14=16*该字符
可能是两个字节两个字节取,如果第二个是数字则将v10=v14+该数字,如果是大写字母v10=v14+该字符-55
然后还有很长的一段的运算两种情况都需要进行
AA 复制16份然后每八个一份放入
1 | .rdata:00404340 xmmword_404340 xmmword 3000000020000000100000000h |
AA 先被扩展为AAAAAAAA 然后再被扩展为相同的四个整数,然后每个再*01010101
xmm4 = (AA*4)*01010101
xmm5 = A1BAAAAA*4
状态变量1 0x405018 更新:
xmm1=(xmm4 + 0x405018)
xmm2 = (xmm5 + 0x404340)
0x405018 = xmm2 ^ xmm1
状态变量2 0x405028更新:
xmm1 = (xmm4 + 0x405028)
xmm2 = (0x404340 + 0x404350 + xmm5)
0x405028 = xmm2 ^ xmm1
状态变量3 0x405038更新:
xmm1 = (xmm4 + 0x405038)
xmm2 = (0x404340 + 0x404360 + xmm5)
0x405038 = xmm2 ^ xmm1
状态变量4 0x405048更新:
xmm4 = xmm4 + 0x405048
xmm1 = (0x404340 + 0x404370 + xmm5)
0x405048 = xmm4 ^ xmm1
v2 = A1BAAAAA v10 = AA
v19=16
while(v19<24){
*( 0x405018 + v19 )= (v19 + v2)^ (16843009 * v10 + *( 0x405018 + v19 ));
++v19;
}
实际上就是input[i] = (flag1+i) ^ (flag2 + input[i])
但是最后比较的步骤只需要关注第一个32位的部分就行了,所以除状态一外都不需要分析,因为只有两个未知量,所以用三个式子就能爆破出来。
最后对比的字节为:
1 | 0x83EC8B55, 0xEC81F0E4, 0x00000278 |
所以爆破就行了:
1 |
|
然后就是后16个字符,这里的检验函数4018A0用了SMC,需要解密一下,来到4018A0使用OD将其dump出来
用ida打开,即可查看逻辑
查看逻辑,这里sub_401250明显是des算法,调用三次,分别是加密、解密、加密,即3DES算法
到底是不是,找个网站解密试试就知道了,发现可以解出来:
最后放入程序验证,成功得到flag
flag{13242238160dcc509a6f75849b}
2019 re webAssemble
.wasm文件逆向管用套路:
1 | ~/tools/wasm2c webassembly.wasm -o web.c |
然后ida分析
main函数主要加密函数就这俩,f54有点复杂,先瞅瞅f15
观察到魔数9E3779B9,然后循环了32次,应该是一个循环32次的XTEA算法
该算法被调用了4次,四次算法密钥均为0
那这样的话就找找后续的对比操作,找最后需对比的字符串
可以看到后面一堆赋值操作
是用加密后的每一位分别异或一个值,然后取低8位,最后和前面一次算出来的值迭代相加。
然后最后判断是否相等
那就假设每个式子的结果为0,那么所有式子就都成立,也就是说最后经算法加密后的值为:
1 | 0x4e,0x6e,0xc7,0xea, |
先找个现成的算法解密一下:
1 |
|
运行发现直接跑出来flag了:
并且验证成功
那就不需要再继续分析f54了,证明该函数和flag验证流程没关系
2019 re BoringCrypto
安卓native类型逆向,首先分析java中main函数,可知check函数使用的是jni的静态注册方法,因此应该比较容易找到,那么就找到native.so然后用ida打开:
这个函数反汇编之后都有将近5000行,太复杂,看不太懂qaq

