如何根据 C 反汇编确定此字符串值?

How can I determine this string value based on the C disassembly?

所以我正在研究“查找密码”类型的二进制反汇编问题,但无法完全弄清楚。

组装如下:

函数checkpw

                         **************************************************************
                         *                                                            *
                         *  FUNCTION                                                  *
                         **************************************************************
                         undefined8 __stdcall checkpw(void)
         undefined8        RAX:8          <RETURN>
                         checkpw                                         XREF[2]:     Entry Point(*), main:001012a1(c)  
    00101179 48 89 c8        MOV        RAX,RCX
    0010117c 48 31 c1        XOR        RCX,RAX
    0010117f 8a 08           MOV        CL,byte ptr [RAX]
    00101181 80 f1 52        XOR        CL,0x52
    00101184 80 f9 11        CMP        CL,0x11
    00101187 75 5e           JNZ        LAB_001011e7
    00101189 8a 48 07        MOV        CL,byte ptr [RAX + 0x7]
    0010118c 80 e9 16        SUB        CL,0x16
    0010118f 80 f9 0d        CMP        CL,0xd
    00101192 75 53           JNZ        LAB_001011e7
    00101194 8a 48 01        MOV        CL,byte ptr [RAX + 0x1]
    00101197 48 31 d2        XOR        RDX,RDX
    0010119a fe c2           INC        DL
    0010119c 48 d1 e2        SHL        RDX,1
    0010119f 40 8a 3c 10     MOV        DIL,byte ptr [RAX + RDX*0x1]
    001011a3 40 30 cf        XOR        DIL,CL
    001011a6 40 80 ff 40     CMP        DIL,0x40
    001011aa 75 3b           JNZ        LAB_001011e7
    001011ac 80 c1 63        ADD        CL,0x63
    001011af 80 f9 d6        CMP        CL,0xd6
    001011b2 75 33           JNZ        LAB_001011e7
    001011b4 8a 4c 10 01     MOV        CL,byte ptr [RAX + RDX*0x1 + 0x1]
    001011b8 80 f9 23        CMP        CL,0x23
    001011bb 7e 2a           JLE        LAB_001011e7
    001011bd 80 c1 5b        ADD        CL,0x5b
    001011c0 70 25           JO         LAB_001011e7
    001011c2 48 8d 0c 50     LEA        RCX,[RAX + RDX*0x2]
    001011c6 8a 09           MOV        CL,byte ptr [RCX]
    001011c8 80 f1 f3        XOR        CL,0xf3
    001011cb 80 f9 c7        CMP        CL,0xc7
    001011ce 75 17           JNZ        LAB_001011e7
    001011d0 8a 48 05        MOV        CL,byte ptr [RAX + 0x5]
    001011d3 8a 68 06        MOV        CH,byte ptr [RAX + 0x6]
    001011d6 66 81 f1        XOR        CX,0x4c47
             47 4c
    001011db 66 81 f9        CMP        CX,0x1234
             34 12
    001011e0 75 05           JNZ        LAB_001011e7
    001011e2 48 31 c0        XOR        RAX,RAX
    001011e5 eb 05           JMP        LAB_001011ec
                         LAB_001011e7                                    XREF[8]:     00101187(j), 00101192(j), 
                                                                                      001011aa(j), 001011b2(j), 
                                                                                      001011bb(j), 001011c0(j), 
                                                                                      001011ce(j), 001011e0(j)  
    001011e7 b8 01 00        MOV        EAX,0x1
             00 00
                         LAB_001011ec                                    XREF[1]:     001011e5(j)  
    001011ec c3              RET

根据Ghidra的说法,反编译后的函数是:

undefined8 checkpw(void)

{
  undefined8 uVar1;
  char *in_RCX;
  
  if (((((*in_RCX != 'C') || (in_RCX[7] != '#')) || ((byte)(in_RCX[2] ^ in_RCX[1]) != 0x40)) ||
      ((in_RCX[1] != 0x73 || (in_RCX[3] < '$')))) ||
     ((SCARRY1(in_RCX[3],'[') || ((in_RCX[4] != '4' || (*(short *)(in_RCX + 5) != 0x5e73)))))) {
    uVar1 = 1;
  }
  else {
    uVar1 = 0;
  }
  return uVar1;
}

它将main函数反编译为:

void main(int param_1,long param_2)

{
  long lVar1;
  size_t sVar2;
  undefined8 uVar3;
  
  lVar1 = ptrace(PTRACE_TRACEME,0,1,0);
  if (lVar1 < 0) {
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  if (param_1 != 2) {
                    /* WARNING: Subroutine does not return */
    exit(2);
  }
  sVar2 = strlen(*(char **)(param_2 + 8));
  if (sVar2 != 8) {
                    /* WARNING: Subroutine does not return */
    exit(3);
  }
  uVar3 = checkpw();
  if ((int)uVar3 != 0) {
    puts("Invalid Password!");
                    /* WARNING: Subroutine does not return */
    exit(4);
  }
  puts("Correct Password!");
                    /* WARNING: Subroutine does not return */
  exit(0);
}

至此,我可以看出密码必须是8个字符。

同样基于checkpw反编译,我相信以下内容是正确的(假设变量passwd是包含有效密码的字符数组):

passwd[0] = 'C'
passwd[1] = 's'
passwd[2] = '3'
passwd[3] = '$'
passwd[4] = '4'
passwd[7] = '#'

虽然我对其中一些并不完全有信心,但我确实对 位置 5 和 6 中的那些有问题。

反编译的函数似乎没有引用第七个字符,所以我假设它可以是任何东西,但不确定这与此问题相关的含义:

(*(short *)(in_RCX + 5) != 0x5e73)

这个:

(*(short *)(in_RCX + 5) != 0x5e73)

正在同时比较两个字符。该语句正在计算 in_RCX + 5 然后将其转换为 short * 即指向 16 位有符号整数的指针(函数体中使用的寄存器实际上是 RAX,尽管在反编译代码中这个变量的名称)。然后取消引用所述指针以一次获取两个字节,并将它们与 0x5e73 进行比较。当然,这只是反编译后的代码,并不意味着程序真的在做一个16位的MOV(实际上是在做两个8位的MOV),它只是反编译器认为的C版本继续。

你可以在反汇编中更清楚地看到这一点:

    001011d0 8a 48 05        MOV        CL,byte ptr [RAX + 0x5]
    001011d3 8a 68 06        MOV        CH,byte ptr [RAX + 0x6]
    001011d6 66 81 f1        XOR        CX,0x4c47
             47 4c
    001011db 66 81 f9        CMP        CX,0x1234
             34 12
    001011e0 75 05           JNZ        LAB_001011e7

反汇编中的检查是使用 XOR + CMP + JNZ 完成的,因此 Ghidra 已经为您将 0x4c470x1234 异或在一起,即 0x5e73。这意味着为了通过检查,passwd[5] 必须是 0x73 ('s') 并且 passwd[6] 必须是 0x5e ('^') .