HZNUCTF2023-部分wp

文章介绍了如何处理被修改UPX特征的加壳文件,通过使用十六进制编辑器修复特征并成功脱壳。接着,展示了程序中的RC4加密过程,并提供了解密脚本。最后,提到了一种利用永真或永假条件的花指令,并给出了清除这些指令的方法,以暴露main函数。
摘要由CSDN通过智能技术生成

esay signin

打开查壳,发现是upx壳,尝试脱壳失败,直接去网上搜索原因,是因为upx特征修改了,使用upx -d 文件名.exe无法脱壳,需要进行upx特征修复

没被修改upx特征的upx加壳文件,在winhex中打开程序后,可以看到三个区段名。“UPX0”和“UPX1”是加UPX壳后的两个区段名。其中UPX1区段包含了需要解压的数据块。“.rsrc”是程序资源信息区段名,这个区段含有原资源段的完整头部以及图标、Manifest、版本等未被压缩的资源,当然还有UPX自身需要的导入信息等(如果程序自身不含资源段,加壳后就是“UPX2”)(摘自一位大佬的博客)

这是正常的upx加壳文件,可以看到upx0,upx1的特征 ,再往后看可以看到upx的头

UPX头是供UPX通过“upx-d”命令脱壳用的,并不影响程序运行,可以把它权改为0,或者其他的数,这样就无法使用upx -d命令来快速脱壳

其实 exeinfope上显示了被修改了的upx特征,只要把它改回upx即可

正在上传…重新上传取消

可以直接在winhex或者其他十六进制编辑器中修复文件,这里使用010editor

 

 这个时候再进行脱壳就可以脱掉

ida中打开程序

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3; // esi
  unsigned int v4; // kr00_4
  int v5; // ebx
  int i; // ecx
  int j; // edi
  unsigned __int8 v8; // dl
  int v9; // ebx
  int v10; // edi
  unsigned __int8 v11; // cl
  int v12; // ecx
  size_t v13; // esi
  char v15; // [esp+0h] [ebp-90Ch]
  size_t k; // [esp+4h] [ebp-908h]
  char v17[512]; // [esp+8h] [ebp-904h] BYREF
  __int128 v18[32]; // [esp+208h] [ebp-704h] BYREF
  char Src[512]; // [esp+408h] [ebp-504h] BYREF
  char v20[256]; // [esp+608h] [ebp-304h] BYREF
  char v21[256]; // [esp+708h] [ebp-204h] BYREF
  char v22[256]; // [esp+808h] [ebp-104h] BYREF

  memset(v22, 0, sizeof(v22));
  memset(v17, 0, sizeof(v17));
  v18[0] = xmmword_402140;
  v18[1] = xmmword_402130;
  memset(&v18[2], 0, 0x1E0u);
  strcpy(v21, "justfortest");
  memset(&v21[12], 0, 0xF4u);
  memset(Src, 0, sizeof(Src));
  sub_401050("%s", (char)byte_403370);
  v3 = strlen(byte_403370);
  memcpy(Src, byte_403370, v3);
  v5 = 0;
  v4 = strlen(v21);
  memset(v20, 0, sizeof(v20));
  for ( i = 0; i < 256; ++i )
  {
    v22[i] = i;
    v20[i] = v21[i % v4];
  }
  for ( j = 0; j < 256; ++j )
  {
    v8 = v22[j];
    v5 = (v8 + v20[j] + v5) % 256;
    v22[j] = v22[v5];
    v22[v5] = v8;
  }
  v9 = 0;
  v10 = 0;
  for ( k = 0; k < v3; ++k )
  {
    v10 = (v10 + 1) % 256;
    v11 = v22[v10];
    v9 = (v11 + v9) % 256;
    v22[v10] = v22[v9];
    v22[v9] = v11;
    Src[k] ^= v22[(unsigned __int8)(v11 + v22[v10])];
  }
  memcpy(v17, Src, v3);
  v12 = 0;
  v13 = v3 - 1;
  if ( v13 )
  {
    while ( v17[v12] == *((_BYTE *)v18 + v12) )
    {
      if ( ++v12 >= v13 )
        goto LABEL_10;
    }
    sub_401020("fail\n", v15);
  }
  else
  {
LABEL_10:
    sub_401020("success\n", v15);
  }
  return 0;
}

很明显的rc4加密,其中密钥是justfortest ,密文是v18中存储的数据,注意v18[0]为xmmword_402140,v18[1]为xmmword_402130,写脚本解密

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'justfortest'
ciphertext = codecs.decode('42FD5561B9276FF5B68623A9EF1C049FD41687D65468BC02156D30084B614C5E', 'hex')
plaintext = rc4_decrypt(key, ciphertext)
print(plaintext)

 解得flag:flag{4950b6562657477e6685828e537f43e5}

虽然他送了我玫瑰花

打开文件,查壳无壳,放入到ida中

main函数f5无法解析,结合题目猜测是花指令

 找到可疑的部分,这部分代码就是花指令,通过设置永真或者永假的条件来使程序执行,具体可参考一位大佬的文章https://blog.csdn.net/abel_big_xu/article/details/117927674

把这部分的垃圾数据nop掉,就可以分析main函数

 main函数出现了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // esi
  int j; // eax
  char v6; // [esp+0h] [ebp-F8h]
  char *v7; // [esp+0h] [ebp-F8h]
  char v8; // [esp+4h] [ebp-F4h]
  char v9[100]; // [esp+Ch] [ebp-ECh]
  __int128 v10; // [esp+70h] [ebp-88h]
  int v11; // [esp+80h] [ebp-78h]
  int v12; // [esp+84h] [ebp-74h]
  int v13; // [esp+88h] [ebp-70h]
  char v14; // [esp+8Ch] [ebp-6Ch]
  char flag[100]; // [esp+90h] [ebp-68h] BYREF

  printf(&Format, v6);
  scanf("%s", flag);
  v11 = -171171450;
  v12 = -669748952;
  v13 = 1651994351;
  v14 = -6;
  v10 = xmmword_402170;
  if ( strlen(flag) == 29 )                     // flag的长度为29
  {
    for ( i = 0; i < 29; ++i )
      v9[i] = funcs_40117E[i % 5u](flag[i]);    // 对余数为0,1,2,3,4,的值分别如下操作sub_401080  sub_401090 sub_4010A0 sub_4010B0 sub_4010C0
    for ( j = 0; j < 29; ++j )
    {
      if ( v9[j] != *(&v10 + j) )               // 比对处理过的flag与xmmword_402170
        break;
    }
    printf(v7, v8);
  }
  else
  {
    printf("wwwhhhaaattt???\n", v8);
  }
  return 0;
}

写脚本解密

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<Windows.h>
#include<stdint.h>
int main(){
unsigned char miwen[] =
{
 0x7F, 0x7E, 0x51, 0xCE, 0xFB, 0x4E, 0x7A, 0x24, 
  0xE8, 0xDF, 0x59, 0x71, 0x26, 0xCA, 0xE1, 0x6C,0x86, 0x21, 0xCC, 0xF5,0x28, 0x71, 0x14, 0xD8,0xEF, 0x6E, 0x77, 0x62, 0xFA
};
 char flag[30];
 int i;
 for(i=0;i<29;i++){
 	int n=i%5;
 	switch(n){
 		case 0:
		  flag[i]=miwen[i]^ 0x19;
		  break;
		case 1:
			flag[i]=miwen[i] - 18;
			break;
		case 2:
			flag[i]=miwen[i]+ 16;
			break;
		case 3:
			flag[i]=(miwen[i]>>1)&0x7f;//2 * (a1 & 0x7F)
			break;
		case 4:
			flag[i]=miwen[i] & 0x7f;
			break;
	 }
 }printf("%s",flag);
 
 
	return 0;
}

注意:密文不仅包含xmmword_402170,后面的v11,v12,v13.....也有部分,为什么说是部分呢,因为v11,v12...虽然数很大,但是其是int型,也就是说数再大也只是取int(4字节)的数据。

学到的点:

  1. upx脱壳
  2. 一种新的花指令类型:利用构造永真永假来实现程序运行,常见的形式为:(引用大佬的文章介绍https://blog.csdn.net/abel_big_xu/article/details/117927674

__asm{
    push ebx
    xor ebx,ebx
    test ebx,ebx
    jnz label1
    jz label2
label1:
    _emit junkcode
label2:
   pop ebx//需要恢复ebx寄存器    
}

__asm{
    clc
    jnz label1:
    _emit junkcode
label1:
}

3.写脚本时注意要提取的数据是什么类型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值