逆向做题记录2023 4.3-4.9

[WUSTCTF2020]level1

查壳,64位无壳,ida打开

int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *stream; // ST08_8
  signed int i; // [rsp+4h] [rbp-2Ch]
  char ptr[24]; // [rsp+10h] [rbp-20h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  stream = fopen("flag", "r");
  fread(ptr, 1uLL, 0x14uLL, stream);
  fclose(stream);
  for ( i = 1; i <= 19; ++i )
  {
    if ( i & 1 )
      printf("%ld\n", (unsigned int)(ptr[i] << i));
    else
      printf("%ld\n", (unsigned int)(i * ptr[i]));
  }
  return 0;
}

fopen()函数:

fopen() 是一个 C 标准库函数,用于打开一个文件并返回一个指向该文件的指针。它的声明如下:

FILE *fopen(const char *filename, const char *mode); 

其中,filename 是一个字符串,指定要打开的文件名及其路径,mode 是一个字符串,指定打开文件的模式,它可以是以下值之一:

  • "r": 以只读方式打开文件,文件必须存在。
  • "w": 以写入方式打开文件,如果文件不存在,则创建文件;如果文件已存在,则清空文件内容。
  • "a": 以追加方式打开文件,如果文件不存在,则创建文件。
  • "r+": 以读写方式打开文件,文件必须存在。
  • "w+": 以读写方式打开文件,如果文件不存在,则创建文件;如果文件已存在,则清空文件内容。
  • "a+": 以读写方式打开文件,如果文件不存在,则创建文件。

fopen() 返回一个 FILE 类型的指针,该指针可以用于后续的文件读写操作。如果打开文件失败,fopen() 将返回 NULL。

__readfsqword 是汇编指令,用于读取 FS 段寄存器中存储的 64 位整数。__readfsqword(0x28u) 赋值给变量 v7 则表示将 FS 段寄存器中偏移为 0x28 的地址处存储的 64 位整数值赋值给变量 v7。

fread() 是 C 标准库中的一个函数,用于从文件中读取数据到内存缓冲区中。其函数原型如下:

size_t fread(void *ptr, size_t size, size_t count, FILE *stream); 

该函数的四个参数分别为:

  • ptr:指向要读取数据存储位置的指针。
  • size:要读取每个数据项的字节数。
  • count:要读取的数据项数量。
  • stream:指向 FILE 结构的指针,指示要读取数据的文件。

函数返回值是成功读取的数据项数量,如果读取失败则会返回一个小于 count 的值。

fread() 函数可以用于读取二进制文件,如图片、视频、音频等。读取二进制文件时,size 参数通常是数据项的大小,count 参数通常是要读取的数据项数量。ptr 参数是指向存储读取数据的缓冲区的指针。

fclose() 是 C 标准库中的一个函数,用于关闭打开的文件。其函数原型如下:

int fclose(FILE *stream); 

该函数的参数为指向 FILE 结构的指针,指示要关闭的文件。函数返回值为 0 表示关闭文件成功,返回值为 EOF(-1)则表示关闭文件失败。

fclose() 函数将写入任何挂起的缓冲区数据,释放与文件流相关的所有缓冲区,并关闭该文件。当 fclose() 函数调用成功后,再次使用该文件句柄会导致未定义的行为。

在 C 语言中,<<>> 是位运算符,用于对二进制数进行左移和右移操作。

<< 左移运算符将一个二进制数向左移动指定的位数,并在右侧用 0 填充。例如,对于二进制数 00000001,左移 3 位得到的结果是 00001000。左移运算符的语法为:

x << n 

其中,x 表示要进行左移操作的二进制数,n 表示要向左移动的位数。

>> 右移运算符将一个二进制数向右移动指定的位数,并在左侧用 0 填充。例如,对于二进制数 00001000,右移 3 位得到的结果是 00000001。右移运算符的语法为:

x >> n 

其中,x 表示要进行右移操作的二进制数,n 表示要向右移动的位数。

在 C 语言中,位运算符通常用于处理二进制数或者掩码,例如:

  • 使用 << 左移运算符将一个整数的二进制表示向左移动若干位,可以实现乘以 2 的 n 次方的运算。
  • 使用 >> 右移运算符将一个整数的二进制表示向右移动若干位,可以实现除以 2 的 n 次方的运算。
  • 使用位运算符可以快速计算掩码,即在二进制数的特定位置置位或清零。

 了解完这些,来看一下这段代码的流程

  • 打开名为 "flag" 的文件,并读取前 19 个字节的内容;
  • 关闭文件;
  • 对读取到的 19 个字节进行处理,并输出处理结果; a. 对于第奇数个字节,将其左移 i 位,并将结果作为无符号整数输出; b. 对于第偶数个字节,将其乘以 i,并将结果作为无符号整数输出。
  • 有一点要注意,if(i&1)是对变量i进行按位与1运算,并将结果作为条件表达式的值进行判断。由于二进制数1的最低位为1,其它位都是0,因此,i&1的结果就是i的二进制表示中最低位的值。如果i的最低位为1,则if(i&1)的条件表达式为真,否则为假。一般情况下,该表达式用来作判断奇数或者偶数。

 然后查看题目给出的另一个文件,名为output,那应该就是输出了:

198
232
816
200
1536
300
6144
984
51200
570
92160
1200
565248
756
1474560
800
6291456
1782
65536000

 写代码:

key = [198, 232, 816, 200, 1536, 300, 6144, 984, 51200,
       570, 92160, 1200, 565248, 756, 1474560, 800, 6291456,
       1782, 65536000]
for i in range(1, 20):
    if i % 2 == 1:
        print(chr(key[i-1] >> i), end='')
    else:
        print(chr(key[i-1] // i), end='')


结果ctf2020{d9-dE6-20c}

 [GUET-CTF2019]re

查壳

对于这种upx壳,直接使用工具就可以。

通过字符串窗口找到可以字符,进入主要函数,

__int64 sub_400E28()
{
  __int64 result; // rax
  __int64 v1; // [rsp+0h] [rbp-30h]
  __int64 v2; // [rsp+8h] [rbp-28h]
  __int64 v3; // [rsp+10h] [rbp-20h]
  __int64 v4; // [rsp+18h] [rbp-18h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v1 = 0LL;
  v2 = 0LL;
  v3 = 0LL;
  v4 = 0LL;
  sub_40F950((unsigned __int64)"input your flag:");
  sub_40FA80((unsigned __int64)"%s");
  if ( (unsigned int)sub_4009AE((char *)&v1) )
    sub_410350("Correct!");
  else
    sub_410350("Wrong!");
  result = 0LL;
  if ( __readfsqword(0x28u) != v5 )
    sub_443550();
  return result;
}

进入这个可疑函数sub_4009AE

_BOOL8 __fastcall sub_4009AE(char *a1)
{
  if ( 1629056 * *a1 != 166163712 )
    return 0LL;
  if ( 6771600 * a1[1] != 731332800 )
    return 0LL;
  if ( 3682944 * a1[2] != 357245568 )
    return 0LL;
  if ( 10431000 * a1[3] != 1074393000 )
    return 0LL;
  if ( 3977328 * a1[4] != 489211344 )
    return 0LL;
  if ( 5138336 * a1[5] != 518971936 )
    return 0LL;
  if ( 7532250 * a1[7] != 406741500 )
    return 0LL;
  if ( 5551632 * a1[8] != 294236496 )
    return 0LL;
  if ( 3409728 * a1[9] != 177305856 )
    return 0LL;
  if ( 13013670 * a1[10] != 650683500 )
    return 0LL;
  if ( 6088797 * a1[11] != 298351053 )
    return 0LL;
  if ( 7884663 * a1[12] != 386348487 )
    return 0LL;
  if ( 8944053 * a1[13] != 438258597 )
    return 0LL;
  if ( 5198490 * a1[14] != 249527520 )
    return 0LL;
  if ( 4544518 * a1[15] != 445362764 )
    return 0LL;
  if ( 3645600 * a1[17] != 174988800 )
    return 0LL;
  if ( 10115280 * a1[16] != 981182160 )
    return 0LL;
  if ( 9667504 * a1[18] != 493042704 )
    return 0LL;
  if ( 5364450 * a1[19] != 257493600 )
    return 0LL;
  if ( 13464540 * a1[20] != 767478780 )
    return 0LL;
  if ( 5488432 * a1[21] != 312840624 )
    return 0LL;
  if ( 14479500 * a1[22] != 1404511500 )
    return 0LL;
  if ( 6451830 * a1[23] != 316139670 )
    return 0LL;
  if ( 6252576 * a1[24] != 619005024 )
    return 0LL;
  if ( 7763364 * a1[25] != 372641472 )
    return 0LL;
  if ( 7327320 * a1[26] != 373693320 )
    return 0LL;
  if ( 8741520 * a1[27] != 498266640 )
    return 0LL;
  if ( 8871876 * a1[28] != 452465676 )
    return 0LL;
  if ( 4086720 * a1[29] != 208422720 )
    return 0LL;
  if ( 9374400 * a1[30] == 515592000 )
    return 5759124 * a1[31] == 719890500;
  return 0LL;
}

大概看一眼,少了第六个元素,16和17是换了一下,写代码的时候要注意一下

大概就是将一个列表,也就是flag的每一位进行加密。

不得不说,头一次觉得复制粘贴这么麻烦。。。

a = [166163712, 731332800, 357245568, 1074393000, 489211344, 518971936, 406741500, 294236496, 177305856, 650683500,
     298351053, 386348487, 438258597, 249527520, 445362764, 981182160, 174988800, 493042704, 257493600,
     767478780, 312840624, 1404511500, 316139670, 619005024, 372641472, 373693320, 498266640, 452465676,
     208422720, 515592000, 719890500]
b = [1629056, 6771600, 3682944, 10431000, 3977328, 5138336, 7532250, 5551632, 3409728, 13013670, 6088797,
     7884663, 8944053, 5198490, 4544518, 10115280, 3645600, 9667504, 5364450, 13464540, 5488432, 14479500,
     6451830, 6252576, 7763364, 7327320, 8741520, 8871876, 4086720, 9374400, 5759124]
flag = []
temp = ''

for i in range(len(a)):
    flag.append(a[i] // b[i])
    flag[i] = chr(flag[i])
for i in range(10):
    uu = flag[:]
    uu.insert(6, str(i))
    print(''.join(uu), end='')
    print('')

 输出结果

 挨个试, flag{e165421110ba03099a1c039337}

[MRCTF2020]Transform

查壳64无壳找到main函数

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rdx
  __int64 v4; // rdx
  char v6[104]; // [rsp+20h] [rbp-70h]
  int j; // [rsp+88h] [rbp-8h]
  int i; // [rsp+8Ch] [rbp-4h]

  sub_402230();
  sub_40E640(argc, (__int64)argv, v3, (__int64)"Give me your code:\n");
  sub_40E5F0(argc, (__int64)argv, (__int64)v6, (unsigned __int64)"%s");
  if ( strlen(*(const char **)&argc) != 33 )
  {
    sub_40E640(argc, (__int64)argv, v4, (__int64)"Wrong!\n");
    system(*(const char **)&argc);
    exit(argc);
  }
  for ( i = 0; i <= 32; ++i )
  {
    byte_414040[i] = v6[dword_40F040[i]];
    v4 = i;
    byte_414040[i] ^= LOBYTE(dword_40F040[i]);
  }
  for ( j = 0; j <= 32; ++j )
  {
    v4 = j;
    if ( byte_40F0E0[j] != byte_414040[j] )
    {
      sub_40E640(argc, (__int64)argv, j, (__int64)"Wrong!\n");
      system(*(const char **)&argc);
      exit(argc);
    }
  }
  sub_40E640(argc, (__int64)argv, v4, (__int64)"Right!Good Job!\n");
  sub_40E640(argc, (__int64)argv, (__int64)v6, (__int64)"Here is your flag: %s\n");
  system(*(const char **)&argc);
  return 0;
}

  然后又找到这两个:

在计算机编程中,8 dup(0) 表示申请 8 个字节(或者 8 个元素)的连续内存空间,并将该空间的值初始化为 0。这是一种数组或者缓冲区的定义方式,通常用于存储一定数量的数据或者字符,比如:

char buffer[8] = {0}; // 等价于 char buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 

该语句申请 8 个 char 类型的元素的内存空间,并将它们初始化为 0。在某些编程语言或者汇编语言中,使用 dup 语句可以重复生成一定数量的相同元素,可以用来快速生成一定数量的数组或者缓冲区。

main函数其实只有中间的一个for有用,写代码

sub1 = [9, 10, 15, 23, 7, 24, 12, 6, 1, 16, 3, 17, 32, 29, 11, 30,
        27, 22, 4, 13, 19, 20, 21, 2, 25, 5, 31, 8, 18, 26, 28, 14, 0]
sub2 = [103, 121, 123, 127, 117, 43, 60, 82, 83, 121, 87, 94, 93, 66, 123, 45,
        42, 102, 66, 126, 76, 87, 121, 65, 107, 126, 101, 60, 92, 69, 111, 98, 77]
temp = []
flag = []

for i in range(33):
    temp.append(sub1[i] ^ sub2[i])
    flag.append(0)

for i in range(33):
    flag[sub1[i]] = temp[i]

for i in flag:
    print(chr(i), end='')

要注意flag原本是空的,如果不append就会报错

MRCTF{Tr4nsp0sltiON_Clph3r_1s_3z}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逆向上报是一种机制,用户在反馈问题时通过技术手段提供更具体的信息以便开发者解决问题。当我们在使用苹果设备的过程中,遇到了一些应用或者系统的问题,可以通过“设置-隐私-诊断与用量-诊断与用量数据”找到所需要的问题上报文件,然后通过iTunes Connect工具进行逆向上包,以分析和解决问题。 关于错误码itms-90725,它是指在上传应用至App Store时出现的问题。这个错误通常与授权文件或者证书相关。解决这个错误的方法包括: 1. 确保你有合适的开发者账号和证书。如果你的开发者账号已经过期或者没有合适的证书,就会导致上传应用时出现itms-90725错误。可以在苹果开发者网站上更新账号和证书信息。 2. 查看你的证书配置是否正确。证书配置错误也可能导致该错误。确保你的证书没有过期并正确配置。 3. 如果你正在使用Xcode和Application Loader工具上传应用,尝试使用最新版本的Xcode和Application Loader。有时,旧版本的工具可能存在一些Bug,导致上传问题。 4. 确保你的应用在上传过程中没有被修改过、篡改过或者使用了非法的插件等等。这可能违反苹果的审核规则,导致上传失败。 5. 如果以上方法都无法解决问题,可以尝试重新生成一个新的开发证书,然后重新签名应用并上传。 总之,itms-90725错误是一个上传应用到App Store过程中可能遇到的问题,根据具体情况,我们可以通过检查开发者账号和证书、更新工具版本、检查应用的合法性等途径解决该问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值