0

Reversing.kr 刷题笔记 - 1

 2 years ago
source link: https://kiprey.github.io/2021/12/reversing_kr-1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

这里将记录一些笔者学习 reversing.kr 中的逆向题所留下的笔记。

这篇笔记所记录的题目分值为 100-120。

1. Easy Crack

IDA 32 位打开,通过交叉引用:

image-20211209225604780

image-20211209225615397

很容易找到目标函数,并定位关键判断语句:

image-20211209225657036

因此可以很容易得出 flag 为: Ea5yR3versing

image-20211209230836059

2. Easy Keygen

下下来一个压缩包,ReadMe.txt 中写道:

Find the Name when the Serial is 5B134977135E7D13

同时打开程序,窗口提示输入 name:

image-20211209231245555

看来这题应该是要我们根据 Serial 来反推输入的 Name。

IDA 打开,发现一个简易的映射算法:

image-20211209231510613

于是我们可以根据该算法来编写一个简易的解密算法:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

int main() {
string serial = "5B134977135E7D13";
char key[3] = { 16, 32, 48 };
int hex_val;
for (int i = 0; i < serial.size(); i += 2) {
sscanf(serial.substr(i, 2).c_str(), "%x", &hex_val);
cout << (char)(hex_val ^ key[(i/2) % 3]);
}

return 0;
}

解出 flag:K3yg3nm3

image-20211209233454523

3. Easy_ELF

确实很 Easy。程序首先会读取一个字符串,之后对字符串进行以下判断:

image-20211209234452851

将该判断逆向一下,就可得到 flag:L1NUX

4. Easy Unpack

看上去这是一个需要脱壳的程序,但根据 IDA 反编译结果来看,应该是一个压缩壳。直接从 main 函数的反汇编列表往下拉到最底下,最后的那个 jmp 指令跳转的位置就是 OEP。

image-20211210001019055

跳转后(该部分代码是 _start 函数的反汇编代码):

image-20211210001149346

因此 OEP 为 00401150,而这也正是要提交的 flag。

5. ImagePrc

首先查看 WinMain 逻辑:

image-20211212115326564

我们可以很容易的找到事件处理例程,并通过字符串交叉对比,找到真正的校验位置:

image-20211212121036289

位图大小为 200 x 150,若有 90000 个像素相同则正确。而 *v13v13[v14] 应该指向的是两块不同的位图,要是能dump下来看看,估计就能看出结果。

位图大小总像素点个数:200x150x3 = 90000,RGB 格式。

别的也看不出什么了,在调试时先随手画个 A 留个标记,之后尝试用 ida dump 内存出来看看。

dump 脚本:

import idaapi
start_address = 0x2FA0048
data_length = 200*150*3
data = idaapi.dbg_read_memory(start_address , data_length)
fp = open('./dump.bin', 'wb')
fp.write(data)
fp.close()

用 Python 处理一下 dump 出来的内存:

#! python3
# `pip3 install pillow` to enable PIL
from PIL import Image, ImageDraw
import sys

im = Image.new("RGB", (200, 150))

with open("./dump.bin", "rb") as f:
for j in range(150):
for i in range(200):
r = int.from_bytes(f.read(1), 'little')
g = int.from_bytes(f.read(1), 'little')
b = int.from_bytes(f.read(1), 'little')
im.putpixel((i, j), (r,g,b))

# im.save("dump.png")
im.show()

v13 对应的图像如下:

image-20211212145026054

可以看到刚好 dump 出来的图片是上下倒置的。

接着我们就如法炮制,将 v13[v14] 的图片也 dump 出来:

image-20211212145155253

即 flag 为 GOT

6. Ransomware

压缩包解压,根据 readme 的描述,可以看到要求我们解密 file 文件。

把目标文件拖到 Exeinfo 里一看,加了个 UPX压缩壳:

image-20211212155306762s

因此直接用脱壳机脱壳:

image-20211212155515369

之后用 IDA 打开看看:

image-20211212155709684

可以发现在 main 函数中存在超大量无用指令,使得 IDA 无法进行反汇编,提高分析难度,因此我们需要尝试去掉这些指令:

data = None
with open("./run.exe", "rb") as f:
data = f.read()
'''
UPX0:0044A73D 60 pusha
UPX0:0044A73E 61 popa
UPX0:0044A73F 90 nop
UPX0:0044A740 50 push eax
UPX0:0044A741 58 pop eax
UPX0:0044A742 53 push ebx
UPX0:0044A743 5B pop ebx
'''
new_data = data.replace(b"\x60\x61\x90\x50\x58\x53\x5b", b"\x90" * 7)
with open("./run_dump_patch.exe", "wb") as f:
f.write(new_data)

而且在 sub_401000 函数中,整个函数体全部充斥着这类指令,即该函数是一个空函数体。为了防止混淆,我们将 sub_401000 函数名称修改为 nop_func

接着非常悲剧的发现,main 函数还是因为函数太大无法被反汇编…

莫得办法了,只能将函数头的

UPX0:004135E0 55                                      push    ebp
UPX0:004135E1 8B EC mov ebp, esp
UPX0:004135E3 83 EC 24 sub esp, 24h
UPX0:004135E6 53 push ebx
UPX0:004135E7 56 push esi
UPX0:004135E8 57 push edi

移动到末尾:

image-20211212154417401

然后修改一下函数的起始位置:

image-20211212154503604

之后就可以照常反编译了,以下是经过简化的反汇编代码。

不过需要注意的是,这里修改函数的起始地址,指的是IDA 静态分析的起始地址。实际上函数调用 main 时仍然会跳转回原先的地址。

int __cdecl main(int argc, const char **argv, const char **envp)
{
[...]
printf("Key : ");
scanf("%s", key);
v3 = strlen(key);
v7 = 0;
Stream = fopen("file", "rb");
if (!Stream)
;/* exit */
fseek(Stream, 0, 2);
v6 = ftell(Stream); // 获取文件长度
rewind(Stream);
while (!feof(Stream)) // 将文件数据读入 buf
{
buf[v7] = fgetc(Stream);
++v7;
}
for ( i = 0; i < v6; ++i ) // 尝试加密
{
buf[i] ^= key[i % v3];
buf[i] = ~buf[i];
}
fclose(Stream);
v5 = fopen("file", "wb");
for ( j = 0; j < v6; ++j )
{
fputc(buf[j], v5);
}
printf(asc_44C1E8);
return getch();
}

最核心的就是这部分加密算法:

// v6 为文件数据长度
for ( i = 0; i < v6; ++i ) // 尝试加密
{
// v3 为 key 长度
buf[i] ^= key[i % v3];
buf[i] = ~buf[i];
}

而 readme.txt 的描述是这样的:Decrypt File (EXE)。也就是说那个 file 文件实际上就是加密后的 exe 文件。

在已有明文、密文并了解加密算法的情况下,我们便可以很容易的将密钥解出来。需要注意的是这里选取的是两个 exe 文件(一个加密前一个加密后)的前30 字节,因为应该所有 exe 文件的前30个字节都相同。

以下是暴力枚举密钥的算法:

#include <iostream>
using namespace std;

int main() {
// hexdump -n 30 run.exe.bak -e '30/1 "\\x%x"'
const char* origin_text = "\x4d\x5a\x90\x0\x3\x0\x0\x0\x4\x0\x0\x0\xff\xff\x0\x0\xb8\x0\x0\x0\x0\x0\x0\x0\x40\x0\x0\x0\x0\x0";
// hexdump -n 30 file -e '30/1 "\\x%x"'
const char* cipher_text = "\xde\xc0\x1b\x8c\x8c\x93\x9e\x86\x98\x97\x9a\x8c\x73\x6c\x9a\x8b\x34\x8f\x93\x9e\x86\x9c\x97\x9a\xcc\x8c\x93\x9a\x8b\x8c";
for (size_t i = 0; i < strlen(cipher_text); i++) {
int key;
for (key = 0; key < 255; key++) {
if ((origin_text[i] ^ key) == (~cipher_text[i])) {
cout << (char)key;
break;
}
}
if (key == 0xff)
abort();
}

return 0;
}

输出密钥为 letsplaychess

image-20211212164923948

因此再解密一下 file 文件:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

int main() {
FILE* cipher_file = fopen("C:\\Users\\Kiprey\\Desktop\\rever\\ransomware\\file", "rb");
FILE* output_file = fopen("C:\\Users\\Kiprey\\Desktop\\rever\\ransomware\\flag.exe", "wb");

const char* key = "letsplaychess";

fseek(cipher_file, 0, SEEK_END);
size_t cipher_len = ftell(cipher_file); // 获取文件长度
rewind(cipher_file);

for (size_t i = 0; i < cipher_len; ++i) // 尝试加密
{
unsigned char buf = fgetc(cipher_file);
buf = ~buf;
buf ^= key[i % strlen(key)];
fputc(buf, output_file);
}

fclose(cipher_file);
fclose(output_file);
return 0;
}

解出一个 flag.exe,运行下看看:

image-20211212165915247

晕,从别处拷了一些 DLL 过来,终于解出来了…

image-20211212170048738

flag为 Colle System

7. CSHOP

程序打开后没有任何可交互部分。拖进 IDA 发现是个 .NET 程序,直接用 dnSpy x86 打开(不得不说 dnSpy 的界面是真的好看):

image-20211212173934346

简单通读了一下代码,该窗口有 10 个 Label 和 1 个 Button,当 Button 被按下后,这10个 label 将显示出对应的文字(应该是 flag)。但问题是, Button 的 size 为 (0,0),因此正常情况下我们无法点击该 Button。

不过我们可以尝试修改这个 IL:

image-20211212174305594

这两个值改大一点,然后 File -> Save Module

image-20211212174327671

重新打开被 patch 后的程序,并点击按钮,即可出现 flag:

image-20211212175001051

flag 为 P4W6RP6SES


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK