x32dbg/x64dbg逆向之反向分析还原c语言代码3
1) 反向分析还原c语言函数代码3
还是一样哦温故而知新,复习一下学到新知识
上节课第二个自定义函数地址为:7FF69FCF1226
00007FF69FCF17B9 | E8 68FAFFFF | call project1.7FF69FCF1226 | 注意他自己调用自己了,递归???
参数在经过一系列操作会发生改变,计算公式是:0xE8(开辟空间)-0x20(修正值)+0x10(2个Push)+0x08(返回地址)=0xE0
release优化的优点变态,但是不要急,能发现规律那就是代码会变,但硬编码的字符串内容绝对不会变。
神奇计算公式,当咱们不确定某个值是多少时候,咱们可以把rax看成未知数x。所以 x + 2x = 3x。
数组特性连续,宽度一致
这段汇编代码是标准的数组取值
ss:[rbp+4]取出来,用movsxd指令把这个32位的数字符号扩展拉伸成64位的rax
00007FF6CC3A1830 | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | FileName.cpp:37, rax:__enc$textbss$end+5A,这里不是很理解?数组取值
00007FF6CC3A1834 | 48:8B8D 00010000 | mov rcx,qword ptr ss:[rbp+100] | 第二和参数确认了,这个就是第一个参数了,获取数组的首地址
最终内存地址 = 数组首地址(rcx) + 下标(rax) × 每个元素的大小(4字节)
00007FF6CC3A183B | 8B0C81 | mov ecx,dword ptr ds:[rcx+rax*4] | 真正去数组里拿值
00007FF6CC3A183E | E8 CAF9FFFF | call project1.7FF6CC3A120D | 调用咱们另一个自定义函数
咱们看下代码还原有哪些内容:
分析过程
分析函数参数
分析局部变量
分析全局变量
分析函数功能
分析返回值
最后还原c代码
这一个星期课程咱们都是把汇编给反向还原到C语言哦坚持一下,咱们从简单一点的代码然后再深,一点一点还原咱们写的各种exe应用
2)汇编代码
exe文件下载请从这里下载分别有Debug,Release两个版本。请自己反编译完成代码还原就好。
https://download.csdn.net/download/Strategic__/93160031
Debug版本 main如果找不到,请参考逆向基础的第二课“x32dbg/x64dbg逆向之找main入口方式” 00007FF6E9361BC0 | 40:55 | push rbp | FileName.cpp:73 00007FF6E9361BC2 | 57 | push rdi | 00007FF6E9361BC3 | 48:81EC C8010000 | sub rsp,1C8 | 00007FF6E9361BCA | 48:8D6C24 20 | lea rbp,qword ptr ss:[rsp+20] | 00007FF6E9361BCF | 48:8D7C24 20 | lea rdi,qword ptr ss:[rsp+20] | 00007FF6E9361BD4 | B9 3A000000 | mov ecx,3A | 3A:':' 00007FF6E9361BD9 | B8 CCCCCCCC | mov eax,CCCCCCCC | 00007FF6E9361BDE | F3:AB | rep stosd | 00007FF6E9361BE0 | 48:8B05 59B40000 | mov rax,qword ptr ds:[<__security_cookie>] | rax:__enc$textbss$end+55 00007FF6E9361BE7 | 48:33C5 | xor rax,rbp | rax:__enc$textbss$end+55 00007FF6E9361BEA | 48:8985 98010000 | mov qword ptr ss:[rbp+198],rax | rax:__enc$textbss$end+55 00007FF6E9361BF1 | 48:8D0D 10040100 | lea rcx,qword ptr ds:[<__14F49BB1_FileName@cpp>] | FileName.cpp:15732480 00007FF6E9361BF8 | E8 AAF7FFFF | call project1.7FF6E93613A7 | 00007FF6E9361BFD | 90 | nop | 上面常规操作哦 00007FF6E9361BFE | C745 08 03000000 | mov dword ptr ss:[rbp+8],3 | FileName.cpp:74,看到这个记住不是说前女友是数组,连续性,等宽 00007FF6E9361C05 | C745 0C 09000000 | mov dword ptr ss:[rbp+C],9 | 09:'\t' 00007FF6E9361C0C | C745 10 01000000 | mov dword ptr ss:[rbp+10],1 | 00007FF6E9361C13 | C745 14 08000000 | mov dword ptr ss:[rbp+14],8 | 00007FF6E9361C1A | C745 18 04000000 | mov dword ptr ss:[rbp+18],4 | 00007FF6E9361C21 | C745 1C 07000000 | mov dword ptr ss:[rbp+1C],7 | 00007FF6E9361C28 | C745 20 02000000 | mov dword ptr ss:[rbp+20],2 | 00007FF6E9361C2F | C745 24 05000000 | mov dword ptr ss:[rbp+24],5 | 00007FF6E9361C36 | C745 28 09000000 | mov dword ptr ss:[rbp+28],9 | 09:'\t' 00007FF6E9361C3D | C745 2C 06000000 | mov dword ptr ss:[rbp+2C],6 | 00007FF6E9361C44 | 4C:8D4D 64 | lea r9,qword ptr ss:[rbp+64] | FileName.cpp:77, r9:__enc$textbss$end+55,这两个不知道是什么局部变量没赋值 00007FF6E9361C48 | 4C:8D45 44 | lea r8,qword ptr ss:[rbp+44] | 那咱们就把它全部设为 0 00007FF6E9361C4C | BA 0A000000 | mov edx,A | 0A:'\n',写死的10,应该是宏定义 #define 00007FF6E9361C51 | 48:8D4D 08 | lea rcx,qword ptr ss:[rbp+8] | 获取数组的首地址 00007FF6E9361C55 | E8 07F7FFFF | call project1.7FF6E9361361 | 开始第一个自定义函数 00007FF6E9361C5A | 90 | nop | 00007FF6E9361C5B | 44:8B45 64 | mov r8d,dword ptr ss:[rbp+64] | FileName.cpp:78,打印咱们传递的两个参数 00007FF6E9361C5F | 8B55 44 | mov edx,dword ptr ss:[rbp+44] | 通过字符串咱们可以确定他们变量名,min和max 00007FF6E9361C62 | 48:8D0D D7900000 | lea rcx,qword ptr ds:[<"max=%d min=%d\n"...>] | 00007FF6E936AD40:"max=%d min=%d\n" 00007FF6E9361C69 | E8 E5F5FFFF | call project1.7FF6E9361253 | 00007FF6E9361C6E | 90 | nop | 00007FF6E9361C6F | BA 0A000000 | mov edx,A | FileName.cpp:80, 0A:'\n',宏定义 size = 10 00007FF6E9361C74 | 48:8D4D 08 | lea rcx,qword ptr ss:[rbp+8] | 数组的首地址 00007FF6E9361C78 | E8 50F4FFFF | call project1.7FF6E93610CD | 调用第二个自定义函数 00007FF6E9361C7D | 90 | nop | 00007FF6E9361C7E | 48:8D0D D3900000 | lea rcx,qword ptr ds:[<"Sorted array: "...>] | FileName.cpp:81, 00007FF6E936AD58:"Sorted array: " 00007FF6E9361C85 | E8 C9F5FFFF | call project1.7FF6E9361253 | 同过字符串可以确定是数组的排序操作 00007FF6E9361C8A | 90 | nop | 00007FF6E9361C8B | C785 84000000 00000000 | mov dword ptr ss:[rbp+84],0 | FileName.cpp:82,局部变量赋值 00007FF6E9361C95 | EB 0E | jmp project1.7FF6E9361CA5 | 直接跳转到7FF6E9361CA5 00007FF6E9361C97 | 8B85 84000000 | mov eax,dword ptr ss:[rbp+84] | i 00007FF6E9361C9D | FFC0 | inc eax | i++ 00007FF6E9361C9F | 8985 84000000 | mov dword ptr ss:[rbp+84],eax | 00007FF6E9361CA5 | 83BD 84000000 0A | cmp dword ptr ss:[rbp+84],A | 0A:'\n',可以确定这里是把sort后的数组进行打印 00007FF6E9361CAC | 7D 1A | jge project1.7FF6E9361CC8 | 大于等于跳出循环 00007FF6E9361CAE | 48:6385 84000000 | movsxd rax,dword ptr ss:[rbp+84] | FileName.cpp:83, rax:__enc$textbss$end+55,获取i的值,扩展 00007FF6E9361CB5 | 8B5485 08 | mov edx,dword ptr ss:[rbp+rax*4+8] | 最终内存地址 = 数组首地址(rbx)+下标(rax)×元素的大小+偏移 00007FF6E9361CB9 | 48:8D0D AC900000 | lea rcx,qword ptr ds:[<"%d "...>] | 00007FF6E936AD6C:"%d " 00007FF6E9361CC0 | E8 8EF5FFFF | call project1.7FF6E9361253 | printf 00007FF6E9361CC5 | 90 | nop | 00007FF6E9361CC6 | EB CF | jmp project1.7FF6E9361C97 | 00007FF6E9361CC8 | BA 0A000000 | mov edx,A | FileName.cpp:85, 0A:'\n',宏定义 10 00007FF6E9361CCD | 48:8D4D 08 | lea rcx,qword ptr ss:[rbp+8] | 获取数组首地址 00007FF6E9361CD1 | E8 A7F3FFFF | call project1.7FF6E936107D | 调用第三个自定义函数 00007FF6E9361CD6 | 8985 A4000000 | mov dword ptr ss:[rbp+A4],eax | 00007FF6E9361CDC | 48:8D0D 8D900000 | lea rcx,qword ptr ds:[<"\nDeduplicated array: "...>] | FileName.cpp:86, 00007FF6E936AD70:"\nDeduplicated array: " 00007FF6E9361CE3 | E8 6BF5FFFF | call project1.7FF6E9361253 | printf 00007FF6E9361CE8 | 90 | nop | 00007FF6E9361CE9 | C785 C4000000 00000000 | mov dword ptr ss:[rbp+C4],0 | FileName.cpp:87,下面又是一个for,同样的原理就不多聊了 00007FF6E9361CF3 | EB 0E | jmp project1.7FF6E9361D03 | 00007FF6E9361CF5 | 8B85 C4000000 | mov eax,dword ptr ss:[rbp+C4] | 00007FF6E9361CFB | FFC0 | inc eax | 00007FF6E9361CFD | 8985 C4000000 | mov dword ptr ss:[rbp+C4],eax | 00007FF6E9361D03 | 8B85 A4000000 | mov eax,dword ptr ss:[rbp+A4] | 00007FF6E9361D09 | 3985 C4000000 | cmp dword ptr ss:[rbp+C4],eax | 00007FF6E9361D0F | 7D 1A | jge project1.7FF6E9361D2B | 00007FF6E9361D11 | 48:6385 C4000000 | movsxd rax,dword ptr ss:[rbp+C4] | FileName.cpp:88, rax:__enc$textbss$end+55 00007FF6E9361D18 | 8B5485 08 | mov edx,dword ptr ss:[rbp+rax*4+8] | 00007FF6E9361D1C | 48:8D0D 49900000 | lea rcx,qword ptr ds:[<"%d "...>] | 00007FF6E936AD6C:"%d " 00007FF6E9361D23 | E8 2BF5FFFF | call project1.7FF6E9361253 | 00007FF6E9361D28 | 90 | nop | 00007FF6E9361D29 | EB CA | jmp project1.7FF6E9361CF5 | 00007FF6E9361D2B | 33C0 | xor eax,eax | FileName.cpp:90 00007FF6E9361D2D | 8BF8 | mov edi,eax | FileName.cpp:91 00007FF6E9361D2F | 48:8D4D E0 | lea rcx,qword ptr ss:[rbp-20] | 00007FF6E9361D33 | 48:8D15 C68F0000 | lea rdx,qword ptr ds:[7FF6E936AD00] | rdx:__enc$textbss$end+55 00007FF6E9361D3A | E8 25F3FFFF | call project1.7FF6E9361064 | 00007FF6E9361D3F | 8BC7 | mov eax,edi | 00007FF6E9361D41 | 48:8B8D 98010000 | mov rcx,qword ptr ss:[rbp+198] | 00007FF6E9361D48 | 48:33CD | xor rcx,rbp | 00007FF6E9361D4B | E8 32F3FFFF | call project1.7FF6E9361082 | 00007FF6E9361D50 | 48:8DA5 A8010000 | lea rsp,qword ptr ss:[rbp+1A8] | 00007FF6E9361D57 | 5F | pop rdi | 00007FF6E9361D58 | 5D | pop rbp | 00007FF6E9361D59 | C3 | ret | 发现第一个自定义函数 0x108(开辟空间)-0x20(修正值)+0x10(2个Push)+0x08(返回地址)=0x100 00007FF6E93619B0 | 4C:894C24 20 | mov qword ptr ss:[rsp+20],r9 | FileName.cpp:13, [rsp+20]:__scrt_release_startup_lock+D,获取参数临时保存 00007FF6E93619B5 | 4C:894424 18 | mov qword ptr ss:[rsp+18],r8 | 00007FF6E93619BA | 895424 10 | mov dword ptr ss:[rsp+10],edx | 00007FF6E93619BE | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | 00007FF6E93619C3 | 55 | push rbp | 00007FF6E93619C4 | 57 | push rdi | 00007FF6E93619C5 | 48:81EC 08010000 | sub rsp,108 | 00007FF6E93619CC | 48:8D6C24 20 | lea rbp,qword ptr ss:[rsp+20] | [rsp+20]:__scrt_release_startup_lock+D 00007FF6E93619D1 | 48:8D0D 30060100 | lea rcx,qword ptr ds:[<__14F49BB1_FileName@cpp>] | FileName.cpp:15732480 00007FF6E93619D8 | E8 CAF9FFFF | call project1.7FF6E93613A7 | 00007FF6E93619DD | 90 | nop | 00007FF6E93619DE | B8 04000000 | mov eax,4 | FileName.cpp:14,eax = 4 00007FF6E93619E3 | 48:6BC0 00 | imul rax,rax,0 | rax = 4 * 0 00007FF6E93619E7 | 48:8B8D 10010000 | mov rcx,qword ptr ss:[rbp+110] | 某个参数的值 00007FF6E93619EE | 48:8B95 00010000 | mov rdx,qword ptr ss:[rbp+100] | 某个参数的值arr 00007FF6E93619F5 | 8B0402 | mov eax,dword ptr ds:[rdx+rax] | 咱们现在可以确定一个东西了,数组[0] 00007FF6E93619F8 | 8901 | mov dword ptr ds:[rcx],eax | 某个参数 = 数组[0] 00007FF6E93619FA | B8 04000000 | mov eax,4 | FileName.cpp:15,下面同样操作,通过字符串咱们可以确定这里是为min,max 初始值为arr[0] 00007FF6E93619FF | 48:6BC0 00 | imul rax,rax,0 | 00007FF6E9361A03 | 48:8B8D 18010000 | mov rcx,qword ptr ss:[rbp+118] | 00007FF6E9361A0A | 48:8B95 00010000 | mov rdx,qword ptr ss:[rbp+100] | arr 00007FF6E9361A11 | 8B0402 | mov eax,dword ptr ds:[rdx+rax] | 00007FF6E9361A14 | 8901 | mov dword ptr ds:[rcx],eax | 00007FF6E9361A16 | C745 04 01000000 | mov dword ptr ss:[rbp+4],1 | FileName.cpp:16,既然是min和max比较,那肯定有个for循环,加上下面jmp可以确定了i = 1 00007FF6E9361A1D | EB 08 | jmp project1.7FF6E9361A27 | 00007FF6E9361A1F | 8B45 04 | mov eax,dword ptr ss:[rbp+4] | 00007FF6E9361A22 | FFC0 | inc eax | 00007FF6E9361A24 | 8945 04 | mov dword ptr ss:[rbp+4],eax | 00007FF6E9361A27 | 8B85 08010000 | mov eax,dword ptr ss:[rbp+108] | 咱们输入的参数,数组长度 n 00007FF6E9361A2D | 3945 04 | cmp dword ptr ss:[rbp+4],eax | 和咱们输入的参数比较 00007FF6E9361A30 | 7D 62 | jge project1.7FF6E9361A94 | 大于等于跳出循环 00007FF6E9361A32 | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | FileName.cpp:18,看到下面这的操作,需要知道这就是数组取值,获取rbp+4扩展到64 00007FF6E9361A36 | 48:8B8D 00010000 | mov rcx,qword ptr ss:[rbp+100] | arr 00007FF6E9361A3D | 48:8B95 10010000 | mov rdx,qword ptr ss:[rbp+110] | 00007FF6E9361A44 | 8B12 | mov edx,dword ptr ds:[rdx] | rdx:&"C:\\Users\\admin\\Desktop\\x64\\Debug\\Project1.exe" 00007FF6E9361A46 | 391481 | cmp dword ptr ds:[rcx+rax*4],edx | arr[i] 00007FF6E9361A49 | 7E 17 | jle project1.7FF6E9361A62 | arr[i] <= 00007FF6E9361A4B | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | FileName.cpp:19 00007FF6E9361A4F | 48:8B8D 10010000 | mov rcx,qword ptr ss:[rbp+110] | 00007FF6E9361A56 | 48:8B95 00010000 | mov rdx,qword ptr ss:[rbp+100] | 00007FF6E9361A5D | 8B0482 | mov eax,dword ptr ds:[rdx+rax*4] | arr[i] 00007FF6E9361A60 | 8901 | mov dword ptr ds:[rcx],eax | 把数组值给max 00007FF6E9361A62 | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | FileName.cpp:20,下面几乎一样 00007FF6E9361A66 | 48:8B8D 00010000 | mov rcx,qword ptr ss:[rbp+100] | 那就可以确定是两个if了,if (arr < mix)和 if (arr > max),如果成立赋值新的数据 00007FF6E9361A6D | 48:8B95 18010000 | mov rdx,qword ptr ss:[rbp+118] | 00007FF6E9361A74 | 8B12 | mov edx,dword ptr ds:[rdx] | rdx:&"C:\\Users\\admin\\Desktop\\x64\\Debug\\Project1.exe" 00007FF6E9361A76 | 391481 | cmp dword ptr ds:[rcx+rax*4],edx | 00007FF6E9361A79 | 7D 17 | jge project1.7FF6E9361A92 | 00007FF6E9361A7B | 48:6345 04 | movsxd rax,dword ptr ss:[rbp+4] | FileName.cpp:21 00007FF6E9361A7F | 48:8B8D 18010000 | mov rcx,qword ptr ss:[rbp+118] | 00007FF6E9361A86 | 48:8B95 00010000 | mov rdx,qword ptr ss:[rbp+100] | 00007FF6E9361A8D | 8B0482 | mov eax,dword ptr ds:[rdx+rax*4] | 00007FF6E9361A90 | 8901 | mov dword ptr ds:[rcx],eax | 00007FF6E9361A92 | EB 8B | jmp project1.7FF6E9361A1F | FileName.cpp:22 00007FF6E9361A94 | 48:8DA5 E8000000 | lea rsp,qword ptr ss:[rbp+E8] | FileName.cpp:23 00007FF6E9361A9B | 5F | pop rdi | 00007FF6E9361A9C | 5D | pop rbp | 00007FF6E9361A9D | C3 | ret | 发现第二个自定义函数 第二个函数咱们看名字就知道是个排序算法,至于是冒泡,快排,二叉,红黑咱们还需要进一步去确认一下 0x148(开辟空间)-0x20(修正值)+0x10(2个Push)+0x08(返回地址)=0x140 00007FF6E9361780 | 895424 10 | mov dword ptr ss:[rsp+10],edx | FileName.cpp:31,一样哦先提前保存好参数 00007FF6E9361784 | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | [rsp+08]:invoke_main+39 00007FF6E9361789 | 55 | push rbp | 00007FF6E936178A | 57 | push rdi | 00007FF6E936178B | 48:81EC 48010000 | sub rsp,148 | 开辟 0x148空间 00007FF6E9361792 | 48:8D6C24 20 | lea rbp,qword ptr ss:[rsp+20] | 00007FF6E9361797 | 48:8D0D 6A080100 | lea rcx,qword ptr ds:[<__14F49BB1_FileName@cpp>] | FileName.cpp:15732480 00007FF6E936179E | E8 04FCFFFF | call project1.7FF6E93613A7 | 00007FF6E93617A3 | 90 | nop | 00007FF6E93617A4 | C745 04 00000000 | mov dword ptr ss:[rbp+4],0 | FileName.cpp:32,三行汇编一起理解,可以去确定是循环,虽然有区别 00007FF6E93617AB | EB 08 | jmp project1.7FF6E93617B5 | 直接跳转 00007FF6E93617AD | 8B45 04 | mov eax,dword ptr ss:[rbp+4] | i = 0 00007FF6E93617B0 | FFC0 | inc eax | i++ 00007FF6E93617B2 | 8945 04 | mov dword ptr ss:[rbp+4],eax | i 00007FF6E93617B5 | 8B85 48010000 | mov eax,dword ptr ss:[rbp+148] | 某个参数 00007FF6E93617BB | FFC8 | dec eax | 某个参数 - 1 00007FF6E93617BD | 3945 04 | cmp dword ptr ss:[rbp+4],eax | 开始比较 00007FF6E93617C0 | 0F8D 97000000 | jge project1.7FF6E936185D | 大于等于 00007FF6E93617C6 | C745 24 00000000 | mov dword ptr ss:[rbp+24],0 | FileName.cpp:34,上面理解以后可以确定下面也是循环 00007FF6E93617CD | EB 08 | jmp project1.7FF6E93617D7 | 00007FF6E93617CF | 8B45 24 | mov eax,dword ptr ss:[rbp+24] | 00007FF6E93617D2 | FFC0 | inc eax | 00007FF6E93617D4 | 8945 24 | mov dword ptr ss:[rbp+24],eax | 00007FF6E93617D7 | 8B45 04 | mov eax,dword ptr ss:[rbp+4] | 00007FF6E93617DA | 8B8D 48010000 | mov ecx,dword ptr ss:[rbp+148] | 00007FF6E93617E0 | 2BC8 | sub ecx,eax | 00007FF6E93617E2 | 8BC1 | mov eax,ecx | 00007FF6E93617E4 | FFC8 | dec eax | 00007FF6E93617E6 | 3945 24 | cmp dword ptr ss:[rbp+24],eax | 00007FF6E93617E9 | 7D 6D | jge project1.7FF6E9361858 | 00007FF6E93617EB | 48:6345 24 | movsxd rax,dword ptr ss:[rbp+24] | FileName.cpp:36,符号扩展为64位 00007FF6E93617EF | 8B4D 24 | mov ecx,dword ptr ss:[rbp+24] | j 00007FF6E93617F2 | FFC1 | inc ecx | j + 1 00007FF6E93617F4 | 48:63C9 | movsxd rcx,ecx | 00007FF6E93617F7 | 48:8B95 40010000 | mov rdx,qword ptr ss:[rbp+140] | 通过下面可以确定140是arr 00007FF6E93617FE | 4C:8B85 40010000 | mov r8,qword ptr ss:[rbp+140] | 00007FF6E9361805 | 41:8B0C88 | mov ecx,dword ptr ds:[r8+rcx*4] | arr[j + 1] 00007FF6E9361809 | 390C82 | cmp dword ptr ds:[rdx+rax*4],ecx | 既然是数组可以确定了,arr[j] 和 arr[j + 1] 比较 00007FF6E936180C | 7E 45 | jle project1.7FF6E9361853 | 如果 arr[j] <= arr[j + 1],跳出内层循环 00007FF6E936180E | 48:6345 24 | movsxd rax,dword ptr ss:[rbp+24] | FileName.cpp:38,既然是两个数组比较,大概率就是交换,或者赋值操作了,rax = j 00007FF6E9361812 | 48:8B8D 40010000 | mov rcx,qword ptr ss:[rbp+140] | rcx = arr 00007FF6E9361819 | 8B0481 | mov eax,dword ptr ds:[rcx+rax*4] | eax = arr[j] 00007FF6E936181C | 8945 44 | mov dword ptr ss:[rbp+44],eax | 前面没看到这个值,那咱们想把它想成某个变量吧temp 00007FF6E936181F | 8B45 24 | mov eax,dword ptr ss:[rbp+24] | FileName.cpp:39, eax = j 00007FF6E9361822 | FFC0 | inc eax | eax = j + 1 00007FF6E9361824 | 48:98 | cdqe | 扩展为64位 (rax = j + 1) 00007FF6E9361826 | 48:634D 24 | movsxd rcx,dword ptr ss:[rbp+24] | rcx = j 00007FF6E936182A | 48:8B95 40010000 | mov rdx,qword ptr ss:[rbp+140] | arr 00007FF6E9361831 | 4C:8B85 40010000 | mov r8,qword ptr ss:[rbp+140] | 00007FF6E9361838 | 41:8B0480 | mov eax,dword ptr ds:[r8+rax*4] | arr[j + 1],通过上面一些列操作可以知道 00007FF6E936183C | 89048A | mov dword ptr ds:[rdx+rcx*4],eax | arr[j] = arr[j + 1] 00007FF6E936183F | 8B45 24 | mov eax,dword ptr ss:[rbp+24] | FileName.cpp:40 00007FF6E9361842 | FFC0 | inc eax | 00007FF6E9361844 | 48:98 | cdqe | 00007FF6E9361846 | 48:8B8D 40010000 | mov rcx,qword ptr ss:[rbp+140] | 00007FF6E936184D | 8B55 44 | mov edx,dword ptr ss:[rbp+44] | 咱们看到了临时变量。edx = temp 00007FF6E9361850 | 891481 | mov dword ptr ds:[rcx+rax*4],edx | arr[j + 1] = temp 00007FF6E9361853 | E9 77FFFFFF | jmp project1.7FF6E93617CF | FileName.cpp:42 00007FF6E9361858 | E9 50FFFFFF | jmp project1.7FF6E93617AD | FileName.cpp:43 00007FF6E936185D | 48:8DA5 28010000 | lea rsp,qword ptr ss:[rbp+128] | FileName.cpp:44 00007FF6E9361864 | 5F | pop rdi | 00007FF6E9361865 | 5D | pop rbp | 00007FF6E9361866 | C3 | ret | 发现第三个自定义函数 通过下面分析咱们可以确定了也是数组操作,具体是什么数组操作请自己还原哦 0x168(开辟空间)-0x20(修正值)+0x10(2个Push)+0x08(返回地址)=0x160 00007FF6E93618A0 | 895424 10 | mov dword ptr ss:[rsp+10],edx | FileName.cpp:53,保存好参数,防止后面操作找不到,在通过公式来获取 00007FF6E93618A4 | 48:894C24 08 | mov qword ptr ss:[rsp+8],rcx | 00007FF6E93618A9 | 55 | push rbp | 00007FF6E93618AA | 57 | push rdi | 00007FF6E93618AB | 48:81EC 68010000 | sub rsp,168 | 00007FF6E93618B2 | 48:8D6C24 20 | lea rbp,qword ptr ss:[rsp+20] | [rsp+20]:_initterm+58 00007FF6E93618B7 | 48:8D0D 4A070100 | lea rcx,qword ptr ds:[<__14F49BB1_FileName@cpp>] | FileName.cpp:15732480 00007FF6E93618BE | E8 E4FAFFFF | call project1.7FF6E93613A7 | 00007FF6E93618C3 | 90 | nop | 00007FF6E93618C4 | C745 04 01000000 | mov dword ptr ss:[rbp+4],1 | FileName.cpp:54,两个局部变量赋值为1 00007FF6E93618CB | C745 24 01000000 | mov dword ptr ss:[rbp+24],1 | FileName.cpp:55,然后下面跟着jmp和cmp可以确定第一个循环了 , 00007FF6E93618D2 | EB 08 | jmp project1.7FF6E93618DC | 00007FF6E93618D4 | 8B45 24 | mov eax,dword ptr ss:[rbp+24] | i 00007FF6E93618D7 | FFC0 | inc eax | i++ 00007FF6E93618D9 | 8945 24 | mov dword ptr ss:[rbp+24],eax | 赋值回去自增结果 00007FF6E93618DC | 8B85 68010000 | mov eax,dword ptr ss:[rbp+168] | 某个参数重新赋值给eax 00007FF6E93618E2 | 3945 24 | cmp dword ptr ss:[rbp+24],eax | 比较 00007FF6E93618E5 | 7D 7A | jge project1.7FF6E9361961 | 大于等于跳到函数结尾 00007FF6E93618E7 | C745 44 01000000 | mov dword ptr ss:[rbp+44],1 | FileName.cpp:57,又是两个局部变量和上面一样可以确定了两层循环 00007FF6E93618EE | C745 64 00000000 | mov dword ptr ss:[rbp+64],0 | FileName.cpp:58,int temp = 1 ; for (j;j;j) 00007FF6E93618F5 | EB 08 | jmp project1.7FF6E93618FF | 00007FF6E93618F7 | 8B45 64 | mov eax,dword ptr ss:[rbp+64] | j 00007FF6E93618FA | FFC0 | inc eax | j++ 00007FF6E93618FC | 8945 64 | mov dword ptr ss:[rbp+64],eax | 和上面操作一样 00007FF6E93618FF | 8B45 04 | mov eax,dword ptr ss:[rbp+4] | 00007FF6E9361902 | 3945 64 | cmp dword ptr ss:[rbp+64],eax | 00007FF6E9361905 | 7D 2A | jge project1.7FF6E9361931 | 00007FF6E9361907 | 48:6345 24 | movsxd rax,dword ptr ss:[rbp+24] | FileName.cpp:60,看到这个要知道是符号扩展64位,加上参数可以确定160是arr 00007FF6E936190B | 48:634D 64 | movsxd rcx,dword ptr ss:[rbp+64] | 00007FF6E936190F | 48:8B95 60010000 | mov rdx,qword ptr ss:[rbp+160] | 00007FF6E9361916 | 4C:8B85 60010000 | mov r8,qword ptr ss:[rbp+160] | 00007FF6E936191D | 41:8B0C88 | mov ecx,dword ptr ds:[r8+rcx*4] | 00007FF6E9361921 | 390C82 | cmp dword ptr ds:[rdx+rax*4],ecx | arr[i] == arr[j] 00007FF6E9361924 | 75 09 | jne project1.7FF6E936192F | 如果不等于跳出当前循环,继续下轮检测 00007FF6E9361926 | C745 44 00000000 | mov dword ptr ss:[rbp+44],0 | FileName.cpp:62,注意看某个临时变量赋值为0了,之前的值 temp = 1, 现在的值 temp = 0 00007FF6E936192D | EB 02 | jmp project1.7FF6E9361931 | FileName.cpp:63 00007FF6E936192F | EB C6 | jmp project1.7FF6E93618F7 | FileName.cpp:65 00007FF6E9361931 | 837D 44 00 | cmp dword ptr ss:[rbp+44],0 | FileName.cpp:66,如果temp = 0 00007FF6E9361935 | 74 25 | je project1.7FF6E936195C | 如果等于0跳出循环 00007FF6E9361937 | 48:6345 24 | movsxd rax,dword ptr ss:[rbp+24] | FileName.cpp:67,上面理解以后下面应该很好理解了 00007FF6E936193B | 48:634D 04 | movsxd rcx,dword ptr ss:[rbp+4] | 00007FF6E936193F | 48:8B95 60010000 | mov rdx,qword ptr ss:[rbp+160] | 00007FF6E9361946 | 4C:8B85 60010000 | mov r8,qword ptr ss:[rbp+160] | 00007FF6E936194D | 41:8B0480 | mov eax,dword ptr ds:[r8+rax*4] | 00007FF6E9361951 | 89048A | mov dword ptr ds:[rdx+rcx*4],eax | arr[第一个局部变量++] = arr[i] 00007FF6E9361954 | 8B45 04 | mov eax,dword ptr ss:[rbp+4] | 00007FF6E9361957 | FFC0 | inc eax | 00007FF6E9361959 | 8945 04 | mov dword ptr ss:[rbp+4],eax | 00007FF6E936195C | E9 73FFFFFF | jmp project1.7FF6E93618D4 | FileName.cpp:68 00007FF6E9361961 | 8B45 04 | mov eax,dword ptr ss:[rbp+4] | FileName.cpp:69 00007FF6E9361964 | 48:8DA5 48010000 | lea rsp,qword ptr ss:[rbp+148] | FileName.cpp:70 00007FF6E936196B | 5F | pop rdi | 00007FF6E936196C | 5D | pop rbp | 00007FF6E936196D | C3 | ret | Release版本 优化的不成样了,不再使用rbp寄存器做修正。直接用 rsp + 偏移。 00007FF6C4C411E0 | 48:895C24 08 | mov qword ptr ss:[rsp+8],rbx | FileName.cpp:73,保存旧的rbx寄存器 00007FF6C4C411E5 | 57 | push rdi | 00007FF6C4C411E6 | 48:83EC 50 | sub rsp,50 | 开辟空间 00007FF6C4C411EA | 48:8B05 0F2E0000 | mov rax,qword ptr ds:[<__security_cookie>] | rax:mainCRTStartup,防溢出Cookie 00007FF6C4C411F1 | 48:33C4 | xor rax,rsp | rax:mainCRTStartup 00007FF6C4C411F4 | 48:894424 48 | mov qword ptr ss:[rsp+48],rax | rax:mainCRTStartup 00007FF6C4C411F9 | 66:0F6F05 4F200000 | movdqa xmm0,xmmword ptr ds:[<__xmm@00000008000000010000000900000003> | FileName.cpp:74,看到这个可以取得有个数组[3, 9, 1, 8, 4, 7, 2, 5,按照之前学习应该还有几个一会找一下]; 00007FF6C4C41201 | 48:8D0D F81F0000 | lea rcx,qword ptr ds:[<"max=%d min=%d\n"...>] | FileName.cpp:78, 00007FF6C4C43200:"max=%d min=%d\n" 00007FF6C4C41208 | 66:0F6F0D 30200000 | movdqa xmm1,xmmword ptr ds:[<__xmm@00000005000000020000000700000004> | 00007FF6C4C41210 | BA 09000000 | mov edx,9 | 09:'\t',这些写死的值,应该是编译器提前算好的了 00007FF6C4C41215 | 41:B8 01000000 | mov r8d,1 | 00007FF6C4C4121B | C74424 40 09000000 | mov dword ptr ss:[rsp+40],9 | 09:'\t',这里应该就是剩下的两个数组元素了, 【3, 9, 1, 8, 4, 7, 2, 5, 9, 6】 00007FF6C4C41223 | F3:0F7F4424 20 | movdqu xmmword ptr ss:[rsp+20],xmm0 | 00007FF6C4C41229 | C74424 44 06000000 | mov dword ptr ss:[rsp+44],6 | 00007FF6C4C41231 | F3:0F7F4C24 30 | movdqu xmmword ptr ss:[rsp+30],xmm1 | 00007FF6C4C41237 | E8 D4FDFFFF | call <project1.printf> | 然后直接printf大于max和min 00007FF6C4C4123C | 45:33D2 | xor r10d,r10d | 这个应该就是第二个函数开始了 00007FF6C4C4123F | 90 | nop | 00007FF6C4C41240 | 41:B9 09000000 | mov r9d,9 | FileName.cpp:80, 09:'\t',某个参数 = 9 00007FF6C4C41246 | 33C0 | xor eax,eax | FileName.cpp:77,eax = 0 00007FF6C4C41248 | 45:2BCA | sub r9d,r10d | FileName.cpp:80,9 - 0 00007FF6C4C4124B | 45:85C9 | test r9d,r9d | <= 0 00007FF6C4C4124E | 7E 1E | jle project1.7FF6C4C4126E | 00007FF6C4C41250 | 8B5484 24 | mov edx,dword ptr ss:[rsp+rax*4+24] | edx = arr[j + 1] 00007FF6C4C41254 | 44:8B4484 20 | mov r8d,dword ptr ss:[rsp+rax*4+20] | r8d = arr[j] 00007FF6C4C41259 | 44:3BC2 | cmp r8d,edx | 比较 arr[j] 和 arr[j + 1] 00007FF6C4C4125C | 7E 09 | jle project1.7FF6C4C41267 | 如果成立,不交换,跳去j++ 00007FF6C4C4125E | 895484 20 | mov dword ptr ss:[rsp+rax*4+20],edx | 这里应该就是交换逻辑了 00007FF6C4C41262 | 44:894484 24 | mov dword ptr ss:[rsp+rax*4+24],r8d | 00007FF6C4C41267 | FFC0 | inc eax | 00007FF6C4C41269 | 41:3BC1 | cmp eax,r9d | 00007FF6C4C4126C | 7C E2 | jl project1.7FF6C4C41250 | 00007FF6C4C4126E | 41:FFC2 | inc r10d | 00007FF6C4C41271 | 41:83FA 09 | cmp r10d,9 | 09:'\t' 00007FF6C4C41275 | 7C C9 | jl project1.7FF6C4C41240 | 00007FF6C4C41277 | 48:8D0D 921F0000 | lea rcx,qword ptr ds:[<"Sorted array: "...>] | FileName.cpp:81, 00007FF6C4C43210:"Sorted array: " 00007FF6C4C4127E | E8 8DFDFFFF | call <project1.printf> | 打印就好 00007FF6C4C41283 | 33DB | xor ebx,ebx | ebx = 0 00007FF6C4C41285 | 666666:0F1F8400 00000000 | nop word ptr ds:[rax+rax],ax | 空指令 00007FF6C4C41290 | 8B549C 20 | mov edx,dword ptr ss:[rsp+rbx*4+20] | FileName.cpp:83, arr[ebx] 00007FF6C4C41294 | 48:8D0D 851F0000 | lea rcx,qword ptr ds:[<"%d "...>] | 00007FF6C4C43220:"%d " 00007FF6C4C4129B | E8 70FDFFFF | call <project1.printf> | 开始打印 00007FF6C4C412A0 | 48:FFC3 | inc rbx | rbx++ 00007FF6C4C412A3 | 48:83FB 0A | cmp rbx,A | 0A:'\n',比较10 00007FF6C4C412A7 | 75 E7 | jne project1.7FF6C4C41290 | 如果没有到10 继续上面操作 00007FF6C4C412A9 | 48:8D4C24 20 | lea rcx,qword ptr ss:[rsp+20] | FileName.cpp:85 00007FF6C4C412AE | E8 BDFDFFFF | call <project1.int __cdecl deduplicate(int *, int)> | 下面是另一个函数 00007FF6C4C412B3 | 48:8D0D 6E1F0000 | lea rcx,qword ptr ds:[<"\nDeduplicated array: "...>] | FileName.cpp:86, 00007FF6C4C43228:"\nDeduplicated array: " 00007FF6C4C412BA | 8BF8 | mov edi,eax | 00007FF6C4C412BC | E8 4FFDFFFF | call <project1.printf> | 00007FF6C4C412C1 | 33DB | xor ebx,ebx | 00007FF6C4C412C3 | 85FF | test edi,edi | FileName.cpp:87 00007FF6C4C412C5 | 7E 1F | jle project1.7FF6C4C412E6 | 00007FF6C4C412C7 | 66:0F1F8400 00000000 | nop word ptr ds:[rax+rax],ax | 00007FF6C4C412D0 | 8B549C 20 | mov edx,dword ptr ss:[rsp+rbx*4+20] | FileName.cpp:88 00007FF6C4C412D4 | 48:8D0D 451F0000 | lea rcx,qword ptr ds:[<"%d "...>] | 00007FF6C4C43220:"%d " 00007FF6C4C412DB | E8 30FDFFFF | call <project1.printf> | 00007FF6C4C412E0 | FFC3 | inc ebx | 00007FF6C4C412E2 | 3BDF | cmp ebx,edi | 00007FF6C4C412E4 | 7C EA | jl project1.7FF6C4C412D0 | 00007FF6C4C412E6 | 33C0 | xor eax,eax | FileName.cpp:91 00007FF6C4C412E8 | 48:8B4C24 48 | mov rcx,qword ptr ss:[rsp+48] | 00007FF6C4C412ED | 48:33CC | xor rcx,rsp | 00007FF6C4C412F0 | E8 1B000000 | call <project1.__security_check_cookie> | 00007FF6C4C412F5 | 48:8B5C24 60 | mov rbx,qword ptr ss:[rsp+60] | 00007FF6C4C412FA | 48:83C4 50 | add rsp,50 | 00007FF6C4C412FE | 5F | pop rdi | 00007FF6C4C412FF | C3 | ret |
3)还原的C语言代码
咱们会带着各位简单过一遍哦,关键部分会标注好,剩下部分请自己逆向分析并还原成C语言,考虑到一些初学者还是不很理解,下面是部分伪代码和说明,实际代码不写哦,Release版本优化很多地方,如果Release不好理解,可以参考Debug版本的汇编进行代码还原操作。
通过上面的汇编分析,咱们可以知道这几个点
有三个函数
void test(四个参数,有三个指针变量)
{
}
void test1(俩个参数,有一个指针变量)
{
}
void test2(俩个参数,有一个指针变量)
{
}
main()
{
}
反向分析C语言代码可以在这里自行下载哦(前提你自己实现过一遍了),大部分都是过去帮国外客户授课,指导,教学时候给的内容,如果接触过国外计算机相关的内容应该知道CS,Assignment,CITS2002-SIFS,COMP 3350-Computer Organization and Assembly Language等等。正向咱们写的够多了吧这1年,各种汇编,c/c++,那如果反正来呢?把某个语言下写的功能用其他语言来实现(很多人叫代码还原,代码互换,咱们用自己的话来理解一下,代码还原是从汇编反向推导出程序原本的c/c++逻辑,想要完整还原一模一样的源码其实很难实现特别release大量优化的时候,但咱们做逆向重点不是拿源码,而是吃透整套软件实现流程就行。原因很简单咱们只要吃透了软件整体逻辑和各个功能的底层原理,咱们完全能用任意编程语言重新实现相同功能,就算只用汇编也能单独复刻程序里某一个模块。咱们日常开发无非就是CRUD,各种业务,各种功能,各种算法,各种数据结构,各种网络通信,各种第三方库调用,WinAPI调用,各种加密,各种模块,各种新的开发框架,各种套路对吧???)!
https://download.csdn.net/download/Strategic__/93160029