Trao đổi với tôi
http://www.buidao.com 
3/28/17
3/27/17
[Word], Hướng dẫn đánh chỉ số trên, chỉ số dưới trong Word
Bài hướng dẫn chèn số mũ, đánh chỉ sổ trên chỉ số dưới trong Word
Sau đây, Thuthuatphanmem.vn xin hướng dẫn các bạn theo 3 cách sau (áp dụng với Microsoft 2007, Microsoft 2010 và Microsoft 2013):
Cách 1: Dùng phím tắt
-- Muốn viết chỉ số trên nhấn đồng thời các phím: Ctrl+ Shift+ =
Ví dụ muốn viết X3 bạn làm như sau: Viết X rồi nhấn đồng thời tổ hợp Ctrl+ Shift+ = rồi viết số 3, để kết thúc việc viết chỉ số trên bạn lại nhấn đồng thời tổ hợp Ctrl+ Shift+ =
- Muốn viết chỉ số dưới nhấn đồng thời các phím: Ctrl+ =
Ví dụ để viết công thức của nước các bạn làm như sau: viết H rồi nhấn Ctrl+ = viết số 2 rồi lại nhấn Ctrl+ = (để kết thúc việc viết chỉ số dưới) và viết số O --> ta thu được công thức như sau: H2O
Cách 2:
- Kích chuột chọn số muốn tạo chỉ số
- Đối với chỉ số dưới ta làm như sau: Chọn chỉ số, trên tab Home chọn Font chọn, cửa sổ Fonts mở ra ta đánh dấu vào mục Subscript
Và kết quả thu được là:
- Đối với chỉ số trên ta làm như sau: Chọn chỉ số, trên tab Home chọn Font chọn, cửa sổ Fonts mở ra ta đánh dấu vào mục Superscript
Và kết quả thu được là:
Cách 3:
- Nếu muốn tạo chỉ số trên thì chọn X2 như hình:
- Nếu bạn muốn tạo chỉ số dưới thì chọn X2 theo như hình sau:
Chú ý: Để chọn nhiều chỉ số cùng lúc ta chọn số đầu tiên rồi giữ phím Ctrl và bôi đen các số tiếp theo mà bạn muốn tạo chỉ số.
Tham khảo : http://thuthuatphanmem.vn/huong-dan-danh-chi-so-tren-chi-so-duoi-trong-word/
3/13/17
[Guitar Pro], Guitar Pro 6 Full - Phần mềm hữu ích cho người chơi Guitar
Phiên bản Guitar Pro 6 với giao diện đẹp, và âm thanh cải thiện đáng kể so với bản 5, nhiều công cụ và tính năng mới cho người sử dụng và soạn thảo chắc chắn sẽ khiến bạn hài lòng.
Các bạn có thể tải về: https://www.fshare.vn/folder/UAKSX11QGSC2
Giờ có thể sử dụng phần mềm rồi.
Nếu trong quá trình cài đặt xảy ra lỗi thì xoá hết dữ liệu trong thư mục sau:
2000/XP: Documents and Settings\All Users\Application Data\Guitar Pro 6\
Vista/7: ProgramData\Guitar Pro 6\
và xoá hết các khoá registry có từ 'Guitar Pro 6', 'GuitarPro' hoặc 'Arobas Music' trong registry sau đó cài lại.
Nếu được nên thêm đoạn mã sau vào file host ở địa chỉ:
C:\WINDOWS\system32\drivers\etc\hosts
Mã:
127.0.0.1 87.98.165.24 127.0.0.1 91.121.154.202
Hoặc IP khác của phần mềm mà nó chạy từ đó. Nhớ tắt firewall cho phần mềm.
https://vnguitar.net/threads/guitar-pro-6-full-phan-mem-huu-ich-cho-nguoi-choi-guitar.4285/
3/4/17
[Audio], [Social Network], 3 cách tải file MP3 từ YouTube trực tuyến
3 cách tải file MP3 từ YouTube trực tuyến
Trong quá trình xem video trên YouTube, bạn có thể sẽ thích và muốn tải chúng về máy để sau này xem lại. Tuy nhiên, nếu như thay vì tải cả video về thì sẽ gây tốn bộ nhớ nên bạn chỉ muốn tải về file MP3. Nhưng bạn còn chưa biết nên dùng phần mềm nào? Hôm nay, chúng sẻ chia sẻ với các bạn 3 dịch vụ trực tuyến để bạn tải file MP3 từ YouTube rất nhanh chóng, đơn giản mà lại hoàn toàn miễn phí.
1- ClipConverter
Giao diện trang web rất đơn giản, bạn có thể thay đổi cả ngôn ngữ của trang web để phù hợp với ngôn ngữ quốc gia mình đang sinh sống. Rất tiết là trang này chưa hỗ trợ ngôn ngữ Tiếng Việt.
Với thao tác sử dụng đơn giản, bạn chỉ việc dán URL của video Youtube mà bạn muốn chuyển đổi thành một bài nhạc với định dạng MP3, sau đó chọn chất lượng cho bài nhạc. Ngoài định dạng MP3, trang web còn cho phép bạn chuyển đổi video thành bài nhạc với nhiều định dạng khác nhau như Wav, M4A, Acc,...
 
 
Quá trình chuyển đổi sang file MP3 sẽ diễn ra

Khi quá trình chuyển đổi kết thúc, bạn sẽ có các lựa chọn như lưu vào OneDrive, Dropbox hay tải về máy.

2- MP3Fiber
Tại giao diện chính của trang web, bạn chỉ cần dán URL của video YouTube mà bạn cần chuyển thành định dạng MP3, sau đó chọn chất lượng cho bài nhạc khi xuất ra. Tiếp theo bạn hãy nhấn nút Download Now để trang web tiến hành chuyển đổi định dạng.

Sau đó, quá trình chuyển đổi video sang file MP3 sẽ diễn ra

Cuối cùng, bạn chỉ việc nhấn Download Now để tải về máy là xong.

3- Vubey
Với cách sử dụng vô cùng đơn giản, bạn chỉ cần dán địa chỉ URL vào khung Video URL, sau đó chọn chất lượng âm thanh khi chuyển đổi. Cuối cùng là bấm Convert To MP3 để bắt đầu quá trình chuyển đổi.

Sau đó, quá trình chuyển đổi sang file MP3 sẽ diễn ra

Cuối cùng, bạn cũng tải file MP3 về máy là xong.

Trên đây là 3 dịch vụ trực tuyến để bạn tải file MP3 từ video YouTube về máy tính hoàn hoàn miễn phí. Từ giờ, bạn có thể tha hồ lưu những bản nhạc yêu thích mà khỏi lo tốn dung lượng ổ cứng rồi nhé.
Chúc các bạn thành công!
Cập nhật: 03/03/2017Xuân Linh Nguyễn
3/2/17
[Word], Ẩn hiện comment trong WORD khi in
Ẩn hiện comment trong WORD khi in
Word 2007 thì vào thẻ REVIEW, muốn ẩn không in "ghi chú" thì tìm Show Markup chọn mũi tên để hiện ra các lựa chọn rồi bỏ chọn Comment là được.
Display for review : Cọn Final
 
 
![Comment[SCD1] Comment[SCD1]](https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_uif1e0JPe07v3IVmxI26sph-G7zWsIZEI-mpctcybFReKktQ2-lR4pToTIDBoUsXl6Z_J13ldQY9g1sz3csBUi40v9N6u8OZG05YJUJQyHUrub6W-Lk23rkbg3d3ZKkbVtHLCyBqw=s0-d) 
 
 
 
 
Word 2007 thì vào thẻ REVIEW, muốn ẩn không in "ghi chú" thì tìm Show Markup chọn mũi tên để hiện ra các lựa chọn rồi bỏ chọn Comment là được.
Display for review : Cọn Final
Cách sử dụng chú thích, ghi chú (comment) trong Word
Chức năng comment trong Word giúp các ban tạo các ghi chú cho dòng hay đoạn văn bản trong tài liệu Word. Các bạn có thể ghi chú bất kỳ dòng hay đoạn văn bản nào khi cần thiết.
Bài viết dưới đây hướng dẫn sử dụng chức năng comment (ghi chú) trong Word.
1. Thêm comment (ghi chú).
Bước 1: Để tạo ghi chú cho dòng hay đoạn văn bản trong tài liệu Word, đầu tiên các bạn cần chọn (bôi đen) dòng hay các đoạn văn bản mà các bạn muốn thêm ghi chú.
Bước 2: Chọn tab Review -> New Comment để tạo ghi chú mới.
Ô comment sẽ được xuất hiện bên phải các dòng, đoạn văn các bạn muốn tạo ghi chú.
Bước 3: Các bạn nhập nội dung ghi chú các bạn muốn vào ô Comment[SCD1], với các ghi chú tiếp theo nó sẽ là SCD2, SCD3…
Sau khi nhập nội dung ghi chú xong các bạn nhấn chuột ra khoảng trống trong văn bản Word. Nội dung được ghi chú được lưu lại, dòng hay đoạn văn bản ghi chú được tô màu. Các bạn chỉ cần di chuyển con trỏ chuột vào dòng hay đoạn văn bản đó là sẽ xuất hiện thông tin comment các bạn nhập.
2. Chỉnh sửa comment.
Để chỉnh sửa comment các bạn nhấn chuột phải vào dòng hay đoạn văn bản các bạn đã tạo comment và chọn Edit Comment. Hoặc các bạn có thể đặt con trỏ chuột trực tiếp vào ô Comment[SCD1] và sửa nội dung ghi chú.
3. Xóa comment.
Để xóa comment các bạn nhấn chuột phải vào dòng, đoạn văn bản đã tạo ghi chú và chọn Delete Comment.
Hoặc các bạn đặt con trỏ chuột vào dòng, đoạn văn bản đã tạo comment và chọn Review -> Delete.
Như vậy, khi cần thiết các bạn có thể thêm các ghi chú, bình luận, trích dẫn.. vào ngay các dòng hay các đoạn văn bản trong tài liệu Word. Các bạn sẽ dễ dàng theo dõi và dễ hiểu hơn khi đọc tài liệu Word. Chúc các bạn thành 
[INDEPENDENT CODE], PART 3: GET ADDRESSES OF APIs
PART 3: GET ADDRESSES OF 
APIs
Tranz by : Benina (5/4/2006) 
(fixed 2008)
Sau 
khi có được Base của Kernel, chúng ta sẽ tiếp tục tìm các addr của các hàm API 
mà code độc lập cần dùng. Bài này tôi chỉ dịch lại từ 2 bài tuts rất nổi tiếng 
trong giới Vxer. Các tác giả của 2 bài tuts này là Billy Belceb£ và 
LethalMind/29A trong team 29A. Như 
thông lệ, cuối tut, tôi sẽ đưa ra ví dụ được code trong Masm32. 
Một 
lời khuyên, để hiểu được tut này cặn kẻ, bạn nên đọc trước lọat tuts 
PE của Iczlion. Đặc biệt là tut 7 trong lọat tut nói trên. Và ít nhất 
phải hiểu được định dạng file PE format (xem tut của Kienmanowar có trên 
site Benina’s)
Và 
cuối cùng, vì là bài dịch, nên văn phong ko được mượt mà cho lắm do 
cố gắng giữ nguyên ý nghĩa từ bản gốc, mong các bạn thông 
cảm.
1.Bài 
tut của Billy Belceb£
% Get those crazy APIs!!! 
%
 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Ring-3 
, như 
tôi đã nói trong chương introduction, là 
lớp người dùng (user level), vì 
vậy chúng ta có thể chỉ truy xuất các đặc quyền trong giới hạn của 
nó. Nghĩa là chúng ta ko thể dùng các ports, read hay write đến các 
vùng memory đã xác định dành cho kernel, vân vân. Micro$oft đã đưa ra các 
tuyên bố khi phát triển Win95 (đã được nói rất nhiều, đó là "Win32 
platformz are uninfectable"), trong thực tế nếu họ đã “tịch thu” tất cả 
những gì mà virus trước đó trong DOS đã dùng để “sản sinh” ra,thì họ 
có thể defeat (đánh bại) chúng ta (các vxers) . Trong giấc mơ của họ, 
họ nghĩ rằng chúng ta ko thể dùng các hàm APIs của họ, và hơn nữa, 
họ ko thể hình dung chúng ta có thể jmp đến Ring-0, nhưng việc nhảy 
đến ring0 là một câu chuyện khác sẽ đề cập trong các tut 
sau.
Well, 
như tôi đã nói trước đó, chúng ta sử dụng tên hàm API name  kết hợp với chỉ lệnh extern lúc biên 
dịch, do vậy import32.lib đã cho chúng ta address of function, và nó đã 
được assembled một các chính xác trong code, nhưng chúng ta có một vấn 
đề khi writing  virus. Nếu chúng ta 
hardcode (ứng với name của hàm API mà chúng ta cần dùng khi viết virus, 
chúng ta dùng một fixed offset cố định để call hàm API đó),thì điều 
hầu như chắc chắn có thể xảy ra là addr đó ko làm việc trong các 
Win32 version kế tiếp. Bạn có một ví dụ trong virus Bizatch. Vậy chúng 
ta sẽ làm gì bây giờ? Well, chúng ta có một function được gọi là 
GetProcAddress, mà nó returns cho chúng ta offset của API mà chúng ta 
muốn có nó. Bạn là người thông minh phải ko, bạn phải chú ý rằng 
GetProcAddress lại là một hàm API , vì vậy khốn thật, mèo lại hòan 
mèo, làm thế nào mà chúng ta có thể dùng một hàm API cho việc search 
các hàm APIs  nếu chúng ta ko có 
hàm API đó?. Như điều tương tự trong cuộc sống, chúng ta chắc rằng sẽ 
có nhiều khả năng khác nhau để làm được điều mình muốn, và tôi sẽ 
đưa ra 2 cách mà tôi nghĩ là tốt nhất để lấy offset hàm API mong muốn: 
 1. Search hàm GetProcAddress API 
trong Exports 
table.
 2. Khi 
chúng ta infect một 
file, 
hảy 
tìm trong các hàm nhập khẩu (imported 
functions) 
hàm 
GetProcAddress có 
trong chúng không.
Cách 
dễ nhất là cách đầu tiên, hảy 
đón xem những gì tôi sẽ giải thích ngay bây giờ cho bạn :)Ok, chúng 
ta hảy bắt đầu với các bài học lý thuyết , và sau đó là sum 
coding.
Nếu 
bạn nhìn vào PE header format, 
chúng 
ta có trong offset 78h (of PE header, 
ko 
phải của  file) là 
RVA  of exports table. Ok, chúng 
ta cần lấy addr của exports of   kernel. Đối 
với Windows 
95/98, kernel định 
trú tại offset 
0BFF70000h,và 
trong Windows 
NT, 
kernel 
dường 
như ở tại 077F00000h. Trong 
Win2k 
chúng 
ta có nó ở tại offset 
077E00000h. Vì 
vậy, trước tiên chúng ta load address của nó trong một thanh ghi 
(register) mà chúng ta sẽ dùng như một con trỏ pointer. Tôi mạnh dạng 
đề xuất dùng thanh ghi ESI, chủ yếu bởi vì chúng ta có thể tối ưu 
hóa vài code bằng cách dùng lệnh LODSD sử dụng đến thanh ghi ESI. Well, 
chúng ta check xem có hay ko tại addr đầu tiên của kernel signal "MZ" word 
(well, signal là "ZM" cho lọai CPU “goddamn intel  processor  
architecture” :),  bởi vì kernel 
là một library (.DLL), và các libraries có một PE header, và như chúng 
ta thấy trước đó, trước PE  header, 
là thành phần của DOS-compatible stuff. Sau khi so sánh , chúng ta đã 
check addr kernel có đúng là một base của PE hay ko rồi, nếu mọi thứ 
đều OK, thì chúng ta làm tiếp là tìm giá trị tại header offset image_base+[3Ch] (= offset  of nơi mà kernel được định vị + address 
được chỉ ra bởi offset tại 3Ch của KERNEL's PE header), và so sánh với 
signal "PE\0\0" chính là PE signature.
Nếu 
tất cả đều đúng, thì chúng ta sẽ đi tiếp. Chúng ta cần RVA của export 
table. Như bạn có thể thấy, nó ở trong offset 78h of PE header. 
Vậy 
chúng ta lấy nó. Nhưng như bạn biết , RVA (Relative Virtual Address), như 
cái tên nó đã nói, liên quan đến một offset cơ sở, trong trường hợp 
này là image  base of kernel, đó là 
nơi nó định vị, như tôi nói trước đó . Đơn giản ở đây chính là, chỉ 
cần add kernel offset với giá trị đã tìm được trong Export Table RVA. Ok. 
Bây giờ chúng ta đang ở trong export 
table 
:)
Chúng 
ta hảy xem định dạng của nó : 
 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿  ÄÄÄ 
+00000000h 
 ³           
Export Flags            ³       Size : 1 DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000004h 
 ³         
Time/Date stamp           ³       Size : 1 WORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000006h 
 ³          
Major version            ³       Size : 1 WORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000008h  
 ³          
Minor version            ³       Size : 1 DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+0000000Ch  
 ³            
Name RVA               ³       Size : 1 DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000010h  
 ³   
Number Of Exported Functions    
³       Size : 1 
DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000014h 
 ³     
Number Of Exported Names      
³       Size : 1 
DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000018h 
 ³     
Export Address Table RVA      
³       Size : 1 
DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+0000001Ch 
 ³   
Export Name Pointers Table RVA  
³       Size : 1 
DWORD
 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´  ÄÄÄ 
+00000020h 
 ³       
Export Ordinals RVA         ³       Size : 1 DWORD
 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ              ÄÄÄÄÄÄÄÄÄ
                                       Total 
Size : 24h BYTES
Các 
vùng quan trọng đối với chúng ta là 6 fields sau 
cùng. 
Như 
bạn có thể hình dung, các giá trị trong: Address Table RVA, Name 
Pointers RVA và Ordinals RVA liên 
quan hòan tòan với địa chỉ cơ sở Kernel (KERNEL32's base 
address). Vậy, 
bước đầu tiên cho việc lấy một địa chỉ của hàm API là 
phải biết vị trí mà hàm API này đang ở đâu trong các vùng, và cách 
dễ nhất để biết nó là duyệt tìm trong Name 
Pointers' 
offset tên 
hàm API ta cần, so 
sánh từng string trong vùng đó với string tên hàm API mà 
chúng ta muốn lấy addr, và nếu 2 string giống nhau một cách chính xác, 
chúng ta sẽ thử tính tóan API's offset. 
Well,coi 
như chúng ta đã đi tới đây, và bây giờ chúng ta có một giá trị trong 
biến counter,do 
bới chúng ta dùng biến này tăng lên mỗi lần chúng ta duyệt tìm tên 
hàm API's 
name 
trong giai đọan trên đã đề cập. Well, biến counter 
này, 
như 
bạn có thể hình dung, đã nhảy qua các tên hàm API names mà 
chúng ta đã duyệt qua và các hàm đó ko phù hợp với tên hàm API ta 
cần. Counter 
có 
thể là một 
word hay 
một dword, 
nhưng 
chưa bao giờ là một byte. Bởi vì chúng ta có rất nhiều hàm APIs , hơn 
255 hàm lận:)
NOTE: Tôi 
cho rằng bạn đã chứa địa chỉ VA (RVA+kernel 
image base) of 
Address, Name 
và Ordinal 
tables 
trong các biến tương ứng là AddressTableVA, NameTableVA và 
OrdinalTableVA
Ok, 
hảy 
tưởng tượng chúng ta đã lấy được name của API mà 
chúng ta mong muốn trong vùng Name Pointers table, vậy 
chúng ta có trong counter là 
vị trí nó chiếm giữ trong Name Pointers table. 
Well, bây 
giờ có lẽ sẽ phiền phức cho bạn đây, những người mới bắt đầu code 
trên Win32 
coding. Hmm, 
chúng ta hảy tiếp tục. Chúng 
ta đã có counter, và 
bây giờ chúng ta phải search trong 
Ordinal Table 
(một  array of  
dwords) tìm 
ordinal  of  
API mà 
chúng ta muốn lấy addr. Khi chúng ta có được số thứ tự vị trí của 
hàm API cần tìm trong array Name (giá trị trong biến counter) chúng ta 
chỉ phải nhân nó với 2 ( hảy nhớ, array of Ordinals được tạo ra bới 
các thành phần là words, vậy chúng ta phải tính tóan với words, mà 
mỗi word là 2 bytes...), và tất nhiên, phải cộng nó với offset đầu tiên 
của array Ordinal  Table.  Nhắc lại những gì tôi đã giải thích, 
chúng tôi cần một word được tính bởi công thức sau: 
 API's Ordinal location: ( 
counter * 2 ) + Ordinal Table  VA 
Đơn 
giản đúng ko?. Well, bước kế tiếp (và là bước sau cùng) là get (lấy) 
địa chỉ hàm API đã được định sẳn trong mảng Address Table. Chúng ta có 
API's 
ordinal 
rồi, 
đúng 
ko? 
Có 
nó ,công việc chúng ta sẽ dễ dàng hơn. Chúng ta chỉ phải nhân ordinal 
cho 4 (mảng Addresses array  được định 
dạng bởi các thành phần là dwords thay vì words, và một dword có size 
là 4) và cũng cộng nó với offset of điểm bắt đầu of Address  Table, chúng ta có nó dễ dàng phải ko. 
Hehe, 
bây 
giờ chúng ta có API  Address RVA. Vậy 
chúng ta phải normalize 
(chuẩn hóa) nó, cộng 
tiếp thêm kernel  offset, và 
đúng như thế. Chúng ta đã có được nó!!!! Chúng 
ta hảy xem công thức  của nó như 
sau:
 API's Address: ( API's Ordinal * 4 ) + Address 
Table  VA 
 ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ 
Tóm 
lại, khi chúng ta nhận được vị trí 
 ³ EntryPoint ³ Ordinal ³ Name             ³ string 
xuất hiện trong Name table, 
 ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵ 
chúng 
ta cần biết ordinal của nó 
 ³   
00005090 ³    0001 ³ AddAtomA         ³ (mỗi 
name có một ordinal ở vị trí
 ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵ 
tương 
xứng với API name), và 
 ³   
00005100 ³    0002 ³ AddAtomW         ³ biết 
được ordinal, chúng ta có 
 ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵ 
thể 
biết addr của nó , đó là, 
 ³   
00025540 ³    0003 ³ 
AddConsoleAliasA ³ entrypoint 
RVA của nó.         
 ÆÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵ 
Chúng 
ta chuẩn hoá nó và cuối cùng
 ³   
00025500 ³    0004 ³ 
AddConsoleAliasW ³ bạn 
có những gì cần thiết về
 \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ 
API address đã 
yêu cầu.
 [...] Các 
tables này có nhiều các entries, nhưng với các thành phần liệt kê như 
trên là đủ xài rồi ... 
 Tôi hy vọng bạn hiểu những gì tôi đã 
giải thích. Tôi đã cố trình bày đơn giản với những gì tôi có thể , 
nếu bạn chưa hiểu nó , đừng bỏ qua các dòng này , và hảy đọc lại 
nó từng bước từng bước một . Hảy kiên nhẫn. Tôi chắc bạn sẽ hiểu 
thôi. Hmmm, có lẻ những gì bạn cần bây giờ là vài đọan code để thấy 
nó thực hiện như thế nào .Sau đây bạn có các dòng lệnh do tôi viết, 
được sử dụng làm ví dụ, trong Iced Earth virus của 
tôi.
;ÄÄÄ[ CUT HERE 
]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; 
GetAPI & GetAPIs procedures
;  
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
;
; 
Đây 
là procedures  dùng để tìm tất cả các APIs yêu 
cầu... 
Chúng 
được chia làm 2
; 
parts. GetAPI procedure chỉ 
lấy một API 
mà 
chúng ta cần, 
và 
GetAPIs 
proce-
; 
dure để 
searches 
tất 
cả các hàm APIs nào 
cần dùng bởi virus.
;
 GetAPI         
proc
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Ok, let's rock. Các 
tham số cần thiết cho hàm này và kết quả returns     ;
 ; như 
sau :                                                                ;
 ;                                                                          
;
 ; INPUT   
ESI : Con 
trỏ đến API 
name (chú 
ý chữ Hoa và chử thường )       ;
 ; OUTPUT  
EAX : API address               
                                ;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
        mov     
edx,esi                         ; 
Save ptr to name
 @_1:   
cmp     byte ptr [esi],0                ; Null-terminated 
char?
        jz      
@_2                             ; 
Yeah, we got it.
        inc     
esi                             ; 
Nopes, continue searching
        jmp     
@_1                             ; 
bloooopz...
 @_2:   
inc     esi                             ; heh, don't 
forget this ;)
        sub     
esi,edx                         ; 
ESI = API Name size
        mov     
ecx,esi                         ; 
ECX = ESI = 
size of name 
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Well, well, thưa 
các bạn , ở đây dễ dàng để hiểu, chúng ta có            ;
 ; ESI trỏ 
đến điểm bắt đầu của API's name. chúng 
ta hảy tưởng tượng chúng  
;
 ; ta 
tìm hàm  "FindFirstFileA":                                            
;
 ;        
                                                                  ;
 ; FFFA         
db   "FindFirstFileA",0                                     
;
 ;                    ÀÄ Pointer đang 
ở đây                                 
;
 ;                                                                          ;
 ; và 
chúng ta cần lưu trử pointer 
này, 
và 
để biết API's 
name size, 
chúng   ;
 ; ta 
lưu trử pointer 
khởi đầu đến 
API name trong 
1 register 
là EDX mà      ;
 ; chúng 
ta chưa dùng làm gì và rồi tăng pointer trong 
 ESI cho 
đến khi     ;
 ;[ESI] = 0.                                                            
    ;
 ;                                                                          
;
 ; FFFA         
db   "FindFirstFileA",0                                     
;
 ;                                    ÀÄ 
Pointer bây 
giờ đang ở đây         ;
 ;                                                                          
;
 ; Đó 
là, null 
terminated :) Rồi 
bằng cách trừ pointer đầu tiên với pointer ;
 ; mới 
chúng ta có API Name size,size 
này cần cho search engine. Và         ;
 ; tôi 
chứa nó trong ECX, register khác 
ko thích hợp dùng cho công việc này ;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
        xor     
eax,eax                         ; EAX = 0
        mov     
word ptr [ebp+Counter],ax       ; 
Counter set to 0
        mov     
esi,[ebp+kernel]                ; 
Get kernel's PE head. offset
        add     
esi,3Ch
        lodsw                                   ; in 
AX
        add    
 eax,[ebp+kernel]                ; Normalize 
it
        mov     
esi,[eax+78h]                   ; 
Get Export Table RVA
        add     
esi,[ebp+kernel]                ; 
Ptr to Address Table RVA
        add     
esi,1Ch
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Well, trước 
tiên xóa EAX, 
và 
rồi tạo cho biến counter bằng 0,            ;
 ; để 
tránh lỗi ko mong đợi. Nếu u nhớ offset 3Ch là 
gì  of  một           
 ;
 ; PE file (đếm 
từ image base, 
MZ mark), u sẽ 
hiểu ở đây. Chúng ta đang cần ;
 ; điểm 
có điểm đầu of KERNEL32 PE header 
offset. Well, nhưng 
nó là một 
RVA,;
 ; chúng 
ta normalize 
nó 
và xong xuôi, 
chúng ta có nó là PE headeroffset.   ;
 ; Những gì chúng ta làm bây giờ là 
get Export 
Table address                ;
 ; (in PE 
Header+78h),và 
sau đó chúng ta bỏ qua các data ko quan tâm of     ;
 ; structure, và 
get 
một 
cách trực tiếp Address Table 
RVA.( add 
với 1Ch)    ;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
        lodsd                                   ; EAX = 
Address Table RVA
        add     
eax,[ebp+kernel]                ; 
Normalize
        mov     
dword ptr [ebp+AddressTableVA],eax ; Store it in VA 
form
        lodsd                                   ; EAX = Name 
Ptrz Table RVA
        add     
eax,[ebp+kernel]                ; 
Normalize
        push    
eax                             ; 
mov [ebp+NameTableVA],eax
        lodsd                                   ; EAX = Ordinal 
Table RVA
        add     
eax,[ebp+kernel]                ; 
Normalize
        mov     
dword ptr [ebp+OrdinalTableVA],eax ; Store in VA 
form
        pop     
esi                             ; 
ESI = Name Ptrz Table VA
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Nếu 
u nhớ lại, chúng ta có trong ESI con 
trỏ đến Address Table RVA, 
vậy  ;
 ; để 
lấy được address 
đó, ta 
thực thi lệnh LODSD, đó 
chính là ta puts      ;
 ; DWORD được định vị bởi ESI vào trong 
EAX. Vì nó là 1 RVA, chúng ta cần   
;    
 ; chuẩn hóa nó                                                          
   ;
 ;                                                                          
;
 ; Chúng 
ta xem những gì Matt Pietrek nói 
về vùng đầu tiên này:             
;
 ;                                                                          
;
 ; "Vùng 
này là một RVA và 
trỏ đến một array các 
địa chỉ hàm. Địa 
chỉ       ;
 ; hàm 
là các thành phần trỏ đến (RVA) mỗi 
hàm exported 
function            ;
 ; trong 
module này”.                                                       ;
 ;                                                                          
;
 ; Và 
tất nhiên, chúng ta chứa nó trong biến của nó. Sau đó,kế đến chúng ta 
;
 ; nhận 
được Name 
Pointers Table, Matt Pietrek mô 
tả như sau :              ;
 ;                                                                          
;
 ; "Vùng 
này là một RVA và 
points 
đến 
một array of 
string pointers. Các 
    ;
 ; strings là 
các names of 
các 
hàm exported 
trong module 
này". 
             ;
 ;                                                                          
;
 ; Nhưng 
tôi ko chứa nó trong một biến, tôi đã pushed nó, chỉ 
vì tôi sẽ dùng;
 ; nó 
rất sớm. Well, và 
cuối cùng chúng ta nhận được tiếp array Ordinals    ;
 ; và 
ở đây Matt 
Pietrek mô 
ta về nó:                                       ;
 ;                                                                          ;
 ; "Vùng 
này là một RVA và 
points 
đến 
1 array of 
WORDs. WORDs là 
các        ;
 ; export ordinals of tất 
cả các 
exported functions trong 
module 
này".      ;
 ;                                                                          ;
 ; Well, đó 
là những gì tôi đã làm                                          ;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 @_3:   
push    esi                             ; Save ESI for l8r 
restore
        lodsd                                   ; Get value 
ptr ESI in EAX
        add     
eax,[ebp+kernel]                ; 
Normalize
        mov     
esi,eax                         ; 
ESI = VA of API name
        mov     
edi,edx                         ; 
EDI = ptr to wanted API
        push    
ecx                             ; 
ECX = API size
        cld                                     ; Clear 
direction flag
        repe     cmpsb                           ; Compare both API 
names
        pop     
ecx                             ; 
Restore ECX
        jz      
@_4                             ; 
Jump if APIs are 100% equal
        pop     
esi                             ; 
Restore ESI
        add     
esi,4                           ; And get next value of 
array
        inc     
word ptr [ebp+Counter]          ; 
Increase counter
        jmp     
@_3                             ; 
Loop again
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Heh, tôi 
ko phải là loại người put nhiều 
code mà ko chú thích gì, như 
tôi;
 ; vừa 
làm, 
nhưng 
hảy hiểu rằng block of code này 
ko thể tách ra để giải    
;
 ; thích nó. Những gì chúng ta làm trước 
tiên là push ESI(do nó thay đổi bên;
 ; trong code bởi chỉ thị CMPSB) để restore 
sau này. Sau đó, chúng ta lấy   
;
 ; DWORD được trỏ bởi ESI (Name Pointerz 
Table) trong accumulator (EAX),tất 
;
 ; cả điều này thực hiện bởi chỉ thị LOSD. 
Chúng ta chuẩn hóa nó bằng 
cách  ;
 ; add 
với kernel 
base address. Well, bây giờ chúng ta có trong EAX một     ;
 ; pointer 
trỏ đến một name of một hàm API, nhưng chúng ta vẫn ko biết API  ;
 ; nào . Ví dụ, EAX  có thể trỏ đến hàm nào đó giống như 
"CreateProcessA"   
;
 ; và hàm API này ko cần cho virus chúng ta 
...                             
;
 ; Well, để so sánh string đó với string hàm 
API chúng ta cần tìm (được trỏ ;
 ; đến bởi EDX) chúng ta dùng chỉ thị CMPSB. 
Vậy chúng ta sửa chửa lại các  
;
 ; parameters của nó: trong 
ESI chúng ta put pointer đến điểm đầu API ngay  
;
 ; bây giờ trong Name Pointerz Table, và trong 
EDI chúng ta put pointer đến ;  
 ; API mong muốn. Trong ECX chúng ta put size 
của nó,và rồi chúng ta so 
sánh;
 ; byte với byte. Nếu các 
string giống nhau hòan tòan,cờ 
zero 
flag được set,;
 ; và chúng ta nhảy 
đến routine để 
lấy address of API đó, nhưng nếu sai 
khác;
 ; ,chúng ta phục hồi lại ESI, và add nó với 
size of một DWORD để lấy giá   
;
 ; trị kế tiếp trong Name Pointerz Table array. 
Chúng ta tăng biến đếm      ;
 ; counter(RẤT 
QUAN TRỌNG)lên 
và chúng ta 
tiếp tục searching.               
;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 @_4:   
pop     esi                             ; Avoid shit in 
stack
        movzx   
eax,word ptr [ebp+Counter]      ; 
Get in AX the counter
        shl     
eax,1                           ; 
EAX = AX * 2
        add     
eax,dword ptr [ebp+OrdinalTableVA] ; Normalize 
        xor     
esi,esi                         ; 
Clear ESI
        xchg    
eax,esi                         ; 
EAX = 0, ESI = ptr to Ord
        lodsw                                   ; Get 
Ordinal in AX
        shl     
eax,2                           ; 
EAX = AX * 4
        add     
eax,dword ptr [ebp+AddressTableVA] ; Normalize
        mov     
esi,eax                         ; 
ESI = ptr to Address RVA
        lodsd                                   ; EAX = 
Address RVA
        add     
eax,[ebp+kernel]                ; 
Normalize and all is done.
        ret
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Pfff, 1 
block 
code lớn khác, 
và dường như khó hiểu, đúng ko?. Heh đừng lo;
 ; lắng, tôi sẽ chú thích ngay đây ;)                                       
;
 ; Ehrm, lệnh pop chỉ đơn giản clear stack, nếu 
như API names phù hợp,chúng 
;
 ; ta có 1 
shit trong 
đó 
(stack). Chúng 
ta mov 
phần lower of 
EAX với 
giá    ;
 ; trị 
của biến counter(là một WORD) và make 
zero phần high register EAX.   ;
 ; Chúng ta nhân nó với 2,vậy 
chúng ta có một 
số nó đang 
lưu giữ, và 
array  ;
 ; mà chúng ta sẽ search là một 
array of WORDs. Bây giờ chúng ta add cho 
nó ;
 ; một pointer đến beginning of array 
mà chúng ta muốn search, 
và  trong    ;
 ; EAX chúng ta có một pointer trỏ 
đến ordinal of 
API mà chúng ta cần. 
Vậy  ;
 ; chúng ta put EAX vào trong ESI để dùng con 
trỏ đó lấy giá trị đang trỏ   
;
 ; đến, đó là, Ordinal trong EAX, sử dụng 
lệnh LODSW . Heh, chúng ta có     
;
 ; Ordinal, nhưng những gì 
chúng ta muốn là EntryPoint of code of API, vậy  
;
 ; chúng ta nhân ordinal (nó 
nắm giử vị trí 
mà EntryPoint of hàm API chúng  ;
 ; ta 
cần trong 
Address Table) cho 4, đó là DWORD size, và chúng  ta 
có     ;
 ; một giá trị RVA, liên quan đến AddressTable 
RVA, vậy chúng ta chuẩn      ;
 ; hóa nó, và bây giờ chúng ta có trong EAX con 
trỏ trỏ 
đến giá trị 
của     ;
 ; EntryPoint of hàm API trong Address Table. 
Chúng ta put EAX trong ESI, và;
 ; chúng ta có giá trị EAX 
trỏ đến . Vậy 
chúng ta có trong EAX              ;
 ; EntryPoint RVA of hàm API mong muốn. Heh, 
ở 
đây chúng ta 
cần 
phải làm    ;
 ; bây giờ là chuẩn hóa (normalize) address với 
KERNEL32's image base, và   
;
 ; cuối 
cùng, ta 
đã thực hiện 
xong, chúng ta có API 
address 
gốc 
trong 
EAX!! 
;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 GetAPI         
endp
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 GetAPIs        
proc
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Ok, đây là code cho việc lấy ALL hàm APIs 
mong muốn bởi dùng procedure   
;
 ; trước đó. Các tham số của nó là :                                        
;
 ;                                                  
                        ;
 ; INPUT   
ESI : Pointer trỏ 
đến tên hàm API (ASCIIz) 
mong muốn đầu tiên   ;
 ;         
EDI : Pointer trỏ 
đến biến lưu giữ hàm API mong 
muốn đầu tiên   ;
 ; OUTPUT  
Nothing.                                             
           ;
 ;                                                                          
;
 ; Well, structure tôi xem như get all các giá 
trị này là như sau:          
;
 ; one:                                                                     
;
 ;       
                                                                   ;
 ; ESI points to ÄÄ db         "FindFirstFileA",0                          
;
 ;                   db         "FindNextFileA",0                           
;
 ;                   db         "CloseHandle",0                             
;
 ;                   [...]                                                  
;
 ;                   db         0BBh ; Marks the end of this 
array          ;
 ;                                                    
                      ;
 ; EDI points to ÄÄ dd         00000000h ; address 
tương lai of 
FFFA       ;
 ;                   dd         00000000h ; address tương 
lai of 
FNFA       ;
 ;                   dd         00000000h ; address tương 
lai of CH         ;
 ;                   [...]                                                  
;
 ;                                                                          
;
 ; Tôi hy vọng bạn đủ thông minh và bạn nắm bắt 
được nó                     ;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 @@1:   
push    esi
        push    
edi
        call    
GetAPI
        pop     
edi
        pop     
esi
        stosd
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Chúng ta push các giá trị mà chúng ta xử 
lý 
trong procedure 
này,để tránh 
;
 ; thay đổi chúng, và chúng ta call thủ tục 
GetAPI. Chúng ta giả sử tại 
đây ;
 ; trỏ đến ESI như một ptr 
trỏ đến tên 
hàm API mong muốn, và EDI như con    
;
 ; trỏ trỏ 
đến biến mà sẽ 
xử lý tên 
hàm API. Khi hàm return cho chúng ta API;
 ; offset trong EAX, chúng ta save nó trong 
biến chịu trách nhiệm cho 
nó    ;
 ; 
được 
trỏ 
đến bởi EDI bằng 
lệnh 
STOSD đơn                                 
;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 @@2:   
cmp     byte ptr 
[esi],0
        jz      
@@3
        inc     
esi
        jmp     
@@2
 @@3:   
cmp     byte ptr 
[esi+1],0BBh
        jz      
@@4
        inc     
esi
        jmp     
@@1
 @@4:   
ret
 GetAPIs        
endp
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
 ; Có thể tối 
ưu hóa thêm code trên, tôi biết thế, nhưng chủ 
yếu ở 
đây là   ;
 ; để làm công việc giải thích cho tôi.                                     ;
 ; Well, những gì tôi làm trước tiên trong code 
trên là hướng đến cuối của  
;
 ; string of những gì chúng ta đã hỏi addr 
trước đó, và bây giờ nó trỏ đến  
;
 ; API kế tiếp.                                                         
    ;
 ; Nhưng chúng ta muốn biết có phải là API 
sau cùng ko, vì vậy chúng ta     
;
 ; check bytes đánh dấu của chúng ta, đó 
là 0BBh (đoán xem tại sao là 0BBh).;
 ; Nếu OK, chúng ta đã lấy tất cả các APIs 
cần thiết, và nếu ko, chúng ta   ; 
 ; tiếp tục công việc search của chúng ta.                                  ;
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú;
;ÄÄÄ[ 
CUT HERE ]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Heh, tôi đã cố 
gắng viết thủ 
tục này thật 
dễ hiểu, và tôi chú thích rất nhiều 
với mong muốn rằng bạn sẽ hiểu nội dung mà ko copy. Và nếu bạn copy cũng ko phải 
là vấn đề của tôi....hehe, tôi ko cho điều đó là xấu hổ. Nhưng , bây giờ câu hỏi 
đặt 
ra là 
các 
hàm APIs nào mà 
chúng ta cần 
search, và phần 
chính này sẽ 
dựa trên cách 
ta 
đã đạt được 
trước đó bởi thao tác bằng tay trên 
PE. Tôi 
sẽ chỉ cho bạn phiên bản hành động trực tiếp của một virus dùng kỷ thuật file 
mapping (dễ hơn thao tác và nhanh hơn cách infection) , tôi sẽ trình bày cho bạn 
các hàm APIs nào mà bạn cần dùng. 
2.Bài 
tut của By 
LethalMind/29A
Û 
Now let's deal with export table
    Tôi ko dạy bạn ở đây cấu trúc của export 
table. Điều đó có thể tìm thấy trong hầu hết mọi zine ngày nay. Nhưng tôi sẽ chỉ 
cho bạn như thế nào tôi dùng nó để get hàm API bằng checksum. Một checksum là 
cái quái gì vậy?. Một checksum là một số được tính tóan từ một số lớn hơn 
nó, để 
đại diện số lớn hơn đó, nó là một lọai 
fingerprint. Ví dụ, chúng ta hảy lấy 1234. Chúng ta có thể sinh ra một checksum 
bởi cộng tất cả các số của nó giữa chúng. Vì vậy checksum của 1234 là 1+2+3+4 = 
10. Bây giờ, nếu chúng ta thấy 1234, và chúng ta có checksum của nó, chúng ta có 
thể nói rằng của hai phù hợp với nhau. Nhưng nếu 1235, 1+2+3+5= 11 và checksum 
sẽ ko phù hợp. Checksum cho chúng ta một cách để chỉ ra vài thứ mà ko biết nó 
hòan tòan. Đó là cách rất thường dùng, bởi vì để 
lấy một 
hàm API, chúng 
ta cần hàm API đó ,chúng ta cần phải chứa name rất dài của chúng trong 1 biến, 
và điều đó sẽ gia tăng kích thước các viruses của chúng ta, và các string này 
cũng rất đáng nghi ngờ cho các reverser. Tôi đã chọn làm 
một 32-bit 
checksum bởi vì nó bảo đảm hơn và ko mất nhiều space, nhưng tôi cũng 
có thể 
lựa 
chọn lọai 
checksum khác 
lớn hơn hay nhỏ 
hơn 32-bits,nếu lớn hơn, 
bạn sẽ có xác xuất giảm thiểu chỉ định sai 
lệnh một 
tên 
hàm API. Bây 
giờ, tôi nghĩ một snippet of code sẽ là tiện dụng hơn là lời nói. Routine này đã 
quá tối ưu hóa rồi, nhưng tôi nghĩ rằng nó được chú thích tốt hơn là tối ưu hóa 
tốt hơn, và hey, đó là phần chính trong tut này :)
8<------------------------------ ---------------------------------="" e="" i="" n="" o:p="" p="" s="" t="">------------------------------>
      mov      
eax,ecx                        ; 
EAX = KernelBase
      mov      
ebx,eax                        ; 
EBX = KernelBase
      add      
eax,[eax.MZ_lfanew]            ; 
Get address of PE header
      mov      
esi,ebx                        ; 
Get address of Export
                                              ; 
directory
      add      
esi,[eax.NT_OptionalHeader             
\
                                .OH_DirectoryEntries  \
                                .DE_Export            \
                                
.DD_VirtualAddress]
      mov      
edx,ebx                         ; 
Get address of exported
      add      
edx,[esi.ED_AddressOfNames]     ; 
API names
      mov      
ecx,[esi.ED_NumberOfNames]      ; 
Get number of exported
      xor      
eax,eax                         ; 
API names
      lea      
edi,WORD PTR [ebp+BeginAPIList] ; Point to beginning of 
list
      mov      
[SESI+ebp],esi                  ; 
Save some regs
      mov      
[SEAX+ebp],eax                  
;
      mov      
[SECX+ebp],ecx                  
;
      mov      
[SEBX+ebp],ebx                  
;
      mov      
[SEDX+ebp],edx                  
;
Search_for_API_name:
      mov      
esi,ebx                         ; 
Get address of next exported
      add      
esi,[edx+eax*4]                 ; 
API name
Next_API_name:
      pusha
      xor      
edx,edx
      mov      
edx,DWORD PTR[edi]     ; Take the 
checksum
      add      
edi,4                  ; Point To 
Next
      test     
edx,edx                ; Is this 
checksum 0 ?
      jz       
EndAPIRetrieving       ; Yeah, 
we're done with them
LoopChsksm:
      xor      
eax,eax
      lodsb                           ; Take a char (ie : 
"X")
      shl      
ax,8                   ; move it 
left (ie : "X_")
      sub      
edx,eax                ; Substract 
that from checksum
      cmp      
ax,0                   ; Is the 
char 0 ?
      jz       
LoopConti              ; Yes, 
check if checksum too
      xor      
ax,ax                  
;
      lodsb                           ; Load another char 
(ie : "Y")
      sub      
edx,eax                ; Substract 
that from checksum
      cmp      
ax,0                   ; Is the 
char 0 ?
      jnz      
LoopChsksm             ; No, 
Continue looping
LoopConti:
      test     
edx,edx                ; Have we 
zeroed our checksum ?
      jz       
FoundAPI               ; YES, we 
have found the right API
      popa                            
;
      inc      
eax                    ; Nope, 
next name....
      loop     
Search_for_API_name
FoundAPI:
      popa
      mov      
esi,[SESI+ebp]
      mov      
edx,ebx                ; Get 
address of exp. API ordinal
      add      
edx,[esi.ED_AddressOfOrdinals]
      movzx    
eax,word ptr [edx+eax*2]  ; Get 
index into exp.API functions
Check_Index:
      mov      
edx,ebx                ; Get 
address of exported API function
      add      
edx,[esi.ED_AddressOfFunctions]
      add      
ebx,[edx+eax*4]        ; Get address of requested API 
function
      mov      
eax,ebx                
;
End_GetProcAddressET:
      add      
edi,4
      stosd                           ; Save API's adress 
into our array
      mov      
eax,[SEAX+ebp]         ; Restore 
some regs
      mov      
ebx,[SEBX+ebp]         
;
      mov      
ecx,[SECX+ebp]         
;
      mov      
edx,[SEDX+ebp]         
;
      jmp      
Search_for_API_name    ; Next 
Name
SESI      dd      
0
SEAX      dd      
0
SECX      dd      
0
SEBX      dd      
0
SEDX      dd      
0
BeginAPIList:
sCloseHandle            dd    
'Cl'+'os'+'eH'+'an'+'dl'+'e'*100h
aCloseHandle            dd    0
sCreateFileA            dd    
'Cr'+'ea'+'te'+'Fi'+'le'+'A'*100h
aCreateFileA            dd    0
sCreateFileMappingA     dd    'Cr'+'ea'+'te'+'Fi'+'le'+'Ma'+'pp'+'in'+'gA'
aCreateFileMappingA     dd    
0
                        dd    0
8<---------------------------e -----------------------------="" -="" d="" e="" i="" n="" o:p="" p="" s="" t="">---------------------------e>
3.Ví 
dụ coding trong MASM32
Tôi 
code lại 1 chương trình ví dụ hiển thị 1 hộp thông báo “All are Oki!”, 
chỉ đơn giản vậy thôi. Nhưng đặc điểm của đọan code này là ko sử dụng 
các file .inc để hổ trợ biên dịch. Đồng thời nó hòan tòan độc lập, 
ko ảnh hưởng đến offset của đọan code. Như đã nói trong tut 2 trong lọat 
tut này, tôi sử dụng 2 hàm LoadLibraryA và GetProcAddress để lấy base 
của user32.dll và offset của hàm MessageBoxA với mục đích hiển thị hộp 
thông báo. Và cuối cùng tôi muốn nhắc các bạn, trong thủ tục GetAPIs, 
các bạn chú ý, nếu một trong các tên hàm cần tìm ko chính xác thì 
chương trình sẽ bị crash. Bạn hảy thử thay đổi chuổi “LoadLibraryA” 
thành “LoadLibrary” thì các bạn sẽ thấy chương trình crash ngay. 
| 
.586 
.model  flat, stdcall 
option 
casemap:none 
.code 
start: 
jmp 
indep_start 
incode 
segment 
;============================================================================== 
indep_start: 
call 
Delta 
Delta: 
pop 
ebp 
sub ebp,offset 
Delta 
;--------------------------------------------------------------- 
;Get base 
Kernel 
;--------------------------------------------------------------- 
assume fs:nothing                        
find_kernel32: 
    push esi 
    xor eax,eax 
    mov eax,fs:[eax+30h] 
    test eax,eax 
    js find_kernel32_9x 
find_kernel32_nt: 
    mov eax,[eax+0ch] 
    mov eax,[eax+1ch] 
    mov eax,[eax] 
    mov eax,[eax+08h] 
    jmp 
find_kernel32_finished 
find_kernel32_9x: 
    mov eax,[eax+34h] 
    lea eax,[eax+7ch] 
    mov eax,[eax+3ch] 
find_kernel32_finished: 
    pop esi 
mov [ebp+kernel],eax          ; save base kernel into kernel 
var    
;------------------------------------------------------------------ 
;Get required 
APIs 
;------------------------------------------------------------------ 
        lea     
edi,[ebp+@@Offsetz] 
        lea     
esi,[ebp+@@Namez] 
        call    
GetAPIs                         ; 
Retrieve all APIs 
;------------------------------------------------------------------ 
; Main Code 
;------------------------------------------------------------------ 
        lea   
  esi,[ebp+szUser32dll] 
        push    
esi 
        call    [ebp+_LoadLibrary] 
        lea     esi,[ebp+szMessageBoxA] 
        push    
esi 
        push    
eax         
        call    [ebp+_GetProcAddress] 
        mov     [ebp+_MessageBoxA],eax 
        push    
00h                     ; Sytle of 
MessageBox 
        lea     esi,[ebp+szTitle1] 
        push    
esi                     ; Title of 
MessageBox 
        
lea     esi,[ebp+szMessage] 
        push    
esi                     ; The 
message itself 
        push    
00h                     ; Handle 
of owner 
        call   
 [ebp+_MessageBoxA]      ; The API call 
itself 
        push    
00h 
        call    
[ebp+_ExitProcess]  ; End 
program       
;------------------------------------------------------------ 
; Procedures GetAPI and 
GetAPIs 
;------------------------------------------------------------ 
GetAPI         proc 
        mov     
edx,esi                         ; 
Save ptr to name 
 @_1:   
cmp     byte ptr [esi],0                ; Null-terminated 
char? 
        jz      
@_2                             ; Yeah, we got 
it. 
        inc     
esi                             ; 
Nopes, continue searching 
        jmp     
@_1                             ; 
bloooopz... 
 @_2:   
inc     esi                             ; heh, don't 
forget this ;) 
        sub  
   esi,edx                         ; ESI = API Name 
size 
        mov     
ecx,esi                         ; 
ECX = ESI = size of name  
        xor     
eax,eax                         ; 
EAX = 0 
        mov     
word ptr [ebp+Counter],ax       ; 
Counter set to 0 
        mov     
esi,[ebp+kernel]                ; 
Get kernel's PE head. offset 
        add     
esi,3Ch 
        lodsw                                   ; in 
AX 
        add     
eax,[ebp+kernel]                ; 
Normalize it 
        mov     
esi,[eax+78h]                   ; 
Get Export Table RVA 
        add     
esi,[ebp+kernel]                ; 
Ptr to Address Table RVA 
        add     
esi,1Ch 
        lodsd                                   ; EAX = 
Address Table RVA 
        add     
eax,[ebp+kernel]                ; Normalize 
        mov     
dword ptr [ebp+AddressTableVA],eax ; Store it in VA 
form 
        lodsd                                   ; EAX = Name 
Ptrz Table RVA 
        add     
eax,[ebp+kernel]                ; 
Normalize 
        push    
eax                             ; mov 
[ebp+NameTableVA],eax 
        lodsd                                   ; EAX = 
Ordinal Table RVA 
        add     
eax,[ebp+kernel]                ; 
Normalize 
        mov     
dword ptr [ebp+OrdinalTableVA],eax ; Store in VA 
form 
        pop     
esi                             ; 
ESI = Name Ptrz Table VA 
 @_3:   
push    esi                             ; Save ESI for l8r 
restore 
        lodsd                                   ; Get value 
ptr ESI in EAX 
        add     
eax,[ebp+kernel]                ; 
Normalize 
        mov     
esi,eax                         ; 
ESI = VA of API name 
        mov     
edi,edx                         ; 
EDI = ptr to wanted API 
        push    
ecx                             ; 
ECX = API size 
        cld                                     ; Clear 
direction flag      
 
        repe    
cmpsb                           ; 
Compare both API names 
        pop     
ecx                             ; 
Restore ECX 
        jz      
@_4                             ; 
Jump if APIs are 100% equal 
        pop     
esi                             ; 
Restore ESI 
        add     
esi,4                           ; 
And get next value of array 
        inc     
word ptr [ebp+Counter]          ; 
Increase counter 
        jmp     
@_3                             ; Loop 
again 
 @_4:   
pop     esi                             ; Avoid shit in 
stack 
        movzx   
eax,word ptr [ebp+Counter]      ; 
Get in AX the counter 
        shl     
eax,1                           ; 
EAX = AX * 2 
        add     
eax,dword ptr [ebp+OrdinalTableVA] ; Normalize  
        xor     
esi,esi                         ; 
Clear ESI 
        xchg    
eax,esi                         ; 
EAX = 0, ESI = ptr to Ord 
        lodsw                                   ; Get 
Ordinal in AX 
        shl     
eax,2                           ; 
EAX = AX * 4 
        add     
eax,dword ptr [ebp+AddressTableVA] ; Normalize 
        mov     
esi,eax                         ; 
ESI = ptr to Address RVA 
        lodsd                                   ; EAX = 
Address RVA 
        add     
eax,[ebp+kernel]                ; 
Normalize and all is done. 
        ret 
 GetAPI         
endp 
 ;-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú-ú; 
 GetAPIs        
proc 
 @@1:   
push    esi 
        push    
edi 
        call    
GetAPI 
        pop     
edi 
        pop     
esi 
        stosd 
 @@2:   
cmp     byte ptr 
[esi],0 
        jz      
@@3 
        inc     
esi 
        jmp     
@@2 
 @@3:   
cmp     byte ptr 
[esi+1],0BBh 
        jz      
@@4 
        inc     
esi 
        jmp     
@@1 
 @@4:   
ret 
 GetAPIs        
endp 
;------------------------------------------------------------------ 
;Data for independent code 
;------------------------------------------------------------------ 
kernel                 dd      00000000h 
Counter                 dd      00000000h 
AddressTableVA          dd      00000000h 
NameTableVA             dd      00000000h 
OrdinalTableVA          dd      00000000h 
@@Namez                 label   byte 
@GetProcAddress         db  
    "GetProcAddress",0 
@LoadLibrary            db      "LoadLibraryA",0 
@ExitProcess            db      "ExitProcess",0 
                        db      0BBh 
@@Offsetz               label   byte 
_GetProcAddress         dd      00000000h 
_LoadLibrary            dd      00000000h 
_ExitProcess            dd      00000000h 
 szUser32dll            db      "user32.dll",0 
szMessageBoxA           db      "MessageBoxA",0 
_MessageBoxA            dd      00000000h 
szTitle                 db      "A 
Example Get 
APIs",0 
szMessage               db      "All are Oki!",0 
;=============================================================== 
incode 
ends 
end     start | 
Các 
bạn hảy tự thực hành viết lại đọan code độc lập với tính năng như 
trên nhưng dùng thủ tục Get APIs trong bài tut của LethalMind nhé. Coi như 
là 1 bài tập.
-----------------------------------------------------------------------------------
Benina 
05/04/2006
Update 
14/05/2006
Mail: 
benina.rea@gmail.com
(Không 
đồng ý bất kỳ ai sử dụng tài liệu này cho mục đích thương mại nếu 
ko được phép của người dịch)
Đăng lại 2017
Subscribe to:
Comments (Atom)