1. 程式人生 > >C庫字串反彙編分析

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 &lt;stdio.h&gt; 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 語言獨有的概念,