找了个C语言100列 用来练习OD还原C语言代码 至于C+的 以后也会写
我们先来看第一个程序,我们先分析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.
00401077 |> 837D F4 05 || cmp dword ptr ss:[ebp-0xC],0x5
0040107B |. 7D 33 |||jge short Test1_Dd.004010B0
嗯?如果j
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.
004010A1 |. 68 1C204200 |||push Test1_Dd.0042201C ; ASCII "%d,%d,%d\n"
004010A6 |. E8 55000000 |||call
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.
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
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 ; 这里调用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);
}
}
}
}
打赏作者
微信、支付宝扫一扫,鼓励一下作者吧~