AndroDbgMe
Use JEB to open (jadx opens with garbled characters), the main logic is as follows:
The application will detect whether it is a debugging environment. If it is a debugging environment, it will output a fixed value s1, s2 is fixed, and perform RC4 encryption. If it is not debugging, input will be used as the parameter encryption of RC4. After installing and using it on a mobile phone, I found that the direct input and output would be garbled. Based on the title of the question, I guessed that the test center is an Android dynamic tune. Therefore, the results can be obtained by direct tuning. For static analysis, you can also find an RC4 decryption script on the Internet.
Use JEB dynamic tuning
When you click the button on your mobile phone, there will be a flag.
AndroGenshin
Use gadx-gui to open the AndroidManifest.xml found under the resource file and find the MainActivity as follows
There are two encryption functions, which according to their names are modified RC4 and modified Base64.
RC4 is a symmetric encryption. All you need to do is use existing codes.
public class it_is_not_RC4 { public static String rc4(String keyStr, int[] data) { byte[] key = keyStr.getBytes(); int[] s = new int[256]; int[] k = new int[256]; int j = 0; for (int i = 0; i < 256; i + + ) { s[i] = i; k[i] = key[i % key.length]; } for (int i2 = 0; i2 < 256; i2 + + ) { j = (s[i2] + j + k[i2]) & 255; int temp = s[i2]; s[i2] = s[j]; s[j] = temp; } StringBuilder result = new StringBuilder(); int j2 = 0; int i3 = 0; for (int i4 : data) { i3 = (i3 + 1) & 255; j2 = (s[i3] + j2) & 255; int temp2 = s[i3]; s[i3] = s[j2]; s[j2] = temp2; int rnd = s[(s[i3] + s[j2]) & amp; 255]; result.append((char) (i4 ^ rnd)); } return result.toString(); } public static void main(String[] args) { String username = "genshinimpact"; int[] base64_table = {125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211, 164, 94, 75, 16, 32, 33, 193, 160, 120, 47, 30, 127, 157, 66, 163, 181, 177, 47, 0, 236, 106, 107, 144, 231, 250, 16, 36, 34, 91, 9, 188, 81, 5, 241, 235, 3, 54, 150, 40, 119, 202, 150}; String ret1 =rc4(username, base64_table); System.out.println(ret1); // BADCFEHGJILKNMPORQTSVUXWZYbadcfehgjilknmporqtsvuxwzy1032547698/ + } }
The retval obtained by RC4 encrypting username and base64table (BADCFEHGJILKNMPORQTSVUXWZYbadcfehgjilknmporqtsvuxwzy1032547698/ +) is used as the Base64 code table. Use this code table to decode the base64 of the parameters in equals.
C_C + +
exeinfo is written in C# and opened using 32/64-bit dnspy
Enter the main function
It’s just some addition and subtraction operations that you can understand if you look carefully
Just solve it in reverse and you’ll be fine.
data = [68, 75, 66, 72, 99, 19, 19, 78, 83, 74, 91, 86, 35, 39, 77, 85, 44, 89, 47, 92, 49, 88, 48, 91, 88, 102, 105, 51, 76, 115, 0x84, 125, 79, 122, 0x99] text2 = "NEWSTAR" #text2 Converting to int is easier to calculate data2= [78, 69, 87, 83, 84, 65, 82] for i in range(7): data[i] -= (i ^ (-(data2[i] % 4))) data[i + 7] -= data2[i] % 5 data[i + 14] -= (2 * i) data[i + 21] -= (i ^ 2) data[i + 28] -= (data2[i] // 5) + 10 for i in range(35): data[i] + = 32 data[i] -= i print(chr(data[i]),end='')#flag{45dg_ng78_d8b5_1a7d_gh47_kd5b}
easy_enc
Open 64-bit IDA, create a thread and execute 4 encryption functions on the input str (if you are not sure, you can adjust the order to know the execution order of the four functions)
data part
The encryption functions are not complicated and easy to understand, but there are many multiplication and modular operations that can lead to data loss. Therefore, we cannot solve it directly in reverse, and we need to use (Z3) blasting.
from z3 import * import numpy as np data = [0xE8, 0x80, 0x84, 0x08, 0x18, 0x3c, 0x78, 0x68, 0x00, 0x70, 0x7C, 0x94, 0xC8, 0xE0, 0x10, 0xEC, 0xB4, 0xAC, 0x68, 0xA8, 0xC, 0x1C, 0x90, 0xCC, 0x54, 0x3C, 0x14, 0xDC, 0x30] data_len = len(data) # Solve the first function z3 # Uppercase letters -52(4) & + 65(A) # Lowercase letters -89(Y) & + 97(a) # Number -45(-) + 48(0) # The second function # input[i] + = str[i%len] # The third function # Bitwise negation # The fourth function # Each bit * 0x34 prefix = "NewStarCTF" solver = Solver() tmp = [BitVec(f'tmp_{i}', 8) for i in range(len(data))] #Add constraints for i in range(data_len): char_code = data[i] t1 = (tmp[i] - 52) % 26 + 65 t2 = t1 + ord(prefix[i]) t3 = ~t2 t4 = t3 * 0x34 t4 = t4 & 0xFF container1 = t4 == char_code t1 = (tmp[i] - 89) % 26 + 97 t2 = t1 + ord(prefix[i]) t3 = ~t2 t4 = t3 * 0x34 t4 = t4 & 0xFF container2 = t4 == char_code solver.add(Or(And(tmp[i] >= ord('A'), tmp[i] <= ord('Z'),container1), And(tmp[i]>= ord('a'), tmp[i] <= ord('z'), container2) ) ) # t1 = (tmp[i] - 52) % 26 + 65 # t2 = t1 + ord(prefix[i]) #t3 = ~t2 #t4 = t3 * 0x34 # t4 = t4 & 0xFF # container3 = t4 == char_code if solver.check() == sat: # Get solution model = solver.model() # print(model) # # Print solution solution = [chr(model[tmp[i]].as_long()) if model[tmp[i]] is not None else None for i in range(len(data))] print("".join(solution[i] for i in range(len(data)))) else: print("No solution") # BruteForceIsAGoodwaytoGetFlag
Perals
Use IDA64 to open it and find that some places are popular. Check the assembly and see the adding instructions, which causes IDA analysis to report an error. My solution is to put breakpoints in all the popular places of JUMP() and then dynamically debug it again to make IDA analysis normal.
The main function is like this
Enter the encode function
v4 The value determined by a2 (fixed value 25) can be obtained directly from the stack.
Then a1[j]=v4[a1[j]] inverse v4.index(data[i])
import hashlib def md5_encode(data): md5 = hashlib.md5() md5.update(data.encode('utf-8')) return md5.hexdigest() data = [ 0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89, 0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1, 0xD2, 0x82, 0xD3, 0xDE, 0x87] table = [ 0xE6, 0xE7, 0xE4, 0xE5, 0xE2, 0xE3, 0xE0, 0xE1, 0xEE, 0xEF, 0xEC, 0xED, 0xEA, 0xEB, 0xE8, 0xE9, 0xF6, 0xF7, 0xF4, 0xF5, 0xF2, 0xF3, 0xF0, 0xF1, 0xFE, 0xFF, 0xFC, 0xFD, 0xFA, 0xFB, 0xF8, 0xF9, 0xC6, 0xC7, 0xC4, 0xC5, 0xC2, 0xC3, 0xC0, 0xC1, 0xCE, 0xCF, 0xCC, 0xCD, 0xCA, 0xCB, 0xC8, 0xC9, 0xD6, 0xD7, 0xD4, 0xD5, 0xD2, 0xD3, 0xD0, 0xD1, 0xDE, 0xDF, 0xDC, 0xDD, 0xDA, 0xDB, 0xD8, 0xD9, 0xA6, 0xA7, 0xA4, 0xA5, 0xA2, 0xA3, 0xA0, 0xA1, 0xAE, 0xAF, 0xAC, 0xAD, 0xAA, 0xAB, 0xA8, 0xA9, 0xB6, 0xB7, 0xB4, 0xB5, 0xB2, 0xB3, 0xB0, 0xB1, 0xBE, 0xBF, 0xBC, 0xBD, 0xBA, 0xBB, 0xB8, 0xB9, 0x86, 0x87, 0x84, 0x85, 0x82, 0x83, 0x80, 0x81, 0x8E, 0x8F, 0x8C, 0x8D, 0x8A, 0x8B, 0x88, 0x89, 0x96, 0x97, 0x94, 0x95, 0x92, 0x93, 0x90, 0x91, 0x9E, 0x9F, 0x9C, 0x9D, 0x9A, 0x9B, 0x98, 0x99, 0x66, 0x67, 0x64, 0x65, 0x62, 0x63, 0x60, 0x61, 0x6E, 0x6F, 0x6C, 0x6D, 0x6A, 0x6B, 0x68, 0x69, 0x76, 0x77, 0x74, 0x75, 0x72, 0x73, 0x70, 0x71, 0x7E, 0x7F, 0x7C, 0x7D, 0x7A, 0x7B, 0x78, 0x79, 0x46, 0x47, 0x44, 0x45, 0x42, 0x43, 0x40, 0x41, 0x4E, 0x4F, 0x4C, 0x4D, 0x4A, 0x4B, 0x48, 0x49, 0x56, 0x57, 0x54, 0x55, 0x52, 0x53, 0x50, 0x51, 0x5E, 0x5F, 0x5C, 0x5D, 0x5A, 0x5B, 0x58, 0x59, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 0x2E, 0x2F, 0x2C, 0x2D, 0x2A, 0x2B, 0x28, 0x29, 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3E, 0x3F, 0x3C, 0x3D, 0x3A, 0x3B, 0x38, 0x39, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, 0x0E, 0x0F, 0x0C, 0x0D, 0x0A, 0x0B, 0x08, 0x09, 0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1E, 0x1F, 0x1C, 0x1D, 0x1A, 0x1B, 0x18, 0x19] flag ='' for i in range(len(data)): flag + =chr(table.index(data[i])) print(md5_encode(flag)) #d780c9b2d2aa9d40010a753bc15770de
Pzthon
Python packaged exe can see more information using exeinfo
If you want to unpack the program packaged by pyinstaller, you need the corresponding python version to unpack it.
Use 010 to open PZthon.exe and search for the keyword MEI to find out that the Python version is 3.9
I installed python3.9 on the virtual machine and used the script pyinstxtractor.py to unpack and obtain pyc (the virtual machine was reset and no files were left)
Find the PZthon.pyc file in this output file. Use the online website python decompilation – online tool (tool.lu)
Get source code
Reverse the process
enc = [ 115, 121, 116, 114, 110, 76, 37, 96, 88, 116, 113, 112, 36, 97, 65, 125, 103, 37, 96, 114, 125, 65, 39, 112, 70, 112, 118, 37, 123, 113, 69, 79, 82, 84, 89, 84, 77, 76, 36, 112, 99, 112, 36, 65, 39, 116, 97, 36, 102, 86, 37, 37, 36, 104] flag = "" for i in enc: flag + = chr(i ^ 21) print(flag) #flag{Y0uMade1tThr0ughT2eSec0ndPZGALAXY1eve1T2at1sC001}
R4ndom
IDA64 opens the main program with clear logic
But Rkey is generated by rand() and I was dumbfounded at first sight.
But then I learned about pseudo-random (the random number generated using a fixed seed is fixed). Find the Srand() function used to set the seed. In the b function,
There is an anti-debugging function in function a. If you want to adjust the function, you can skip it by changing the flag directly.
The srand() function generates random numbers differently in win and linux. Use this script to generate Rkey under linux.
//Run in linux system #include<stdio.h> #include<stdlib.h> void main(){ unsigned int seed = 0x5377654E; int i =0; srand(seed); for (i=0;i<42;i + + ){ printf("0x x ,",rand()%5); } }
Then there is the encryption of shift and module operations. I directly blasted it with Z3.
from z3 import * flag ="" res = [153, 245, 13, 62, 207, 14, 130, 217, 106, 3, 18, 219, 28, 192, 83, 195, 205, 146, 20, 153, 177, 225, 174, 255, 17, 0, 73, 226, 186, 155, 8, 118, 56, 36, 79, 139, 129, 36, 161, 187, 237, 197] Rkey = [0x33, 0x89, 0xac, 0xd7, 0x54, 0xcc, 0x4a, 0xa5, 0x35, 0xd1, 0xdb, 0xa3, 0xe6, 0x93, 0x0f, 0x7f, 0x95, 0x4d, 0xe7, 0x65, 0x80, 0xaf, 0x6b, 0xd2, 0xcc, 0xcd, 0x14, 0xad, 0x8d, 0x69, 0xc6, 0x40, 0xf2, 0xf2, 0x18, 0x47, 0x40, 0xe2, 0x6c, 0x75, 0xb4, 0x48] #Create symbolic variable tmp tmp = [BitVec(f'tmp_{i}', 8) for i in range(len(res))] # Create Z3 solver solver = Solver() #Add constraints for i in range(len(res)): constraint = (16 * ((tmp[i] + Rkey[i]) >> 4) + 15) & amp; (tmp[i] + Rkey[i]) == res[i] solver.add(constraint) # Check if solution exists if solver.check() == sat: # Get solution model = solver.model() # print solution for i in range(len(res)): # print(f"tmp[{i}] = {model[tmp[i]].as_long()}") flag + =(chr(model[tmp[i]].as_long())) else: print("No solution") print(flag) #flag{B8452786-DD8E-412C-E355-2B6F27DAB5F9}
SMC
There is an anti-debugging function that changes the flag bit to skip
Change the ZF flag to 1 when running the JZ instruction
The JZ line turns green, indicating that the conditions are met and the jump is about to take place.
Sub_401042() contains some XOR operations on memory, similar to unpacking.
Because the main encryption function (byte_403040) is still some data. Move over directly. Then F7 enters and finds that it is on the data side (explosive).
Because IDA analyzes this as a data segment during static analysis, we enter the encryption function and press P to let IDA recognize it as function F5 to take a look at the encryption logic.
Write code
data =[ 0x7C, 0x82, 0x75, 0x7B, 0x6F, 0x47, 0x61, 0x57, 0x53, 0x25, 0x47, 0x53, 0x25, 0x84, 0x6A, 0x27, 0x68, 0x27, 0x67, 0x6A, 0x7D, 0x84, 0x7B, 0x35, 0x35, 0x48, 0x25, 0x7B, 0x7E, 0x6A, 0x33, 0x71] flag ="" for i in data: flag + = chr((i - 0x5)^ 0x11 ) print(flag) #flag{SMC_1S_1nt3r3sting!!R1ght?}
When writing this question in WP, I found that IDA was completely messed up when I opened it the second time. It can only be used once:) I need to delete the IDA record file before using it once.