如何給正在運行的Linux應用程式注入代碼

2020-12-20 網盾網絡安全

1、簡介

假設Linux上正在運行某程序,像Unix守護程序等,我們不想終止該程序,但是同時又需要更新程序的功能。首先映入腦海的可能是更新程序中一些已知函數,添加額外的功能,這樣就不會影響到程序已有的功能,且不用終止程序。考慮向正在運行的程序中注入一些新的代碼,當程序中已存在的另一個函數被調用時觸發這些新代碼。也許這種想法有些異想天開,但並不是不能實現的,有時我們確實需要向正在運行的程序中注入一些代碼,當然其與病毒的代碼注入技術與存在一定關聯。

在本文中,我會向讀者解釋如何向正在Linux系統上運行的程序中注入一段C函數代碼,而不必終止該程序。文中我們會討論Linux目標文件格式Executable and Linkable Format(ELF),討論目標文件sections(段)、symbols(符號)以及relocations(重定位)。

2、示例概述筆者會利用以下簡單的示例程序向讀者一步步解釋代碼注入技術。示例由以下三部分組成:

(1)由源碼dynlib.h與dynlib.c編譯的動態(共享)庫libdynlib.so(2)由源碼app.c編譯的app程序,會連結libdynlib.so庫(3)injection.c文件中的注入函數

下面看一下這些代碼:

//dynlib.hextern void print();

dynlib.h文件中聲明了printf()函數。

//dynlib.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include "dynlib.h" extern void print() { static unsigned int counter = 0; ++counter; printf("%d : PID %d : In print()\n", counter, getpid()); }

dynlib.c文件實現了print()函數,該函數只是列印一個計數(每次函數被調用時都會使該值增加)以及當前進程的pid。

//app.c#include <stdio.h>#include <unistd.h>#include "dynlib.h"int main(){ while(1) { print(); printf("Going to sleep...\n"); sleep(3); printf("Waked up...\n"); } return 0;}

app.c文件中的函數調用print()函數(來自libdynlib.so動態庫),之後睡眠幾秒鐘,然後繼續執行該無限循環。

//injection.c#include <stdlib.h>extern void print();extern void injection(){ print(); //原本的工作,調用print()函數 system("date"); //添加的額外工作}

injection()函數調用會替換app.c文件中main()函數調用的print()函數調用。injection()函數首先會調用原print()函數,之後進行額外的工作。例如,它可以利用system()函數運行一些外部可執行程序,或者像本例中一樣列印當前的日期。

3、編譯並運行程序

首先利用gcc編譯器編譯這些源文件:

$ gcc -g -Wall dynlib.c -fPIC -shared -o libdynlib.so$ gcc –g app.c –ldynlib –L ./ -o app$ gcc -Wall injection.c -c -o injection.o

編譯後的程序為:

-rwxrwxr-x 1 0×80 0×80 6224 Oct 15 14:04 app-rw-rw-r– 1 0×80 0×80 888 Oct 16 17:53 injection.o-rwxrwxr-x 1 0×80 0×80 5753 Oct 16 17:52 libdynlib.so

需要注意的是動態庫libdynlib.so在編譯時指定了-fPIC選項,用來生成地址無關的程序。下面運行app可執行程序:

[0x80@localhost dynlib]$ ./app./app: error while loading shared libraries: libdynlib.so: cannot open shared object file: No such file or directory

如果產生以上錯誤,我們需要將生成的libdynlib.so文件拷貝到/usr/lib/目錄下,再執行該程序,得到如下結果:

[0x80@localhost dynlib]$ ./app1 : PID 25658 : In print()Going to sleep…Waked up…2 : PID 25658 : In print()Going to sleep…Waked up…3 : PID 25658 : In print()Going to sleep…

4、調試應用程式程序app只是一個簡單的循環程序,這裡我們假設其已經運行了幾周,在不終止該程序的情況下,將我們的新代碼注入到該程序中。在注入過程中利用Linux自帶的功能強大的調試器gdb。首先我們需要利用pid(見程序的輸出)將程序附著到gdb:

[0x80@localhost dynlib]$ gdb app 25658GNU gdb Red Hat Linux (6.3.0.0-1.122rh)Copyright 2004 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type 「show copying」 to see the conditions.There is absolutely no warranty for GDB. Type 「show warranty」 for details.This GDB was configured as 「i386-redhat-linux-gnu」…Using host libthread_db library 「/lib/libthread_db.so.1″.Attaching to program: /home/0×80/dynlib/app, process 25658Reading symbols from shared object read from target memory…done.Loaded system supplied DSO at 0×464000`shared object read from target memory』 has disappeared; keeping its symbols.Reading symbols from /usr/lib/libdynlib.so…done.Loaded symbols for /usr/lib/libdynlib.soReading symbols from /lib/libc.so.6…done.Loaded symbols for /lib/libc.so.6Reading symbols from /lib/ld-linux.so.2…done.Loaded symbols for /lib/ld-linux.so.20×00464410 in __kernel_vsyscall ()(gdb)

5、將注入代碼加載到可執行程序的內存中如前所述,目標文件injection.o初始並不包含在app可執行進程鏡像中,我們首先需要將injection.o加載到進程的內存地址空間。可以通過mmap()系統調用,該系統調用可以將injection.o文件映射到app進程地址空間中。在gdb調試器中:

(gdb) call open(「injection.o」, 2)$1 = 3(gdb) call mmap(0, 888, 1|2|4, 1, 3, 0)$2 = 1118208(gdb)

首先利用O_RDWR(值為2)的讀/寫權限打開injection.o文件。一會之後我們在加載注入代碼時做寫修改,因此需要寫權限。返回值為系統分配的文件描述符,可以看到值為3。之後調用mmap()系統調用將該文件載入進程的地址空間。mmap()函數原型如下:

#include <sys/mman.h>void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

函數包含6個參數:

start表示映射區的開始地址,設置為0時表示由系統決定映射區起始地址。length表示映射區的長度,這裡為injection.o文件的長度,該值在前文第3節出現過。prot表示期望的內存保護標誌(即映射權限),不能與文件的打開模式衝突,這裡為1|2|4(即PROT_READ | PROT_WRITE | PROT_EXEC,讀/寫/執行)flags指定映射對象的類型,映射選項和映射頁是否可以共享,fd表示已經打開的文件描述符,這裡為3。offset表示被映射對象內容的起點,這裡為0。如果函數執行成功,則返回被映射文件在映射區的起始地址 通過查看/proc/[pid]/maps的內容(這裡pid為要注入的可執行進程的pid,本例為25593),我們可以確定injection.o文件實際被映射到的進程地址空間,在Linux系統中,文件包含當前正在運行的進程的內存布局信息

[0x80@localhost ~]$ cat /proc/25658/maps00111000-00112000 rwxs 00000000 03:02 57933979 /home/0x80/dynlib/injection.o00464000-00465000 r-xp 00464000 00:00 0 [vdso]00500000-00501000 r-xp 00000000 03:01 5464089 /usr/lib/libdynlib.so00501000-00502000 rw-p 00000000 03:01 5464089 /usr/lib/libdynlib.so007bb000-007d4000 r-xp 00000000 03:01 1311704 /lib/ld-2.4.so007d4000-007d5000 r--p 00018000 03:01 1311704 /lib/ld-2.4.so007d5000-007d6000 rw-p 00019000 03:01 1311704 /lib/ld-2.4.so007d8000-00904000 r-xp 00000000 03:01 1311705 /lib/libc-2.4.so00904000-00907000 r--p 0012b000 03:01 1311705 /lib/libc-2.4.so00907000-00908000 rw-p 0012e000 03:01 1311705 /lib/libc-2.4.so00908000-0090b000 rw-p 00908000 00:00 008048000-08049000 r-xp 00000000 03:02 57933977 /home/ 0x80 /dynlib/app08049000-0804a000 rw-p 00000000 03:02 57933977 /home/ 0x80 /dynlib/app09ca5000-09cc6000 rw-p 09ca5000 00:00 0 [heap]b7f94000-b7f95000 rw-p b7f94000 00:00 0b7fa4000-b7fa6000 rw-p b7fa4000 00:00 0bfb91000-bfba6000 rw-p bfb91000 00:00 0 [stack][0x80@localhost ~]$

可以看到/home/0×80/dynlib/injection.o起始於進程地址空間的0×00111000地址處(轉換成十進位即為1118208),終止於地址空間的0×00112000地址處。以上輸出同時包含了其它動態庫的映射信息。現在我們已經將所有需要的組件加載到可執行進程的內存空間中了。

6、重定位下面,我們從內部檢查ELF格式的二進位可執行文件程序app。我們使用Linux自帶的readelf程序,來顯示ELF格式的目標文件(Linux中的任意object文件、庫或可執行文件)中的不同數據,即查看app程序中的符號重定位信息。我們只對其中的print()函數調用的重定位感興趣。

[0x80@localhost dynlib]$ readelf -r appRelocation section 『.rel.dyn』 at offset 0×338 contains 1 entries:Offset Info Type Sym.Value Sym. Name08049678 00000c06 R_386_GLOB_DAT 00000000 __gmon_start__Relocation section 『.rel.plt』 at offset 0×340 contains 5 entries:Offset Info Type Sym.Value Sym. Name08049688 00000107 R_386_JUMP_SLOT 00000000 print0804968c 00000207 R_386_JUMP_SLOT 00000000 puts08049690 00000407 R_386_JUMP_SLOT 00000000 sleep08049694 00000607 R_386_JUMP_SLOT 00000000 __libc_start_main08049698 00000c07 R_386_JUMP_SLOT 00000000 __gmon_start__[0x80@localhost dynlib]$

如讀者所見,print符號重定位位於app程序的絕對(虛擬)地址0×08049688偏移處,重定位的類型為R_386_JUMP_SLOT。在程序被加載到內存且在運行之前,重定位地址是一個絕對虛擬地址。注意該重定位駐留在程序二進位鏡像的.rel.plt段內。PLT即Procedure Linkage Table的縮寫,是為函數間接調用提供的表,即在調用一個函數是,不是直接跳轉到函數的位置,而是首先跳轉到Procedure Linkage Table的入口處,之後再從PLT跳轉到函數的實際代碼處。

如果要調用的函數位於一個動態庫中(如本例中的libdynlib.so),那麼這種做法是必要的,因為我們不可能提前知道動態庫會被加載到進程空間的什麼位置,以及動態庫中的第一個函數是什麼(本位中為print()函數)。所有這些知識只在程序被加載到內存之後且運行之前有效,這時系統的動態連結器(Linux系統中為ld-linux.so)會解決重定位的問題,使請求的函數能夠被正確調用。在本文的例子中,動態連結器會將libdynlib.so加載到可執行進程的地址空間,找到print()函數在庫中的地址,並將該地址設置為重定位地址0×08049688。

我們的目標是用injection.o目標文件中injection()函數的地址替換print()函數的地址,該函數在程序剛開始運行之初並不包含在它的進程地址空間中。更多關於ELF格式、重定位以及動態連結器的的信息,讀者可以參考Executable and Linkable Format(ELF)文檔。

我們可以檢查地址0×08049688正是函數print()函數的地址:

(gdb) p & print$3 = (void (*)()) 0x50051c (gdb) p/x * 0×08049688$4 = 0x50051c(gdb)

injection()函數的地址可以通過對injection.o文件運行readelf –s(顯示目標文件的符號表)得到:

[0x80@localhost dynlib]$ readelf -s injection.oSymbol table 『.symtab』 contains 11 entries:Num: Value Size Type Bind Vis Ndx Name0: 00000000 0 NOTYPE LOCAL DEFAULT UND1: 00000000 0 FILE LOCAL DEFAULT ABS injection.c2: 00000000 0 SECTION LOCAL DEFAULT 13: 00000000 0 SECTION LOCAL DEFAULT 34: 00000000 0 SECTION LOCAL DEFAULT 45: 00000000 0 SECTION LOCAL DEFAULT 56: 00000000 0 SECTION LOCAL DEFAULT 77: 00000000 0 SECTION LOCAL DEFAULT 68: 00000000 25 FUNC GLOBAL DEFAULT 1 injection9: 00000000 0 NOTYPE GLOBAL DEFAULT UND print10: 00000000 0 NOTYPE GLOBAL DEFAULT UND system[0x80@localhost dynlib]$

函數(符號)injection位於injection.o文件.text段的偏移0處,但.text段起始於injection.o文件的偏移0×000034處:

[0x80@localhost dynlib]$ sudo readelf -S injection.oThere are 11 section headers, starting at offset 0xd4:Section Headers:[Nr] Name Type Addr Off Size ES Flg Lk Inf Al[ 0] NULL 00000000 000000 000000 00 0 0 0[ 1] .text PROGBITS 00000000 000034 000019 00 AX 0 0 4[ 2] .rel.text REL 00000000 000360 000018 08 9 1 4[ 3] .data PROGBITS 00000000 000050 000000 00 WA 0 0 4[ 4] .bss NOBITS 00000000 000050 000000 00 WA 0 0 4[ 5] .rodata PROGBITS 00000000 000050 000005 00 A 0 0 1[ 6] .comment PROGBITS 00000000 000055 00002d 00 0 0 1[ 7] .note.GNU-stack PROGBITS 00000000 000082 000000 00 0 0 1[ 8] .shstrtab STRTAB 00000000 000082 000051 00 0 0 1[ 9] .symtab SYMTAB 00000000 00028c 0000b0 10 10 8 4[10] .strtab STRTAB 00000000 00033c 000024 00 0 0 1Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)[0x80@localhost dynlib]$

7、用injection()函數替換print()函數這裡提醒讀者,injection.o文件已經被加載到app進程內存空間的地址0×00111000處(見上文)。因此injection()函數的最終絕對虛擬地址為0×00111000+0×000034.下面用該地址替換print()函數的重定位地址0×08069688:

(gdb) set *0×08049688 = 0×00111000 + 0×000034(gdb)

到這裡,我們已經成功用對injection()函數的調用替換了對print()函數的調用。

8、解決injection()函數的重定位

不過我們還有一些工作要做。injection()函數的代碼目前還不能運行,因為我們仍有3個重定位沒有解決:

[0x80@localhost dynlib]$ readelf -r injection.oRelocation section 『.rel.text』 at offset 0×360 contains 3 entries:Offset Info Type Sym.Value Sym. Name00000007 00000902 R_386_PC32 00000000 print0000000e 00000501 R_386_32 00000000 .rodata00000013 00000a02 R_386_PC32 00000000 system[0x80@localhost dynlib]$

print重定位引用libdynlib.so庫中的print()函數調用,.rodata重定位指向保存在.rodata只讀數據段的「date」常量字符串(譯者註:即system(date)調用中的「date」),system重定位引用系統的system()函數調用。需要注意的是所有這三個重定位是駐留在.rel.text段中的,因此它們的偏移是相對於.text段而言的。

我們需要手動解決以上三個重定位,為這三個內存位置設置適當的地址。程序進程地址空間中的這些重定位地址是通過求和計算出來的:

(1)injection.o在進程地址空間中的起始地址(0×00111000)。(2).text段在injection.o目標文件中的起始偏移量(0×000034)。(3)相對於.text段的重定位偏移量(print為0×00000007, .rodata為0x0000000e,system為0×00000013)。

可以看到print與system的重定位類型為R_386_PC32,意味著要設置的重定位地址的值應該利用程序計數寄存器PC來計算,這樣才是相對於重定位地址的。

(註:所謂重定位類型,就是規定了使用何種方式,去計算這個值,具體有哪些變量參與計算如同如何進行計算一樣也是不固定的,各種重定位類型有自己的規定。據規範裡面的規定,重定位類型R_386_PC32的計算需要有三個變量參與:S,A和P。其計算方式是 S+A-P。根據規範,當R_386_PC32類型的重定位發生在link editor連結若干個.o對象文件從而形成可執行文件的過程中的時候,變量S指代的是被重定位的符號的實際運行時地址,而變量P是重定位所影響到的地址單元的實際運行時地址。在運行於x86架構上的Linux系統中,這兩個地址都是虛擬地址。變量A最簡單,就是重定位所需要的附加數,它是一個常數。別忘x86架構所使用的重定位條目結構體類型Elf32_Rela,所以附加數就存在於受重定位影響的地址單元中。重定位最後將計算得到的值patch到這個地址單元中。)

R_386_32表示絕對地址的重定位,可以直接使用符號的地址;R_386_PC32表示對相對地址的重定位,要用「符號地址-重定位地址」得出相對地址。R_386_32 類型規定只是將附加數加上符號的值作為所需要的值,即.rodata的重定位需要在地址0×00111000的基礎上加上一個附加數。計算方法如下:

(gdb) p & system$7 = ( *) 0×733650 //system()函數的地址(gdb) p * (0×00111000 + 0×000034 + 0×000000013)$8 = -4 // system符號重定位的加數(gdb) set * (0×00111000 + 0×000034 + 0×000000013) = 0×733650 – (0×00111000 + 0×000034 + 0×000000013) – 4(gdb) p & print$9 = (void (*)(void)) 0x40000be8 // print()函數的地址(gdb) p * (0×00111000 + 0×000034 + 0×0000007)$10 = -4 // print符號重定位的加數(gdb) set * (0×00111000 + 0×000034 + 0×0000007) = 0x40000be8 – (0×00111000 + 0×000034 + 0×0000007) – 4(gdb) p * (0×00111000 + 0×000034 + 0x0000000e)$11 = 0 // .rodata符號重定位的加數(gdb) set * (0×00111000 + 0×000034 + 0x0000000e) = 0×00111000 + 0×000050//0×000050為.rodata 段在injection.o目標文件中的偏移(見上文第6節結尾處)

解決了injection()函數代碼中的所有3個重定位,那麼要做的準備工作就做完了,可以退出gdb調試器了。應用程式會繼續運行,並且在此之後,除了繼續之前的列印工作,程序同時還會輸出當前的日期。

(gdb) qA debugging session is active.Inferior 1 [process 25658] will be detached.Quit anyway? (y or n) yDetaching from program: /home/0×80/dynlib/app, process 25658[0x80@localhost dynlib]$ [lnx63:code_injection]// app程序會繼續執行Waked up …Thu Oct 12 20:09:40 IST 20124: PID 25658: In print()Going to sleep …Waked up …Thu Oct 12 20:09:43 IST 20125: PID 25658: In print()Going to sleep …Waked up …Thu Oct 12 20:09:46 IST 20126: PID 25658: In print()Going to sleep …Waked up …Thu Oct 12 20:09:49 IST 20127: PID 25658: In print()Going to sleep …Waked up …

9、結論在本文中,筆者演示了如何向正在運行於Linux系統上的應用程式注入一個C函數,而不必終止該程序。需要注意的是當前用戶必須是被注入的進程的,或者擁有對進程內存處理的相應權限。

相關焦點

  • Linux下C應用程式開發
    本文介紹了在 Linux 下能用於 C 應用程式開發和調試的工具. 本文的主旨是介紹如何在 Linux 下使用 C 編譯器和其他 C 編程工具, 而非 C 語言編程的教程.本節將介紹如何使用 GCC 和一些 GCC 編譯器最常用的選項.
  • 嵌入式Linux啟動時間優化的秘密之一工具鏈/應用程式優化
    而且在有的應用場合,對啟動時間具有嚴格的時間要求,尤其在工業或者醫療器械應用領域。此時如何加快Linux的啟動,將成為一個挑戰,對於大多數應用開發人員而言,由於Linux系統的複雜性,對於如何提高啟動速度,往往無從下手。那麼閱讀完本文,將獲得清晰完整的解決思路。
  • SQLServer應用程式中的高級SQL注入
    WINNT4+IIS4平臺上,那麼通過這個程序運行的命令是以系統權限運行的。[高級SQL注入]通常情況下,一個web應用程式將會過濾單引號(或其他符號),或者限定用戶提交的數據的長度。在這部分,我們討論一些能幫助攻擊者饒過那些明顯防範SQL注入,躲避被記錄的技術。
  • 麒麟作業系統:在linux上運行安卓應用
    使用安卓生態,對於發展linux系統來說相當重要,畢竟安卓也是基於linux內核,兩者關係密切。麒麟軟體和技徳系統團隊合作,讓麒麟系統可以運行安卓應用。其依託安卓運行環境Kydroid 3.0,可提供完全原生、高兼容性的使用體驗。
  • linux靜態庫和動態庫分析
    靜態庫的代碼在編譯過程中已經被載入可執行程序,因此體積較大。  共享庫的代碼是在可執行程序運行時才載入內存的,在編譯過程中僅簡單的引用,因此代碼體積較小。  3.庫存在的意義  庫是別人寫好的現有的,成熟的,可以復用的代碼,你可以使用但要記得遵守許可協議。
  • ARM/uClinux應用程式的開發
    更適合於資源緊張的嵌入式系統(上回分解已經說了,應用程式很大一部分是在和庫函數打交道,而且大家最終是鏈在一起,所以庫函數大了,你的程序也小不了)。  於是基於這種開發模式的應用程式開發變成了linux下的程序開發。
  • VBA 代碼程序運行時間的優化與視覺效果的兼顧
    在《VBA代碼解決方案》一書完結後,我寫了《VBA程序調試》一文,在文章中,我著重講了如何進行程序的調試,這篇文章的內容其實是很深的,為了讀者能更好地領會其中的意思,我將就幾個重點的部分內容再做側重地解釋,這些內容和我編程搭積木的思想是一脈相承的,希望給朋友分享些有用的經驗。
  • 應用:Linux中安裝Visual Studio Code
    1下載並安裝運行VS Code  Visual Studio Code是一款基於Electron優化代碼編輯器,作為微軟開發並支持Linux在內的全平臺代碼編輯器和文本編輯器。它是免費軟體但不開源,在專有軟體許可條款下發布。
  • 在 Mac 上運行 Windows 應用程式,只需一個CrossOver!
    Windows軟體,且不需要重啟電腦也不需要使用虛擬機就可以直接啟動Windows應用軟體,與您的Mac/linux系統功能無縫集成,實現跨平臺的複製粘貼和文件互通。CrossOver Mac 只佔用 90 MB 的下載,便可高效地使您的 Windows 程序運行在 Mac 上。。
  • 用Visual Studio調試Linux程序
    使用Visual Studio+VisualGDB調試遠程linux程序需要工具:Visual Studio 2013或以上版本(以下簡稱VS)VisualGDB(一款VS插件,官網為:http://visualgdb.com/)含有調試符號的linux程序文件(該程序文件為調試目標)Visual Assistant(番茄助手
  • 如何在虛擬機(vmware11)上運行linux系統
    有些人想體驗linux系統,又不想在自己的電腦上裝linux。那麼,在這裡小編將教大家如何在虛擬機上運行linux系統。下載正版VMware11可以同時下載linux系統鏡像VMware11下載完成以後,開始安裝。
  • 為何Cortex-M處理器運行不了linux
    單片機與應用處理器的核心區別到底是什麼呢?是核心主頻的差異?還是Linux系統的支持?又或者是處理器的架構?本文將以NXP的Cortex-M系列為例做簡要介紹。不同進程中的同一個虛擬地址被MMU映射到不同的物理地址,並且在某一個進程中訪問任何地址都不可能訪問到另外一個進程的數據,這樣使得任何一個進程由於執行錯誤指令或惡意代碼導致的非法內存訪問都不會意外改寫其它進程的數據,不會影響其它進程的運行,從而保證整個系統的穩定性。另一方面,每個進程都認為自己獨佔整個虛擬地址空間,這樣連結器和加載器的實現會比較容易,不必考慮各進程的地址範圍是否衝突。
  • 課程設計指導——如何應用Java反射技術靈活地創建程序類對象實例
    軟體項目實訓及課程設計指導——如何應用Java反射技術靈活地創建程序類的對象實例1、如何應用屬性配置文件實現對系統中的配置信息進行讀寫操作Java中的屬性配置文件主要可以作為軟體應用系統及項目的配置文件,比如許多J2EE的開源框架系統中都提供了屬性配置文件作為該應用框架的對外配置文件
  • Linux——Shell腳本的應用1(基礎)
    Shell腳本的應用(基礎)簡介:隨著linux系統在企業中的應用越來越多,伺服器的自動化管理也變得越來越重要。在linux伺服器的自動化維護工作中,除了計劃任務的設置以外,shell腳本的應用也是非常重要的一部分。
  • 利用運行按鈕,從模塊(Module)運行代碼
    今日的內容是第一章「初識VBA代碼及應用VBA代碼」的第三節「利用運行按鈕,從模塊(Module)運行代碼」第三節 利用運行按鈕,從模塊(Module)運行代碼作為Excelvba的初學者,您可能會發現很難決定將VBA代碼放在哪裡。
  • 一行JAVA代碼如何運行起來?
    現在我們所使用的淘寶系統,80%以上的後端程序都是Java開發,可見笑到最後才是贏家啊。不過JAVA語言的上手難度就比PHP、前端高很多了,所以今天我們給大家講解下一行JAVA代碼到底是如何運行起來的,JAVA後浪們可以以此為入門Java的基礎,開啟Java開發、人生贏家之路。
  • aPaas與低代碼又是如何促進應用程式開發現代化的?
    aPaas與低代碼又是如何促進應用程式開發現代化的? 從軟體即服務(SaaS)到基礎設施即服務(IaaS),雲計算的興起使「一切皆服務」(XaaS)模型得以泛濫,而aPaaS可能是這些模型中最鮮為人知的模型。
  • 為Linux應用程式編寫DLL程序函數 (2)
    以下是 dlTest 的測試運行:      dlTest構建 dlTest 啟用運行時動態連結需要三步: 將 dll 編譯為位置無關代碼; 創建 dll 共享目標文件; 編譯主程序並同 dl 庫相連結。 編譯 UPPERCASE.c 和 lowercase.c 的 gcc 命令包含 -fpic 選項。
  • Linux 內核通知鏈和例程代碼
    在通知這個事件時所運行的函數由被通知方決定,實際上也即是被通知方註冊了某個函數,在發生某個事件時這些函數就得到執行。其實和系統調用signal的思想差不多。代碼位置include/linux/notifier.h kernel/notifier.c 代碼不超過 1000 行,但是也是因為代碼少,才顯現出大神的厲害之處。
  • 什麼是應用程式安全性?保護軟體的過程和工具有哪些?
    然後如果黑客發現了數據洩,這個錯誤可以轉化為SQL注入攻擊。集成到應用程式開發環境中的應用程式安全工具可以使這個過程和工作流更簡單、更有效。如果您正在進行遵從性審計,這些工具也很有用,因為它們可以在審計人員發現問題之前捕獲問題,從而節省時間和費用。