目录

概述

範例程式

Assembly

smc.n
;****************************************************************************
;****************************************************************************
;*
;* USING SELF MODIFYING CODE UNDER LINUX
;*
;* written by Karsten Scheibler, 2004-AUG-09
;*
;****************************************************************************
;****************************************************************************
 
global smc_start
 
;****************************************************************************
;* some assign's
;****************************************************************************
 
%assign SYS_WRITE     4
%assign SYS_MPROTECT  125
 
%assign PROT_READ     1
%assign PROT_WRITE    2
%assign PROT_EXEC     4
 
;****************************************************************************
;* data
;****************************************************************************
section .bss
          alignb  4
modified_code:        resb  0x2000
 
;****************************************************************************
;* smc_start
;****************************************************************************
section .text
smc_start:
 
  ;calculate the address in section .bss, it must lie on a page
  ;boundary (x86: 4KB = 0x1000)
  ;NOTE: In this example obsolete because each segment is page
  ;      aligned and we use it only once, so we know that it is
  ;      aligned to a page boundary, but if you have more than
  ;      one section .bss in your code (or link several objects
  ;      together) you can't be sure about that
 
  mov dword ebp, (modified_code + 0x1000)
  and dword ebp, 0xfffff000
 
  ;change flags of this page to read + write + executable,
  ;NOTE: On x86 Architecture this call is obsolete, because for
  ;      section .bss PROT_READ and PROT_WRITE are already set.
  ;      PROT_EXEC is on x86 also set if PROT_READ is set, this
  ;      results in rwx for this segment, but this behavior may
  ;      change with appearance of the NX-flag in modern processors
 
  mov dword eax, SYS_MPROTECT  ; int mprotect(const void *addr, size_t len, int prot);
  mov dword ebx, ebp           ; ebp 存放 bss 段所在頁面開頭的位址
  mov dword ecx, 0x1000
  mov dword edx, (PROT_READ | PROT_WRITE | PROT_EXEC)
  int byte  0x80
  test  dword eax, eax
  js  near  smc_error
 
  ;<a name="example1"></a>execute unmodified code first
 
code1_start:
  mov dword eax, SYS_WRITE  ; ssize_t write(int fd, const void *buf, size_t count);
  mov dword ebx, 1
  mov dword ecx, hello_world_1
code1_mark_1:
  mov dword edx, (hello_world_2 - hello_world_1)
code1_mark_2:
  int byte  0x80
code1_end:
 
  ;copy code snippet from above to our page (address is still in ebp)
  ; 把 code1 copy 到 bss 段 (ebp)。
  ; http://courses.engr.illinois.edu/ece390/archive/fall2001/books/labmanual/inst-ref-movsb.html
  ; movsb 把 esi 的內容拷貝到 edi,重複 ecx 次。
 
  mov dword ecx, (code1_end - code1_start)
  mov dword esi, code1_start
  mov dword edi, ebp
  cld
  rep movsb
 
  ;append 'ret' opcode to it, so that we can do a call to it
 
  mov byte  al, [return]  ; return label 處放的是 ret 指令
  stosb
 
  ;change some values in the copied code: start address of the text
  ;and its length
 
  ; ebp 是 bss 段的開頭
  mov dword eax, hello_world_2
  mov dword ebx, (code1_mark_1 - code1_start)
  mov dword [ebx + ebp - 4], eax
  mov dword eax, (hello_world_3 - hello_world_2)
  mov dword ebx, (code1_mark_2 - code1_start)
  mov dword [ebx + ebp - 4], eax
 
  ;finally call it
 
  call  dword ebp
 
  ; 如果只要展現第一個範例,加上底下這行。否則執行流會執行到不該到的地方。
  ; jmp smc_end。
 
  ;copy second example
  ; 拷貝的同時會更新 esi 和 edi。 
 
  mov dword ecx, (code2_end - code2_start)  ; 欲拷貝的長度 (byte)
  mov dword esi, code2_start                ; 來源
  mov dword edi, ebp                        ; 目的 (bss 段)
  rep movsb                                 ; 開始搬移
 
  ;do something real nasty: edi points right after the 'rep stosb'
  ;instruction, so this will really modify itself
  ; edi 指向?
 
  mov dword edi, ebp
  add dword edi, (code2_mark - code2_start)  ; 去掉此行,輸出應為 ebx: 08h。這裡是將 edi 調整成指向 code2_mark,之後再將
                                                     ; code2_mark 後的內容覆寫。
  call  dword ebp
 
  ;<a name="example3"></a>modify code in section .text itself
 
endless:
  ;allow us to write to section .text
 
  mov dword eax, SYS_MPROTECT  ; int mprotect(const void *addr, size_t len, int prot);
  mov dword ebx, smc_start
  and dword ebx, 0xfffff000
  mov dword ecx, 0x2000
  mov dword edx, (PROT_READ | PROT_WRITE | PROT_EXEC)
  int byte  0x80
  test  dword eax, eax
  js  near  smc_error
 
  ;write message to screen
 
  mov dword eax, SYS_WRITE
  mov dword ebx, 1
  mov dword ecx, endless_loop
  mov dword edx, (hello_world_1 - endless_loop)
  int byte  0x80
 
  ;here comes the magic, which prevents endless execution
  ; 將終止程序的代碼 (smc_end) 拷貝到 endless,如此便不會有無窮迴圈。
 
  mov dword ecx, (smc_end_1 - smc_end)
  mov dword esi, smc_end
  mov dword edi, endless
  rep movsb
 
  ;do it again
 
  jmp short endless
 
;****************************************************************************
;* code2
;****************************************************************************
 
  ;this is the ret opcode we copy above
  ;and the nop opcode needed by code2
 
return:
  ret
no_operation:
  nop
 
  ;<a name="example2"></a>here some real selfmodifying code, if copied
  ;to .bss and edi correctly loaded ebx should contain 0x4 instead
  ;of 0x8
  ; http://courses.engr.illinois.edu/ece390/archive/fall2001/books/labmanual/inst-ref-stosb.html
 
code2_start:
  mov byte  al, [no_operation]  
  xor dword ebx, ebx            ; 清空 ebx
  mov dword ecx, 0x04           ; 看要覆寫掉幾個後面的 inc dword ebx。
  rep stosb                     ; 將 al 的內容寫到 edi 所指的地方,重複 ecx 次。
code2_mark:
  inc dword ebx
  inc dword ebx
  inc dword ebx
  inc dword ebx
  inc dword ebx
  inc dword ebx
  inc dword ebx
  inc dword ebx                  ; 如果 edi 沒有被改寫,輸出應為 ebx = 08h
  call  dword [function_pointer] ; 印出 ebx: 04h
  ret
code2_end:
          align 4
function_pointer:     dd  write_hex
 
;****************************************************************************
;* write_hex
;****************************************************************************
write_hex:
  mov byte  bh, bl
  shr byte  bl, 4
  add byte  bl, 0x30
  cmp byte  bl, 0x3a
  jb  short .number_1
  add byte  bl, 0x07
.number_1:
  mov byte  [hex_number], bl
  and byte  bh, 0x0f
  add byte  bh, 0x30
  cmp byte  bh, 0x3a
  jb  short .number_2
  add byte  bh, 0x07
.number_2:
  mov byte  [hex_number + 1], bh
  mov dword eax, SYS_WRITE
  mov dword ebx, 1
  mov dword ecx, hex_text
  mov dword edx, 9
  int byte  0x80
  ret
 
section .data
hex_text:   db  "ebx: "
hex_number:   db  "00h", 10
 
;****************************************************************************
;* some text
;****************************************************************************
endless_loop:   db  "No endless loop here!", 10  ; 10 代表回車
hello_world_1:    db  "Hello World!", 10
hello_world_2:    db  "This code was modified!", 10
hello_world_3:
 
;****************************************************************************
;* smc_error
;****************************************************************************
section .text
smc_error:
  xor dword eax, eax  ; 清空 eax
  inc dword eax       ; _exit 呼叫號
  mov dword ebx, eax  ; ebx 為狀態號
  int byte  0x80
 
;****************************************************************************
;* smc_end
;****************************************************************************
section .text
smc_end:
  xor dword eax, eax  ; 清空 eax
  xor dword ebx, ebx  ; 清空 ebx
  inc dword eax       ; _exit 呼叫號
  int byte  0x80
smc_end_1:
;*********************************************** linuxassembly@unusedino.de *
Makefile
NASM=nasm -w+orphan-labels -w+macro-params -w+number-overflow -f elf
LD=ld -m elf_i386 -s
STRIP=strip -R .note -R .comment
RM=rm -f
 
.PHONY: all clean
 
all: smc
 
smc: smc.n
  ${NASM} -o smc.o smc.n
  ${LD} -e smc_start -o smc smc.o
  ${STRIP} smc
 
clean:
  ${RM} *.bak *~ *.o smc core
$ ./smc
Hello World!
This code was modified!
ebx: 04h
No endless loop here!

C

smc.c
/***************************************************************************
                          sm.c - self modifying C
                             -------------------
    begin                : Wed Nov 23 21:05:29 CET 2004
    copyright            : (C) 2004 by Juraj Brecak
    e-mail               : jbrecak (AT) jagor (DOT) srce (DOT) hr
 
 ***************************************************************************
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
 
/*	this is an example of self modifying code in C.
 
	it works for me, on a gnu/linux & gcc 2.95.4
	i have no idea if this is portable to other systems/compilers.
	there are no big reasons why it shouldn't be. if you try it out,
	please let me know how it went.
 
	the code was inspired by Karsten Scheibler's article
	"Using self modifying code under Linux" at linuxassembly.org
	(http://linuxassembly.org/articles/smc.html) and a guy from
	the web (whose name i had forgotten) asking around whether
	it was possible to write SMC in C. i was as courious as he was
	and this was one way to check it out. as you can see it (almost) is.
	i had to cheat just a bit and label my code from asm. ok,
	for the impatient: make; ./smc; echo $?
 
	if you have any questions feel free to contact me.
 
	happy coding.. */
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
 
/*	gcc is oblivious to asm level so we have to spell out
	our intentions.. it's an ugly hack. if you know of
	a proper way to do it in pure C please tell me. */
 
extern char injectHere;
extern char injectionStart;
extern char injectionEnd;
 
void dummyTextContext( void )
{
/*	sometimes gcc is just too smart.. in an ideal world
	we would write smthng like (*(&exit))( 42 ); and get
	an indirect call (via absolute address). anyway,
	we don't get that so we have to make one pointer for
	every type of function we want to call */
 
	int (*callPrintf)( const char *format, ... ); 
	void (*callExit)( int ); /* man 3 printf exit */
 
	asm volatile( "injectionStart:" );
 
/*	do what you like here but keep it relocatable
	fire up objdump, gdb or whatever to check what you get */
 
	(*(callPrintf=&printf))( "Hello :-)\n" );
	(*(callPrintf=&printf))( "No endless loop here!\n" );
 
	(*(callExit=&exit))( 42 ); /* the meaning of life (for this proggie anyway..) */
 
	asm volatile( "injectionEnd:" );
}
 
int main( void )
{
/*	it's fairly safe to assume page is 2^N,
	now.. let's find us a page to bash >:*/
 
	unsigned page = (unsigned)&injectHere & ~( getpagesize() - 1 );
 
/*	chmod u=rwx page ;-) */
 
	if( mprotect( (char*)page, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC ) )
	{
		perror( "mprotect failed" );
		exit( errno );
	}
 
endlessLoop:
 
	asm volatile( "injectHere:" );	/* ground zero */
 
/*	a few stats to ease debugging (and some code to fill our loop too) */
 
	printf( "PAGE(0x%x): cp <0x%x>[0..%u] <0x%x>\n",
		page, (int)&injectionStart, &injectionEnd - &injectionStart, (int)&injectHere );
 
/*	inject some */
 
	memcpy( &injectHere, &injectionStart, &injectionEnd - &injectionStart );
 
goto endlessLoop;
 
	return 0;
}
 
/*	we can inject into the loop (ie. stomp over current code) but
	then we have to make sure there's enough padding (or real code)
	so we don't trample the jmp (goto endlessLoop;). if we do that,
	the injection never gets executed because when memcpy returns
	the execution will continue in the middle of our injected code
	and that is *bad*. sooo.. we can either inject into the loop
	and make sure the whole	new code fits inside..
	or start injecting over the "goto". that way injected code will
	get executed as soon as memcpy returns. also note, that that
	would not literally be self modifying code as we would be
	injecting over a code that has not yet run. now *thats*
	pointless. OTH we could completely change the insides of the loop
	and neatly pad potential leftover space with nops thus
	keeping the loop runing. */
 
/* EOF */
$ gcc -m32 -O0 smc.c -o smc

Python

smc.py
import datetime
import inspect
import re
import sys
 
def main():
    # print the time it is last run
    lastrun = 'Mon Jun  8 16:31:27 2009'
    print "This program is last run at <<%s>>." % lastrun
 
    # read in the source code of itself
    srcfile = inspect.getsourcefile(sys.modules[__name__])
    f = open(srcfile, 'r')
    src = f.read()
    f.close()
 
    # modify the embedded timestamp
    timestamp = datetime.datetime.ctime(datetime.datetime.now())
    match = re.search("lastrun = '(.*)'", src)
    if match:
        src = src[:match.start(1)] + timestamp + src[match.end(1):]
 
    # write the source code back
    f = open(srcfile, 'w')
    f.write(src)
    f.close()
 
if __name__=='__main__':
    main()

實際應用

外部連結