zl程序教程

您现在的位置是:首页 >  其它

当前栏目

HSC-1th WP REVERSE

wp reverse HSC 1th
2023-06-13 09:13:36 时间

HSC-1th WP REVERSE

1.hiahia o(*^▽^*)┛

使用 ida 反编译,space 切换至主函数,F5 查看伪代码

函数 flag 对加密得 flag 进行异或处理。对于前十位(数组 0-9 位), 除以二余一则减三, 除以二余零则加五。对于后十位(数组 10-19 位),除以二余一则 ASCCII 码加十三。除以 二余零则减十一。根据分析结果写出解题脚本。

flag{RrrrEe33202111}

2.ANDROID

apk⽂件,Android Killer查看java

根据加密逻辑写出解密脚本即可。

strs = [102, 13, 99, 28, 127, 55, 99, 19, 109, 1, 121, 58, 83, 30, 79, 0, 64, 42]
flag = ''
j=[]
for i in range(len(strs)-2,-1,-1):
  if i%2 == 0:
    strs[i] ^= i
  else:
    strs[i] ^= (strs[i+1])
print("".join(chr(i) for i in strs))
flag{Reverse__APP}

3.WAY

maze-upx.exe,upx脱壳后使用ida分析字符串,迷宫题:

OIIII
OOIO#
IOOOI
IOIOI
IIIII

sdsddwd,MD5加密得到flag

flag{6654b3343f6f3f6223a721e7f65e87f8}

4.SPARK

sparc指令集,ida⽆法反编译,使用ghidra得到伪代码。

a = '37463f3044413243';
b = '3429000000000000';
enc=bytes.fromhex('37463f30444132433429')
flag=''
for i in range(10):
 flag+=chr((enc[i]+0x2f)&0xff)
print(flag)
 flag{fun_sparcX}

5.VM

方法一

从sub_400FB4函数知道输⼊的⻓度应为16

所有vm的操作都在sub_400EBF函数的第12⾏执⾏

调试,构造⼀个⻓度为16的输⼊,”abcdefghijklmn12” 经过调试知道,输⼊的每个字符先异或8,再异或3,然后加1,最后和已知的res⽐较 对res写逆运算脚本即可得到flag

res="m<epbe<j~g;yo@ys"
s="abcdefghijklmn12"
print(hex(ord("a")^8^3))# 0x6a
print(hex(0x6a+1))# 0x6b
flag=""
for c in res:
  flag+=chr((ord(c)-1)^8^3)
print(flag)
flag{g0odjo0bvm1se4sy}

方法二

其中sub_ 400DD8是准备代码和函数的,sub 400F56是运行加密程序的,sub_ 400FB4是输出结果的。

Sub_ 400DD8里面是个结构体(? )里面有几个重要信息

* (a1+4) =8; * (a1+8) =1

Unk_ 6B9040是代码,有个头有个尾,中间一共17组数据(最后一组没用),每组数据有3*4条代码。前8组和最后一组操作相同, 剩下的几组操作相同

Sub_ 400CE1的代码是0xD4,里面判断代码运行不同程序,貌似是寄存器的存

取。每组数据的2, 10固定出现,均为*a1=*v3+基址和*v3+基址=*a1

Sub_ 400C15的代码是0x90,固定*a1^=* (a1+4) ^3,而* (a1 +4)固定为

8,每组操作均在第5个代码出现

Sub_ 400C7A的代码是0x8C,里面是对数据的读取

Sub_ 400B5D的代码是0x86,里面是判断每组第7个代码并判断,为0x19则

*a1 +=代码的第8位,每组的第8位均为1;为0x21则*a1+=* (a1+8),恒为1

所以可以将unknown中的代码翻译出来,逆向

using namespace std;
int main() {
  char str[]="m<epbe<j~g;yo@ys";
  int a4=8;
  int a8=1;
  for (int i=0;i<16;i++) {
    if(i<8||i==16) {
      str[i]-=1;
    }
    if(i>=8&&i<16) {
      str[i]-=a8;
    }
    str[i]^=a4^3;
  }
  cout<<str<<endl;
  return 0;
}
得到 flag:flag {g0odjo0bvm1se4sy

6.SHORT-LIVED

在 tanhua.exe 的 if ( hFile == (HANDLE)-1 ) 处断点, 取出 SystemVolumeInformation.exe 程序

提示jack skip

skipjack密码

使用网上的脚本:

// skipjack.cpp - modified by Wei Dai from Paulo Barreto's skipjack32.c,
// which is public domain according to his web site.

#include "pch.h"

#ifndef CRYPTOPP_IMPORTS

#include "skipjack.h"

/*
 *  Optimized implementation of SKIPJACK algorithm
 *
 *  originally written by Panu Rissanen <bande@lut.fi> 1998.06.24
 *  optimized by Mark Tillotson <markt@chaos.org.uk> 1998.06.25
 *  optimized by Paulo Barreto <pbarreto@nw.com.br> 1998.06.30
 */

NAMESPACE_BEGIN(CryptoPP)

/**
 * The F-table byte permutation (see description of the G-box permutation)
 */
const byte SKIPJACK::Base::fTable[256] = {
  0xa3,0xd7,0x09,0x83,0xf8,0x48,0xf6,0xf4,0xb3,0x21,0x15,0x78,0x99,0xb1,0xaf,0xf9,
  0xe7,0x2d,0x4d,0x8a,0xce,0x4c,0xca,0x2e,0x52,0x95,0xd9,0x1e,0x4e,0x38,0x44,0x28,
  0x0a,0xdf,0x02,0xa0,0x17,0xf1,0x60,0x68,0x12,0xb7,0x7a,0xc3,0xe9,0xfa,0x3d,0x53,
  0x96,0x84,0x6b,0xba,0xf2,0x63,0x9a,0x19,0x7c,0xae,0xe5,0xf5,0xf7,0x16,0x6a,0xa2,
  0x39,0xb6,0x7b,0x0f,0xc1,0x93,0x81,0x1b,0xee,0xb4,0x1a,0xea,0xd0,0x91,0x2f,0xb8,
  0x55,0xb9,0xda,0x85,0x3f,0x41,0xbf,0xe0,0x5a,0x58,0x80,0x5f,0x66,0x0b,0xd8,0x90,
  0x35,0xd5,0xc0,0xa7,0x33,0x06,0x65,0x69,0x45,0x00,0x94,0x56,0x6d,0x98,0x9b,0x76,
  0x97,0xfc,0xb2,0xc2,0xb0,0xfe,0xdb,0x20,0xe1,0xeb,0xd6,0xe4,0xdd,0x47,0x4a,0x1d,
  0x42,0xed,0x9e,0x6e,0x49,0x3c,0xcd,0x43,0x27,0xd2,0x07,0xd4,0xde,0xc7,0x67,0x18,
  0x89,0xcb,0x30,0x1f,0x8d,0xc6,0x8f,0xaa,0xc8,0x74,0xdc,0xc9,0x5d,0x5c,0x31,0xa4,
  0x70,0x88,0x61,0x2c,0x9f,0x0d,0x2b,0x87,0x50,0x82,0x54,0x64,0x26,0x7d,0x03,0x40,
  0x34,0x4b,0x1c,0x73,0xd1,0xc4,0xfd,0x3b,0xcc,0xfb,0x7f,0xab,0xe6,0x3e,0x5b,0xa5,
  0xad,0x04,0x23,0x9c,0x14,0x51,0x22,0xf0,0x29,0x79,0x71,0x7e,0xff,0x8c,0x0e,0xe2,
  0x0c,0xef,0xbc,0x72,0x75,0x6f,0x37,0xa1,0xec,0xd3,0x8e,0x62,0x8b,0x86,0x10,0xe8,
  0x08,0x77,0x11,0xbe,0x92,0x4f,0x24,0xc5,0x32,0x36,0x9d,0xcf,0xf3,0xa6,0xbb,0xac,
  0x5e,0x6c,0xa9,0x13,0x57,0x25,0xb5,0xe3,0xbd,0xa8,0x3a,0x01,0x05,0x59,0x2a,0x46
};

/**
 * The key-dependent permutation G on V^16 is a four-round Feistel network.
 * The round function is a fixed byte-substitution table (permutation on V^8),
 * the F-table.  Each round of G incorporates a single byte from the key.
 */
#define g(tab, w, i, j, k, l) \
{ \
  w ^= (word)tab[i*256 + (w & 0xff)] << 8; \
  w ^= (word)tab[j*256 + (w >>   8)]; \
  w ^= (word)tab[k*256 + (w & 0xff)] << 8; \
  w ^= (word)tab[l*256 + (w >>   8)]; \
}

#define g0(tab, w) g(tab, w, 0, 1, 2, 3)
#define g1(tab, w) g(tab, w, 4, 5, 6, 7)
#define g2(tab, w) g(tab, w, 8, 9, 0, 1)
#define g3(tab, w) g(tab, w, 2, 3, 4, 5)
#define g4(tab, w) g(tab, w, 6, 7, 8, 9)

/**
 * The inverse of the G permutation.
 */
#define h(tab, w, i, j, k, l) \
{ \
  w ^= (word)tab[l*256 + (w >>   8)]; \
  w ^= (word)tab[k*256 + (w & 0xff)] << 8; \
  w ^= (word)tab[j*256 + (w >>   8)]; \
  w ^= (word)tab[i*256 + (w & 0xff)] << 8; \
}

#define h0(tab, w) h(tab, w, 0, 1, 2, 3)
#define h1(tab, w) h(tab, w, 4, 5, 6, 7)
#define h2(tab, w) h(tab, w, 8, 9, 0, 1)
#define h3(tab, w) h(tab, w, 2, 3, 4, 5)
#define h4(tab, w) h(tab, w, 6, 7, 8, 9)

/**
 * Preprocess a user key into a table to save an XOR at each F-table access.
 */
void SKIPJACK::Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &)
{
  AssertValidKeyLength(length);

  /* tab[i][c] = fTable[c ^ key[i]] */
  int i;
  for (i = 0; i < 10; i++) {
    byte *t = tab+i*256, k = key[9-i];
    int c;
    for (c = 0; c < 256; c++) {
      t[c] = fTable[c ^ k];
    }
  }
}

typedef BlockGetAndPut<word16, LittleEndian> Block;

/**
 * Encrypt a single block of data.
 */
void SKIPJACK::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{
  word16 w1, w2, w3, w4;
  Block::Get(inBlock)(w4)(w3)(w2)(w1);

  /* stepping rule A: */
  g0(tab, w1); w4 ^= w1 ^ 1;
  g1(tab, w4); w3 ^= w4 ^ 2;
  g2(tab, w3); w2 ^= w3 ^ 3;
  g3(tab, w2); w1 ^= w2 ^ 4;
  g4(tab, w1); w4 ^= w1 ^ 5;
  g0(tab, w4); w3 ^= w4 ^ 6;
  g1(tab, w3); w2 ^= w3 ^ 7;
  g2(tab, w2); w1 ^= w2 ^ 8;

  /* stepping rule B: */
  w2 ^= w1 ^  9; g3(tab, w1);
  w1 ^= w4 ^ 10; g4(tab, w4);
  w4 ^= w3 ^ 11; g0(tab, w3);
  w3 ^= w2 ^ 12; g1(tab, w2);
  w2 ^= w1 ^ 13; g2(tab, w1);
  w1 ^= w4 ^ 14; g3(tab, w4);
  w4 ^= w3 ^ 15; g4(tab, w3);
  w3 ^= w2 ^ 16; g0(tab, w2);

  /* stepping rule A: */
  g1(tab, w1); w4 ^= w1 ^ 17;
  g2(tab, w4); w3 ^= w4 ^ 18;
  g3(tab, w3); w2 ^= w3 ^ 19;
  g4(tab, w2); w1 ^= w2 ^ 20;
  g0(tab, w1); w4 ^= w1 ^ 21;
  g1(tab, w4); w3 ^= w4 ^ 22;
  g2(tab, w3); w2 ^= w3 ^ 23;
  g3(tab, w2); w1 ^= w2 ^ 24;

  /* stepping rule B: */
  w2 ^= w1 ^ 25; g4(tab, w1);
  w1 ^= w4 ^ 26; g0(tab, w4);
  w4 ^= w3 ^ 27; g1(tab, w3);
  w3 ^= w2 ^ 28; g2(tab, w2);
  w2 ^= w1 ^ 29; g3(tab, w1);
  w1 ^= w4 ^ 30; g4(tab, w4);
  w4 ^= w3 ^ 31; g0(tab, w3);
  w3 ^= w2 ^ 32; g1(tab, w2);

  Block::Put(xorBlock, outBlock)(w4)(w3)(w2)(w1);
}

/**
 * Decrypt a single block of data.
 */
void SKIPJACK::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{
  word16 w1, w2, w3, w4;
  Block::Get(inBlock)(w4)(w3)(w2)(w1);

  /* stepping rule A: */
  h1(tab, w2); w3 ^= w2 ^ 32;
  h0(tab, w3); w4 ^= w3 ^ 31;
  h4(tab, w4); w1 ^= w4 ^ 30;
  h3(tab, w1); w2 ^= w1 ^ 29;
  h2(tab, w2); w3 ^= w2 ^ 28;
  h1(tab, w3); w4 ^= w3 ^ 27;
  h0(tab, w4); w1 ^= w4 ^ 26;
  h4(tab, w1); w2 ^= w1 ^ 25;

  /* stepping rule B: */
  w1 ^= w2 ^ 24; h3(tab, w2);
  w2 ^= w3 ^ 23; h2(tab, w3);
  w3 ^= w4 ^ 22; h1(tab, w4);
  w4 ^= w1 ^ 21; h0(tab, w1);
  w1 ^= w2 ^ 20; h4(tab, w2);
  w2 ^= w3 ^ 19; h3(tab, w3);
  w3 ^= w4 ^ 18; h2(tab, w4);
  w4 ^= w1 ^ 17; h1(tab, w1);

  /* stepping rule A: */
  h0(tab, w2); w3 ^= w2 ^ 16;
  h4(tab, w3); w4 ^= w3 ^ 15;
  h3(tab, w4); w1 ^= w4 ^ 14;
  h2(tab, w1); w2 ^= w1 ^ 13;
  h1(tab, w2); w3 ^= w2 ^ 12;
  h0(tab, w3); w4 ^= w3 ^ 11;
  h4(tab, w4); w1 ^= w4 ^ 10;
  h3(tab, w1); w2 ^= w1 ^  9;

  /* stepping rule B: */
  w1 ^= w2 ^ 8; h2(tab, w2);
  w2 ^= w3 ^ 7; h1(tab, w3);
  w3 ^= w4 ^ 6; h0(tab, w4);
  w4 ^= w1 ^ 5; h4(tab, w1);
  w1 ^= w2 ^ 4; h3(tab, w2);
  w2 ^= w3 ^ 3; h2(tab, w3);
  w3 ^= w4 ^ 2; h1(tab, w4);
  w4 ^= w1 ^ 1; h0(tab, w1);

  Block::Put(xorBlock, outBlock)(w4)(w3)(w2)(w1);
}
int main() {
  //byte inp[8] = { 0x33, 0x22, 0x11, 0x00, 0xdd, 0xcc, 0xbb, 0xaa };
  byte inp[8] = { 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,0x31 };
  //byte key[10] = { 0x00, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44,0x33, 0x22, 0x11 };
  byte key[10] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x55, 0x44,0x33, 0x22, 0x11 };
  byte enc[8]={0x92,0xa8,0x12,0x6,0x66,0xda,0x32,0xf5}, dec[8];
  //byte chk[8] = { 0x25, 0x87, 0xca, 0xe2, 0x7a, 0x12, 0xd3,0x00 };
  byte chk[8] = {0x35, 0xb1, 0x54, 0xb4, 0x11, 0xa1, 0x51, 0xb8};
  byte tab[10][256];
  int i;
  clock_t elapsed;
  makeKey(key, tab);
  //encrypt(tab, inp, enc);
  //printf((memcmp(enc, chk, 8) == 0) ? "encryption OK!\n" : "en
  cryption failure!\n");
  decrypt(tab, enc, dec);
  printf((memcmp(dec, inp, 8) == 0) ? "decryption OK!\n" : "decr
  yption failure!\n");
  for(i=0;i<7;i++){
    printf("%c",dec[i]);
  }
  printf("%c\n",dec[7]);
  elapsed = -clock();
  for (i = 0; i < 1000000; i++) {
    encrypt(tab, inp, enc);
  }
  elapsed += clock();
  printf ("elapsed time: %.1f s.\n", (float)elapsed/CLOCKS_PER_SEC);
  return 0;
}

解出结果

flag{02ea2ea21868385fa6b410a0b603f343}

7.RE

打开程序测试,可以输入单行字符串,程序会给出判断结果。

程序有反调试,虚拟机检测,反 dump 等机制,动态调试较难,尝试静态分 析。

建议复制并备份题目,ida 打补丁后会导致程序 crc 检测失败无法启动。

1,扔进 ida32 打开,寻找主函数:导出表只有一个 start 函数(OEP),进入__scrt_common_main_seh 初始化函 数,可见调用 main 函数。

2,分析 main 流程:403713 开始有花指令,典型的不定跳转+脏字节,undefine 从 403713 到403721 的垃圾字节,patch+nop 掉。(403722 等是 memset 的参数需要保留) 重启 ida,现在 main 函数可以 f5:

可以看到程序流程是:

1,开一个大小 58h 的数组,用 scanf 读取用户输入。

2,限制输入长度在 8 和 84 之间;然后计算输入长度,若不是 8 的倍数则用 一个字符串填充到 8 的倍数,但是填充字符 串被加密。

可以看到数组循环赋值语句上方有 sub_4028d0 和一个执行地址 loc_42c000 处函数的代码块,这个代码块 也出现在程序中其它有加密字符串的地方,shift+f12 搜索字符串未发现明 文,猜测上述两者与字符加密有关。

前往 sub_4028d0,此函数接收一个地址,并循环对地址内 167 个字节进行异 或 0x42 的操作。

前往 loc_42C000,发现汇编代码和代码段名称都非常奇怪。

ida 查看 segment 信息,textds 和 textdx 段都是 rwx 权限,怀 疑关键函数被 smc,而 sub_4028d0 和 sub_402920 为 smc 加解密代码。

使用 010editor 对两个代码段的机器码分别异或 0x42 和 0x33,得到正常代码。

分析字符串加密函数 sub_42c000:

函数接收一个字符指针,计算字符串长度,将最后一个字符与 0x17 异或,对 满足一个表达式的指定下标的字符有额 外处理,完整加解密 C 代码:

也可解密其它加密字符串。得到填充字符串为"h@CkfuN

3,填充后的输入首先被 sub_402c80 处理,该函数同样有花指令(402c86- 402c8f),典型的恶意破坏栈平衡;nop 掉继 续分析。

可以看到向函数传入四个 int 和一个指针,指针为传入的明文,猜测 4 个参与 运算的 int 是 key。

函数将输入分成两段 32 位明文(处理单位为长度是 8 的字符串),经过 32 轮交叉加密与一个初始化向 量生成一个整型数组(类似 des),可得逆算法如下:

4,继续分析主函数,Str字符数组携带sub_ 402c80的结 果进入loc_402de0,这里是第二个处理函数,同样有花指令(402de9-402def)。

ida无法确定跳转,使得假xor(33h)干扰后续分析, nop掉。

sub_ _402de0接收-个int类型的key。

每轮先通过sub _402d70调整key的字节序(类似hton1函数),再将明文每一位减去key后异或上一轮明文,(类似rc4) 。

字节序转换与逆算法还原代码如下:

5, 继续分析main函数:上述被加密的str数组前32位被传入st2数组, 且每个值都加上255, str2又被传入loc_42b000处的函数,这里的函数被sub_ 402920处理,已经被我们之前的smc步骤修复。分析代码,是一个复杂嵌套异或,对每个str2中的元素进行处理,返回-个代表密文的整形:求逆可以通过等量代换入手。

等价形式构造如下:

转换成如上形式,可通过位运算逐步递推求p。分成左移右移两种情况:移位后低n位高n位都为0,所以p^(p <<28)低28位与p的低28位相同,而高(32-28) 位则等于p^(p << 2))^(p << 28),右侧两者都已知,可求出p。

逆算法还原如下:

6, 继续分析main图数: st2数组被sub_401200处理,通过格式化字符串和参数数量叫看出是sprintf()每个数组元素以8位长16进制数转为字符串存入v28字符数组,变成一个长度256的字符串。

紧接着是loc_402ff0和loc_402ff0处理之前的两个256个元素的数组。

进入loc_402ec0去除花指令(402ec9-402ed1,e8h脏字节与不定跳转);

再进入loc. 402f0去除花指令(403006- 403011)。

继续分析:

sub_402ec0类似aes的初始化s盒,先将密钥每位加上基于下标运算出的数值,再将从0-255值初始化的s盒用密钥打乱,经过32轮生成s盒和逆s盒(s盒元素下标是逆s盒下标为s盒元素的值)。

生成的sbox传入sub_402加密函数,第一个参数v44为v28的拷贝, 是要加密的目标数组。

有一个明文的256位字符串为key;加密函数通过key和两个计数器生成临时变量,然后将临时变量分成4组,分别进行模256的四则运算,写回目标数组。

逆算法如下:

程序最后将生成的密文,一个长度256数组中每个元素与一个同长度的v52数组比较,完全一致则输出正确提示。

将v52数组传入解密函数,倒序使用四个阶段的解密函数即可得到flag。

f1ag{r3ve2se_is_So_ FuN_CarRy0n}

8.DRIVER

方法一

一道很简单的读 api 题,题目分为用户态的一个 exe 和一个内核驱动,可以通过 SRVINSTW.EXE 工具 以设备服务方式安装驱动,net start <服务名> 即可运行。

Exe 在根目录释放一个 txt 文件,然后获取自身进程 pid,打开一个内核设 备实现向驱动发送 pid 号功能 驱动接收到 pid,循环枚举关键结构体判断 ctfhide.exe 是否在运行,若没 有运行则什么都不做。

为了解密得到 flag 需要保持 exe 进程打开。紧接着驱动循环读取 exe 根目录下 hidden.txt 的文件大小,若不为 0 则什 么都不做。

为了解密得到 flag 需要让释放的文件长变为 0,也就是删除、重命 名该文件,但驱动事先通过 ZwOpenFile 获取了内核文件句柄,实现了简单的文 件保护。

这里最简单的方法就是使用杀毒的文件粉碎机进行删除操作。删除后等 待 3 秒,驱动将调用 cng api 解密 blob 数组中的密钥,解密 aes-128 加密的 flag 并自动打印在 dgbprint。

方法二

import 查看 api 函数

定位查看关键参数

cbc 模式,有 iv 和 key

密钥生成

Src 存放 iv 的值,按 x 查看交叉引用可以找到赋值

搜索函数,第二个参数为密文。解码即可。

Flag{youareaolddriver}

红客突击队于2019年由队长k龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。