HZNUCTF2023部分wp-Reverse

checkIn

直接打开,看到flag,但是是小端存储要倒过来

 也可以直接在hex中查看,中间不需要的部分删除

得到flag:HZNUCTF{We1c0me_t0_Reverse}

Bytecode

下载附件可以看到如下,这个好像是python的字节码(python -m dis xxx.py可以看到字节码)

  0 LOAD_GLOBAL              0 (input)
  2 LOAD_CONST               1 ('plz input your flag:')
  4 CALL_FUNCTION            1
  6 STORE_FAST               0 (a)//值a开始

  8 LOAD_CONST               2 (25)
 10 LOAD_CONST               3 (108)
 12 LOAD_CONST               3 (108)
 14 LOAD_CONST               4 (176)
 16 LOAD_CONST               5 (18)
 18 LOAD_CONST               3 (108)
 20 LOAD_CONST               6 (110)
 22 LOAD_CONST               7 (177)
 24 LOAD_CONST               8 (64)
 26 LOAD_CONST               9 (29)
 28 LOAD_CONST              10 (134)
 30 LOAD_CONST               9 (29)
 32 LOAD_CONST              11 (187)
 34 LOAD_CONST              12 (103)
 36 LOAD_CONST              13 (32)
 38 LOAD_CONST              14 (139)
 40 LOAD_CONST              15 (144)
 42 LOAD_CONST              16 (179)
 44 LOAD_CONST              10 (134)
 46 LOAD_CONST               7 (177)
 48 LOAD_CONST              13 (32)
 50 LOAD_CONST              17 (24)
 52 LOAD_CONST              15 (144)
 54 LOAD_CONST               2 (25)
 56 LOAD_CONST              18 (111)
 58 LOAD_CONST              19 (14)
 60 LOAD_CONST              18 (111)
 62 LOAD_CONST              19 (14)//结束
 64 BUILD_LIST              28
 66 STORE_FAST               1 (c)

 68 LOAD_GLOBAL              1 (len)
 70 LOAD_FAST                0 (a)
 72 CALL_FUNCTION            1
 74 LOAD_CONST              20 (28)
 76 COMPARE_OP               3 (!=)//判断a的长度是否等于28
 78 POP_JUMP_IF_FALSE       92

 80 LOAD_GLOBAL              2 (print)
 82 LOAD_CONST              21 ('wrong length')
 84 CALL_FUNCTION            1
 86 POP_TOP

 88 LOAD_CONST              22 (0)
 90 RETURN_VALUE

 92 LOAD_GLOBAL              3 (range)//做一个循环,在0到28范围内
 94 LOAD_GLOBAL              1 (len)
 96 LOAD_FAST                0 (a)
 98 CALL_FUNCTION            1
100 CALL_FUNCTION            1
102 GET_ITER
104 FOR_ITER                46 (to 152)
106 STORE_FAST               2 (i)

108 LOAD_GLOBAL              4 (ord)
110 LOAD_FAST                0 (a)
112 LOAD_FAST                2 (i)
114 BINARY_SUBSCR
116 CALL_FUNCTION            1
118 LOAD_CONST              23 (39)//先乘39再对196取模
120 BINARY_MULTIPLY//乘法
122 LOAD_CONST              24 (196)
124 BINARY_MODULO//取模
126 LOAD_FAST                1 (c)
128 LOAD_FAST                2 (i)
130 BINARY_SUBSCR
132 COMPARE_OP               3 (!=)//判断c是否与a相等,相等即正确
134 POP_JUMP_IF_FALSE      104

136 LOAD_GLOBAL              2 (print)
138 LOAD_CONST              25 ('wrong')
140 CALL_FUNCTION            1
142 POP_TOP

144 POP_TOP
146 LOAD_CONST               0 (None)
148 RETURN_VALUE
150 JUMP_ABSOLUTE          104

152 LOAD_GLOBAL              2 (print)
154 LOAD_CONST              26 ('win')
156 CALL_FUNCTION            1
158 POP_TOP
160 LOAD_CONST               0 (None)
162 RETURN_VALUE

分析完代码,来写脚本爆破

#include <stdio.h>
#include <string.h>

int main() {
	int a[28];
	int i=0,j=0;
    int c[28] = {25, 108, 108, 176, 18, 108, 110, 177, 64, 29, 134, 29, 187, 103, 32, 139, 144, 179, 134, 177, 32, 24, 144, 25, 111, 14, 111, 14};
for(j=0;j<=28;j++){
  for(i=0;i<222;i++){
	if(i * 39 % 196 == c[j]){
		a[j]=i;
	}
    
  } 
  
}for(i=0;i<28;i++){
  	printf("%c",a[i]);
  }return 0;
}

直接爆出flag :flag{G00dj0&_H3r3-I$Y@Ur_$L@G!~!~},这个非常奇怪,i循环的范围太大了(0~256)就会乱码,显示错误的,太小了(0~122)之间flag中~符号就不会显现,搞了很久才发现这个问题。

easyAPK

这题是个安卓逆向的题,打开通过.xml文件可以找到主函数登录页面,猜测flag就在这里

 

 往下找可以看到加密方式为AES/CBC/PKCS5Padding加密,具体内容可以看一位师傅的博客(https://blog.csdn.net/wangmx1993328/article/details/106170060

 

 可以看到这段代码,是进行AES/CBC/PKCS5Padding解密的过程,传进decrypt函数的参数ciphertext是加密后再用base64加密的密文,key.getBytes()是加密密钥,因为AES加密是对称加密,即加密和解密的密钥要一致,“iviviviviviv".getBytes()是加密的偏移量,注意密钥key和偏移量iv必须是AES加密是必须是16位,且iv通常不为中文,现在就来找key和iv,key值看到在这

直接文本搜索

 可以看到选中的这一行

kkkeyyy404404404

 注意啊key不是这个,当时一度被这个迷惑了...

分析完解密过程后可以尝试2种解密方法

一,在线工具解密(在线AES加密解密高级版-ME2在线工具

 二,解密脚本

import base64
from Crypto.Cipher import AES
def AES_Decrypt(key, data):
  vi = 'iviviviviviviviv'
  data = data.encode('utf8')
  encodebytes = base64.decodebytes(data)
  # 将加密数据转换位bytes类型数据
  cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
  text_decrypted = cipher.decrypt(encodebytes)
  unpad = lambda s: s[0:-s[-1]]
  text_decrypted = unpad(text_decrypted)
  # 去补位
  text_decrypted = text_decrypted.decode('utf8')
  return text_decrypted

key = 'kkkeyyy404404404' #自己密钥
data = 'Lz49p2OjPZzUMXakynHQuw==' #需要加密的内容
text_decrypted = AES_Decrypt(key, data)
print(text_decrypted)

#reiseasy

最后在真机或者虚拟机上运行

 

 题目提示要Base58加密,得到flag:

 注意:要打开magic功能(https://gchq.github.io/CyberChef/放上解密工具)

flower

打开文件,拖入到ida中

可以看到部分伪代码

 但是后面的代码无法查看,到汇编窗口看看,

可以看到这里有一大段数据,

可以看到004011EA这里跳到了loc_401200这个地方,loc_401200这里调用了loc_401206这个地方,中间的db 00E8h是没有任何用处的,实际上loc_401206这里也没有什么用处,都是花指令。全部nop掉。

 实际上前面nop掉后,后面的代码会自动解析,接着f5分析,可以得到完整代码。通过代码可以看出来是rc4加密算法,但是改了一点,比rc4算法多了一个异或0x37(加密过程有很多%256的,要考虑一下是不是rc4)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-DF4h]
  char v5; // [esp+0h] [ebp-DF4h]
  char v6; // [esp+0h] [ebp-DF4h]
  int v7[604]; // [esp+Ch] [ebp-DE8h]
  int v8; // [esp+97Ch] [ebp-478h]
  int v9; // [esp+980h] [ebp-474h]
  _BYTE *v10; // [esp+984h] [ebp-470h]
  int v11; // [esp+988h] [ebp-46Ch]
  int v12; // [esp+98Ch] [ebp-468h]
  char *v13; // [esp+990h] [ebp-464h]
  _BYTE *v14; // [esp+994h] [ebp-460h]
  unsigned int m; // [esp+998h] [ebp-45Ch]
  int v16; // [esp+99Ch] [ebp-458h]
  unsigned int k; // [esp+9A0h] [ebp-454h]
  int j; // [esp+9A4h] [ebp-450h]
  char *v19; // [esp+9A8h] [ebp-44Ch]
  char *p_Arglist; // [esp+9ACh] [ebp-448h]
  char *v21; // [esp+9B0h] [ebp-444h]
  _BYTE *v22; // [esp+9B4h] [ebp-440h]
  int i; // [esp+9B8h] [ebp-43Ch]
  int v24; // [esp+9BCh] [ebp-438h]
  int v25; // [esp+9C4h] [ebp-430h]
  int v26; // [esp+9C8h] [ebp-42Ch]
  char Arglist; // [esp+9CCh] [ebp-428h] BYREF
  _BYTE v28[1023]; // [esp+9CDh] [ebp-427h] BYREF
  int v29[3]; // [esp+DCCh] [ebp-28h] BYREF
  char v30; // [esp+DD8h] [ebp-1Ch] BYREF
  char v31[23]; // [esp+DD9h] [ebp-1Bh] BYREF

  v7[601] = 9;
  v7[600] = 0;
  v26 = 0;
  qmemcpy(v29, "tellmewhy", 9);                 // 密匙
  v30 = 2;
  qmemcpy(v31, "9+", 2);
  v31[2] = -84;
  v31[3] = -44;
  v31[4] = 120;
  v31[5] = -82;
  v31[6] = -93;
  v31[7] = 66;
  v31[8] = 58;
  v31[9] = 17;
  v31[10] = -7;
  v31[11] = 90;
  v31[12] = -55;
  v31[13] = -68;
  v31[14] = -84;
  v31[15] = 22;
  v31[16] = -80;
  v31[17] = -57;
  v31[18] = 85;
  v31[19] = -72;
  v31[20] = 99;
  v31[21] = 0x80;
  printf(Format, v4);
  sub_401050("%s", &Arglist);
  v14 = v28;
  v22 = &v28[strlen(&Arglist)];
  v12 = v22 - v28;
  v13 = v31;
  v21 = &v31[strlen(&v30)];
  v11 = v21 - v31;
  if ( v22 - v28 == v21 - v31 )
  {
    while ( v24 < 256 )
    {
      v7[v24] = *(v29 + v24 % 9);
      v7[v24 + 300] = v24;
      ++v24;
    }
    for ( i = 0; i < 256; ++i )
    {
      v26 = (v7[i] + v7[i + 300] + v26) % 256;
      v16 = v7[i + 300];
      v7[i + 300] = v7[v26 + 300];
      v7[v26 + 300] = v16 ^ 0x37;//多的异或过程
    }
    for ( j = 0; j < 256; ++j )
      ;
    printf("\n", v5);
    v25 = 0;                                    // 设置两个变量
    v26 = 0;
    for ( k = 0; ; ++k )
    {
      p_Arglist = &Arglist;
      v10 = v28;
      p_Arglist += strlen(p_Arglist);
      v9 = ++p_Arglist - v28;
      if ( k >= p_Arglist - v28 )               // 生成密匙流
        break;
      v25 = (v25 + 1) % 256;
      v26 = (v7[v25 + 300] + v26) % 256;
      v16 = v7[v25 + 300];
      v7[v25 + 300] = v7[v26 + 300];            // 交换
      v7[v26 + 300] = v16;
      v8 = (v7[v26 + 300] + v7[v25 + 300]) % 256;
      *(&v26 + v25 + 3) ^= LOBYTE(v7[v8 + 300]);// 加密过程(异或)
    }
    for ( m = 0; ; ++m )
    {
      v19 = &Arglist;
      v7[603] = v28;
      v19 += strlen(v19);
      v7[602] = ++v19 - v28;
      if ( m >= v19 - v28 )
        break;
      if ( v28[v25 - 1] != v31[v25 - 1] )       // 最后密文要等于v31
      {
        printf("Maybe you should think about it", v6);
        return 0;
      }
    }
    printf("Wow,tqllll!", v6);
    return 0;
  }
  else
  {
    printf("Tooooo short!", v5);
    return 0;
  }
}

写解密脚本

int main() {
    // int i, m, n; 
    int key_len = 9;//密钥长度; 
    int buf_len = 0;//明文长度 
    int j=0, q, n;
    int b[300];
    int s[300];
    unsigned char key_data[9] = "tellmewhy";
    unsigned char buf_data[] = { 0x02,0x39,0x2b,0xac,0xd4,0x78,0xae,0xa3,0x42,0x3a,0x11,0xf9,0x5a,0xc9,0xbc,0xac,0x16,0xb0,0xc7,0x55,0xb8,0x63,0x80 };

    for (int i = 0; i < 256; i++) {
        b[i] = key_data[i % 9];  //8是密钥的长度 (变) 
        s[i] = i;
    }
    for (int i = 0; i < 256; i++) {
        j = (j + s[i] + b[i]) % 256;
        q = s[i];
        s[i] = s[j];
        s[j] = q ^ 0x37;
    }
    int i = 0, t;
    j = 0;
    for (int w = 0; w < strlen(buf_data); w++) {  //32是data的长度 (变) 

        i = (i + 1) % 256;
        j = (j + s[i]) % 256;

        q = s[i];
        s[i] = s[j];
        s[j] = q; //交换 

        t = (s[i] + s[j]) % 256;
        buf_data[i - 1] ^= s[t];//s[t]是最后的密钥 
    }
    
    printf("%s\\n",buf_data);
    return 0;
}

再贴一个脚本,可以适用于没被魔改的rc4算法

import codecs

def rc4_decrypt(key, ciphertext):
    S = list(range(256))
    j = 0
    out = []
    # KSA Phase
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    # PRGA Phase
    i = j = 0
    for char in ciphertext:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        out.append(chr(char ^ S[(S[i] + S[j])%256]))
    return ''.join(out)

key = b''#密钥
ciphertext = codecs.decode('', 'hex')#密文
plaintext = rc4_decrypt(key, ciphertext)
print(plaintext)

学到的知识点:

1.什么是python字节码,如何处理pytho字节码

2.apk文件,放入jadx中,并且通过文本搜索找到相关信息,通过.xml文件可以找到主函数登录页面,查看相关函数,还可以直接下载在手机上进一步操作。

3。rc4算法原理和解密脚本。

4.注意:nop的机器码是90,可以把数据改成90,实现nop,但是在代码段中数据的机器码被改成90后还要按c识别为代码,否则5无法重新识别

5.常见的花指令机器码要熟悉:call:E8   jmp:E9    jmp short:EB

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值