C庫字串反彙編分析
FROM:http://bbs.pediy.com/showthread.php?t=127475
1、strlen
.text:00408150 mov ecx, [esp+arg_0]
.text:00408154 test ecx, 3 ; if (str & 3) 11B檢測地址末位是0 4 8 c
.text:00408154 ; 即判斷字串的起始地址是否以4對齊 glibc中基本的對齊
.text:00408154 ; 策略是2 * sizeof(size_t)
.text:0040815A jz short main_loop ; 如果地址末尾與3與運算不為0
.text:0040815C
.text:0040815C str_misaligned: ; CODE XREF: _strlen+19j
.text:0040815C mov al, [ecx] ; 取一個字元
.text:0040815E inc ecx ; 增加串指標
.text:0040815F test al, al ; 比較al是否為0字元
.text:00408161 jz short loc_4081A3 ; 是0則跳到lea eax,[ecx-1]可見strlen()不會包括0字元長度的
.text:00408163 test ecx, 3 ;再次判斷下一個地址是否進行了對齊如果已對齊則直接跳到main_loop中
.text:00408169 jnz short str_misaligned ; 取一個字元
.text:0040816B add eax, 0
.text:00408170
.text:00408170 main_loop: ; CODE XREF: _strlen+Aj
.text:00408170 ; _strlen+36j ...
.text:00408170 mov eax, [ecx] ; 這裡判斷4位元組字串中是否有0字元
.text:00408172 mov edx, 7EFEFEFFh
.text:00408177 add edx, eax
.text:00408179 xor eax, 0FFFFFFFFh
.text:0040817C xor eax, edx
.text:0040817E add ecx, 4 ; while
.text:0040817E ; {
.text:0040817E ; int x = xxxxx;
.text:0040817E ; int y = x + 0x7efefeff;
.text:0040817E ; x ^= 0xffffffff;
.text:0040817E ; x ^= y;
.text:0040817E ; x & 0x81010100 ==0表示無0字串,相反則表示發現字串
.text:00408186 jz short main_loop
.text:00408188 mov eax, [ecx-4] ; 比較第一個字元是否為0
.text:0040818B test al, al
.text:0040818D jz short loc_4081C1
.text:0040818F test ah, ah ; 比較第二個字元是否為0
.text:00408191 jz short loc_4081B7
.text:00408193 test eax, 0FF0000h ; 比較第三個字元是否為0
.text:00408198 jz short loc_4081AD
.text:0040819A test eax, 0FF000000h ; 比較第4個字元是否為0
.text:0040819F jz short loc_4081A3
.text:004081A1 jmp short main_loop
.text:004081A3 ; ---------------------------------------------------------------------------
.text:004081A3
.text:004081A3 loc_4081A3: ; CODE XREF: _strlen+11j
.text:004081A3 ; _strlen+4Fj
.text:004081A3 lea eax, [ecx-1]
.text:004081A6 mov ecx, [esp+arg_0]
.text:004081AA sub eax, ecx
.text:004081AC retn
.text:004081AD ; ---------------------------------------------------------------------------
.text:004081AD
.text:004081AD loc_4081AD: ; CODE XREF: _strlen+48j
.text:004081AD lea eax, [ecx-2]
.text:004081B0 mov ecx, [esp+arg_0]
.text:004081B4 sub eax, ecx
.text:004081B6 retn
.text:004081B7 ; ---------------------------------------------------------------------------
.text:004081B7
.text:004081B7 loc_4081B7: ; CODE XREF: _strlen+41j
.text:004081B7 lea eax, [ecx-3] ; 得到ecx-4 的地址相減即可
.text:004081BA mov ecx, [esp+arg_0]
.text:004081BE sub eax, ecx
.text:004081C0 retn
.text:004081C1 ; ---------------------------------------------------------------------------
.text:004081C1
.text:004081C1 loc_4081C1: ; CODE XREF: _strlen+3Dj
.text:004081C1 lea eax, [ecx-4] ; 得到ecx-4的地址
.text:004081C4 mov ecx, [esp+arg_0] ; 得到字串起始地址
.text:004081C8 sub eax, ecx ; 減去地址
.text:004081CA retn ; 返回數值
.text:004081CA _strlen endp
.text:004081CA
//vc6.0中的strlen()
關於對齊的問題:
x86如果地址不對齊則影響程式執行速度,在x64中會引發一個異常,在其它的一些硬體平臺比如mips則會引發bus error
x86彙編編譯器中有兩個偽指令用於對齊even align
even 偶對齊
if (addr % 2==0) return addr; else return addr + 1;
align num //num必須是2^n次方
if ((num & (num-1))==0)
{
if (addr %num==0) return addr; else return (addr / num + 1) *num;
}
//最後逆向出來的函式如下:
int mystrlen(char *p)
{
int str = *(int *)p;
int y = 0x7efefeff;
int count = -1; //為了方便操作使用計數器,初始為-1與字元陣列的序號相對應
while (1)
{
y += str; //這裡比較複雜,vc6.0的C庫中很多地方都用到了這個判斷方式
str ^= 0xffffffff;
str ^= y;
if (str &= 0x81010100) //用於判斷整型中是否有0字元
{
if ((str>>24)==0) return count; //0號字元為0 沒有字元
else if ((str & 0x00ff0000)==0) return count+1; //1號字元為0 有1個字元
else if ((str & 0x0000ff00)==0) return count+2; //2號字元為0 有2個字元
else if ((str & 0x000000ff)==0) return count+3; //3號字元為0 有3個字元
}
else
{
(int *)p++;
count += 4;
}
}
}
//逆向掃描0x80來判斷0號位置的演算法
unsigned ZeroByte(int x)
{
int y = (x & 0x7f7f7f7f) + 0x7f7f7f7f; //1xxxxxxx
y = ~(y | x | 0x7f7f7f7f); //80xxxxxx
if (y==0) return 4; //如果y==0說明是4個字串
else if (y>0x0000ffff) return (y>>31) ^ 1; //如果y>0x0000ffff即0個或1號位為0字串,然後右移31位就是0字串的值,與1進行二進位制相加s
//如果為0表示0x80在0號位置,即最高位異或得0,如果為1則0x80在2號位置。
else return (y>>15) ^ 3; //同理
}
//直接用指標檢測
size_t mystrlen(char const *str)
{
char const *p = str;
while (*p++);
return (p - str - 1);
}
//vc6.0的release版程式中對於strlen()的呼叫進行了優化。
00401080 57 push edi ; 儲存edi原資料
00401081 BF 20304000 mov edi,api.00403020 ; edi = 字串起始地址
00401086 83C9 FF or ecx,FFFFFFFF ;設定ecx = 0xffffffff 計數器從-1開始
00401089 33C0 xor eax,eax ;eax清0
0040108B F2:AE repne scas byte ptr es:[edi] ;ecx!=0 ecx=ecx-1 scas edi與al
0040108D F7D1 not ecx ;把ecx求反得出repne操作的次數
0040108F 49 dec ecx ;ecx-1減去0號字串(因為repne先對計數器進行操作再執行scas)
這個程式碼很棒,有一種補碼的思想在裡面,任何有限重複操作都可以用補碼錶示,對於計算機中的數來說,儲存上無符號,它就是一個太極(模),超過模就減去模來表
示,邏輯上有正數和負數之分,正數為陽 負數為陰,超過模也是減去模進行操作,負數與模相加用正數表示,總的來說一句話太極 與 陰陽 合稱太極。太極為陰為常,
陰陽為陽為動態。不要把陰陽看成太極分出來的,實際太極與陰陽組成另一個太極。
//strlen函式
__strlen proc uses ebx esi edi,pstr
mov edi,pstr
cld
mov ecx,0ffffffffh
xor eax,eax
repnz scasb
not ecx
dec ecx
mov eax,ecx
ret
__strlen endp
//C++迴圈控制結構中的前++與後++問題
在for迴圈中前++與後++是相同的,但是在while迴圈中關係到條件判斷 它們還是區別的
//示例程式碼
int main()
{
int n = 3;
while (n--)
{
cout<<n<<endl;
}
/*
; CODE XREF: _main+49j
.text:0040158F mov eax, [ebp+var_4] ; eax=3
.text:00401592 mov ecx, [ebp+var_4] ; ecx=3
.text:00401595 sub ecx, 1 ; ecx=2
.text:00401598 mov [ebp+var_4], ecx ; n=2
.text:0040159B test eax, eax ; eax=3 用3作為條件判斷 (n--)格式
.text:0040159D jz short loc_4015BB ; eax!=0 zf!=1跳走
.text:0040159F push offset loc_4010C8
.text:004015A4 mov edx, [ebp+var_4]
.text:004015A7 push edx
.text:004015A8 mov ecx, offset [email protected]@@[email protected][email protected]@[email protected]@@[email protected] ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:004015AD call [email protected][email protected]@[email protected]@@[email protected]@[email protected]@Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(int)
.text:004015B2 mov ecx, eax
.text:004015B4 call [email protected][email protected]@[email protected]@@[email protected]@[email protected]@[email protected]@[email protected] ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:004015B9 jmp short loc_40158F ; 回跳的方式在c++主要是goto和迴圈結構
*/
cout<<"|-------------|"<<endl;
n = 3;
while (--n)
{
cout<<n<<endl;
}
/*
.text:004015E0 mov eax, [ebp+var_4] ; eax=3
.text:004015E3 sub eax, 1 ; eax -= 1;
.text:004015E6 mov [ebp+var_4], eax ; 變數 -= 1;
.text:004015E9 cmp [ebp+var_4], 0 ; 從2開始比較
.text:004015ED jz short loc_40160B
.text:004015EF push offset loc_4010C8
.text:004015F4 mov ecx, [ebp+var_4]
.text:004015F7 push ecx
.text:004015F8 mov ecx, offset [email protected]@@[email protected][email protected]@[email protected]@@[email protected] ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:004015FD call [email protected][email protected]@[email protected]@@[email protected]@[email protected]@Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(int)
.text:00401602 mov ecx, eax
.text:00401604 call [email protected][email protected]@[email protected]@@[email protected]@[email protected]@[email protected]@[email protected] ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:00401609 jmp short loc_4015E0 ; eax=3
*/
return 0;
}
i++;判斷i的值是否有效,然後再給i+1
++i;判斷i+1後的值
int i = 0;
while (i++)
{
//不會執行
}
while (++i)
{
//會執行
}
//關於整數表示
整數表示的方法常用的是原碼 反碼 補碼,以char 型別為例
char 1 byte ,因此有2^8 = 256 = 0x100 種表示方法,即mod = 0x100,表示為整數範圍即為 0--255
unsigned char : 0-255的範圍,對於任意int 如果超出 其最大值 255則執行 x % mod 即
if (x<0) return;
else (x>255) return x % 256;
singled char: 機器並無正負數的概念,正負數只是編碼規則中的一條,在機器碼中,人為的規定0表示正數 1表示負數。
1 111 1111 //負數中最大的值 0xff = -1
1 000 0000 //負數中最小的值 0x80 = -128
0 111 1111 //正數中最大的值 0x7f = 127
0 000 0000 //正數中最小的值 0
反碼:符號位不變 各位求反
對於任意長度 x 的整數 n求反為 n = 2^x - 1 - n
2^x //mod
2^x - 1 //max最大值
2^x - 1 - n //即為其反碼
根據上面的原理__strlen()中的not ecx,dec ecx都是一樣的
int a = 1; //mov a,1
~a; //mov a,0xffffffff -1 即為0xfffffffe
補碼:符號位不變各位求反後 + 1,對於任意長度x的整數n來說,求補即以最右側的1開始,前面的所有位求反。因此很容易求得一個數最右側的1的位置
unsigned x;
cin>>x;
bitset<32> bit;
bit = x & (-x);
cout<<bit<<endl;
for (size_t inx=0; inx!=32; ++inx)
{
if (bit.test(inx))
{
cout<<inx<<" 為最右側1的位置!"<<endl;
}
}
對於任意整數 x其補碼求得方式有多種。
x = -x = ~x + 1 = ~(x - 1) IA32中對應的彙編
neg x //求補
not x
inc x //求反+1
dec x
not x //-1 求反
//C++中的位運算
>>//減小2^n
<<//增大2^n
~//求反 同上
&//模為2的乘法,即遇0得 0
|//有1得1
xor //模為2的加法。
2、strupr
//vc6.0中的反彙編程式碼
push ebp
.text:004180F1 mov ebp, esp
.text:004180F3 sub esp, 0Ch
.text:004180F6 mov [ebp+lpDestStr], 0
.text:004180FD cmp dword ptr [email protected][email protected]@@[email protected]+90h, 0
.text:00418104 jnz short loc_41814D ; 這裡應該是一個判斷使用哪種方式LC_TYPE
.text:00418104 ; if (xxxxx)
.text:00418104 ; {
.text:00418104 ; 通過-0x20或者+0xe0來實現轉換
.text:00418104 ; }
相關推薦
C庫字串反彙編分析
FROM:http://bbs.pediy.com/showthread.php?t=127475 1、strlen .text:00408150 mov ecx, [esp+arg_0] .text:00408154
C語言三種迴圈反彙編分析
#include <stdio.h> #include <stdlib.h> int main() { 00AB13D0 push ebp 00AB13D1 mov ebp,esp 00AB13D3 sub
Android Smali反彙編分析與除錯
很多Android APK由於加密了(比如加了花指令), 無法反彙編為Java程式。此時只能檢視Smali原始碼, 用Smali原始碼進行除錯分析。 smali以及baksmali的jar包用法 baksmali下載:jar包下載地址 命令列:java -jar baksm
LPC824 ROM-bootloader反彙編分析
1 ROM-bootloader反彙編分析 在Keil中(IAR暫不能實現,其他IDE未曾嘗試,本部分內容集中在對程式碼分析上,無需對工具軟體進行過多考慮)取消Option的Debug標籤頁上的“Run to main”選項,並且在Setting中選擇Stop after
linux 反彙編分析變數地址並用gdb修改執行中的程式記憶體變數實驗
準備樣本檔案: a.c #include <stdio.h> #include <unistd.h> unsigned int a=0xFFFFFFFF; unsigned
switch語句反彙編分析
假設switch語句的分支比較少的時候(例如3,少於4的時候沒有意義)沒有必要使用此結構,相當於if(可觀察反彙編得出此結論)。 1、各個分支常量的差值較大的時候,編譯器會在效率還是記憶體進行取捨,這個時候編譯器還是會編譯成類似於if,else的結構。 2、在分支比較多的時
C庫-----字串(string)與整型(int)、浮點型(float)等之間的轉換
#include <stdlib.h> 1.int/float to string/array: C語言提供了幾個標準庫函式,可以將任意型別(整型、長整型、浮點型等)的數字轉換為字串
一個簡單程式的 gdb 反彙編 分析
《深入理解計算機》的第三章已經看了兩遍了,一直都是在看書裡的程式,一些內容都是第一次接觸,也一直沒有在真正的Linux上用過,今天寫了下發現和書裡的還是有些出入,下面是我自己的理解。 gdb除了反彙編外,還可以用於程式碼的除錯。下面就簡單記錄下容易忘記的部分。 1.gcc -
mingw32 exception在sjlj與dwarf差別-反彙編分析
sjlj (setjump/longjump)與dwarf-2為mingw32兩種異常處理模型的實現。sjlj有著開銷,而隨linux發行的mingw32開發庫包都是用sjlj版編譯的,而Qt卻採用dwarf-2版,那麼兩者之間有多少差異,本文就這問題對兩版的異常程式碼的反彙編進行分析比較。 我使用mingw
反彙編C++ OOP程式碼 分析建構函式如何被呼叫 以及簡單的C++物件記憶體模型
在今天進行C++程式碼的思考時,產生一個疑問,就是C++類的建構函式是如何被呼叫的 於是就做了一個簡單的實驗來驗證自己的想法。 //main.cpp #include <stdio.h> class People{ private: int i; i
《C++反彙編與逆向分析技術揭祕》讀書總結——建構函式與解構函式
建構函式的必要條件: 這個函式的呼叫,是這個物件在作用域內的第一次成員函式呼叫,看this指標即可以區分物件,是哪個物件的this指標就是哪個物件的成員函式。 使用thiscall呼叫方式,使用ecx傳遞this指標; 返回值為this指標。 解構函式的必要條件: 這
反彙編一個簡單的C程式,分析彙編程式碼
李峰 原創作品轉載請註明出處 實驗 原始碼: int g(int x) { return x + 4; } int f(int x) { return g(x); }
c&c++反彙編與逆向分析學習筆記(2)--反彙編靜態分析工具IDA
所謂“靜態分析”,是相對於前面提到的“動態分析”而言的。在“動態分析”過程中,偵錯程式載入程式,並以除錯模式執行起來,分析者可以在程式的執行過程中觀察程式的執行流程和計算記過。但是,在實際分析中,很多場合不方便執行目標,比如軟體的某一模組(無法單獨執行)、病
《linux核心分析》作業一:反彙編一個C語言程式並分析彙編程式碼執行過程
楊新峰原創作品轉載請註明出處 《Linux核心分析》 MOOC課程http://mooc.study.163.com/course/USTC-1000029000 實驗環境:實驗樓網站64位linux虛擬機器 原始碼如下: int g(int x){ re
《Linux作業系統分析》之使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫
本篇文章分析的是使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫,來說明在Linux系統中,系統呼叫的實現機制。 相關知識 首先關於這篇文章會介紹一些用到的知識。 一、什麼是核心態,什麼又是使用者態。 核心態:在高執行級別下,程式碼可以執行特權指令,
c/c++彙編及反彙編命令執行語句
上圖來源於中國MOOK大學南京大學袁春風老師課件,從高階語言到底層二進位制程式碼按此流程一步步進行。 我們首先可以在建立一個c檔案,如果虛擬機器與你的Windows還不可以進行檔案共享,可以開啟Terminal(同windows下的cmd。我的是在/usr/share/Terminal這個
簡單例項分析objdump反彙編用法
objdump -rdS 可可執行檔案 objdump命令是用檢視目標檔案或者可執行的目標檔案的構成的gcc工具。 1. 準備程式碼hello.c #include <linux/module.h> #include <linux/
反彙編演算法介紹和應用——線性掃描演算法分析
做過逆向的朋友應該會很熟悉IDA和Windbg這類的軟體。IDA的強項在於靜態反彙編,Windbg的強項在於動態除錯。往往將這兩款軟體結合使用會達到事半功倍的效果。可能經常玩這個的朋友會發現IDA反彙
反彙編演算法介紹和應用——遞迴下降演算法分析
上一篇博文我介紹了Windbg使用的線性掃描(linear sweep)反彙編演算法。本文我將介紹IDA使用的遞迴下降(recursive descent)反彙編演算法。(轉載請指明來源於breaksoftware的csdn部落格) &
利用反彙編手段解析C語言函式
轉自: https://blog.csdn.net/songjinshi/article/details/8450419 1、問題的提出 函式是 C語言中的重要概念。利用好函式能夠充分利用系統庫的功能寫出模組獨立、易於維護和修改的程式。函式並不是 C 語言獨有的概念,