找了个C语言100列 用来练习OD还原C语言代码 至于C+的 以后也会写

今天的课件下载 链接: http://pan.baidu.com/s/1nvApltJ 密码: sgur

我们先来看第一个程序,我们先分析Debug的 Debug为了方便调试,代码都是一对一的翻译 没做什么优化,还原起来比较好上手,

先用OD载入,我们看到一个GetCommandLineA 我们知道 C语言真正的入口并不是main 而在main之前 会调用GetCommandLineA ,也就是说,我们往下面翻一下 可能会找到main函数,我们往下翻一下

这段代码很可疑,可能传递了3个参数,前面的ecx edx也可能是参数,所以我说可能传递了3个, 然后 call下面 add esp,0xc 表示这个函数是外平栈,那么ecx edx应该不是参数,这个call很可能是main 我们进去看看 是个jmp 再跟过去 打个断点 

看到如下的代码:

我鼠标选中的(紫色背景)的代码 是debug版本初始化栈用的,先不管他,看箭头处 这里有个call push进去的参数是”\n” 我们看看这个call是什么,选中这行 按回车键跟过去

看这代码 应该是C语言的printf函数没错了,我们加个标签,这样再调用这个函数的时候就能看出来是他了,我们选中这行代码的段首 然后 右键 –标签 或者按快捷键 : 在弹出来的对话框中输入 printf 如下图

这样 以后调用printf的地方就会显示出来了,看下面的效果

OK ~

我们看看Call的下面的代码

代码有点多,先不急,我们先简单的浏览下 我们知道 ebp+4是函数返回地址 ebp+8 +c ….是函数的参数,而ebp-4 -8 ……是函数的局部变量,简单的看一下,这一段代码里面 到retn之前有3个ebp- 也就是有3个参数咯~ 我们来简单规定下 ebp-4 我们叫他i -8 叫j -c叫k

00401035  |.  C745 FC 01000>mov dword ptr ss:[ebp-0x4],0x1
0040103C  |.  EB 09         jmp short Test1_Dd.00401047
0040103E  |>  8B45 FC       /mov eax,dword ptr ss:[ebp-0x4]
00401041  |.  83C0 01       |add eax,0x1
00401044  |.  8945 FC       |mov dword ptr ss:[ebp-0x4],eax          ;  kernel32.BaseThreadInitThunk
00401047  |>  837D FC 05     cmp dword ptr ss:[ebp-0x4],0x5
0040104B  |. /7D 67         |jge short Test1_Dd.004010B4


我们先看下这一段,先给i赋值1 然后立马就跳了,跳到和5比较的地方了,然后判断 如果大于5 又跳了,我们看看他跳过去的地方的代码

004010B4  |> \33C0          xor eax,eax                              ;  kernel32.BaseThreadInitThunk
004010B6  |.  5F            pop edi                                  ;  kernel32.7578336A
004010B7  |.  5E            pop esi                                  ;  kernel32.7578336A
004010B8  |.  5B            pop ebx                                  ;  kernel32.7578336A
004010B9  |.  83C4 4C       add esp,0x4C

也就是说 大于5 就结束了 如果小于5的话 就执行

0040104D  |.  C745 F8 01000>|mov dword ptr ss:[ebp-0x8],0x1
00401054  |.  EB 09         |jmp short Test1_Dd.0040105F
00401056  |>  8B4D F8       |/mov ecx,dword ptr ss:[ebp-0x8]         ;  kernel32.7578336A
00401059  |.  83C1 01       ||add ecx,0x1
0040105C  |.  894D F8       ||mov dword ptr ss:[ebp-0x8],ecx
0040105F  |>  837D F8 05    | cmp dword ptr ss:[ebp-0x8],0x5  
00401063  |. /7D 4D         ||jge short Test1_Dd.004010B2  

也就说 如果小于5的话 就执行j=1 然后判断j是不是大于5 如果大于5的话 就跳转 看看跳到哪里去了

004010B2  |>^\EB 8A         \jmp short Test1_Dd.0040103E

调到一个jmp 我们再看看jmp跳过去的地方的代码

0040103E  |> /8B45 FC       /mov eax,dword ptr ss:[ebp-0x4]
00401041  |. |83C0 01       |add eax,0x1
00401044  |. |8945 FC       |mov dword ptr ss:[ebp-0x4],eax          ;  kernel32.BaseThreadInitThunk
00401047  |> |837D FC 05     cmp dword ptr ss:[ebp-0x4],0x5
0040104B  |. |7D 67         |jge short Test1_Dd.004010B4

是不是有点眼熟,刚才判断i小于5的地方么?而且在判断之前还对i+1了

那么我们就能确定了 最起码 j外面有个for循环 代码应该是这样

for (i=1;i<5;i++)
{
    j = 1;
    if(j<5)
    {
    }
}

我们再看看 刚刚判断j大于5的那里 如果小于5会怎么样

00401065  |.  C745 F4 01000>||mov dword ptr ss:[ebp-0xC],0x1
0040106C  |.  EB 09         ||jmp short Test1_Dd.00401077
0040106E  |>  8B55 F4       ||/mov edx,dword ptr ss:[ebp-0xC]
00401071  |.  83C2 01       |||add edx,0x1
00401074  |.  8955 F4       |||mov dword ptr ss:[ebp-0xC],edx        ;  Test1_Dd.<ModuleEntryPoint>
00401077  |>  837D F4 05    || cmp dword ptr ss:[ebp-0xC],0x5
0040107B  |.  7D 33         |||jge short Test1_Dd.004010B0

嗯?如果j<小于5就给K赋值1 然后立马jmp到比较处 判断k是不是小于5 如果不小于就跳过去 老规矩 我们看看 跳过去会怎么样

004010B0  |>^\EB A4         |\jmp short Test1_Dd.00401056

又是一个jmp 看看跳过去的地址,我们分析分析

00401056  |> /8B4D F8       |/mov ecx,dword ptr ss:[ebp-0x8]         ;  kernel32.7578336A
00401059  |. |83C1 01       ||add ecx,0x1
0040105C  |. |894D F8       ||mov dword ptr ss:[ebp-0x8],ecx
0040105F  |> |837D F8 05    | cmp dword ptr ss:[ebp-0x8],0x5
00401063  |. |7D 4D         ||jge short Test1_Dd.004010B2

这明显是比较j小于5的代码 而且是调到j+1的地方

那么 我们的更新刚才还原出来的代码了

应该是这样

for (i=1;i<5;i++)
{
    for (j=1;j<5;j++)
    {
        k=1;
        if(k<5)
        {}
    }
}

好,我们在看看 k如果不跳会怎么样

0040107D  |.  8B45 FC       |||mov eax,dword ptr ss:[ebp-0x4]
00401080  |.  3B45 F4       |||cmp eax,dword ptr ss:[ebp-0xC]
00401083  |.  74 29         |||je short Test1_Dd.004010AE
00401085  |.  8B4D FC       |||mov ecx,dword ptr ss:[ebp-0x4]
00401088  |.  3B4D F8       |||cmp ecx,dword ptr ss:[ebp-0x8]        ;  kernel32.7578336A
0040108B  |.  74 21         |||je short Test1_Dd.004010AE
0040108D  |.  8B55 F8       |||mov edx,dword ptr ss:[ebp-0x8]        ;  kernel32.7578336A
00401090  |.  3B55 F4       |||cmp edx,dword ptr ss:[ebp-0xC]
00401093  |.  74 19         |||je short Test1_Dd.004010AE
00401095  |.  8B45 F4       |||mov eax,dword ptr ss:[ebp-0xC]
00401098  |.  50            |||push eax                              ;  kernel32.BaseThreadInitThunk
00401099  |.  8B4D F8       |||mov ecx,dword ptr ss:[ebp-0x8]        ;  kernel32.7578336A
0040109C  |.  51            |||push ecx
0040109D  |.  8B55 FC       |||mov edx,dword ptr ss:[ebp-0x4]
004010A0  |.  52            |||push edx                              ;  Test1_Dd.<ModuleEntryPoint>
004010A1  |.  68 1C204200   |||push Test1_Dd.0042201C                ;  ASCII "%d,%d,%d\n"
004010A6  |.  E8 55000000   |||call <Test1_Dd.prinf>
004010AB  |.  83C4 10       |||add esp,0x10

好吧~k如果不跳 执行的代码有点小多。。。先不管,我们看到k里面有3个跳转,跳转的目标地址都一样 我们看看跳过去会怎么样

004010AE  |>^\EB BE         ||\jmp short Test1_Dd.0040106E
跳过去又是一个jmp 难道k也是循环?我们过去看看

0040106E  |> /8B55 F4       ||/mov edx,dword ptr ss:[ebp-0xC]
00401071  |. |83C2 01       |||add edx,0x1
00401074  |. |8955 F4       |||mov dword ptr ss:[ebp-0xC],edx        ;  Test1_Dd.<ModuleEntryPoint>
00401077  |> |837D F4 05    || cmp dword ptr ss:[ebp-0xC],0x5
0040107B  |. |7D 33         |||jge short Test1_Dd.004010B0

果然,k也是循环 那么 我们目前还原出来的代码应该是这样

int i,j,k;
for(i=1;i<5;i++)
{
	for(j=1;j<5;j++)
	{
		for(k=1;k<5;k++)
		{
		}
	}
}

好了 接着 我们来看看k这个循环里面的代码

0040107D  |.  8B45 FC       |||mov eax,dword ptr ss:[ebp-0x4]
00401080  |.  3B45 F4       |||cmp eax,dword ptr ss:[ebp-0xC]        ;  Test1_Dd.00404270
00401083  |.  74 29         |||je short Test1_Dd.004010AE
00401085  |.  8B4D FC       |||mov ecx,dword ptr ss:[ebp-0x4]
00401088  |.  3B4D F8       |||cmp ecx,dword ptr ss:[ebp-0x8]        ;  Test1_Dd.00422138
0040108B  |.  74 21         |||je short Test1_Dd.004010AE
0040108D  |.  8B55 F8       |||mov edx,dword ptr ss:[ebp-0x8]        ;  Test1_Dd.00422138
00401090  |.  3B55 F4       |||cmp edx,dword ptr ss:[ebp-0xC]        ;  Test1_Dd.00404270
00401093  |.  74 19         |||je short Test1_Dd.004010AE
00401095  |.  8B45 F4       |||mov eax,dword ptr ss:[ebp-0xC]        ;  Test1_Dd.00404270
00401098  |.  50            |||push eax
00401099  |.  8B4D F8       |||mov ecx,dword ptr ss:[ebp-0x8]        ;  Test1_Dd.00422138
0040109C  |.  51            |||push ecx
0040109D  |.  8B55 FC       |||mov edx,dword ptr ss:[ebp-0x4]
004010A0  |.  52            |||push edx
004010A1  |.  68 1C204200   |||push Test1_Dd.0042201C                ;  ASCII "%d,%d,%d\n"
004010A6  |.  E8 55000000   |||call <Test1_Dd.printf>
004010AB  |.  83C4 10       |||add esp,0x10

看到这么多代码 先不要慌,我们先 一句一句来读 ,下面的代码跟上面一样 但是加了注释

0040107D  |.  8B45 FC       |||mov eax,dword ptr ss:[ebp-0x4]        ;  把i放到eax
00401080  |.  3B45 F4       |||cmp eax,dword ptr ss:[ebp-0xC]        ;  比较i和k
00401083  |.  74 29         |||je short Test1_Dd.004010AE            ;  如果相等就调到4010ae
00401085  |.  8B4D FC       |||mov ecx,dword ptr ss:[ebp-0x4]        ;  取出i 放到ecx
00401088  |.  3B4D F8       |||cmp ecx,dword ptr ss:[ebp-0x8]        ;  比较i和k
0040108B  |.  74 21         |||je short Test1_Dd.004010AE            ;  如果相等就跳到4010ae
0040108D  |.  8B55 F8       |||mov edx,dword ptr ss:[ebp-0x8]        ;  取出j
00401090  |.  3B55 F4       |||cmp edx,dword ptr ss:[ebp-0xC]        ;  比较k和j
00401093  |.  74 19         |||je short Test1_Dd.004010AE            ;  相等就跳转
00401095  |.  8B45 F4       |||mov eax,dword ptr ss:[ebp-0xC]        ;  如果i不等于k 且i不等于j 且j不等于k就会执行这里
00401098  |.  50            |||push eax                              ;  上面那里是取出k 这一句把k压入栈中
00401099  |.  8B4D F8       |||mov ecx,dword ptr ss:[ebp-0x8]        ;  取出j
0040109C  |.  51            |||push ecx                              ;  入栈
0040109D  |.  8B55 FC       |||mov edx,dword ptr ss:[ebp-0x4]        ;  取出i
004010A0  |.  52            |||push edx                              ;  入栈
004010A1  |.  68 1C204200   |||push Test1_Dd.0042201C                ;  ASCII "%d,%d,%d\n"
004010A6  |.  E8 55000000   |||call <Test1_Dd.printf>                ;  这里调用print函数

通过上面的注释可以得出一个结论,这里是判断如果 i j k互不相等 就输出 也就是说 能还原出如下代码

int i,j,k;
for(i=1;i<5;i++)
{
	for(j=1;j<5;j++)
	{
		for(k=1;k<5;k++)
		{
			if(i != k)
			{
				if(i != j)
				{
					if(j != k)
					{
						printf("%d,%d,%d",i,j,k);
					}
				}
			}
		}
	}
}

3个if我们可以用 && 优化一下 最终的结果如下

int i,j,k;
for(i=1;i<5;i++)
{
	for(j=1;j<5;j++)
	{
		for(k=1;k<5;k++)
		{
			if(i != k && i != j && j != k)
			{
				printf("%d,%d,%d",i,j,k);
			}
		}
	}
}

打赏