用调试器结构和替换iat表来对writefile函数来hook
1 |
|
1 |
|
这两个实现的效果都是在文件保存后中的英文字母大小写互换
1 | #include "pch.h" |
1 | #include<stdio.h> |
这两个实现的效果都是在文件保存后中的英文字母大小写互换
练一下手脱upx+用esp定律加scylla的方法

直接在内存中打开栈顶并下硬件断点4字节的访问断点然后运行(原理就是upx解压相当于一个函数开头会压入栈结尾会输出栈)

再用scylla来dump和修复
依次是dump将内存中的代码复制下来
再是iat autosearch 再到iat表中每个地址对应得是那个dll中得函数
然后get imports来建立函数和dll中导出表得关系
最后fixdunmp重新来填入INT让程序可以运行

顺便解密下这个题

这个头就是解密完了但是upx的效验头其中byte_401171就是主函数

这里是一个常见的花指令由于一定会跳到loc_409030+1去执行后面的

loc_409030位置的E8是call命令就形成干扰直接nop加u c p一下重新编译


找到主函数和地图起点终点得答案



打开程序可以看到逻辑非常简单但是sub_7FF7C68B1320很明显是scanf函数答案又不可能是scuctf{fakeflag}
所以猜测有stl反调
打开TlsDirectory查看回调函数


这里就是两个个反调试

这个是挂钩函数的主函数这里nop掉前面所有反函数后就可以看到
v5的值

unk_14001D178的值

可以看到它将_imp_strcmp指向的ucrtbased_strcmp放在了unk_14001D178上

这个是sub_14001132A(func1)函数的主要逻辑
其中sub_7FF628F611FE()的逻辑就是下面的那张图将unk_14001D178保存的值还给_imp_strcmp指向地方
而下面的就是rc4加密然后再调用__imp_memcmp


这张图是sub_7FF628F61046()的主要逻辑就是将__imp_memcmp指向的值换为sub_14001132A(func1)函数
总结一下这个题的逻辑就是在tls中将j_strcmp钩取然后在主程序使用时调用先还原j_strcmp然后rc4加密再用还原的j_strcmp将密文和下面j_strcmp真正的密钥对比,而scuctf{fakeflag}时密钥。

密文就是unk_7FF628F6D120( 0x98, 0x11, 0xA1, 0x15, 0x1B, 0xFA, 0x99, 0xAB, 0xAF, 0xEA, 0x63, 0xCB, 0xB3, 0x98, 0x52, 0x1C,
0x0D, 0xD5, 0xCE, 0xDA, 0xC4, 0xAF, 0x07, 0xA3, 0x4F, 0xB2, 0x65, 0x99, 0x15, 0x8A, 0xE1, 0x02, 0x4C, 0x3A, 0x1A, 0x69, 0x86, 0xCD, 0x56, 0x9C, 0xCA, 0x2C, 0x40, 0x4A)
由于这个rc4加密将交换改为了异或交换使得它不能用在线工具接的这是解密脚本
1 | #include<stdio.h> |


这是主函数
先用findcrypt扫描算法发现有aes和base64编码并回溯找到aes函数位置为sub_4020D0(Block, (int)v22)

接着运行调试时进入sub_402320(ModuleHandleW)发现有int3断点

打开汇编流程图分析

左边的为seh函数右边为主程序
并且在右边发现了注册she函数的汇编


其中seh结构体中loc_4023EF就是主程序中左边的seh函数

这个程序在注册seh后在下面的DebugBreak()会强制出发int3断点然后交给调试器进而反调试我们调试的时候可以选择交给程序自己解决来跳过
在seh中有个函数sub_402450


这其中就是smc代码对每个字节和aSycloversyclov中的字符循环加密然后取反
至于smc的区域肯定在后面可以找到

这个函数中return ((int (*)(void))dword_404000[0])();就是很明显的smc代码

我们用ida脚本来还原

重新编码后可以看到

这是一个对aPvfqyc4ttc2uxr每个字符减一再逆序的函数

解密后可以得到nKnbHsgqD3aNEB91jB3gEzAr+IklQwT1bSs3+bXpeuo=
这很明显是一个base64加密后的结果
结合前面的加密函数中两串连续的字符


推测是aes的cbc模式

解密拿到答案sctf{Ae3_C8c_I28_pKcs79ad4}

这个题打开就知道是ollvm混淆但是用d—810就是去不掉混淆找不到主要逻辑但是我动调拿到了密钥,那个出题人说这个题的密钥生成是根据注册码来的。

可以看到最后v78和v77对比,然而v78是有Src来的然后就直接动调拿答案


1 | HANDLE X1(char* x, unsigned char** pbuf) |
1 | PIMAGE_DOS_HEADER DOS = (PIMAGE_DOS_HEADER)pbuf; |
1 | DWORD SectionNumber = NT->FileHeader.NumberOfSections; |
1 | if (NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0) |
相关加载重定位表的理解可以看这篇文章:https://blog.csdn.net/Apollon_krj/article/details/77370452
1 | PIMAGE_IMPORT_DESCRIPTOR IntImport = (PIMAGE_IMPORT_DESCRIPTOR)(NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (PCHAR)Alloc); |
相关填充IAT表的理解可以看这篇文章:https://blog.csdn.net/qq_35289660/article/details/107329444
1 | FARPROC EOP = (FARPROC)((LPBYTE)Alloc + NT->OptionalHeader.AddressOfEntryPoint);//修改程序入口点 |
peloader的加载原理就是将pe文件的内容读取后根据当中的VirtualAddress将内容分配到申请空间里,然后执行。(下面是所有代码)
1 | #define _CRT_SECURE_NO_WARNINGS |