VSCode IDAPython 开发环境配置

VSCode IDAPython 开发环境配置

开发工具 版本
OS Windows 10
IDA Version IDA Pro 7.5
IDACode(VSCode插件) 0.3.0

0x00 安装IDACode插件

仓库地址 https://github.com/ioncodes/idacode

VSCode中的安装

直接在vscode的插件商店中搜索idacode安装即可

IDA中的安装

  1. 将代码仓库中的/ida目录下所有文件复制到本地IDA的plugins目录下,例如我的是C:\Program Files\IDA 7.5\plugins
  2. 修改上述文件夹中的idacode_utils/settings.py, 端口如无特殊需求保持默认即可, PYTHON设置为本地IDA所使用的python解释器, 例如我使用的是conda的默认解释器: PYTHON = "C:\\ProgramData\\Miniconda3\\python.exe"
  3. 给本地IDA所使用的python解释器安装依赖:python -m pip install --user debugpy tornado
  4. 此时重启IDA, 在插件目录中应该可以看到IDACode这一项, 单击可以看到output栏中有[IDACode] Listening on 127.0.0.1:7065的输出, 说明安装成功

0x01 配置VSCode开发环境

自动补全配置

在vscode的settings(json)中把本地IDA Python库的位置添加进去, 例如我的是 C:\Program Files\IDA 7.5\python\3, 则settings中添加的项为:

"python.autoComplete.extraPaths": [
   "C:\\Program Files\\IDA 7.5\\python\\3"
],
"python.analysis.extraPaths": [
   "C:\\Program Files\\IDA 7.5\\python\\3"
],

此时把ida相关的包import进来就可以用自动补全了:

image.png

连接VSCode与IDA


在VSCode中按下Ctrl+Shift+P呼出命令面板, 输入ida, 可以发现有4个命令:

image.png

可以看出来主要支持的功能是 执行当前脚本, 连接到IDA, 启动调试器.

当第一次打开IDA的时候, 需要在这里执行Connect to IDA, 会要求设定工作目录, 一般就是当前目录, 比如我的是c:\Users\hp\Desktop\se\Reverse\IDAPythonScripts. 执行完之后会看到IDA中输出:

[IDACode] Client connected
[IDACode] Set workspace folder to c:\Users\hp\Desktop\se\Reverse\IDAPythonScripts
[IDACode] IDACode debug server listening on 127.0.0.1:7066

此时就可以用Execute script in IDA来执行脚本了. 注意这里插件默认设置的是Ctrl+S时会自动在IDA中执行脚本, 由于我本人不太喜欢这种模式所以取消了这个选项:

image.png

启动调试

IDACode插件带来的最大的便利就是可以在VSCode中调试IDAPython脚本. 首先在工作目录的.vscode文件夹下, 新建launch.json, 修改内容为:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: 远程连接",
            "type": "python",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 7066
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "."
                }
            ]
        }
    ]
}

上面这一步并不是必须的,IDACode本身的attach debugger是可以直接启动一个调试进程的. 但是作者也在repo里提到了, 直接attach debugger to IDA的话, 每次都会启动一个新的debug session, 这在大多数情况下都不是我们想要的 (没人想每次都重启IDA吧). 所以这里用了VSCode自身的远程调试配置

确保之前的连接到IDA的步骤都走完了之后, 我们在自己写的IDAPython脚本中需要打断点的地方手动加上breakpoint()(这里直接在文件上点小红点打断点会出bug, 目前还没有看到解决方案), 然后在vscode中按F5开始调试, 此时看到VSCode的调试界面出现就说明启动成功了:

image.png

但是此时我们仅仅是连接到了调试器, 并没有开始run我们的脚本. 下一步, 我们Ctrl+Shift+P然后使用Execute Script in IDA来开始执行 (如果你配置的是Ctrl+S自动执行的话不需要这一步), 然后就可以在VSCode中看到一个正常的Python调试界面了:

image.png

如上图所示, 可以在左侧调试窗口中看到变量值和调用栈等调试信息. 跟在VSCode中调试普通的Python脚本相同, 可以用F10 F11来单步调试.

注意, 跟javascript不同, debugger会停在breakpoint()的下一行

0x02 测试

题目: 西湖论剑2021 Reverse TacticalArmed

过反调试

主要是检测current process,如果有被调试则无限createprocess,会卡死

  1. 首先patch掉sub_401070中启动401160的部分
  2. 然后在00401649下断,把 0x405AC4 patch为1 0x405644 patch为1
  3. 取消00401649的断点,然后就可以正常调试了

逻辑

每次lpAddress指向一个几行汇编的函数,然后sub_4011F0会调整这个函数内的代码,每次的0040146D call [ebp+var_8]是真正在执行对Str操作的代码 把每次执行的代码搞出来看看

image.png

import sys
import struct
import idc
import idaapi
import idautils

size_addr = 0x405220
des = 0x8000000
src = 0x405010
size = idautils.ida_bytes.get_dword(size_addr)
v14=0
v16=0

def sub_4011F0(a1, a2, a3, size):
    pos = 0
    tmp_byte = 0
    while True:
        tmp_byte = idautils.ida_bytes.get_byte(a1+pos)
        if tmp_byte == 0:
            break
        pos+=1

    table_elem = idautils.ida_bytes.get_dword(0x4052A8+4 * a2)
    v5 = table_elem % 0x10
    v4 = table_elem >> 4

    ori_ins = idautils.ida_bytes.get_bytes(a1, size)
    patched_data = None
    if v4 == 1:
        patched_data = struct.pack("<I", 4 * (v5 + 2 * a3) + 4216392)
    if v4 == 2:
        patched_data = struct.pack("<I", 4 * v5 + 4214784)
    if v4 == 3:
        patched_data = struct.pack("<I", 0x405748)

    if patched_data != None:
        return ori_ins[:pos] + patched_data + ori_ins[pos+4:]
    return ori_ins

set_ebp_esp = b"\x55\x8B\xEC"
idautils.ida_bytes.patch_bytes(des,set_ebp_esp)
des+=len(set_ebp_esp)

while size!=0 :
    breakpoint()
    patched_data = sub_4011F0(src, v14, 0, size)
    # print(patched_data)
    idautils.ida_bytes.patch_bytes(des,patched_data)
    des+=size
    v14+=1
    src+=16
    size_addr += 4
    size = idautils.ida_bytes.get_dword(size_addr)

ret_ebp_esp = b"\x5D\xC3"
idautils.ida_bytes.patch_bytes(des, ret_ebp_esp)


breakpoint()
print("END")

搞出来就是个TEA:

int sub_8000000()
{
  int result; // eax

  dword_405748 -= 0x7E5A96D2;
  *Str += (16 * *&Str[4] + dword_405000) ^ (*&Str[4] + dword_405748) ^ ((*&Str[4] >> 5) + dword_405004[0]);
  result = ((16 * *Str + dword_405004[1]) ^ (*Str + dword_405748) ^ ((*Str >> 5) + dword_405004[2])) + *&Str[4];
  *&Str[4] = result;
  return result;
}

image.png

动态调试拿一下密钥, 然后写一下解密函数就可以了:

#include <stdint.h>
#include <cstdio>


uint32_t sum=0;
void encrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], i;           /* set up */
    uint32_t delta=0x7E5A96D2;//0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i < 33; i++) {                       /* basic cycle start */
        sum -= delta;
        v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);  
        // printf("%x\n", sum);
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
    printf("%x\n", sum);
}

void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], i;  /* set up */
    uint32_t delta=0x7E5A96D2;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<33; i++) {                         /* basic cycle start */
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum += delta;                                   
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}

// dword_405000 dd 22836719h               ; DATA XREF: StartAddress+4C↑w
// .data:00405000                                         ; StartAddress+5E↑w ...
// .data:00405004 dd 0A5978C21h
// .data:00405008 dd 79573824h
// .data:0040500C dd 330B55EFh

int main(){
    // uint32_t k[4]={0x7CE45630, 0x58334908, 0x66398867, 0x0C35195B1};//{0x22836719, 0x0A5978C21, 0x79573824, 0x330B55EF};
    // uint32_t v0[2]={0x30303030, 0x30303030};
    // encrypt(v0,k);
    // printf("%x  %x\n",v0[0], v0[1]);
    // uint32_t v1[2]={0x30303030, 0x30303030};
    // encrypt(v1,k);
    // printf("%x  %x\n",v1[0], v1[1]);
    // uint32_t v2[2]={0x30303030, 0x30303030};
    // encrypt(v2,k);
    // printf("%x  %x\n",v2[0], v2[1]);
    // uint32_t v3[2]={0x30303030, 0x30303030};
    // encrypt(v3,k);
    // printf("%x  %x\n",v3[0], v3[1]);
    // uint32_t v4[2]={0x30303030, 0x30303030};
    // encrypt(v4,k);
    // printf("%x  %x\n",v4[0], v4[1]);


    sum=0x8f9ccaa6;
    uint32_t k[4]={0x7CE45630, 0x58334908, 0x66398867, 0x0C35195B1};
    uint32_t ct[10] = {0x422F1DED, 0x1485E472, 0x35578D5, 0x0BF6B80A2, 0x97D77245, 0x2DAE75D1, 0x665FA963, 0x292E6D74, 0x9795FCC1, 0x0BB5C8E9};

    for (int i=4;i>=0;i--){
        uint32_t v0[2]={ct[2*i], ct[2*i+1]};
        // printf("%x\n",v0[0]);
        // printf("%x\n",v0[1]);
        decrypt(v0,k);
        printf("%x  %x\n",v0[0], v0[1]);
        // printf("%x\n", sum);
    }

}
/*

flag=[0x72,0x50,0x53,0x48,
0x4b,0x35,0x35,0x6e
0x43,0x7a,0x4c,0x6e,
0x46,0x72,0x5f,0x4a
0x47,0x69,0x58,0x65,
0x71,0x61,0x5f,0x38
0x32,0x61,0x47,0x79,
0x41,0x69,0x6f,0x72
0x31,0x44,0x67,0x6b,
0x32,0x42,0x67,0x6f]
*/

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据