HZNUCTF2023部分wp-Reverse

文章详细介绍了几个网络安全挑战的解决方案,包括基于Python字节码的解码,AES/CBC/PKCS5Padding的解密方法,以及RC4加密算法的分析和解密脚本编写。此外,还提到了AndroidAPK文件的逆向工程和花指令的识别与处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.youkuaiyun.com/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

<think>好的,我现在需要帮用户解答关于“conda init --reverse $SHELL”的问题。首先,我得确认自己对这个命令的理解是否正确。记得conda init是用来初始化shell以便使用conda命令的,而--reverse参数可能是用来撤销这个初始化的。 接下来,我应该先分解这个命令的各个部分。$SHELL是环境变量,代表当前使用的shell,比如bash、zsh或fish。用户运行这个命令时,conda会针对他们当前使用的shell进行反向操作,也就是移除之前conda init所做的修改。 需要思考一下这个命令的具体作用。通常conda init会在shell的配置文件中添加一些启动脚本,比如.bashrc或.zshrc,这样每次打开终端时conda环境会自动激活。使用--reverse应该会删除这些添加的部分,恢复配置文件到初始化之前的状态。 然后,用户可能想知道使用这个命令的常见场景。比如,如果用户不再需要conda,或者遇到了环境变量冲突的问题,可能需要卸载conda的自动初始化。另外,如果用户误操作导致终端启动时自动激活了某个conda环境,也可以通过这个命令来解除。 还要考虑执行后的影响。移除初始化后,用户可能需要手动管理conda环境,比如每次使用时需要手动运行conda activate。同时,确保用户了解这个操作不会卸载conda本身,只是移除了shell的集成部分。 另外,应该提醒用户注意,不同的shell配置文件可能不同,确保他们正确替换$SHELL为实际的shell名称,或者直接使用变量。比如,如果用户使用的是bash,命令应该是conda init --reverse bash,但使用$SHELL可以自动获取当前shell。 最后,需要给出一些补充说明,比如如何重新启用conda初始化,或者彻底卸载conda的方法,帮助用户全面了解选项。同时,提醒谨慎操作,因为修改配置文件可能存在风险,建议备份。</think>### 关于 `conda init --reverse $SHELL` 的逐步解释 #### 1. **命令作用** - `conda init --reverse $SHELL` 用于 **撤销 Conda 对 Shell 的初始化配置**,即移除 Conda 在 Shell 配置文件(如 `.bashrc`、`.zshrc` 等)中添加的启动脚本。 - 执行后,Conda 将不再自动激活基础环境,且 `conda` 命令可能无法直接在 Shell 中使用(需手动配置路径)。 --- #### 2. **参数解析** - `--reverse`:表示“反向操作”,即撤销初始化。 - `$SHELL`:环境变量,表示当前 Shell 的类型(如 `/bin/bash`、`/bin/zsh`)。 *若不确定,可手动替换为具体 Shell 名称,例如 `conda init --reverse bash`。* --- #### 3. **典型使用场景** - **卸载 Conda 前的清理**:移除自动生成的 Shell 配置。 - **解决环境冲突**:若 Conda 初始化导致 Shell 启动异常(如提示 `conda init` 错误)。 - **禁用自动激活基础环境**:避免每次打开终端时自动进入 `base` 环境。 --- #### 4. **执行后影响** - **Shell 配置文件**: 会删除类似以下内容: ```bash # >>> conda initialize >>> ... # <<< conda initialize <<< ``` - **Conda 命令依赖**: 若未将 Conda 的 `bin` 目录加入 `PATH`,可能需通过绝对路径使用 Conda(如 `/opt/anaconda3/bin/conda`)。 --- #### 5. **操作示例** ```bash # 查看当前 Shell 类型 echo $SHELL # 执行反向初始化(假设 Shell 是 bash) conda init --reverse bash # 重启终端或重新加载配置 source ~/.bashrc ``` --- #### 6. **注意事项** - **备份配置文件**:建议先备份 Shell 配置文件(如 `cp ~/.bashrc ~/.bashrc.bak`)。 - **仅影响配置**:此命令不会卸载 Conda,仅移除 Shell 集成。需彻底卸载 Conda 需手动删除安装目录。 - **恢复初始化**:若需重新启用,可运行 `conda init $SHELL`。 --- ### 总结 此命令是 Conda 环境管理的“清理工具”,适用于需要解除 Conda 与 Shell 的绑定关系时使用。操作前请确保理解其影响,并做好必要备份。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值