Reversing.kr
Easy_CrackMe
IDA打开 一搜字符串 定位congratulations 对比字符串
得到 Ea5yR3versing
Easy_Keygen
sub_4011B9(aInputName);
scanf("%s", v8);
v3 = 0;
for ( i = 0; v3 < (int)strlen(v8); ++i )
{
if ( i >= 3 )
i = 0;
sprintf(Buffer, "%s%02X", Buffer, v8[v3++] ^ v7[i - 1]);
}
memset(v8, 0, sizeof(v8));
sub_4011B9(aInputSerial);
scanf("%s", v8);
if ( !strcmp(v8, Buffer) )
sub_4011B9(aCorrect);
else
sub_4011B9(aWrong);
这里动调看看v7 v7[-1~1]:0x10,0x20,0x30
注意这里的sprintf是%02x 所以最后的序列号5B134977135E7D13
要转为 [0x5b,0x13,0x49,0x77,0x13,0x5e,0x7d,0x13]
对应xor回去即可
flag: K3yg3nm3
EasyELF
if ( byte_804A021 != 49 )
return 0;
byte_804A020 ^= 0x34u;
byte_804A022 ^= 0x32u;
byte_804A023 ^= 0x88u;
if ( byte_804A024 != 88 )
return 0;
if ( byte_804A025 )
return 0;
if ( byte_804A022 != 124 )
return 0;
if ( byte_804A020 == 120 )
return byte_804A023 == -35;
return 0;
对应xor回去就行了 注意byte_804A023这里负数用C unsigned char来写
L1NUX
CSHOP
C#程序 直接dnspy可以找到可以字符串
但是加了反混淆 把变量名做了处理 而且应该给的也是乱序
可以找到相关button大小的地方 但是直接修改编译过不了 所以dnspy目前行不通
学到了另一种奇技淫巧
用SpyLite窗口句柄查看器
直接找到子窗口列表后改button大小 改大点 点击即可看到flag
P4W6RP6SES
以后再来看看 能不能反混淆+算法逆向 或者dnspy直接patch
HateIntel
终于有道偏ctf的题了(被干碎了...)
IDA一拖 楽 ARM指令集 IDA反编译不了
但是 我们有Ghidra~ 🎉🎉🎉
Ghidra打开
主函数:
undefined4 FUN_00002224(void)
{
char acStack_64 [80];
undefined4 local_14;
int local_10;
int local_c;
local_14 = 4;
FUN_0000264c("Input key : ");
FUN_00002670("%s",acStack_64);
local_10 = FUN_0000267c(acStack_64);
FUN_0000232c(acStack_64,local_14);
local_c = 0;
while( true ) {
if (local_10 <= local_c) {
FUN_00002664("Correct Key! ");
return 0;
}
if (acStack_64[local_c] != (&DAT_00003004)[local_c]) break;
local_c = local_c + 1;
}
FUN_00002664("Wrong Key! ");
return 0;
}
分析下各个函数
FUN_0000264c: cout
FUN_00002670: cin
FUN_0000267c: strlen
首先可以注意到最后的循环逐字节check
那么我们可以在内存中提取出00003004处的值
44 f6 f5 57 f5 c6 96 b6 56 f5 14 25 d4 f5 96 e6 37 47 27 57 36 47 96 03 e6 f3 a3 92
接下来关注加密函数
void FUN_0000232c(int param_1,int param_2)
{
undefined uVar1;
int iVar2;
int local_14;
int local_10;
for (local_14 = 0; local_14 < param_2; local_14 = local_14 + 1) {
for (local_10 = 0; iVar2 = FUN_0000267c(param_1), local_10 < iVar2; local_10 = local_10 + 1) {
uVar1 = FUN_00002494(*(undefined *)(local_10 + param_1),1);
*(undefined *)(local_10 + param_1) = uVar1;
}
}
return;
}
前面传入的param_2=4 里面还有一个加密函数
uint FUN_00002494(byte param_1,int param_2)
{
uint local_10;
int local_c;
local_10 = (uint)param_1;
for (local_c = 0; local_c < param_2; local_c = local_c + 1) {
local_10 = local_10 << 1;
if ((local_10 & 0x100) != 0) {
local_10 = local_10 | 1;
}
}
return local_10 & 0xff;
}
逻辑都比较清晰 懒得推导 直接爆破即可
enc = "44 f6 f5 57 f5 c6 96 b6 56 f5 14 25 d4 f5 96 e6 37 47 27 57 36 47 96 03 e6 f3 a3 92"
enc = enc.split(' ')
enc = [int(x,16) for x in enc]
for i in range(len(enc)):
c = enc[i]
for j in range(32,128):
x = j
for v14 in range(4):
x = x<<1
if(x&0x100!=0):
x |= 1
x &= 0xff
if(x==c):
print(chr(j),end='')
break
# Do_u_like_ARM_instructi0n?:)
flag: Do_u_like_ARM_instructi0n?😃
做完后怼了怼ARM汇编 勉强也能看 但还是Ghidra方便 (为什么看有的师傅IDA可以F5呐?)
我怀疑当年出题人是没想到现在工具这么强大了😄
Multiplicative
package defpackage;
/* renamed from: JavaCrackMe reason: default package */
/* loaded from: JavaCrackMe.jar:JavaCrackMe.class */
public class JavaCrackMe {
public static final synchronized /* bridge */ /* synthetic */ void main(String... strArr) {
try {
System.out.println("Reversing.Kr CrackMe!!");
System.out.println("-----------------------------");
System.out.println("The idea came out of the warsaw's crackme");
System.out.println("-----------------------------\n");
if (Long.decode(strArr[0]).longValue() * 26729 == -1536092243306511225L) {
System.out.println("Correct!");
} else {
System.out.println("Wrong");
}
} catch (Exception e) {
System.out.println("Please enter a 64bit signed int");
}
}
}
这涉及到signed __int64的溢出问题
就当复习计组了
将-1536092243306511225转为它的补码形式
1110101010101110101101000011111001000111011110111000010010000111
发现还是不能整除
再思考 注意到乘数26729比较大 所以可能溢出不止一位 只要保证溢出截断后的低64位一样即可
所以我们可以爆破前面的数
这里写的比较丑
from Crypto.Util.number import *
from tqdm import *
s = "1110101010101110101101000011111001000111011110111000010010000111"
for a in trange(2):
for b in range(2):
for c in range(2):
for d in range(2):
for e in range(2):
for f in range(2):
for g in range(2):
for h in range(2):
for i in range(2):
for j in range(2):
for k in range(2):
for l in range(2):
for m in range(2):
for n in range(2):
for o in range(2):
ss = str(a)+str(b)+str(c)+str(d)+str(e)+str(f)+str(g)+str(h)+str(i)+str(j)+str(k)+str(l)+str(m)+str(n)+str(o)+s
x = int(ss,2)
# print(bin(x)[2:])
if(x%26729==0):
y = x//26729
print(x)
print(y)
print(hex(y))
print(long_to_bytes(y))
# exit()
最后爆破得到 y=0x83676f67696c676f
但是这里发现这个p y也溢出了...
所以最后提交 -8978084842198767761
Easy_Unpackme
这方面还是学的不怎么扎实
题目要求找到OEP
IDA硬怼 可以找到这段:
.GWan:0040A1FB ; ---------------------------------------------------------------------------
.GWan:0040A1FB
.GWan:0040A1FB loc_40A1FB: ; CODE XREF: start+188↑j
.GWan:0040A1FB ; start+190↑j ...
.GWan:0040A1FB jmp loc_401150
.GWan:0040A1FB start endp ; sp-analysis failed
.GWan:0040A1FB
.GWan:0040A1FB ;
这里有个可以跳转 loc_401150 在老前面了 猜测这就是OEP
查看loc_401150
.text:00401150 loc_401150: ; CODE XREF: start:loc_40A1FB↓j
.text:00401150 jnz short loc_40110D
.text:00401152 lodsb
.text:00401153 cmp ch, bh
.text:00401155 dec eax
.text:00401156 loopne loc_401168
.text:00401158 adc [eax], dl
.text:0040115A dec eax
.text:0040115B sub al, 5Eh ; '^'
.text:0040115D adc [eax], dl
.text:0040115F inc esp
.text:00401160 xchg eax, ecx
.text:00401161 inc eax
.text:00401162 push eax
.text:00401163 adc [eax], ah
.text:00401165 pusha
.text:00401166 and al, 0D9h
.text:00401168
挺像对的
这其实是结合x32dbg搜索汇编指令pushad找到401xxx段 然后再在IDA看反汇编找的
x32dbg的反汇编401150处会识别出错...
真正的方法应该是动调来找 但我真不太会... 抽空再来看吧~
ImagePrc
IDA找到这里:
if ( wParam == 100 )
{
GetObjectA(hbm, 24, pv);
memset(&bmi, 0, 0x28u);
bmi.bmiHeader.biHeight = cLines;
bmi.bmiHeader.biWidth = v16;
bmi.bmiHeader.biSize = 40;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = 0;
GetDIBits(hdc, (HBITMAP)hbm, 0, cLines, 0, &bmi, 0);
v8 = operator new(bmi.bmiHeader.biSizeImage);
GetDIBits(hdc, (HBITMAP)hbm, 0, cLines, v8, &bmi, 0);
ResourceA = FindResourceA(0, (LPCSTR)101, (LPCSTR)0x18);
Resource = LoadResource(0, ResourceA);
v11 = LockResource(Resource);
v12 = 0;
v13 = v8;
v14 = v11 - (_BYTE *)v8;
while ( *v13 == v13[v14] )
{
++v12;
++v13;
if ( v12 >= 90000 )
{
sub_401500(v8);
return 0;
}
}
MessageBoxA(hWnd, Text, Caption, 0x30u);
sub_401500(v8);
return 0;
}
可以知道是将读取的图像与LoadResource里加载的进行对比 有90000个相同即正确
这种直接ResourceHacker将101资源提取出来 python将FF转成字节写入bin 然后PIL库操作即可:
s = "FF...FF"
s = s.split(" ")
print(len(s))
from Crypto.Util.number import *
cnt = 0
with open(r"C:\Users\asus\Desktop\Python\dump.bin","wb") as f:
for c in s:
c = c.strip()
if(c!=" "):
try:
# print(c)
cnt += 1
f.write(long_to_bytes(int(c,16)))
except:
pass
print(cnt)
from PIL import Image
width = 200
height = 150
fp = open(r'C:\Users\asus\Desktop\Python\dump.bin', 'rb+')
data = fp.read()
im = Image.frombytes('RGB', (width, height), data)
im = im.transpose(Image.FLIP_TOP_BOTTOM)
im.show()
im.save('result.bmp')
得到图片:
flag:
GOT
Direct3D FPS
逆向很简单 学到了一点:用IDC/IDApython 提取数据
很容易根据关键字符跟到这里:
int __thiscall SUPRISE(void *this)
{
int result; // eax
int v2; // edx
result = sub_A23440(this);
if ( result != -1 )
{
v2 = dword_A29190[132 * result];
if ( v2 > 0 )
{
dword_A29190[132 * result] = v2 - 2;
}
else
{
dword_A29194[132 * result] = 0; // 标记清零 代表这个地方上的怪被清了
// 在另一个函数里统计 是否 Game Clear
byte_A27028[result] ^= byte_A29184[528 * result];// 这应该就是加密的flag
// 确实在后面检测到GameClear后会将这个作为lptext传入参数 输出 所以关键就是怎么找到密钥
// 只是它不是同一个key加密的 貌似
// 应该是 一个key[]数组
}
}
return result;
}
然后有个小坑点: 这个byte_A29184是动态赋值的 也就是要把游戏开着再来找数据...(想起当时在车上逆的 没开游戏...)
auto i;
for(i=0;i<100;i++)
Message("%d ",Byte(0xa29184+528*i));
发现就是 4*i
这样xor回去即可
得到:
Congratulation~ Game Clear! Password is Thr3EDPr0m
flag:
Thr3EDPr0m
应该还有patch游戏无敌后硬打的解法 以后再来试吧~
Position
mfc 找到关键逻辑
check逻辑就是name的四个字符1,2 3,4 两组 进行check 跟序列号比较
没什么技术含量 直接爆破
exp:
# Re.kr Position
"""
Find the Name when the Serial is 76876-77776
This problem has several answers.
Password is ***p
"""
"""
一共四个字符 1,2 3,4 两组check
本来如果能直接枚举爆破process是最快的 但是不怎么会这种mfc填空类似的爆破...
"""
table = "abcdefghijklmnopqrstuvwyxz"
for i1 in table:
for i2 in table:
v6 = ord(i1)
v7 = ord(i2)
v40 = (v6 & 1) + 5
v48 = ((v6 & 0x10) != 0) + 5
v42 = ((v6 & 2) != 0) + 5
v44 = ((v6 & 4) != 0) + 5
v46 = ((v6 & 8) != 0) + 5
v32 = (v7 & 1) + 1
v38 = ((v7 & 0x10) != 0) + 1
v34 = ((v7 & 2) != 0) + 1
v8 = ((v7 & 4) != 0) + 1
v36 = ((v7 & 8) != 0) + 1
# 76876-77776
if((v8+v40)==7):
if((v46+v36)==7):
if((v42+v38)==7):
if(v44+v32==7):
if(v48+v32==6):
print(i1,i2,sep='')
"""
ftmp
fvmp
gpmp
grmp
mp
"""
但离谱的是auth不对。。。 就离谱...
Music_Player
Readme要求让播放器播放>1min
自己瞎patch了一堆... 结果不如直接改两个jmp
++++++
记得patch jmp后把后面一个字节改为nop
还是学到了一个关键点 要跳过_vbaHresultCheckObj
的执行
这个貌似是VB程序的一些反xx
的措施? 搜了一圈也没搜到具体的作用 长见识了
Ransomware
勒索病毒~
先virustool分析一波
楽
看一看加密的file文件
妥妥的PE结构
看run.exe
F5不了 因为加了很多很多无意义的花指令
输入key:
加密:
这个xor 0xFF 就是 ~
加密过程就是 与输入的key循环xor后取反
选取PE头部分 去反后与已知的PE头结构xor
letsplaychessletsplaychessletsplaychessletsplaychessletsplayiessletsplaychessle
key: letsplaychess
对应解密即可得到一个UPX加壳的PE文件 脱壳后找到key
Colle System