如何根据 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 已经为您将 0x4c47
和 0x1234
异或在一起,即 0x5e73
。这意味着为了通过检查,passwd[5]
必须是 0x73
('s'
) 并且 passwd[6]
必须是 0x5e
('^'
) .
所以我正在研究“查找密码”类型的二进制反汇编问题,但无法完全弄清楚。
组装如下:
函数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 已经为您将 0x4c47
和 0x1234
异或在一起,即 0x5e73
。这意味着为了通过检查,passwd[5]
必须是 0x73
('s'
) 并且 passwd[6]
必须是 0x5e
('^'
) .