1: X
主要逻辑在X.dll
中,直接使用dnspy
进行反编译
可以看到当为42的时候,为判定条件
2: ItsOnFire
是一个apk的文件,直接使用jeb进行反编译,找来找去,发现主要的函数在f b
中,实现的AES
代码贴出来 主要的地方:
private final long a(byte[] arr_b) {
CRC32 cRC320 = new CRC32();
cRC320.update(arr_b);
return cRC320.getValue();
}
private final byte[] b(String s, byte[] arr_b, SecretKeySpec secretKeySpec0, IvParameterSpec ivParameterSpec0) {
Cipher cipher0 = Cipher.getInstance(s);
cipher0.init(2, secretKeySpec0, ivParameterSpec0);
byte[] arr_b1 = cipher0.doFinal(arr_b);
Intrinsics.checkNotNullExpressionValue(arr_b1, "cipher.doFinal(input)");
return arr_b1;
}
private final File c(int v, Context context0) {
Resources resources0 = context0.getResources();
Intrinsics.checkNotNullExpressionValue(resources0, "context.resources");
byte[] arr_b = this.e(resources0, v);
byte[] arr_b1 = this.d(context0).getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(arr_b1, "this as java.lang.String).getBytes(charset)");
SecretKeySpec secretKeySpec0 = new SecretKeySpec(arr_b1, "AES");
String s = "AES/CBC/PKCS5Padding";
Intrinsics.checkNotNullExpressionValue(s, "context.getString(R.string.alg)");
String s1 = "abcdefghijklmnop";
Intrinsics.checkNotNullExpressionValue(s1, "context.getString(\n … R.string.iv)");
byte[] arr_b2 = s1.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(arr_b2, "this as java.lang.String).getBytes(charset)");
byte[] arr_b3 = this.b(s, arr_b, secretKeySpec0, new IvParameterSpec(arr_b2));
File file0 = new File(context0.getCacheDir(), "playerscore.png");
FilesKt__FileReadWriteKt.writeBytes(file0, arr_b3);
return file0;
}
private final String d(Context context0) {
String s = "https://flare-on.com/evilc2server/report_token/report_token.php?token=";
Intrinsics.checkNotNullExpressionValue(s, "context.getString(R.string.c2)");
String s1 = "wednesday";
Intrinsics.checkNotNullExpressionValue(s1, "context.getString(R.string.w1)");
String s2 = s.subSequence(4, 10) + s1.subSequence(2, 5);
Intrinsics.checkNotNullExpressionValue(s2, "StringBuilder().apply(builderAction).toString()");
byte[] arr_b = s2.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(arr_b, "this as java.lang.String).getBytes(charset)");
long v = this.a(arr_b);
String s3 = v + v;
Intrinsics.checkNotNullExpressionValue(s3, "StringBuilder().apply(builderAction).toString()");
return StringsKt___StringsKt.slice(s3, new IntRange(0, 15));
}
上面的代码可以知道:
-
iv = “abcdefghijklmnop”
-
AES加密 模式CBC
-
函数a为CRC加密
-
key为s = “https://flare-on.com/evilc2server/report_token/report_token.php?token=" 的 4到10位,s1 = “wednesday” 的 2到5位,切割后,得到arr_b后进行crc
-
import binascii s = "https://flare-on.com/evilc2server/report_token/report_token.php?token=" s1 = "wednesday" # 从字符串构建字节对象 s_bytes = s.encode() s1_bytes = s1.encode() # 构建新字符串s2 s2 = s_bytes[4:10] + s1_bytes[2:5] print(s2) # 计算CRC32值并进行位运算,得到无符号整数 v = binascii.crc32(s2) & 0xFFFFFFFF print(v) 4508305374508305
-
key = 4508305374508305
#iv = "abcdefghijklmnop"
#key = 4508305374508305
#AES CBC
from Crypto.Cipher import AES
from hexdump import hexdump
iv = b'abcdefghijklmnop'
key = b'4508305374508305'
file = 'iv.png'
cipher = AES.new(key, AES.MODE_CBC, iv)
with open(file, 'rb') as bin:
enc = bin.read()
data = cipher.decrypt(enc)
hexdump(data)
with open('right_' + file, 'wb') as bin:
bin.write(data)
3: mypassion
Part1: 01cdeR@brUc3E
第一个判断条件为 参数第一个的第一个字符等于0x30
,并且第二个字母 左移4
+ 第三个字母 等于需要等于0x127
for i in range(0x20, 0x7f):
for j in range(0x20, 0x7f):
if j + i * 4 == 0x127:
print(chr(i), chr(j))
爆破出来的序列是:
+ {
, w
- s
. o
/ k
0 g
1 c
2 _
3 [
4 W
5 S
6 O
7 K
8 G
9 C
: ?
; ;
< 7
= 3
> /
? +
@ '
A #
一会再看看 选择哪个序列,目前根据上面所有的条件知道 1 2 3 6位的数据
这里v12
代表了后续的VirtualAlloc
申请的属性,后面需要执行的话 只能是PAGE_EXECUTE_READWRITE
所以第7位的为0x40
申请完内存后,可以发现,我们的memset
清空后,将shellcode
写入了申请的空间并且执行
但是其中的0xC
的位置,由于是我们的输入会导致shellcode
的不正确
这个位置自己修改了一下,为45(E)
的时候为正常
目前的参数修改为:01cdeR@hijklEnopqrstuvwxyz
这里有进行比较brUc3
得到第一部分 01cdeR@brUc3E
Part2: file.bin
会在该文件目录创建一个文件
暂定 输入为 01cdeR@brUc3E/file.bin/
写入文件的是一个加密的数据,还暂时不知道是什么,下面对shellcode进行解密了,我们直接dump出来
48 89 5C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B 81 08 01 00 00 48 8B D9 C7 40 08 03 00 00 00 48 8B 81 08 01 00 00 8B 50 08 FF 91 90 03 00 00 41 B8 04 00 00 00 48 C7 44 24 30 00 00 00 00 48 8B C8 48 8D 54 24 30 48 8B F0 FF 93 80 03 00 00 48 8B 4C 24 30 BA 20 00 00 00 8B F8 FF 93 88 03 00 00 3B C7 74 12 48 8B 8B 08 01 00 00 8B 49 08 FF 93 58 03 00 00 EB 0C 69 CF E8 03 00 00 FF 93 50 03 00 00 48 8B CE FF 93 68 03 00 00 48 8B CB FF 93 B0 03 00 00 48 8B 5C 24 38 48 8B 74 24 40 48 83 C4 20 5F C3
0000000000000000 48895C2410 MOV QWORD PTR [RSP+10],RBX
0000000000000005 4889742418 MOV QWORD PTR [RSP+18],RSI
000000000000000A 57 PUSH RDI
000000000000000B 4883EC20 SUB RSP,0000000000000020
000000000000000F 488B8108010000 MOV RAX,QWORD PTR [RCX+00000108]
0000000000000016 488BD9 MOV RBX,RCX
0000000000000019 C7400803000000 MOV DWORD PTR [RAX+08],00000003
0000000000000020 488B8108010000 MOV RAX,QWORD PTR [RCX+00000108]
0000000000000027 8B5008 MOV EDX,DWORD PTR [RAX+08]
000000000000002A FF9190030000 CALL WrapStrcpy
0000000000000030 41B804000000 MOV EAX,00000004
0000000000000036 48C744243000000000 MOV QWORD PTR [RSP+30],00000000
000000000000003F 488BC8 MOV RCX,RAX
0000000000000042 488D542430 LEA RDX,[RSP+30]
0000000000000047 488BF0 MOV RSI,RAX
000000000000004A FF9380030000 CALL strtol
0000000000000050 488B4C2430 MOV RCX,QWORD PTR [RSP+30]
0000000000000055 BA20000000 MOV EDX,00000020
000000000000005A 8BF8 MOV EDI,EAX
000000000000005C FF9388030000 CALL strnlen_0
0000000000000062 3BC7 CMP EAX,EDI
0000000000000064 7412 JE 0000000000000078
0000000000000066 488B8B08010000 MOV RCX,QWORD PTR [RBX+00000108]
000000000000006D 8B4908 MOV ECX,DWORD PTR [RCX+08]
0000000000000070 FF9358030000 CALL ExitProcess
0000000000000076 EB0C JMP 0000000000000084
0000000000000078 69CFE8030000 IMUL ECX,EDI,000003E8
000000000000007E FF9350030000 CALL Sleep
0000000000000084 488BCE MOV RCX,RSI
0000000000000087 FF9368030000 CALL free
000000000000008D 488BCB MOV RCX,RBX
0000000000000090 FF93B0030000 CALL sub_1400018B0
0000000000000096 488B5C2438 MOV RBX,QWORD PTR [RSP+38]
000000000000009B 488B742440 MOV RSI,QWORD PTR [RSP+40]
00000000000000A0 4883C420 ADD RSP,0000000000000020
00000000000000A4 5F POP RDI
00000000000000A5 C3 RET
Part3: 1a
这里判定是否exitprocess的条件是 CMP EAX,EDI
判定的条件那么就是这个部分:
0000000000000030 41B804000000 MOV EAX,00000004
0000000000000036 48C744243000000000 MOV QWORD PTR [RSP+30],00000000
000000000000003F 488BC8 MOV RCX,RAX
0000000000000042 488D542430 LEA RDX,[RSP+30]
0000000000000047 488BF0 MOV RSI,RAX
000000000000004A FF9380030000 CALL strtol
0000000000000050 488B4C2430 MOV RCX,QWORD PTR [RSP+30]
0000000000000055 BA20000000 MOV EDX,00000020
000000000000005A 8BF8 MOV EDI,EAX
000000000000005C FF9388030000 CALL strnlen_0
0000000000000062 3BC7 CMP EAX,EDI
总结来说就是 首先EAX代表了 我们strtol函数的base
//伪代码
int main()
{
char str[] = ""
long int num = strtol(str, &endptr, 4);
if(num == strlen(str))
{
int time = num * 0x3e8;
sleep(time);
free;
call 0x1400018B0;
}
else
{
exitprocess;
}
return 0;
}
所以需要str中前面的数字代表了后面字符串的长度需要一致
暂定 输入为 01cdeR@brUc3E/file.bin/1a
Part4: ?pizza/1337pr.ost(file.bin替换)
排列后check为pizza
ida对应的位置:
因为是v8数组从1开始的check,所以该位置的pizza也是从数组1开始?pizza
暂定 输入为 01cdeR@brUc3E/file.bin/1a/?pizza/
后面继续看的话,这边的check为文件相关的问题,读取了文件buffer中前面的字节,需要等于0x11333377
文件中read的字节和之前的函数write到文件中的字节相关
主要逻辑在这里,这里会根据strtol函数,把我们输入的文件名的前缀的数字根据10进制保存到v5中,然后将v5转化为字符ascii码的值,接着把对应字符的数字换成为十六进制的形式保存到lpBuffer_write
中
暂定 输入为 01cdeR@brUc3E/1337file.bin/1a/?pizza/
后续的时候 对文件名进行了check,文件名需要是pr.ost
,如果不是pr.ost就break出去
暂定 输入为 01cdeR@brUc3E/1337pr.ost/1a/?pizza/
这里还有一个GetTickCount
的Check
,但是由于我是调试器了,等待时间会长所以不用管暂时
Part5: AMu?E`0R.?AZe
后续一共有三段shellcode
第一段shellcode进行切割/ 第五段的内容
我们输入的第五段的内容会影响shellcode2
和 shellcode3
的生成,因为他们修改了里面的字节
shellcode2 :
v3[8] = a1[1];
v3[9] = a1[0xB];
v3[0x19] = a1[4];
v3[0x5C] = a1[2];
v3[0x7A] = a1[3];
v3[0x82] = *a1;
shellcode3:
v8[0x36] = port5_string[9];
HIBYTE(v11) = port5_string[6];
v8[0x18] = port5_string[5];
v8[0] = port5_string[0xC];
LOBYTE(v10) = port5_string[7];
v8[0x1D] = port5_string[8];
因为是先执行shellcode3,所以读取了我们的输入5 6 7 8 9 12位
修改字节码,变成获取kernel32.dll
暂定 输入为 01cdeR@brUc3E/1337pr.ost/1a/?pizza/AMu?E`0R.?AZe/
[12] = 0x65 = e
[5] = 0x60 = `
[8] = 0x2e = .
[9] = 0x83 = ?
[7] = 0x52 = R
[6] = 0x30 = 0
[1] = 0x4d = M
[11] = 0x5a = Z
[4] = 0x45 = E
[2] = 0x75 = u
[3] = 0x24 = $
[0] = [10] = 0x41 = A
Part6: YPXEKCZXYIGMNOXNMXPYCXGXN
输入A-Z,看看有什东西,发现像一个改变的密码对应表
ABCDEFGHIJKLMNOPQRSTUVWXYZ
VPWLCJSFTXKHINGUBYZDOMOERA
由于memcpy的是:RUECKWAERTSINGENIEURWESEN
data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
table = "VPWLCJSFTXKHINGUBYZDOMOERA"
enc = "RUECKWAERTSINGENIEURWESEN"
dec = ""
for char in enc:
if char in table:
index = table.index(char)
dec += data[index]
else:
dec += char
print(dec)
#YPXEKCZXYIGMNOXNMXPYCXGXN
暂定 输入为 01cdeR@brUc3E/1337pr.ost/1a/?pizza/AMu?E`0R.?AZe/YPXEKCZXYIGMNOXNMXPYCXGXN/
part7: ob5cUr3/fin
ob5cUr3进行了check和我们的输入
中间有一个check需要手动改一下je 这里(应该是pizza前面的问号那里进行了爆破,因为是可见字符,所以直接爆破)
最后的check是fin和最后的输入进行比较
解密完事:
4: aimbot
前面会校验C:\Program Files (x86)\Sauerbraten\bin64\sauerbraten.exe
的hash,这里我patch了,到了后面的这些步骤
这边主要在C:\Users\TEST\AppData\Roaming\BananaBot
的文件夹下面会释放三个文件通过aes
进行了解密分别是miner.exe config.json aimbot.dll
会打开miner.exe(XMRig)
挖矿进程
看了一下大概,主要逻辑应该在aimbot.dll
中,直接看这个就行
dllmain
中一共有三个线程会启动
第一个线程好像是游戏相关的,和我平时看外挂那些好像差不多,计算坐标角度的东西
thread_antidebugfuckery
中的这个位置代表了,查看当前的进程是不是aimbot.exe
,并且从0x406220
中取4个字节
EMMMM,感觉还是少了点什么,为了前面可以check成功下载了一下游戏 https://sourceforge.net/projects/sauerbraten/
经过调试后,得到的key = 0x6499F8A9
解密内容:
0: http://127.0.0.1:57328/2/summary
1: bananabot 5000
2: "version": "
3: the decryption of this blob was successful
这里的链接我也不知道为什么不好使,但是aimbot.exe
是OK的,于是我把网络连接得到的buffer
复制到了这里
Shellcode:
大概这里是获取了steam
的什么东西进行了rc4
,下载一个steam
试一下
这里主要是获取steam
的ssfn
文件从而进行盗号
我下载后发现没有ssfn
文件,直接自己下载了一个
获取了rc4
的key = "InstallConfigSt
dump
后发现是盗取discord
的东西
读取的是Cookies
的SQLite format 3
53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00
解密后续的shellcode
dump
后该流程是Sparrow
比特币钱包的盗取
我按照教程创建了一下
生成了文件夹的一些东西,看了一下比较满足代码的条件
继续解密,密钥我好像没看,直接就过来了,先dump
再说,看看后面都是什么
bighackies.flare-on.com
文件的开始位置是(0x1BB5F4235D3 - 0x1BB5F422888)
xdbg里面进行了地址计算一下
r_data = "the decryption of this blob was successful"
f_data = [
0x04, 0x3E, 0x51, 0x32, 0x14, 0x33, 0x57, 0x60, 0x09, 0x26, 0x40, 0x7B, 0x1F, 0x38, 0x14, 0x7D,
0x16, 0x76, 0x40, 0x7A, 0x19, 0x25, 0x14, 0x70, 0x1C, 0x39, 0x56, 0x32, 0x07, 0x37, 0x47, 0x32,
0x03, 0x23, 0x57, 0x71, 0x15, 0x25, 0x47, 0x74, 0x05, 0x3A
]
key = ""
for i in range(len(r_data)):
key = ord(r_data[i]) ^ f_data[i]
print(hex(key), end=" ")
Key: 0x70 0x56 0x34 0x12
from pwn import xor
SIZE = 0xda8
FILE_START = 0xd4b
KEY = [0x70, 0x56, 0x34, 0x12]
with open("shellcode4.bin","rb") as fp:
fp.seek(FILE_START)
data = fp.read(SIZE)
with open("final.bin","wb") as fp:
fp.write(xor(data, KEY))
直接patch到了x64dbg中
check
了游戏里面的一些数据,然后把check
跳了,文件检索了%%PROGRAMFILES(X86)%%\\Sauerbraten\\packages\\base\\%s.cfg
读取spcr2
资源包解密flag
The flag is: computer_ass1sted_ctfing@flare-on.com
5: where_am_i
ide
查看一下是一个mfc
程序
猜测是virtualalloc
主要的shellcode
的生成逻辑在这里,通过rc4
解密shellcode
dump
后的样子都是jmp
这里的jmp
可以用ghidra
中的shared return calls
来处理一下
处理后的结果:
主要的功能是rc6
的解密资源
根据上面的结果来逆,获取key
的位置:
可以查看大概逻辑根据下面的反汇编
APC
注入,使用WriteProcessMemory
到explore.exe
内存中,注入后调用ResumeThread
,执行即可断下
第二段的shellcode
的进行了 异或的自解密
自解密后继续单步发现了关键的位置:
比较用户名时候为flare
,以及路径的名称
比较了 rc6
的key
提示了rc6
的问题,我们直接patch
回到shellcode1
的时候即可
6. FlareSay
一个dos
的游戏
看了一下x64
的逻辑,感觉缺了点什么,这里好像和后续的生成有关系,大概就是对了什么校验的值,然后解密了,因为调试的时候 出现了winning,弹出了窗口,需要分析一下dos
下的东西
DOS
中的主要就是这里,首先设置了屏幕的一些问题,然后通过21
号中断ax = 0x4c00
来进行退出
https://github.com/dosbox-staging/dosbox-staging
使用dosbox
进行dos
程序的调试
check1
的位置,HHPPKMKMBA
获取键盘的输入进行了对于该字符串的检验,好像是算什么东西
经过调试器的分析我patch
了一个地方,这样就可以自动游戏了,直接把键盘输入的地方patch了,然后判定传给了ah,需要等待游戏执行128次的check才可以完毕
0x51a的代码位置都是进行磁盘的读取写入的操作,怀疑这里给后续的地方进行了写入,直接下个段执行后看看原来的位置是不是有被写入
获取了提示好像是Thank you mario but our princess is in another castle
对应上了我们之前分析的思路,这个的flag的生成在运行程序中
因为上面有write file的操作所以这里发现数据被补全了
7. flake
使用CE直接附加该进程,把最高的那个人的分数改成1,自己玩到2,就可以了