Findthekey

文件为pyc, 用uncompyle6进行反编译, 得到python源码如下

# uncompyle6 version 3.7.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.17 (default, Jul 20 2020, 15:37:01)
# [GCC 7.5.0]
# Embedded file name: findkey
# Compiled at: 2016-04-30 17:54:18
import sys
lookup = [
196, 153, 149, 206, 17, 221, 10, 217, 167, 18, 36, 135, 103, 61, 111, 31, 92, 152, 21, 228, 105, 191, 173, 41, 2, 245, 23, 144, 1, 246, 89, 178, 182, 119, 38, 85, 48, 226, 165, 241, 166, 214, 71, 90, 151, 3, 109, 169, 150, 224, 69, 156, 158, 57, 181, 29, 200, 37, 51, 252, 227, 93, 65, 82, 66, 80, 170, 77, 49, 177, 81, 94, 202, 107, 25, 73, 148, 98, 129, 231, 212, 14, 84, 121, 174, 171, 64, 180, 233, 74, 140, 242, 75, 104, 253, 44, 39, 87, 86, 27, 68, 22, 55, 76, 35, 248, 96, 5, 56, 20, 161, 213, 238, 220, 72, 100, 247, 8, 63, 249, 145, 243, 155, 222, 122, 32, 43, 186, 0, 102, 216, 126, 15, 42, 115, 138, 240, 147, 229, 204, 117, 223, 141, 159, 131, 232, 124, 254, 60, 116, 46, 113, 79, 16, 128, 6, 251, 40, 205, 137, 199, 83, 54, 188, 19, 184, 201, 110, 255, 26, 91, 211, 132, 160, 168, 154, 185, 183, 244, 78, 33, 123, 28, 59, 12, 210, 218, 47, 163, 215, 209, 108, 235, 237, 118, 101, 24, 234, 106, 143, 88, 9, 136, 95, 30, 193, 176, 225, 198, 197, 194, 239, 134, 162, 192, 11, 70, 58, 187, 50, 67, 236, 230, 13, 99, 190, 208, 207, 7, 53, 219, 203, 62, 114, 127, 125, 164, 179, 175, 112, 172, 250, 133, 130, 52, 189, 97, 146, 34, 157, 120, 195, 45, 4, 142, 139]
pwda = [
188, 155, 11, 58, 251, 208, 204, 202, 150, 120, 206, 237, 114, 92, 126, 6, 42]
pwdb = [53, 222, 230, 35, 67, 248, 226, 216, 17, 209, 32, 2, 181, 200, 171, 60, 108]
flag = raw_input('Input your Key:').strip()
if len(flag) != 17:
print 'Wrong Key!!'
sys.exit(1)
flag = flag[::-1]
for i in range(0, len(flag)):
if ord(flag[i]) + pwda[i] & 255 != lookup[(i + pwdb[i])]:
print 'Wrong Key!!'
sys.exit(1)

print 'Congratulations!!'
# okay decompiling findkey.pyc

则解密脚本如下:

lookup = [
196, 153, 149, 206, 17, 221, 10, 217, 167, 18, 36, 135, 103, 61, 111, 31, 92, 152, 21, 228, 105, 191, 173, 41, 2, 245, 23, 144, 1, 246, 89, 178, 182, 119, 38, 85, 48, 226, 165, 241, 166, 214, 71, 90, 151, 3, 109, 169, 150, 224, 69, 156, 158, 57, 181, 29, 200, 37, 51, 252, 227, 93, 65, 82, 66, 80, 170, 77, 49, 177, 81, 94, 202, 107, 25, 73, 148, 98, 129, 231, 212, 14, 84, 121, 174, 171, 64, 180, 233, 74, 140, 242, 75, 104, 253, 44, 39, 87, 86, 27, 68, 22, 55, 76, 35, 248, 96, 5, 56, 20, 161, 213, 238, 220, 72, 100, 247, 8, 63, 249, 145, 243, 155, 222, 122, 32, 43, 186, 0, 102, 216, 126, 15, 42, 115, 138, 240, 147, 229, 204, 117, 223, 141, 159, 131, 232, 124, 254, 60, 116, 46, 113, 79, 16, 128, 6, 251, 40, 205, 137, 199, 83, 54, 188, 19, 184, 201, 110, 255, 26, 91, 211, 132, 160, 168, 154, 185, 183, 244, 78, 33, 123, 28, 59, 12, 210, 218, 47, 163, 215, 209, 108, 235, 237, 118, 101, 24, 234, 106, 143, 88, 9, 136, 95, 30, 193, 176, 225, 198, 197, 194, 239, 134, 162, 192, 11, 70, 58, 187, 50, 67, 236, 230, 13, 99, 190, 208, 207, 7, 53, 219, 203, 62, 114, 127, 125, 164, 179, 175, 112, 172, 250, 133, 130, 52, 189, 97, 146, 34, 157, 120, 195, 45, 4, 142, 139]
pwda = [
188, 155, 11, 58, 251, 208, 204, 202, 150, 120, 206, 237, 114, 92, 126, 6, 42]
pwdb = [53, 222, 230, 35, 67, 248, 226, 216, 17, 209, 32, 2, 181, 200, 171, 60, 108]

flag = b""

for i in range(17):
flag += bytes([(lookup[(i + pwdb[i])] - pwda[i]) & 0xff])

print(flag[::-1])

得flag: PCTF{PyC_Cr4ck3r}

CrackMe

.Net架构, 用dnspy反编译, 找到目标函数

Jarvis_OJ Re CrackMe

则解密脚本为

import base64

print(base64.b64decode("UENURntFYTV5X0RvX05ldF9DcjRjazNyfQ=="))

得到flag: PCTF{Ea5y_Do_Net_Cr4ck3r}

CrackMe2

.Net架构, 用dnspy反编译,找到加密函数

20201021101142

设断点进行调试,追溯到函数如下

20201021101244

则函数逻辑如下:

输入进行AES加密再进行base64编码,最终跟一串常量进行对比

常量可由动态调试得到

20201021101456

from Crypto.Cipher import AES
import base64
key = b"pctf2016pctf2016pctf2016pctf2016"
obj = AES.new(key,AES.MODE_ECB)
cipher = b"x/nzolo0TTIyrEISd4AP1spCzlhSWJXeNbY81SjPgmk="
cipher = base64.b64decode(cipher)
plaintext = obj.decrypt(cipher)
print(plaintext)

运行得flag: PCTF{Dot_Net_UnPack3r_yoo}

Fibinacii

本题为用jar2exe打包的程序。jar2exe将java和jvm打包生成exe,从而可以在没有JAVA的机器上直接运行,原理是通过JNI的接口,创建JVM来执行封装的java代码,与exe4j将jar导出生成临时文件不同,jar2exe是在内部执行的,因此无法直接找到临时文件来反编译

jwscan.jar可以检查出程序用什么打包

D:\CTF\Games\Exam\Jarvis\Re\Fibonacci>java -jar JWScan\jwscan.jar Fibonacci.exe
JWScan 0.2.1 -- by Katja Hahn

scanning file ...

file name: Fibonacci.exe

Signatures found:
* Jar2Exe.com signature
* PZIP Magic Number (weak indication for embedded zip)

ZIP/Jar offsets: 0x72e6d

归根到底依旧是Java, 那么仍然是通过JVM来执行字节码的JAVA, 而Jae2Exe的打包有三种等级, No Hiding, No Encryption, Hidden ArchiveHidden Archive + Encrypted Class Names 本题应该属于后两种

根据夜影师傅的博客,找到提取经过Jar2Exe编译加密的源代码 教程1, 原理是通过java提供的javaagent接口,使得每个方法执行之前都先执行dump函数, 缺点就是没有被执行的方法就不会被dump了。

下载https://github.com/slavemaster/e2j的e2j后在所在目录下设置set JAVA_TOOL_OPTIONS=-javaagent:e2j-agent.jar即可

然后运行需要的程序,显示如下

D:\CTF\Games\Exam\Jarvis\Re\Fibonacci>Fibonacci.exe
Picked up JAVA_TOOL_OPTIONS: -javaagent:e2j-agent.jar
来让我们玩一个数列游戏:
a[0]=0,a[1]=1
a[2]=1,a[3]=2
a[4]=3,a[5]=5
..............
请计算a[100000000000000]:

答案错误!!

目录下便产生了e2j-xxx.dump.jar, 拖入jd-gui得到所在类,但类b中因为没有方法,所以没有被JavaAgent抓到,因此得到的类是不完整的

package top.phrack.ctf.Fibonacci;

import java.util.Scanner;

public class Fibonacci {
private static void heheda() {
String hello = hello(new String(b.y), new String(b.x));
}

public static void main(String[] args) {
System.out.println("来让我们玩一个数列游戏:");
System.out.println("a[0]=0,a[1]=1");
System.out.println("a[2]=1,a[3]=2");
System.out.println("a[4]=3,a[5]=5");
System.out.println("..............");
System.out.println("请计算a[100000000000000]:");
String nextLine = new Scanner(System.in).nextLine();
System.out.println("答案错误!!");
}

private static String hello(String aaa, String bbb) {
int[] iS = new int[256];
byte[] iK = new byte[256];
for (int i = 0; i < 256; i++) {
iS[i] = i;
}
for (short i2 = 0; i2 < 256; i2 = (short) (i2 + 1)) {
iK[i2] = (byte) bbb.charAt(i2 % bbb.length());
}
int j = 0;
for (int i3 = 0; i3 < 255; i3++) {
j = ((iS[i3] + j) + iK[i3]) % 256;
int temp = iS[i3];
iS[i3] = iS[j];
iS[j] = temp;
}
int i4 = 0;
int j2 = 0;
char[] iInputChar = aaa.toCharArray();
char[] iOutputChar = new char[iInputChar.length];
for (short x = 0; x < iInputChar.length; x = (short) (x + 1)) {
i4 = (i4 + 1) % 256;
j2 = (iS[i4] + j2) % 256;
int temp2 = iS[i4];
iS[i4] = iS[j2];
iS[j2] = temp2;
iOutputChar[x] = (char) (iInputChar[x] ^ ((char) iS[(iS[i4] + (iS[j2] % 256)) % 256]));
}
return new String(iOutputChar);
}
}

后根据http://reverseengineeringtips.blogspot.com/2014/12/unpacking-jar2exe-21-extracting-jar.html知道java代码被存放在RCDATA数据中, 因此只要找到RCDATA的数据位置,在程序运行过程中设置访问断点,当程序解密时找到解密后的代码位置即可得到相应的java代码

通过CFF_Explorer查到RCDATA的HEX数据

20201021161811

通过x64dbg直接搜索对于的特征值,于0x477398找到

20201021162236

设置内存访问断点,运行到解密循环

20201021162642

根据代码逻辑大致可猜测R10指向的内存是JAVA的字节码,而R11就是范围了

使程序解密完成,即运行到0x000000000041E713, 使用scylla插件Dump内存, 保存为1.jar

20201021163833

使用binwalk进行分离文件

➜  Desktop binwalk -e 1.jar

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
544 0x220 Zip archive data, at least v2.0 to extract, compressed size: 66, uncompressed size: 73, name: b5f78a55
668 0x29C Zip archive data, at least v2.0 to extract, compressed size: 193, uncompressed size: 386, name: 98065b2b
915 0x393 Zip archive data, at least v2.0 to extract, compressed size: 563, uncompressed size: 791, name: c7dce9e9
1532 0x5FC Zip archive data, at least v2.0 to extract, compressed size: 1389, uncompressed size: 2243, name: 1ab0208d

其中有两个文件为标准的class文件

➜  Desktop file _1.jar.extracted/*
_1.jar.extracted/1ab0208d: compiled Java class data, version 51.0 (Java 1.7)
_1.jar.extracted/220.zip: Java archive data (JAR)
_1.jar.extracted/98065b2b: XML 1.0 document, ASCII text, with CRLF line terminators
_1.jar.extracted/b5f78a55: ASCII text, with CRLF line terminators
_1.jar.extracted/c7dce9e9: compiled Java class data, version 51.0 (Java 1.7)

将这俩修改后缀名为*.class拖进jd-gui进行查看,其中c7dce9e9.class可以得到b类

package top.phrack.ctf.Fibonacci;

public class b {
public static char[] x = {'}', 16, 253, 201, 11, 22, '9', 'D', '7', ',', ' ', 205};
public static char[] y = {'t', 150, 174, 'D', 180, 'Z', 214, 189, 'O', '5', 133, 10, '+', '+', 189, 217, 'O', '`', 19, 138, 199, 128, '@', 220, 222, 234, 11, 175, 228, 129};
}

那么完整代码就有了,这题跟斐波那契数列一点关系都没有,cv工程师上线,写个java代码编译运行

import java.util.Scanner;

public class main {
public static void main(String[] args)
{
Fibonacci fibonacci = new Fibonacci();
System.out.println(fibonacci.heheda());
}
}

class Fibonacci
{
public static String heheda()
{
String bb = new String(b.x);
String cb = new String(b.y);
String m = hello(cb, bb);
return m;
}

private static String hello(String aaa, String bbb)
{
int[] iS = new int[256];
byte[] iK = new byte[256];
for (int i = 0; i < 256; i++) {
iS[i] = i;
}
int j = 1;
for (short i = 0; i < 256; i = (short)(i + 1)) {
iK[i] = ((byte)bbb.charAt(i % bbb.length()));
}
j = 0;
for (int i = 0; i < 255; i++)
{
j = (j + iS[i] + iK[i]) % 256;
int temp = iS[i];
iS[i] = iS[j];
iS[j] = temp;
}
int i = 0;
j = 0;
char[] iInputChar = aaa.toCharArray();
char[] iOutputChar = new char[iInputChar.length];
for (short x = 0; x < iInputChar.length; x = (short)(x + 1))
{
i = (i + 1) % 256;
j = (j + iS[i]) % 256;
int temp = iS[i];
iS[i] = iS[j];
iS[j] = temp;
int t = (iS[i] + iS[j] % 256) % 256;
int iY = iS[t];
char iCY = (char)iY;
iOutputChar[x] = ((char)(iInputChar[x] ^ iCY));
}
return new String(iOutputChar);
}
}

public class b {
public static char[] x = {'}', 16, 253, 201, 11, 22, '9', 'D', '7', ',', ' ', 205};
public static char[] y = {'t', 150, 174, 'D', 180, 'Z', 214, 189, 'O', '5', 133, 10, '+', '+', 189, 217, 'O', '`', 19, 138, 199, 128, '@', 220, 222, 234, 11, 175, 228, 129};
}

运行得flag: PCTF{1ts_not_5c2ipt_Chall3nge}

本题参考:

  1. 171130 逆向-JarvisOJ(Fibonacci)

  2. Unpacking Jar2Exe 2.1: Extracting The Jar File At All 3 Protection Levels

软件密码破解-1

程序打开来7千多个函数,找不到入口,大致看了一下,发现有个地方很可疑, 这里由于动态调试过,因此基址会有出入(起始地址0x00B01000)

20201021191849

Alt + A 修改为 Unicode C-style(16 bits) 得到中文 你赢了

20201021191959

找到函数0x00B01BB0, 猜测校验及加密如下

20201021192141

其中byte_C777F8是生成出来的,可用动态调试得到,但是本题用了反调试,这里直接暴力绕过,即jz => jmp即可

text:00B01BE8                 jmp     short loc_B01BF2 ; Keypatch modified this from:
.text:00B01BE8 ; jz short loc_E21BF2
.text:00B01BEA ; ---------------------------------------------------------------------------
.text:00B01BEA push 0 ; uExitCode
.text:00B01BEC call ds:ExitProcess
.text:00B01BF2 ; ---------------------------------------------------------------------------
.text:00B01BF2
.text:00B01BF2 loc_B01BF2: ; CODE XREF: sub_B01BB0+38↑j

则在调试过程中可得byte_C777F8,最终解密脚本如下

s = [0x28, 0x57, 0x64, 0x6B, 0x93, 0x8F, 0x65, 0x51, 0xE3, 0x53,
0xE4, 0x4E, 0x1A, 0xFF]
check_num = [0x1B, 0x1C, 0x17, 0x46, 0xF4,
0xFD, 0x20, 0x30, 0xB7, 0x0C, 0x8E, 0x7E, 0x78, 0xDE]

flag = b""
for i in range(14):
flag += bytes([s[i] ^ check_num[i]])
print(flag)

得到flag:3Ks-grEaT_j0b!