文章总结: 本文解析了CTF逆向挑战babyJava和hardtest的解题过程。babyJava通过反编译发现需将输入字符串反转后Base64编码比对,直接解码反转即可得flag。hardtest涉及复杂加密流程,包括字节移位、模幂运算和S盒替换,需逆向分析各函数后爆破求解。文档提供了完整的Python实现代码和逆向思路。 综合评分: 85 文章分类: CTF,逆向分析,二进制安全,WEB安全,漏洞分析
ctfplus-逆向每周练习-baby_Java、hardtest
原创
李北辰 李北辰
SPEEDCoding
2026年6月14日 20:30 山西
在小说阅读器读本章
去阅读
#
1.baby_Java
使用idea打开class文件反编译,源码非常简短:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//import java.util.Base64;import java.util.Scanner;public class ReverseChallenge {public ReverseChallenge() { }public static void main(String[] args) { String encodedFlag = "fWVzcmV2ZXJfb3RfeXNhZV9zaV9hdmFKe0ZUQ1NEUA=="; Scanner scanner = new Scanner(System.in); System.out.print("Check your flag: "); String input = scanner.nextLine(); String reversed = (new StringBuilder(input)).reverse().toString(); String encodedInput = Base64.getEncoder().encodeToString(reversed.getBytes());if (encodedInput.equals(encodedFlag)) { System.out.println("Correct flag!"); } else { System.out.println("Incorrect flag!"); } }}
可以看到是用户输入字符串,然后反转再base64编码,与目标比较,直接做python脚本解题,把目标base64编码字符串解码然后反转就可以拿到flag:
importbase64target_base64="fWVzcmV2ZXJfb3RfeXNhZV9zaV9hdmFKe0ZUQ1NEUA=="flag_reverse=base64.b64decode(target_base64)print(flag_reverse[::-1])
输出:
'PDSCTF{Java_is_easy_to_reverse}'
2.hardtest
拖入IDA中反编译,进入主函数,发现需要输入一个随机数进入下一步,需要用户输入flag,输入进来后计算长度,接下来就是调用init_v10(重新命名了一下)这个函数,根据输入的flag生成v10。
init_v10函数进入后继续分析,v10的每位都需要根据sub_12A9生成
继续进入sub_12A9函数分析,传入的下标范围限制在1-7(包括7),input_byte左移index位,然后input_byte再右移(8-index)位,两者再做或运算,看汇编代码,这两次都不影响一开始的input_byte的初始值,把它重命名为leftandright_get_onebyte:
转换为python脚本:
def leftandright_get_onebyte(byte, offset): tmp1 = (byte << offset) & 0xFF tmp2 = (byte >> (8 - offset)) & 0xFF return tmp1 | tmp2def init_data(input_array, data): for i in range(len(data)): data[i] = leftandright_get_onebyte(input_array[i], ((i % 7) + 1)) return data
然后开始加密函数sub_13E1,这个函数对刚才生成的data逐字节加密:
这里对每个字节再做一遍刚才分析的左移右移操作,然后调用函数sub_1313,再调用函数sub_12DE,再byte_2020中检索出来对应的字节替换,加密完成,先看sub1313:
这里如果传入的参数等于0则直接返回0,不为0的话,开始进入循环,v3初始值是255(0xff),每次加密完后,右移1位,对应的每次循环的v3等于:0xff,0x7f,0x3f,0x1f,0x0f,0x07,0x03,0x01,8次循环,循环体内v2的初始值是1,然后每次如果v3是奇数的话(确实每次都是奇数),v2等于v4乘v2再对257求余,v4初始值是传入的a1,然后v4又等于v4乘v4再对257求余,sub1313转换成python脚本如下:
def sub1313(b): if b == 0x00: return 0x00 v3 = 255 v2 = 1 v4 = b while(v3 != 0): if (v3 & 0x01) != 0: v2 = v4 * v2 % 257 v4 = v4 * v4 % 257 v3 = v3 >> 1 return v2 & 0xFF
然后看sub_12DE,传入的是上一步函数获取的字节,第二个参数是常数2,然后把这个字节右移对应offset获得一个数,左移(8-offset)位获得一个数,两个数进行或运算
转换成python代码为:
def sub_12DE(b, offset): tmp1 = (b >> offset) & 0xff tmp2 = (b << (8 - offset)) & 0xff return tmp1 | tmp2
这一步结束后拿到一个0-255的下标,然后去sbox里面替换值,sbox是byte_2020:
加密完成后就是跟密文字节数组byte_2120做比较:
完整的python实现就是:
def leftandright_get_onebyte(byte, offset): tmp1 = (byte << offset) & 0xFF tmp2 = (byte >> (8 - offset)) & 0xFF return tmp1 | tmp2def init_data(input_array, data): for i in range(len(data)): data[i] = leftandright_get_onebyte(input_array[i], ((i % 7) + 1)) return datadef sub1313(b): if b == 0x00: return 0x00 v3 = 255 v2 = 1 v4 = b while(v3 != 0): if (v3 & 0x01) != 0: v2 = v4 * v2 % 257 v4 = v4 * v4 % 257 v3 = v3 >> 1 return v2 & 0xFFdef sub_12DE(b, offset): tmp1 = (b >> offset) & 0xff tmp2 = (b << (8 - offset)) & 0xff return tmp1 | tmp2def sub13E1(b): tmp1 = leftandright_get_onebyte(b ^ 0x5a, 3) tmp2 = sub1313(((16 * ((3 * (tmp1 >> 4)) & 0xF)) | (5 * (tmp1 & 0xF)) & 0xF) & 0xFF) return sbox[sub_12DE(tmp2, 2)]sbox = [99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22]input_bytes = b'testtesttesttest'v10 = init_data(bytearray(input_bytes), [0] * len(input_bytes))data = []for i in range(len(input_bytes)): data.append(sub13E1(v10[i]))print(data)
这个加密算法是根据输入字节数组的每位进行运算,每位直接互相不影响,所以可以根据目标字节数组进行爆破即可:
def leftandright_get_onebyte(byte, offset): tmp1 = (byte << offset) & 0xFF tmp2 = (byte >> (8 - offset)) & 0xFF return tmp1 | tmp2def init_data(input_array, data): for i in range(len(data)): data[i] = leftandright_get_onebyte(input_array[i], ((i % 7) + 1)) return datadef sub1313(b): if b == 0x00: return 0x00 v3 = 255 v2 = 1 v4 = b while(v3 != 0): if (v3 & 0x01) != 0: v2 = v4 * v2 % 257 v4 = v4 * v4 % 257 v3 = v3 >> 1 return v2 & 0xFFdef sub_12DE(b, offset): tmp1 = (b >> offset) & 0xff tmp2 = (b << (8 - offset)) & 0xff return tmp1 | tmp2def sub13E1(b): tmp1 = leftandright_get_onebyte(b ^ 0x5a, 3) tmp2 = sub1313(((16 * ((3 * (tmp1 >> 4)) & 0xF)) | (5 * (tmp1 & 0xF)) & 0xF) & 0xFF) return sbox[sub_12DE(tmp2, 2)]sbox = [99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22]input_bytes = b'testtesttesttest'v10 = init_data(bytearray(input_bytes), [0] * len(input_bytes))data = []for i in range(len(input_bytes)): data.append(sub13E1(v10[i]))print(data)target = [151, 213, 96, 67, 180, 16, 67, 115, 15, 218, 67, 205, 211, 232, 115, 74, 148, 195, 205, 113, 189, 220, 151, 26]flag = b''for i in range(len(target)): for c in range(0x20, 0x7f): cipher1 = leftandright_get_onebyte(c, ((i % 7) + 1)) if sub13E1(cipher1) == target[i]: flag += bytes([c]) breakprint(flag)
运行后成功解密:
b'Bl@st1ng_1s_a_g00d_Way!!'
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:SPEEDCoding 李北辰 李北辰《ctfplus-逆向每周练习-baby_Java、hardtest》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论