V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wohenyingyu01
V2EX  ›  C

同样一份 c 代码,用 oc 调用的结果和直接 gcc 编译运行的结果不一样是为什么

  •  
  •   wohenyingyu01 · 2016-01-24 23:13:31 +08:00 · 2371 次点击
    这是一个创建于 3233 天前的主题,其中的信息可能已经有所发展或是发生改变。

    主要在做 aes 加密这块,因为涉及到服务器,安卓和 iOS 的多平台,试过很多的库,同样的 aes - 256-cbc 算法,不同语言的库加密出来的结果都不一样,也不能互相解密,故想用同一个 c 语言库跨平台运行。 main 函数如下:

    int m, n, i, j;
    char c;
    aes_context ctx;
    unsigned char buf[32];
    unsigned char key[32];

    n=2;
    memset(key,0,32);
    

    memcpy(key,(unsigned char)"test",32);
    memset(buf,0,32);
    memcpy(buf,(unsigned char
    )"13911112222+1234567890+313787",32);
    aes_set_key( &ctx, key, 128 + n*64);
    aes_encrypt( &ctx, buf, buf );
    printf("The encrypted data is:\n");
    for(i=0;i<32;i++)
    {
    printf("%02x",buf[i]);
    }
    printf("\n");
    printf("\n");

    aes_set_key( &ctx, key, 128 + n * 64 );
    aes_decrypt( &ctx, buf, buf );
    printf("The decrypted data is:\n");
    
    printf("%s",buf);
    printf("\n");
    return( 0 );
    

    问题来了,当我在 gcc 下运行此 main 函数的结果是
    The encrypted data is:
    bd704722da9e2e97de83a43ab442fb693536373839302b333133373837303000

    The decrypted data is:
    13911112222+1234567890+31378700

    当我将 main 函数改为普通函数,在 xcode 用 swift 调用 oc 去调用这个函数,在不传递任何参数进去的情况下,结果为:

    The encrypted data is:
    562591cb7184ec79998f411268d751423536373839302b333133373837000000

    The decrypted data is:
    13911112222+1234567890+313787

    同样的代码,没有任何改变,为何会运行出两种结果?而且解密只能在同一个函数下解密成功,出了这个函数这个密闻就再也无法解密了,是库有问题吗?求推荐跨平台 aes 库……

    38 条回复    2016-01-25 16:58:59 +08:00
    zhicheng
        1
    zhicheng  
       2016-01-24 23:35:48 +08:00   ❤️ 1
    memcpy(key,(unsigned char)"test",32);

    这行 memcpy 很是酸爽。
    w2exzz
        2
    w2exzz  
       2016-01-24 23:38:16 +08:00 via Android
    1 楼说得对……这里出现了随机数据
    am241
        3
    am241  
       2016-01-24 23:38:49 +08:00
    这算是下标越界吧
    ilotuo
        4
    ilotuo  
       2016-01-24 23:38:56 +08:00 via Android   ❤️ 1
    内存操作错误 导致变量或者函数指针乱了
    我也遇过类似的
    codecrash
        5
    codecrash  
       2016-01-25 00:17:23 +08:00 via Android
    我遇到过同样一份代码 vc 和 gcc 编译都能通过,但是 vc 的会崩溃,一直不明原因。后来调试发现, vc 编译的东西对象的析构顺序和 gcc 不一样,这导致了空指针的出现,所以就无法正常运行了。
    catlove
        6
    catlove  
       2016-01-25 09:19:20 +08:00
    看到这行我就醉了。。。。
    catlove
        7
    catlove  
       2016-01-25 09:20:15 +08:00   ❤️ 1
    memcpy(key,(unsigned char)"test",32);
    看到这行我就醉了。。。。
    jukka
        8
    jukka  
       2016-01-25 09:46:00 +08:00
    man memcpy 了之后再来写代码。
    kobe1941
        9
    kobe1941  
       2016-01-25 09:47:50 +08:00   ❤️ 1
    @catlove
    函数原型:
    void *memcpy(void *dest, const void *src, size_t n);

    功能:
    从源 src 所指的内存地址的起始位置开始拷贝 n 个字节到目标 dest 所指的内存地址的起始位置中

    楼主的操作每次都从随机地址开始拷贝。
    wohenyingyu01
        10
    wohenyingyu01  
    OP
       2016-01-25 10:18:28 +08:00
    @zhicheng
    @kobe1941
    @catlove

    我知道越界了,但是我用 xcode 调试工具仔细看过了,每次运行到这里的时候超过 test 的部分会自动用\0 补全,就没动它,因为是上门给的使用例子……
    wohenyingyu01
        11
    wohenyingyu01  
    OP
       2016-01-25 10:18:59 +08:00
    @wohenyingyu01 而且每次同一环境运行的结果也是固定的……
    wohenyingyu01
        12
    wohenyingyu01  
    OP
       2016-01-25 10:20:25 +08:00
    @wohenyingyu01 打错了,上面给的
    wohenyingyu01
        13
    wohenyingyu01  
    OP
       2016-01-25 10:29:44 +08:00
    @kobe1941
    @zhicheng
    @jukka
    我又看了一遍发现明明是 memset(buf,0,32); 初始化过一遍了,怎么会出现随机数字,我虽然不会 c 但是不要框我啊😰
    xujif
        14
    xujif  
       2016-01-25 10:33:54 +08:00
    @wohenyingyu01
    楼上都告诉你了, memcpy(key,(unsigned char)"test",32); 这个会复制 32 个字节,但是 test 只有 5 个字节
    所以你这里应该 memcpy(key,(unsigned char)"test", strlen("test"));或者 memcpy(key,(unsigned char)"test", strlen("test")+1);
    wohenyingyu01
        15
    wohenyingyu01  
    OP
       2016-01-25 10:41:45 +08:00
    @xujif 但是 aes 要求传入的必需是 32 个字节的,所以它用了 memset(buf,0,32);去初始化 32 字节内存,只要存进去的是 32 字节以下的,多出来的位数都会用'\0'补全,这个理论我也用调试印证了,多出来的部分确实是\0 补全的
    tghgffdgd
        16
    tghgffdgd  
       2016-01-25 10:45:48 +08:00
    @wohenyingyu01 "test"补全的\0 只能保证一位,你复制 32 个进去, 5 个之后已经不一定是\0 了,谁告诉多出来的部分确实是\0 补全的。
    xujif
        17
    xujif  
       2016-01-25 10:46:27 +08:00
    @wohenyingyu01 memcpy 会强制拷贝 32 个字节,在这里前面 memset 是多余的,调试模式很多编译器会对变量填 0,release 并不会。
    wohenyingyu01
        18
    wohenyingyu01  
    OP
       2016-01-25 10:49:39 +08:00
    @tghgffdgd
    @xujif
    额,有道理,可能是这个造成的
    Echoldman
        19
    Echoldman  
       2016-01-25 10:55:18 +08:00
    memcpy 的 size 是字节,楼主你的调用,是按照位来算的
    viikker
        20
    viikker  
       2016-01-25 10:55:28 +08:00 via Android
    cbc 不是要初始化 iv 么? iv 是怎么来的
    skydiver
        21
    skydiver  
       2016-01-25 11:18:56 +08:00 via Android
    为啥是 unsigned char 不应该是转换成 void *么,再不济也是 unsigned char*
    zonyitoo
        22
    zonyitoo  
       2016-01-25 11:25:27 +08:00
    memcpy(key,(unsigned char)"test",32);

    看到这句笑翻了………
    除了上面说的越界之后,我还有个问题,为什么要强转类型为 unsigned char ?
    402124773
        23
    402124773  
       2016-01-25 11:47:27 +08:00
    你把这个程序在同一个平台下连续运行两次,看看结果是否一致,不就可以得出“是否是因为 memcpy 长度太长导致后面的数据有问题”的结论了吗???
    auser
        24
    auser  
       2016-01-25 12:39:41 +08:00 via Android
    楼上说的几处代码都是不好的,楼主还是改过来好了。

    以前用 C 实现过一整套的 aes_cbc.
    里边除了 block cipher modes 需要了解, padding 也要关注的。还有初始化向量 IV 这些细节。

    建议楼主搞清楚这些概念后选择个合适的库好了,不要直接从这些底层入手。
    hitmanx
        25
    hitmanx  
       2016-01-25 12:44:42 +08:00   ❤️ 1
    没看明白.memset 和 memcpy 没关系,可以 memset 去清空 32 位为'\0',但是依然只去 memcpy 前几位,这样后面几位并不会受影响."test"作为 const char*,应该是存在全局变量的 read-only 段的,memcpy(,,32)会把这个段后面的数据一起去拷贝,结果是未定义的.另外这儿感觉用 strncpy 会比 memcpy 好一些.
    wadahana
        26
    wadahana  
       2016-01-25 12:49:53 +08:00
    @402124773 有可能是一样的, 表忘了可能会有 “烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫”
    k9982874
        27
    k9982874  
       2016-01-25 12:50:28 +08:00
    "不同语言的库加密出来的结果都不一样,也不能互相解密"

    只能说你的实现有问题,算法正确的话出来的值是可以用任何语言的实现解密的。

    "memcpy(key,(unsigned char)"test",32);"
    如果算法实现没问题,这句应该是罪魁祸首,原因前面的同学已经说了。

    最近刚实现了一套 C 的 AES ,产生的结果 PHP JAVA 都可以互解。
    jasontse
        28
    jasontse  
       2016-01-25 13:00:26 +08:00 via iPad
    看标题就知道是未定义内存的坑
    wohenyingyu01
        29
    wohenyingyu01  
    OP
       2016-01-25 13:01:14 +08:00
    @402124773 运行过是一样的额
    wohenyingyu01
        30
    wohenyingyu01  
    OP
       2016-01-25 13:03:34 +08:00
    @k9982874 这个代码奇怪的地方在于,无论加密出来的结果有多不一样,只要在同一个环境下对结果去解密都能解出正确的来,如果把结果从外面传进来,就解不出……
    wizardoz
        31
    wizardoz  
       2016-01-25 13:03:36 +08:00
    @wohenyingyu01 不仅仅是 32 字节和 5 字节的问题。你把指针强制转换成 unsigned char 了,少个星号!
    也就是说指针的值溢出后变成了一个 0-255 之间的值。
    wizardoz
        32
    wizardoz  
       2016-01-25 13:06:37 +08:00
    @wizardoz 难道是语言特性?抱歉我以为是 C 语言,请忽略我。
    wohenyingyu01
        33
    wohenyingyu01  
    OP
       2016-01-25 13:07:59 +08:00
    @wizardoz 火眼金睛!不过我看了下代码里面是有的,不知道为啥复制过来没了
    wohenyingyu01
        34
    wohenyingyu01  
    OP
       2016-01-25 13:09:22 +08:00
    @skydiver 我看了下代码里面是有的,不知道为啥复制过来没了额
    waruqi
        35
    waruqi  
       2016-01-25 13:51:14 +08:00
    这代码 也是醉了。。
    sophymax
        36
    sophymax  
       2016-01-25 14:13:11 +08:00
    其实是还没完全理清那个加密算法 , 有些参数需要设置的 , 某个库默认设置的不一样 , salt 值 IV 值 之类的 , 还有可能是最后是不是多了一个字节的内存
    skydiver
        37
    skydiver  
       2016-01-25 14:58:30 +08:00
    @wohenyingyu01 原来是变成 markdown 粗体了……
    wohenyingyu01
        38
    wohenyingyu01  
    OP
       2016-01-25 16:58:59 +08:00
    @sophymax 那个 c 代码完全看不懂,理不清了,可能还需要从 aes 原理入手研究了,实在不行换一个方法,谢啦
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2587 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 05:39 · PVG 13:39 · LAX 21:39 · JFK 00:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.