signin
一个字:麻烦
先打开程序 发现好像是贪吃蛇
ida跟进sub_40100F
,发现有个花指令和SMC,先不管这么多,先把SMC解出来。
用od动态调试,断在0x419054
,此为加密完成的位置。然后到0x401d10
,重新分析代码后,将整个函数选中右键复制到可执行文件-选择,然后保存到新的文件即可看到逻辑。
解出来发现这是flag的逻辑,这里程序先进行了异或,矩阵变换,然后就是一个魔改的类似TEA思想的算法。
这里的dword_42CA44
未知,但是其 小于256
v5 = dword_42CA44 + 1144219440;
可以注意到加密函数这里sub_401055((int)v5, (int)v4, 32);
v5 虽然是一个int 数组,但是他是从char 赋值来的。所以我们可以爆破dword_42CA44
,尝试dword_42CA44
解出来是否<256
#include <ctime>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
unsigned char Enc[] = { 0xA5, 0xD8, 0x8E, 0xBF, 0xF9, 0xA9, 0x15, 0xE1, 0x8A, 0xF0,
0xD3, 0xFC, 0x46, 0x89, 0xBF, 0x8B, 0x62, 0xB1, 0x08, 0xC3,
0x29, 0xCF, 0x19, 0x2B, 0x56, 0x06, 0x77, 0x7A, 0xBA, 0xE4,
0xBA, 0xA4, 0xE4, 0x8C, 0x3E, 0x4E, 0xD9, 0xE1, 0xA7, 0x01,
0x04, 0xCE, 0xE9, 0x75, 0xB9, 0x93, 0xB5, 0x22, 0xB4, 0x42,
0x77, 0x49, 0xF6, 0x15, 0xEB, 0x24, 0x0E, 0xFF, 0xC2, 0xF2,
0x39, 0x30, 0x97, 0x47, 0x0D, 0xCA, 0x01, 0xC8, 0x61, 0x58,
0x12, 0x6A, 0xE8, 0x0B, 0x32, 0x80, 0x47, 0xBD, 0x85, 0x03,
0xDD, 0x6D, 0xF9, 0x69, 0xD1, 0x90, 0x64, 0xE5, 0x4B, 0xAD,
0x3C, 0x2D, 0xBE, 0x00, 0x42, 0x2D, 0x79, 0x69, 0xEF, 0x89,
0x5D, 0x88, 0x91, 0x4A, 0xC7, 0xEB, 0x9D, 0x01, 0x96, 0xFD,
0xF8, 0x3B, 0x57, 0x25, 0xDD, 0x1B, 0xDD, 0x5F, 0x68, 0xB8,
0x14, 0x66, 0x22, 0x57, 0x28, 0x5C, 0x58, 0x9F };
DWORD GetMagic1(int time,int x)
{
DWORD magic = 0x44336730 + x;
DWORD v8 = 0;
for(int i = 0 ; i< time ;i++){
v8 += magic;
}
return (v8 >> 2) & 3;
}
DWORD GetMagic2(int time,int x)
{
DWORD magic = 0x44336730 + x;
DWORD v8 = 0;
for (int i = 0; i < time; i++) {
v8 += magic;
}
return v8;
}
int main()
{
unsigned long ENC[32] = { 0 };
for (int x = 0;x < 256;x++){
memcpy(ENC, Enc, 32 * 4);
DWORD* a1 = (DWORD*)ENC;
DWORD know[] = { 68,48,103,51 };
for (int j = 7; j > 0; j--) {
DWORD v8 = GetMagic2(j,x);
DWORD v6 = GetMagic1(j,x);
DWORD v9 = a1[30];
a1[31] -= ((v9 ^ (know[(v6 ^ 31) & 3])) + (*a1 ^ v8)) ^ (((16 * v9) ^ (*a1 >> 3))
+ ((4 * *a1) ^ (v9 >> 5)));
for (int i = 30; i >= 0; i--) {
if (i == 0) {
v9 = a1[31];
}
else {
v9 = a1[i - 1];
}
a1[i] -= ((v9 ^ (know[(v6 ^ i) & 3])) + (a1[i + 1] ^ v8)) ^ (((16 * v9) ^ (a1[i + 1] >> 3))
+ ((4 * a1[i + 1]) ^ (v9 >> 5)));
}
}
if(ENC[0] < 256){
printf("%d",x);
}
}
}
x = 77
然后再逆矩阵变换,我们可以构造一个{1,2,3,4,5,6,7,...}
的数组然后进行加密拿到置换表
int j = 0;
int i = 0;
int v1[128] = { 0 };
char table[32] = { 0 };
// DWORD* table = ENC;
// char table[32] = { 0x25,0x4f,0x1d,0x32,0x6b,0x5c,0x3c,0x1d,0x5c,0x6e,0x06,0x42,0x40,0x1a,0x0f,0x15,0x1c,0x2b,0x2b,0x57,0x5c,0x00,0x1a,0x36,0x66,0x44,0x2b,0x00,0x09};
for(int sb = 0;sb<32;sb++){
table[sb] = sb + 1;
}
while (i < 32)
{
if (j % 6 >= 3)
v1[32 * (3 - j % 3) + i] = table[i];
else
v1[32 * (j % 3) + i] = table[i];
++i;
++j;
}
char result[32] = { 0 };
int v7 = 0;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 32; ++j)
{
if (v1[32 * i + j])
result[v7++] = (unsigned __int8)v1[32 * i + j];
}
}
__asm int 3;
然后把提取表,逆置换,再逆异或即可拿到flag
char ConverTable[32] = { 0x1,0x07,0x0d,0x13,0x19,0x1f,0x02,0x06,0x08,0x0c,0x0e,0x12,0x14,0x18,0x1a,0x1e,0x20,0x03,0x05,0x09,0x0b,0x0f,0x11,0x15,0x17,0x1b,0x1d,0x04,0x0a,0x10,0x16,0x1c };
for(int i = 0;i<32;i++){
ConverTable[i] -= 1; // 这里对应关系要 -1
}
char sb[33] = { 0 };
for(int i = 0;i<32;i++){
sb[ConverTable[i]] =(char)ENC[i];
}
sb[31] ^= sb[0];
for(int i = 30 ;i>=0;i--){
// printf("%d", i);
sb[i] ^= sb[(i + 1) % 32];
}
printf("%s", sb);
Th4_1mp0rtant_th2n9_is_t0_le@rn!
virus
主体程序是一个process hollowing
sub_40132C
是一个资源提取的函数,这里用0x41
解密资源
用resource hacker
把 资源节的LOCALIZATION
的UNICODE
数据提取,然后异或65 即可拿到真正的exe
sub_4010EA
是process hollowing
没有修改释放的程序内容,因此我们可以直接把提取的exe解包。
解包分析:
首先要输入一个key
这里key经过KeyExpand
函数生成了128位key,程序验证最后32位是否位Lroo
,由于key只有4位,这里我们可以直接爆破。
void __cdecl sub_401790(char* a1, char* a2)
{
DWORD v2[56]; // [esp+4Ch] [ebp-FCh] BYREF
int v3; // [esp+12Ch] [ebp-1Ch]
int v4; // [esp+130h] [ebp-18h]
int v5; // [esp+134h] [ebp-14h]
int j; // [esp+138h] [ebp-10h]
int v7; // [esp+13Ch] [ebp-Ch]
int v8; // [esp+140h] [ebp-8h]
int i; // [esp+144h] [ebp-4h]
for (i = 0; i < 4; ++i)
{
v8 = *(char*)(i + a1);
for (j = 6; j >= 0; --j)
{
v2[7 * i + 28 + j] = v8 % 2;
v8 /= 2;
v2[7 * i + j] = v2[7 * i + 28 + j];
}
}
v5 = 0;
v4 = 0;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 7; ++j)
{
v3 = v2[7 * v5 + v4];
v2[7 * i + 28 + j] = v3;
v5 = (v5 + 1) % 4;
v4 = (v4 + 2) % 7;
}
}
for (i = 0; i < 4; ++i)
{
v7 = 0;
for (j = 0; j < 6; ++j)
{
v7 = 2 * (v7 + v2[7 * i + 28 + j]);
if (v2[7 * i + 29 + j] == 1 && j == 5)
++v7;
}
*(BYTE*)(i + a2) = v7;
}
}
void baopo()
{
char table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/_-" };
char key[5] = { "" };
for (int a1 = 0; a1 < strlen(table); a1++)
for (int a2 = 0; a2 < strlen(table); a2++)
for (int a3 = 0; a3 < strlen(table); a3++)
for (int a4 = 0; a4 < strlen(table); a4++) {
key[0] = table[a1];
key[1] = table[a2];
key[2] = table[a3];
key[3] = table[a4];
char test[5] = { 0 };
lstrcpynA(test, key, 5);
char result[20] = { 0 };
for (int i = 0; i < 4; i++) {
sub_401790(key, result);
lstrcpynA(key, result, 5);
}
if (!strcmp(key, "Lroo")) {
printf("%s", test);
system("pause");
}
}
}
key = _shy
这里key 生成了 v11
,提取v11
然后是最后的加密函数
void __cdecl sub_4012F0(int a1, int a2, int a3)
{
int v3[36]; // [esp+4Ch] [ebp-94h] BYREF
int i; // [esp+DCh] [ebp-4h]
for ( i = 0; i < 4; ++i )
pack(a1 + 4 * i, &v3[i]);
for ( i = 0; i < 32; ++i )
v3[i + 4] = sub_401005(*(_DWORD *)(a2 + 4 * i + 16) ^ v3[i + 3] ^ v3[i + 2] ^ v3[i + 1]) ^ v3[i];
for ( i = 0; i < 4; ++i )
unpack(v3[35 - i], a3 + 4 * i);
}
int __cdecl pack(int a1, int *a2)
{
int result; // eax
int i; // [esp+4Ch] [ebp-4h]
result = (int)a2;
*a2 = 0;
for ( i = 0; i < 4; ++i )
{
result = *a2 | (*(unsigned __int8 *)(i + a1) << (24 - 8 * i));
*a2 = result;
}
return result;
int __cdecl unpack(unsigned int a1, int a2)
{
int result; // eax
int i; // [esp+4Ch] [ebp-4h]
for ( i = 0; i < 4; ++i )
{
*(_BYTE *)(i + a2) = a1 >> (24 - 8 * i);
result = i + 1;
}
return result;
}
sub_40103c
和sub_401019
是大端序的“pack” 和 “unpack” 函数。sub_401005
是一个函数,他传入已知参数,得到的返回值与v3[i]
异或得到加密值,写exp时需要用到这个函数,但是由于这个函数内部流程稍微比较复杂,这里我们可以用远线程注入的方法调用此函数,拿到返回值。
DWORD GetRemoteCallValue(HANDLE hProcess,DWORD v)
{
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)0x4011e0, (LPVOID)v, 0, 0);
WaitForSingleObject(hThread, -1);
DWORD ret = 0;
GetExitCodeThread(hThread, &ret);
CloseHandle(hThread);
return ret;
}
所以最终的exp如下
#include <windows.h>
#include <stdio.h>
void __cdecl sub_401790(char* a1, char* a2)
{
DWORD v2[56]; // [esp+4Ch] [ebp-FCh] BYREF
int v3; // [esp+12Ch] [ebp-1Ch]
int v4; // [esp+130h] [ebp-18h]
int v5; // [esp+134h] [ebp-14h]
int j; // [esp+138h] [ebp-10h]
int v7; // [esp+13Ch] [ebp-Ch]
int v8; // [esp+140h] [ebp-8h]
int i; // [esp+144h] [ebp-4h]
for (i = 0; i < 4; ++i)
{
v8 = *(char*)(i + a1);
for (j = 6; j >= 0; --j)
{
v2[7 * i + 28 + j] = v8 % 2;
v8 /= 2;
v2[7 * i + j] = v2[7 * i + 28 + j];
}
}
v5 = 0;
v4 = 0;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 7; ++j)
{
v3 = v2[7 * v5 + v4];
v2[7 * i + 28 + j] = v3;
v5 = (v5 + 1) % 4;
v4 = (v4 + 2) % 7;
}
}
for (i = 0; i < 4; ++i)
{
v7 = 0;
for (j = 0; j < 6; ++j)
{
v7 = 2 * (v7 + v2[7 * i + 28 + j]);
if (v2[7 * i + 29 + j] == 1 && j == 5)
++v7;
}
*(BYTE*)(i + a2) = v7;
}
}
void baopo()
{
char table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/_-" };
char key[5] = { "" };
for (int a1 = 0; a1 < strlen(table); a1++)
for (int a2 = 0; a2 < strlen(table); a2++)
for (int a3 = 0; a3 < strlen(table); a3++)
for (int a4 = 0; a4 < strlen(table); a4++) {
key[0] = table[a1];
key[1] = table[a2];
key[2] = table[a3];
key[3] = table[a4];
char test[5] = { 0 };
lstrcpynA(test, key, 5);
char result[20] = { 0 };
for (int i = 0; i < 4; i++) {
sub_401790(key, result);
lstrcpynA(key, result, 5);
}
if (!strcmp(key, "Lroo")) {
printf("%s", test);
system("pause");
}
}
}
unsigned char Enc[] = { 0x5C, 0x89, 0xEE, 0xF5, 0x6F, 0xC5, 0x44, 0x92, 0xDB, 0xE3,
0xAE, 0x9C, 0xB5, 0x4F, 0x4A, 0xF4, 0xE7, 0xA3, 0x5E, 0x0F,
0xFC, 0x93, 0xFC, 0x76, 0x6C, 0xFB, 0x29, 0xE0, 0x16, 0x2F,
0xA5, 0x67
};
unsigned long key[] = {
0xCBD6C588, 0x03F17D27, 0x1C18E9CC, 0xFE024DB3, 0xD71737EB, 0x7B9B1EAB, 0x2776BBA4, 0xBD2018C0,
0x356D0553, 0x0C825513, 0xCAAFF094, 0x9DFBCBA1, 0x7EB6B878, 0x47630F35, 0x4B494BBE, 0x34FD620A,
0x14CF85EF, 0xD754E93A, 0x338B4918, 0xC0846091, 0xD526F236, 0xB9CE1FC7, 0xCB537B6A, 0x25FDD8EA,
0x7221094B, 0xA1F73ABF, 0x2473D8CC, 0x8FA4F2F2, 0x1E7CAC59, 0xEC581806, 0x425D33C3, 0xBEB16ED4,
0xE5C0CA70, 0x02B60624, 0x3011744F, 0xF73A6E51
};
DWORD pack(const char * a)
{
int r = 0;
for(int i = 0;i<4;i++){
r <<= 8;
r |= (BYTE)a[i];
}
return r;
}
// char* unpack()
// {
// }
DWORD GetRemoteCallValue(HANDLE hProcess,DWORD v)
{
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)0x4011e0, (LPVOID)v, 0, 0);
WaitForSingleObject(hThread, -1);
DWORD ret = 0;
GetExitCodeThread(hThread, &ret);
CloseHandle(hThread);
return ret;
}
int main(void)
{
for(int x = 0;x<2;x++){
unsigned v1 = pack((const char*)Enc + 12 + 16 *x);
unsigned v2 = pack((const char*)Enc + 8 + 16*x);
unsigned v3 = pack((const char*)Enc + 4 + 16 *x);
unsigned v4 = pack((const char*)Enc + 16 *x);
unsigned Temp[36] = { 0 };
Temp[32] = v1;
Temp[33] = v2;
Temp[34] = v3;
Temp[35] = v4;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 5052); //TODO: 5852为解包出的题目程序运行的pid
for (int i = 31; i >= 0; i--) {
Temp[i] = GetRemoteCallValue(hProcess, Temp[i + 1] ^ Temp[i + 2] ^ Temp[i + 3] ^ key[i + 4]) ^ Temp[i + 4];
}
for (int i = 0; i < 4; i++) {
Temp[i] ^= 0x06070607;
}
char* sb1 = (char*)Temp;
for(int i = 0;i<4;i++){
for(int j = 0 ; j< 4;j++){
printf("%c", sb1[i * 4 + (3 - j)]);
}
}
}
system("pause");
}
Ho3_I_Exp3cTed_n0_pY_1n_the_Ctf!
maze
一个很简单的迷宫,先算出dWWwwdddWWaawwddsssSaw的解法
但是找不到flag验证函数
然后题目提示 hint:字符串
这里输入了正确迷宫后,用x32dbg查找字符串可以看到变换的base64码表和base64字符串
解出前半部分
import base64
import string
str1 = "QCAmN2sYNGUfR3EvOUMuNWYkW3k1JR=="
string1 = "BADCFEHGJILKNMPORQTSVUXWZYbadcfehgjilknmporqtsvuxwzy1032547698+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))
这里是迷宫验证逻辑处
int __cdecl sub_411E30(char a1)
{
unsigned int v1; // eax
_BYTE *v2; // eax
int i; // [esp+D4h] [ebp-40h]
char v5[36]; // [esp+E0h] [ebp-34h] BYREF
int v6; // [esp+110h] [ebp-4h]
v6 = 0;
sub_411406(v5, (char *)byte_41D94C);
LOBYTE(v6) = 1;
for ( i = 0; i < 44; ++i )
{
v1 = sub_4113ED(&a1);
v2 = (_BYTE *)sub_411262(i % v1);
sub_4114AB(byte_42024C[i] ^ *v2);
}
sub_4112C6(std::cout, "Congratulations!!! \nBut more than...");
Sleep(0x989680u);
sub_4112C6(std::cout, "Maybe there's something wrong with the front...");
LOBYTE(v6) = 0;
sub_41132F(v5);
v6 = -1;
return sub_41132F(&a1);
}
题目提示"Maybe there's something wrong with the front..."
,在验证成功的逻辑上方有个很奇怪的逻辑,因为byte_42024C
全是0,但是要异或,而且找不到验证逻辑。但是交叉引用byte_42024C
发现这个函数
int sub_411D80()
{
int v0; // ecx
int result; // eax
int i; // [esp+D0h] [ebp-8h]
for ( i = 1; i < 44; ++i )
byte_420000[i] = byte_42024B[i] & 0xE0 | byte_42024C[i] & 0x1F;
v0 = byte_420277 & 0xE0;
result = v0 | byte_42024C[0] & 0x1F;
byte_420000[0] = v0 | byte_42024C[0] & 0x1F;
return result;
}
然而byte_420000 是有数据的,这里就可以求出byte_42024C
from z3 import *
enc = [0x0e,0x5D, 0x7D, 0x7D, 0x5D, 0x4E, 0x4E, 0x4E, 0x5D, 0x7D, 0x6B, 0x4B, 0x5D, 0x5D, 0x4E, 0x4E, 0x59,
0x59, 0x59, 0x59, 0x6B, 0x5D, 0x53, 0x24, 0x7B, 0x34, 0x07, 0x49, 0x01, 0x1B, 0x23, 0x27, 0x7E,
0x35, 0x3F, 0x12, 0x1B, 0x29, 0x32, 0x09, 0x16, 0x12, 0x60, 0x4A]
sb = [BitVec(f"sb[{i}]",8) for i in range(45)]
s = Solver()
for i in range(1,44):
s.add(sb[i-1] & 0xe0 | sb[i] & 0x1f == enc[i]
print(s.check())
print(s.model())
由于最开始那儿有个异或,我们就可以把解出来的数据和那个地图迷宫路径异或
sb = [0] *44
sb[43] = 10
sb[0] = 64
sb[22] = 51
sb[13] = 93
sb[20] = 75
sb[33] = 53
sb[42] = 64
sb[14] = 78
sb[5] = 78
sb[3] = 93
sb[32] = 62
sb[34] = 31
sb[29] = 59
sb[17] = 89
sb[38] = 18
sb[16] = 89
sb[37] = 41
sb[26] = 71
sb[39] = 9
sb[2] = 125
sb[9] = 125
sb[11] = 75
sb[30] = 35
sb[31] = 103
sb[27] = 9
sb[21] = 93
sb[8] = 125
sb[19] = 121
sb[35] = 18
sb[12] = 93
sb[4] = 93
sb[28] = 1
sb[40] = 22
sb[23] = 100
sb[24] = 59
sb[10] = 75
sb[1] = 125
sb[25] = 20
sb[36] = 59
sb[41] = 114
sb[15] = 78
sb[7] = 78
sb[6] = 78
sb[18] = 89
flag = ""
maze = "dWWwwdddWWaawwddsssSaw"
for i in range(44):
flag += chr(sb[i] ^ ord(maze[i % len(maze)]))
print(flag)
D0g3{Y0u^Can=So1ve_it!W3lc0me_t0_The_Maze!!}
localhost:2333
打开ida 一看start
就知道是UPX,先解开。
打开server_getflag
根据函数名和逻辑猜测此为输入和验证长度
进入MainVM
观察虚拟机结构,发现这是一个基于栈的虚拟机
观察InitVM
观察MainVM
这里根据code 调用了对应的函数
观察每个函数 除了vm___ptr_register__sub_9
以外,F5的伪代码的前半部分完全一样
其实vm___ptr_register__sub_9
为比对算法
唯独最后几句,所以这几句是虚拟机的关键几句
如:vm___ptr_register__sub_2
的最后几句说明这是一个异或
我们这里可以在gdb调试的时候断在此处,根据运算数据猜测加密算法
但是这里只有5个函数,这里的虚拟机指令似乎有8、9种,猜测其他的逻辑被内联了
这里是右移
gdb 断在这些地方
输入"012345678901234567890123456789"
gdb 断下:观察寄存器
0-10的加密
>>> hex(0x30^0xff)
'0xcf'
>>> 0xcf - 0
根据多次断点发现RAX
为index
,下一轮异或值用到了前一轮的结果
10-20:
0x47
不知道哪儿来的,但是注意InitVM
初始化了GOL@nD~!!!
,实际上就是和这个异或
20-30:
>>> 0x30 >> 3 | (0x30 << 5 & 0xff)
6
exp:
enc = [0x9b, 0xaa, 0xcb, 0xf5, 0x8a, 0xc8, 0xa1, 0x89, 0xe0, 0xa5,
0x7e, 0x10, 0x3a, 0x0d, 0x31, 0x75, 0x2d, 0x7e, 0x77, 0x64,
0x4a, 0x2b, 0xeb, 0xac, 0x08, 0x84, 0x2b, 0x24, 0x24, 0xaf]
# xor_key = 0x217E446E404C4F47
xor_key = [0x47, 0x4f, 0x4c, 0x40, 0x6e, 0x44, 0x7e, 0x21, 0x21, 0x21]
magic_key = 0xff
flag = ""
for i in range(0, 10):
if i == 0:
magic_key = 0xff
else:
magic_key = enc[i - 1]
raw = (enc[i] + i ^ magic_key)
flag += chr(raw)
for i in range(10, 20):
flag += chr(enc[i] ^ xor_key[i - 10])
# >> 3 << 5
for i in range(20, 30):
flag += chr(enc[i] << 3 & 0xf8 | (enc[i] >> 5) & 0xff)
print(flag)
d0g3{Go1aN9_vM_1S_VERY_e@$Y!!}
发表评论
您还未登录,请先登录。
登录