简单装配的 C 表示

逆向工程 x86 反编译 C
2021-06-30 11:58:32

我有以下非常简单的 X86 程序集:

shr cx, 1
jae short <addr>

所以基本上,如果未设置进位标志,将进行跳转。在 的情况下shr,CF 将设置为移出寄存器的位的值。

我想知道在 C 中会是什么样子(我知道它必须类似于if ((num >>> 1) <SOME COMPARISON>),因为显然您无法检查进位标志,并且移位位的值是未知的。

我知道我可能遗漏了一些非常明显的东西,但仍然无法找出答案。

更新:感谢所有的评论和答案,尽管它们并没有真正显示出我需要什么。我了解代码的作用(检查奇数/偶数,如果是其中之一则跳转)。我的问题实际上是什么样的 C 代码可以准确地生成我指定的指令(如果是 16/32/64 位则无关紧要),即 shift + jump。我开始认为这要么只是 ASM(考虑到我发现构造的次数,这不太可能,但肯定是可能的)或某些将所有内容分开但在某些时候得到真正优化的 C 代码,像现代编译器中的位旋转

4个回答

好的:JAE 是 JNC 的同义词(如果没有进位标志,则跳转)。

SHR 的文档说:“超出目标的位首先移入 CF 标志。”

所以当最右边的位(移位前)为 0 时你会跳转。

为简单起见,不改变 cx:

if (cx & 1 == 0) ...         

通常在比较无符号类型和负类型时会使用 jae

将无符号类型与正类型进行比较时将使用 jge

您可以在下面的代码中尝试 short 、 byte 、 char 等而不是 unsigned int 来获取 cx 寄存器而不是 eax 寄存器

例如,此代码将获得如下所示的 jae

#include <windows.h>
#include <stdio.h>
int  main (void)
{
    unsigned int a = 1;
    unsigned int b = ( ( (a >> 1) < -1 )  ? 0x1337 : 0xdead );
    return b;    
}

拆卸

cdb -c "g shotcmp!main;uf ." shotcmp.exe

Microsoft (R) Windows Debugger Version 10.0.16299.15 X86

0:000> cdb: Reading initial command 'g shotcmp!main;uf .'

shotcmp!main:
012a6830 55              push    ebp
012a6831 8bec            mov     ebp,esp
012a6833 83ec0c          sub     esp,0Ch
012a6836 c745f801000000  mov     dword ptr [ebp-8],1
012a683d 8b45f8          mov     eax,dword ptr [ebp-8]
012a6840 d1e8            shr     eax,1  <<<<<<<<<<<<<<<<
012a6842 83f8ff          cmp     eax,0FFFFFFFFh <<<<<<<<<<<<<<<<
012a6845 7309            jae     shotcmp!main+0x20 (012a6850) <<<<<<<<<<<<<<

shotcmp!main+0x17:
012a6847 c745fc37130000  mov     dword ptr [ebp-4],1337h
012a684e eb07            jmp     shotcmp!main+0x27 (012a6857)

shotcmp!main+0x20:
012a6850 c745fcadde0000  mov     dword ptr [ebp-4],0DEADh

shotcmp!main+0x27:
012a6857 8b4dfc          mov     ecx,dword ptr [ebp-4]
012a685a 894df4          mov     dword ptr [ebp-0Ch],ecx
012a685d 8b45f4          mov     eax,dword ptr [ebp-0Ch]
012a6860 8be5            mov     esp,ebp
012a6862 5d              pop     ebp
012a6863 c3              ret
0:000>

c 和 asm 中的偶数或奇数测试代码 asm 函数使用 jae 进行分支,而 c 使用 je 对 c 中的进位标志进行分支测试通常不会进行

#include <stdio.h>
char *str[] = {"odd","even"} ;
void c_eve_or_odd( int num){
    (num & 1 ) ? printf("%4d%5s\t",num,str[0]) : printf ("%4d%5s\t",num,str[1]) ;  
}
void asm_eve_or_odd(int num) {
    __asm {
        mov ecx , num
        and ecx,1
        shr ecx ,1
        jae myeven
    }
    printf("%4d%5s\t",num,str[0]);
    goto doexit;
myeven:
    printf ("%4d%5s\t",num,str[1]);
    goto doexit;
doexit: 
    return;   
}
void main(void) {
    for(int i =-5; i<=5; i++) { 
        c_eve_or_odd(i);
        asm_eve_or_odd(i);
        printf("\n"); 
    }
}

在此处输入图片说明

只是为了满足我的好奇心,我在 system32 中搜索了所有 Windows 二进制文件以获得该模式,但我找不到shr cx,1 jae 的一个实例, 确实有shr cx,1但后面没有一个 jae

只是一个后续

grep c:\windows\system32\ 中的所有 exe for shr cx,1 sort and print uniq

for /f %i in ( 'dir /b *.exe ' ) do grep -obUaPH \x66\xd1\xe9 %i  >> checkforshrcx1.txt
uniq -w  5 checkforshrcx1.txt | sort

appidpolicyconverter.exe:18846:f╤Θ
autochk.exe:247427:f╤Θ
autofmt.exe:236801:f╤Θ
cleanmgr.exe:51672:f╤Θ
conhost.exe:86983:f╤Θ
dxcap.exe:479138:f╤Θ
krnl386.exe:34661:f╤Θ
ntkrnlpa.exe:52959:f╤Θ
ntoskrnl.exe:72750:f╤Θ
ntvdm.exe:215256:f╤Θ
PresentationSettings.exe:105027:f╤Θ
RelPost.exe:79287:f╤Θ
rstrui.exe:186600:f╤Θ
smss.exe:12952:f╤Θ
sppsvc.exe:2635363:f╤Θ
TVWSetup.exe:5441147:f╤Θ

grep c:\windows\system32\ 中的所有 exe for shr cx,1 然后是 jae sort 和 print uniq

for /f %i in ( 'dir /b *.exe ' ) do grep -obUaPH \x66\xd1\xe9\x73 %i  >> checkforshrcx1jae.txt
ls -l checkforshrcx1jae.txt
-rw-rw-rw-  1  0 0 2018-03-23 10:53 checkforshrcx1jae.txt

cat checkforshrcx1jae.txt

我不能说这是否确实如此,但右移基本上是除以 2,所以发布的代码实际上是在测试奇数。

您不能编写完全生成给定汇编序列的 C 代码。

C 是一种更高级的语言,它管理的不仅仅是汇编。

基本上是汇编中给定的指令序列

shr cx, 1
jae short <addr>

是否执行以下操作

cx_temp = cx;
cx >>= 1;
if (cx_temp & 0x01) {
}

问题是像汇编这样的快捷方式在高级语言中不可用,因为每个程序集都是不同的,而高级语言(如 C)向您隐藏了这种差异,因此创建了比需要更广泛的汇编级构造......